Adding generated code for service chaining
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / common.go
1 package ovn
2
3 import (
4         "encoding/json"
5         "fmt"
6         "github.com/vishvananda/netlink"
7         "math/big"
8         "math/rand"
9         "net"
10         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
11         "strings"
12         "time"
13 )
14
15 var log = logf.Log.WithName("ovn")
16
17 // CreateVlan creates VLAN with vlanID
18 func CreateVlan(vlanID, interfaceName, logicalInterfaceName string) error {
19         if interfaceName == "" || vlanID == "" || logicalInterfaceName == "" {
20                 return fmt.Errorf("CreateVlan invalid parameters: %v %v %v", interfaceName, vlanID, logicalInterfaceName)
21         }
22         _, err := netlink.LinkByName(logicalInterfaceName)
23         if err == nil {
24                 return err
25         }
26         stdout, stderr, err := RunIP("link", "add", "link", interfaceName, "name", logicalInterfaceName, "type", "vlan", "id", vlanID)
27         if err != nil {
28                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
29                 return err
30         }
31         stdout, stderr, err = RunIP("link", "set", logicalInterfaceName, "alias", "nfn-"+logicalInterfaceName)
32         if err != nil {
33                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
34                 return err
35         }
36         return nil
37 }
38
39 // DeleteVlan deletes VLAN with logicalInterface Name
40 func DeleteVlan(logicalInterfaceName string) error {
41         if logicalInterfaceName == "" {
42                 return fmt.Errorf("DeleteVlan invalid parameters")
43         }
44         stdout, stderr, err := RunIP("link", "del", "dev", logicalInterfaceName)
45         if err != nil {
46                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
47                 return err
48         }
49         return nil
50 }
51
52 // GetVlan returns a list of VLAN configured on the node
53 func GetVlan() []string {
54         var intfList []string
55         links, err := netlink.LinkList()
56         if err != nil {
57
58         }
59         for _, l := range links {
60                 if strings.Contains(l.Attrs().Alias, "nfn-") {
61                         intfList = append(intfList, l.Attrs().Name)
62                 }
63         }
64         return intfList
65 }
66
67 // CreatePnBridge creates Provider network bridge and mappings
68 func CreatePnBridge(nwName, brName, intfName string) error {
69         if nwName == "" || brName == "" || intfName == "" {
70                 return fmt.Errorf("CreatePnBridge invalid parameters")
71         }
72         // Create Bridge
73         stdout, stderr, err := RunOVSVsctl("--may-exist", "add-br", brName)
74         if err != nil {
75                 log.Error(err, "Failed to create Bridge", "stdout", stdout, "stderr", stderr)
76                 return err
77         }
78         stdout, stderr, err = RunOVSVsctl("--may-exist", "add-port", brName, intfName)
79         if err != nil {
80                 log.Error(err, "Failed to add port to Bridge", "stdout", stdout, "stderr", stderr)
81                 return err
82         }
83         stdout, stderr, err = RunOVSVsctl("set", "bridge", brName, "external_ids:nfn="+nwName)
84         if err != nil {
85                 log.Error(err, "Failed to set nfn-alias", "stdout", stdout, "stderr", stderr)
86                 return err
87         }
88         // Update ovn-bridge-mappings
89         updateOvnBridgeMapping(brName, nwName, "add")
90         return nil
91 }
92
93 // DeletePnBridge creates Provider network bridge and mappings
94 func DeletePnBridge(nwName, brName string) error {
95         if nwName == "" || brName == "" {
96                 return fmt.Errorf("DeletePnBridge invalid parameters")
97         }
98         // Delete Bridge
99         stdout, stderr, err := RunOVSVsctl("--if-exist", "del-br", brName)
100         if err != nil {
101                 log.Error(err, "Failed to delete Bridge", "stdout", stdout, "stderr", stderr)
102                 return err
103         }
104         updateOvnBridgeMapping(brName, nwName, "delete")
105
106         return nil
107 }
108
109 // GetPnBridge returns Provider networks with external ids
110 func GetPnBridge(externalID string) []string {
111         if externalID == "" {
112                 log.Error(fmt.Errorf("GetBridge invalid parameters"), "Invalid")
113         }
114         stdout, stderr, err := RunOVSVsctl("list-br")
115         if err != nil {
116                 log.Error(err, "No bridges found", "stdout", stdout, "stderr", stderr)
117                 return nil
118         }
119         brNames := strings.Split(stdout, "\n")
120         var brList []string
121         for _, name := range brNames {
122                 stdout, stderr, err = RunOVSVsctl("get", "bridge", name, "external_ids:"+externalID)
123                 if err != nil {
124                         if !strings.Contains(stderr, "no key") {
125                                 log.Error(err, "Unknown error reading external_ids", "stdout", stdout, "stderr", stderr)
126                         }
127                         continue
128                 }
129                 if stdout == "" {
130                         continue
131                 } else {
132                         brList = append(brList, name)
133                 }
134         }
135         return brList
136 }
137
138 // Update ovn-bridge-mappings
139 func updateOvnBridgeMapping(brName, nwName, action string) error {
140         stdout, stderr, err := RunOVSVsctl("get", "open", ".", "external-ids:ovn-bridge-mappings")
141         if err != nil {
142                 if !strings.Contains(stderr, "no key") {
143                         log.Error(err, "Failed to get ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
144                         return err
145                 }
146         }
147         // Convert csv string to map
148         mm := make(map[string]string)
149         if len(stdout) > 0 {
150                 am := strings.Split(stdout, ",")
151                 for _, label := range am {
152                         l := strings.Split(label, ":")
153                         if len(l) == 0 {
154                                 log.Error(fmt.Errorf("Syntax error label: %v", label), "ovnBridgeMapping")
155                                 return nil
156                         }
157                         mm[strings.TrimSpace(l[0])] = strings.TrimSpace(l[1])
158                 }
159         }
160         if action == "add" {
161                 mm[nwName] = brName
162         } else if action == "delete" {
163                 delete(mm, nwName)
164                 if len(mm) == 0 {
165                         // No mapping needed
166                         stdout, stderr, err = RunOVSVsctl("remove", "open", ".", "external-ids", "ovn-bridge-mappings")
167                         if err != nil {
168                                 log.Error(err, "Failed to remove ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
169                                 return err
170                         }
171                         return nil
172                 }
173         } else {
174                 return fmt.Errorf("Invalid action %s", action)
175         }
176         var mapping string
177         for key, value := range mm {
178                 mapping = mapping + fmt.Sprintf("%s:%s,", key, value)
179         }
180         // Remove trailing ,
181         mapping = mapping[:len(mapping)-1]
182         extIDMap := "external-ids:ovn-bridge-mappings=" + mapping
183
184         stdout, stderr, err = RunOVSVsctl("set", "open", ".", extIDMap)
185         if err != nil {
186                 log.Error(err, "Failed to set ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
187                 return err
188         }
189         return nil
190 }
191
192 func parseOvnNetworkObject(ovnnetwork string) ([]map[string]interface{}, error) {
193         var ovnNet []map[string]interface{}
194
195         if ovnnetwork == "" {
196                 return nil, fmt.Errorf("parseOvnNetworkObject:error")
197         }
198
199         if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
200                 return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
201         }
202
203         return ovnNet, nil
204 }
205
206 func setupDistributedRouter(name string) error {
207
208         // Create a single common distributed router for the cluster.
209         stdout, stderr, err := RunOVNNbctl("--", "--may-exist", "lr-add", name, "--", "set", "logical_router", name, "external_ids:ovn4nfv-cluster-router=yes")
210         if err != nil {
211                 log.Error(err, "Failed to create a single common distributed router for the cluster", "stdout", stdout, "stderr", stderr)
212                 return err
213         }
214         // Create a logical switch called "ovn4nfv-join" that will be used to connect gateway routers to the distributed router.
215         // The "ovn4nfv-join" will be allocated IP addresses in the range 100.64.1.0/24.
216         stdout, stderr, err = RunOVNNbctl("--may-exist", "ls-add", "ovn4nfv-join")
217         if err != nil {
218                 log.Error(err, "Failed to create logical switch called \"ovn4nfv-join\"", "stdout", stdout, "stderr", stderr)
219                 return err
220         }
221         // Connect the distributed router to "ovn4nfv-join".
222         routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtoj-"+name, "mac")
223         if err != nil {
224                 log.Error(err, "Failed to get logical router port rtoj-", "name", name, "stdout", stdout, "stderr", stderr)
225                 return err
226         }
227         if routerMac == "" {
228                 routerMac = generateMac()
229                 stdout, stderr, err = RunOVNNbctl("--", "--may-exist", "lrp-add", name, "rtoj-"+name, routerMac, "100.64.1.1/24", "--", "set", "logical_router_port", "rtoj-"+name, "external_ids:connect_to_ovn4nfvjoin=yes")
230                 if err != nil {
231                         log.Error(err, "Failed to add logical router port rtoj", "name", name, "stdout", stdout, "stderr", stderr)
232                         return err
233                 }
234         }
235         // Connect the switch "ovn4nfv-join" to the router.
236         stdout, stderr, err = RunOVNNbctl("--", "--may-exist", "lsp-add", "ovn4nfv-join", "jtor-"+name, "--", "set", "logical_switch_port", "jtor-"+name, "type=router", "options:router-port=rtoj-"+name, "addresses="+"\""+routerMac+"\"")
237         if err != nil {
238                 log.Error(err, "Failed to add logical switch port to logical router", "stdout", stdout, "stderr", stderr)
239                 return err
240         }
241         return nil
242 }
243
244 // generateMac generates mac address.
245 func generateMac() string {
246         prefix := "00:00:00"
247         newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
248         mac := fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
249         return mac
250 }
251
252 // NextIP returns IP incremented by 1
253 func NextIP(ip net.IP) net.IP {
254         i := ipToInt(ip)
255         return intToIP(i.Add(i, big.NewInt(1)))
256 }
257
258 func ipToInt(ip net.IP) *big.Int {
259         if v := ip.To4(); v != nil {
260                 return big.NewInt(0).SetBytes(v)
261         }
262         return big.NewInt(0).SetBytes(ip.To16())
263 }
264
265 func intToIP(i *big.Int) net.IP {
266         return net.IP(i.Bytes())
267 }