0053e562bc8025639a138c9fe0ff9136f38a8d66
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / common.go
1 /*
2  * Copyright 2020 Intel Corporation, Inc
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package ovn
18
19 import (
20         "encoding/json"
21         "fmt"
22         "github.com/vishvananda/netlink"
23         "math/big"
24         "math/rand"
25         "net"
26         logf "sigs.k8s.io/controller-runtime/pkg/runtime/log"
27         "strings"
28         "time"
29 )
30
31 var log = logf.Log.WithName("ovn")
32
33 // CreateVlan creates VLAN with vlanID
34 func CreateVlan(vlanID, interfaceName, logicalInterfaceName string) error {
35         if interfaceName == "" || vlanID == "" || logicalInterfaceName == "" {
36                 return fmt.Errorf("CreateVlan invalid parameters: %v %v %v", interfaceName, vlanID, logicalInterfaceName)
37         }
38         _, err := netlink.LinkByName(logicalInterfaceName)
39         if err == nil {
40                 return err
41         }
42         stdout, stderr, err := RunIP("link", "add", "link", interfaceName, "name", logicalInterfaceName, "type", "vlan", "id", vlanID)
43         if err != nil {
44                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
45                 return err
46         }
47         stdout, stderr, err = RunIP("link", "set", logicalInterfaceName, "alias", "nfn-"+logicalInterfaceName)
48         if err != nil {
49                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
50                 return err
51         }
52         stdout, stderr, err = RunIP("link", "set", "dev", logicalInterfaceName, "up")
53         if err != nil {
54                 log.Error(err, "Failed to enable Vlan", "stdout", stdout, "stderr", stderr)
55                 return err
56         }
57         return nil
58 }
59
60 // DeleteVlan deletes VLAN with logicalInterface Name
61 func DeleteVlan(logicalInterfaceName string) error {
62         if logicalInterfaceName == "" {
63                 return fmt.Errorf("DeleteVlan invalid parameters")
64         }
65         stdout, stderr, err := RunIP("link", "del", "dev", logicalInterfaceName)
66         if err != nil {
67                 log.Error(err, "Failed to create Vlan", "stdout", stdout, "stderr", stderr)
68                 return err
69         }
70         return nil
71 }
72
73 // GetVlan returns a list of VLAN configured on the node
74 func GetVlan() []string {
75         var intfList []string
76         links, err := netlink.LinkList()
77         if err != nil {
78
79         }
80         for _, l := range links {
81                 if strings.Contains(l.Attrs().Alias, "nfn-") {
82                         intfList = append(intfList, l.Attrs().Name)
83                 }
84         }
85         return intfList
86 }
87
88 // CreatePnBridge creates Provider network bridge and mappings
89 func CreatePnBridge(nwName, brName, intfName string) error {
90         if nwName == "" || brName == "" || intfName == "" {
91                 return fmt.Errorf("CreatePnBridge invalid parameters")
92         }
93         // Create Bridge
94         stdout, stderr, err := RunOVSVsctl("--may-exist", "add-br", brName)
95         if err != nil {
96                 log.Error(err, "Failed to create Bridge", "stdout", stdout, "stderr", stderr)
97                 return err
98         }
99         stdout, stderr, err = RunOVSVsctl("--may-exist", "add-port", brName, intfName)
100         if err != nil {
101                 log.Error(err, "Failed to add port to Bridge", "stdout", stdout, "stderr", stderr)
102                 return err
103         }
104         stdout, stderr, err = RunOVSVsctl("set", "bridge", brName, "external_ids:nfn="+nwName)
105         if err != nil {
106                 log.Error(err, "Failed to set nfn-alias", "stdout", stdout, "stderr", stderr)
107                 return err
108         }
109         // Update ovn-bridge-mappings
110         updateOvnBridgeMapping(brName, nwName, "add")
111         return nil
112 }
113
114 // DeletePnBridge creates Provider network bridge and mappings
115 func DeletePnBridge(nwName, brName string) error {
116         if nwName == "" || brName == "" {
117                 return fmt.Errorf("DeletePnBridge invalid parameters")
118         }
119         // Delete Bridge
120         stdout, stderr, err := RunOVSVsctl("--if-exist", "del-br", brName)
121         if err != nil {
122                 log.Error(err, "Failed to delete Bridge", "stdout", stdout, "stderr", stderr)
123                 return err
124         }
125         updateOvnBridgeMapping(brName, nwName, "delete")
126
127         return nil
128 }
129
130 // GetPnBridge returns Provider networks with external ids
131 func GetPnBridge(externalID string) []string {
132         if externalID == "" {
133                 log.Error(fmt.Errorf("GetBridge invalid parameters"), "Invalid")
134         }
135         stdout, stderr, err := RunOVSVsctl("list-br")
136         if err != nil {
137                 log.Error(err, "No bridges found", "stdout", stdout, "stderr", stderr)
138                 return nil
139         }
140         brNames := strings.Split(stdout, "\n")
141         var brList []string
142         for _, name := range brNames {
143                 stdout, stderr, err = RunOVSVsctl("get", "bridge", name, "external_ids:"+externalID)
144                 if err != nil {
145                         if !strings.Contains(stderr, "no key") {
146                                 log.Error(err, "Unknown error reading external_ids", "stdout", stdout, "stderr", stderr)
147                         }
148                         continue
149                 }
150                 if stdout == "" {
151                         continue
152                 } else {
153                         brList = append(brList, name)
154                 }
155         }
156         return brList
157 }
158
159 // Update ovn-bridge-mappings
160 func updateOvnBridgeMapping(brName, nwName, action string) error {
161         stdout, stderr, err := RunOVSVsctl("get", "open", ".", "external-ids:ovn-bridge-mappings")
162         if err != nil {
163                 if !strings.Contains(stderr, "no key") {
164                         log.Error(err, "Failed to get ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
165                         return err
166                 }
167         }
168         // Convert csv string to map
169         mm := make(map[string]string)
170         if len(stdout) > 0 {
171                 am := strings.Split(stdout, ",")
172                 for _, label := range am {
173                         l := strings.Split(label, ":")
174                         if len(l) == 0 {
175                                 log.Error(fmt.Errorf("Syntax error label: %v", label), "ovnBridgeMapping")
176                                 return nil
177                         }
178                         mm[strings.TrimSpace(l[0])] = strings.TrimSpace(l[1])
179                 }
180         }
181         if action == "add" {
182                 mm[nwName] = brName
183         } else if action == "delete" {
184                 delete(mm, nwName)
185                 if len(mm) == 0 {
186                         // No mapping needed
187                         stdout, stderr, err = RunOVSVsctl("remove", "open", ".", "external-ids", "ovn-bridge-mappings")
188                         if err != nil {
189                                 log.Error(err, "Failed to remove ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
190                                 return err
191                         }
192                         return nil
193                 }
194         } else {
195                 return fmt.Errorf("Invalid action %s", action)
196         }
197         var mapping string
198         for key, value := range mm {
199                 mapping = mapping + fmt.Sprintf("%s:%s,", key, value)
200         }
201         // Remove trailing ,
202         mapping = mapping[:len(mapping)-1]
203         extIDMap := "external-ids:ovn-bridge-mappings=" + mapping
204
205         stdout, stderr, err = RunOVSVsctl("set", "open", ".", extIDMap)
206         if err != nil {
207                 log.Error(err, "Failed to set ovn-bridge-mappings", "stdout", stdout, "stderr", stderr)
208                 return err
209         }
210         return nil
211 }
212
213 func parseOvnNetworkObject(ovnnetwork string) ([]map[string]interface{}, error) {
214         var ovnNet []map[string]interface{}
215
216         if ovnnetwork == "" {
217                 return nil, fmt.Errorf("parseOvnNetworkObject:error")
218         }
219
220         if err := json.Unmarshal([]byte(ovnnetwork), &ovnNet); err != nil {
221                 return nil, fmt.Errorf("parseOvnNetworkObject: failed to load ovn network err: %v | ovn network: %v", err, ovnnetwork)
222         }
223
224         return ovnNet, nil
225 }
226
227 func setupDistributedRouter(name string) error {
228
229         // Create a single common distributed router for the cluster.
230         stdout, stderr, err := RunOVNNbctl("--", "--may-exist", "lr-add", name, "--", "set", "logical_router", name, "external_ids:ovn4nfv-cluster-router=yes")
231         if err != nil {
232                 log.Error(err, "Failed to create a single common distributed router for the cluster", "stdout", stdout, "stderr", stderr)
233                 return err
234         }
235         // Create a logical switch called "ovn4nfv-join" that will be used to connect gateway routers to the distributed router.
236         // The "ovn4nfv-join" will be allocated IP addresses in the range 100.64.1.0/24.
237         stdout, stderr, err = RunOVNNbctl("--may-exist", "ls-add", "ovn4nfv-join")
238         if err != nil {
239                 log.Error(err, "Failed to create logical switch called \"ovn4nfv-join\"", "stdout", stdout, "stderr", stderr)
240                 return err
241         }
242         // Connect the distributed router to "ovn4nfv-join".
243         routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtoj-"+name, "mac")
244         if err != nil {
245                 log.Error(err, "Failed to get logical router port rtoj-", "name", name, "stdout", stdout, "stderr", stderr)
246                 return err
247         }
248         if routerMac == "" {
249                 routerMac = generateMac()
250                 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")
251                 if err != nil {
252                         log.Error(err, "Failed to add logical router port rtoj", "name", name, "stdout", stdout, "stderr", stderr)
253                         return err
254                 }
255         }
256         // Connect the switch "ovn4nfv-join" to the router.
257         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+"\"")
258         if err != nil {
259                 log.Error(err, "Failed to add logical switch port to logical router", "stdout", stdout, "stderr", stderr)
260                 return err
261         }
262         return nil
263 }
264
265 // CreateNetwork in OVN controller
266 func createOvnLS(name, subnet, gatewayIP, excludeIps string) (gatewayIPMask string, err error) {
267         var stdout, stderr string
268
269         output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
270                 "--columns=name", "find", "logical_switch", "name="+name)
271         if err != nil {
272                 log.Error(err, "Error in reading logical switch", "stderr", stderr)
273                 return
274         }
275
276         if strings.Compare(name, output) == 0 {
277                 log.V(1).Info("Logical Switch already exists, delete first to update/recreate", "name", name)
278                 return "", fmt.Errorf("LS exists")
279         }
280
281         _, cidr, err := net.ParseCIDR(subnet)
282         if err != nil {
283                 log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
284                 return
285
286         }
287         firstIP := NextIP(cidr.IP)
288         n, _ := cidr.Mask.Size()
289
290         var gwIP net.IP
291         if gatewayIP != "" {
292                 gwIP, _, err = net.ParseCIDR(gatewayIP)
293                 if err != nil {
294                         // Check if this is a valid IP address
295                         gwIP = net.ParseIP(gatewayIP)
296                 }
297         }
298         // If no valid Gateway use the first IP address for GatewayIP
299         if gwIP == nil {
300                 gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
301         } else {
302                 gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
303         }
304
305         // Create a logical switch and set its subnet.
306         if excludeIps != "" {
307                 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask, "other-config:exclude_ips="+excludeIps)
308         } else {
309                 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask)
310         }
311         if err != nil {
312                 log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
313                 return
314         }
315         return
316 }
317
318 // generateMac generates mac address.
319 func generateMac() string {
320         prefix := "00:00:00"
321         newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
322         mac := fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
323         return mac
324 }
325
326 // NextIP returns IP incremented by 1
327 func NextIP(ip net.IP) net.IP {
328         i := ipToInt(ip)
329         return intToIP(i.Add(i, big.NewInt(1)))
330 }
331
332 func ipToInt(ip net.IP) *big.Int {
333         if v := ip.To4(); v != nil {
334                 return big.NewInt(0).SetBytes(v)
335         }
336         return big.NewInt(0).SetBytes(ip.To16())
337 }
338
339 func intToIP(i *big.Int) net.IP {
340         return net.IP(i.Bytes())
341 }
342
343 // Get Subnet for a logical bridge
344 func GetNetworkSubnet(nw string) (string, error) {
345         stdout, stderr, err := RunOVNNbctl("--if-exists",
346                 "get", "logical_switch", nw,
347                 "other_config:subnet")
348         if err != nil {
349                 log.Error(err, "Failed to subnet for network", "stderr", stderr, "stdout", stdout)
350                 return "", err
351         }
352         return stdout, nil
353 }
354
355 func GetIPAdressForPod(nw string, name string) (string, error) {
356         _, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
357                 "--columns=name", "find", "logical_switch", "name="+nw)
358         if err != nil {
359                 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
360                 return "", err
361         }
362         stdout, stderr, err := RunOVNNbctl("lsp-list", nw)
363         if err != nil {
364                 log.Error(err, "Failed to list ports", "stderr", stderr, "stdout", stdout)
365                 return "", err
366         }
367         // stdout format
368         // <port-uuid> (<port-name>)
369         // <port-uuid> (<port-name>)
370         // ...
371         ll := strings.Split(stdout, "\n")
372         if len(ll) == 0 {
373                 return "", fmt.Errorf("IPAdress Not Found")
374         }
375         for _, l := range ll {
376                 pn := strings.Fields(l)
377                 if len(pn) < 2 {
378                         return "", fmt.Errorf("IPAdress Not Found")
379                 }
380                 if strings.Contains(pn[1], name) {
381                         // Found Port
382                         s := strings.Replace(pn[1], "(", "", -1)
383                         s = strings.Replace(s, ")", "", -1)
384                         dna, stderr, err := RunOVNNbctl("get", "logical_switch_port", s, "dynamic_addresses")
385                         if err != nil {
386                                 log.Error(err, "Failed to get dynamic_addresses", "stderr", stderr, "stdout", dna)
387                                 return "", err
388                         }
389                         // format - mac:ip
390                         ipAddr := strings.Fields(dna)
391                         if len(ipAddr) < 2 {
392                                 return "", fmt.Errorf("IPAdress Not Found")
393                         }
394                         return ipAddr[1], nil
395                 }
396         }
397         return "", fmt.Errorf("IPAdress Not Found %s", name)
398 }
399