6e9cd79b0fc9620a61dbc7f55ecae662ac99d994
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / ovn.go
1 package ovn
2
3 import (
4         "fmt"
5         "github.com/mitchellh/mapstructure"
6         kapi "k8s.io/api/core/v1"
7         kexec "k8s.io/utils/exec"
8         "math/rand"
9         "net"
10         k8sv1alpha1 "ovn4nfv-k8s-plugin/pkg/apis/k8s/v1alpha1"
11         "strings"
12         "time"
13 )
14
15 type Controller struct {
16         gatewayCache map[string]string
17 }
18
19 const (
20         ovn4nfvRouterName = "ovn4nfv-master"
21         // Ovn4nfvAnnotationTag tag on already processed Pods
22         Ovn4nfvAnnotationTag = "k8s.plugin.opnfv.org/ovnInterfaces"
23 )
24
25 type netInterface struct {
26         Name           string
27         Interface      string
28         NetType        string
29         DefaultGateway string
30         IPAddress      string
31         MacAddress     string
32 }
33
34 var ovnCtl *Controller
35
36 // NewOvnController creates a new OVN controller for creating logical networks
37 func NewOvnController(exec kexec.Interface) (*Controller, error) {
38
39         if exec == nil {
40                 exec = kexec.New()
41         }
42         if err := SetExec(exec); err != nil {
43                 log.Error(err, "Failed to initialize exec helper")
44                 return nil, err
45         }
46         if err := SetupOvnUtils(); err != nil {
47                 log.Error(err, "Failed to initialize OVN State")
48                 return nil, err
49         }
50         ovnCtl = &Controller{
51                 gatewayCache: make(map[string]string),
52         }
53         return ovnCtl, nil
54 }
55
56 // GetOvnController returns OVN controller for creating logical networks
57 func GetOvnController() (*Controller, error) {
58         if ovnCtl != nil {
59                 return ovnCtl, nil
60         }
61         return nil, fmt.Errorf("OVN Controller not initialized")
62 }
63
64 // AddLogicalPorts adds ports to the Pod
65 func (oc *Controller) AddLogicalPorts(pod *kapi.Pod, ovnNetObjs []map[string]interface{}) (key, value string) {
66
67         if ovnNetObjs == nil {
68                 return
69         }
70
71         if pod.Spec.HostNetwork {
72                 return
73         }
74
75         if _, ok := pod.Annotations[Ovn4nfvAnnotationTag]; ok {
76                 log.V(1).Info("AddLogicalPorts : Pod annotation found")
77                 return
78         }
79
80         var ovnString, outStr string
81         ovnString = "["
82         var ns netInterface
83         for _, net := range ovnNetObjs {
84
85                 err := mapstructure.Decode(net, &ns)
86                 if err != nil {
87                         log.Error(err, "mapstruct error", "network", net)
88                         return
89                 }
90
91                 if !oc.FindLogicalSwitch(ns.Name) {
92                         log.Info("Logical Switch not found")
93                         return
94                 }
95                 if ns.Interface == "" {
96                         log.Info("Interface name must be provided")
97                         return
98                 }
99                 if ns.DefaultGateway == "" {
100                         ns.DefaultGateway = "false"
101                 }
102                 outStr = oc.addLogicalPortWithSwitch(pod, ns.Name, ns.IPAddress, ns.MacAddress, ns.Interface, ns.NetType)
103                 if outStr == "" {
104                         return
105                 }
106                 last := len(outStr) - 1
107                 tmpString := outStr[:last]
108                 tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + ns.DefaultGateway + "\\\""
109                 tmpString += "," + "\\\"interface\\\":" + "\\\"" + ns.Interface + "\\\"}"
110                 ovnString += tmpString
111                 ovnString += ","
112         }
113         last := len(ovnString) - 1
114         ovnString = ovnString[:last]
115         ovnString += "]"
116         key = Ovn4nfvAnnotationTag
117         value = ovnString
118         return key, value
119 }
120
121 // DeleteLogicalPorts deletes the OVN ports for the pod
122 func (oc *Controller) DeleteLogicalPorts(name, namespace string) {
123
124         logicalPort := fmt.Sprintf("%s_%s", namespace, name)
125
126         // get the list of logical ports from OVN
127         stdout, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
128                 "--columns=name", "find", "logical_switch_port", "external_ids:pod=true")
129         if err != nil {
130                 log.Error(err, "Error in obtaining list of logical ports ", "stdout", stdout, "stderr", stderr)
131                 return
132         }
133         existingLogicalPorts := strings.Fields(stdout)
134         for _, existingPort := range existingLogicalPorts {
135                 if strings.Contains(existingPort, logicalPort) {
136                         // found, delete this logical port
137                         log.V(1).Info("Deleting", "Port", existingPort)
138                         stdout, stderr, err := RunOVNNbctl("--if-exists", "lsp-del",
139                                 existingPort)
140                         if err != nil {
141                                 log.Error(err, "Error in deleting pod's logical port ", "stdout", stdout, "stderr", stderr)
142                         }
143                 }
144         }
145         return
146 }
147
148 // CreateNetwork in OVN controller
149 func (oc *Controller) createOvnLS(name, subnet, gatewayIP, excludeIps string) (gatewayIPMask string, err error) {
150         var stdout, stderr string
151
152         output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
153                 "--columns=name", "find", "logical_switch", "name="+name)
154         if err != nil {
155                 log.Error(err, "Error in reading logical switch", "stderr", stderr)
156                 return
157         }
158
159         if strings.Compare(name, output) == 0 {
160                 log.V(1).Info("Logical Switch already exists, delete first to update/recreate", "name", name)
161                 return "", fmt.Errorf("LS exists")
162         }
163
164         _, cidr, err := net.ParseCIDR(subnet)
165         if err != nil {
166                 log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
167                 return
168
169         }
170         firstIP := NextIP(cidr.IP)
171         n, _ := cidr.Mask.Size()
172
173         var gwIP net.IP
174         if gatewayIP != "" {
175                 gwIP, _, err = net.ParseCIDR(gatewayIP)
176                 if err != nil {
177                         // Check if this is a valid IP address
178                         gwIP = net.ParseIP(gatewayIP)
179                 }
180         }
181         // If no valid Gateway use the first IP address for GatewayIP
182         if gwIP == nil {
183                 gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
184         } else {
185                 gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
186         }
187
188         // Create a logical switch and set its subnet.
189         if excludeIps != "" {
190                 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)
191         } else {
192                 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask)
193         }
194         if err != nil {
195                 log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
196                 return
197         }
198         return
199 }
200
201 // CreateNetwork in OVN controller
202 func (oc *Controller) CreateNetwork(cr *k8sv1alpha1.Network) error {
203         var stdout, stderr string
204
205         // Currently only these fields are supported
206         name := cr.Name
207         subnet := cr.Spec.Ipv4Subnets[0].Subnet
208         gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
209         excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
210
211         gatewayIPMask, err := oc.createOvnLS(name, subnet, gatewayIP, excludeIps)
212         if err != nil {
213                 return err
214         }
215
216         routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtos-"+name, "mac")
217         if err != nil {
218                 log.Error(err, "Failed to get logical router port", "stderr", stderr)
219                 return err
220         }
221         if routerMac == "" {
222                 prefix := "00:00:00"
223                 newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
224                 routerMac = fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
225         }
226
227         _, stderr, err = RunOVNNbctl("--wait=hv", "--may-exist", "lrp-add", ovn4nfvRouterName, "rtos-"+name, routerMac, gatewayIPMask)
228         if err != nil {
229                 log.Error(err, "Failed to add logical port to router", "stderr", stderr)
230                 return err
231         }
232
233         // Connect the switch to the router.
234         stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "lsp-add", name, "stor-"+name, "--", "set", "logical_switch_port", "stor-"+name, "type=router", "options:router-port=rtos-"+name, "addresses="+"\""+routerMac+"\"")
235         if err != nil {
236                 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
237                 return err
238         }
239
240         return nil
241 }
242
243 // DeleteNetwork in OVN controller
244 func (oc *Controller) DeleteNetwork(cr *k8sv1alpha1.Network) error {
245
246         name := cr.Name
247         stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "lrp-del", "rtos-"+name)
248         if err != nil {
249                 log.Error(err, "Failed to delete router port", "name", name, "stdout", stdout, "stderr", stderr)
250                 return err
251         }
252         stdout, stderr, err = RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
253         if err != nil {
254                 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
255                 return err
256         }
257         return nil
258 }
259
260 // CreateProviderNetwork in OVN controller
261 func (oc *Controller) CreateProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
262         var stdout, stderr string
263
264         // Currently only these fields are supported
265         name := cr.Name
266         subnet := cr.Spec.Ipv4Subnets[0].Subnet
267         gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
268         excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
269         _, err := oc.createOvnLS(name, subnet, gatewayIP, excludeIps)
270         if err != nil {
271                 return err
272         }
273
274         // Add localnet port.
275         stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "lsp-add", name, "server-localnet_"+name, "--",
276                 "lsp-set-addresses", "server-localnet_"+name, "unknown", "--",
277                 "lsp-set-type", "server-localnet_"+name, "localnet", "--",
278                 "lsp-set-options", "server-localnet_"+name, "network_name=nw_"+name)
279         if err != nil {
280                 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
281                 return err
282         }
283
284         return nil
285 }
286
287 // DeleteProviderNetwork in OVN controller
288 func (oc *Controller) DeleteProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
289
290         name := cr.Name
291         stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
292         if err != nil {
293                 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
294                 return err
295         }
296         return nil
297 }
298
299 // FindLogicalSwitch returns true if switch exists
300 func (oc *Controller) FindLogicalSwitch(name string) bool {
301         // get logical switch from OVN
302         output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
303                 "--columns=name", "find", "logical_switch", "name="+name)
304         if err != nil {
305                 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
306                 return false
307         }
308         if strings.Compare(name, output) == 0 {
309                 return true
310         }
311         return false
312 }
313
314 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
315         var gatewayIPMaskStr, stderr string
316         var ok bool
317         var err error
318         if gatewayIPMaskStr, ok = oc.gatewayCache[logicalSwitch]; !ok {
319                 gatewayIPMaskStr, stderr, err = RunOVNNbctl("--if-exists",
320                         "get", "logical_switch", logicalSwitch,
321                         "external_ids:gateway_ip")
322                 if err != nil {
323                         log.Error(err, "Failed to get gateway IP", "stderr", stderr, "gatewayIPMaskStr", gatewayIPMaskStr)
324                         return "", "", err
325                 }
326                 if gatewayIPMaskStr == "" {
327                         return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
328                                 logicalSwitch)
329                 }
330                 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
331         }
332         gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
333         if len(gatewayIPMask) != 2 {
334                 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR:  %s",
335                         gatewayIPMaskStr)
336         }
337         gatewayIP := gatewayIPMask[0]
338         mask := gatewayIPMask[1]
339         return gatewayIP, mask, nil
340 }
341
342 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType string) (annotation string) {
343         var out, stderr string
344         var err error
345         var isStaticIP bool
346         if pod.Spec.HostNetwork {
347                 return
348         }
349
350         var portName string
351         if interfaceName != "" {
352                 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, interfaceName)
353         } else {
354                 return
355         }
356
357         log.V(1).Info("Creating logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
358
359         if ipAddress != "" && macAddress != "" {
360                 isStaticIP = true
361         }
362         if ipAddress != "" && macAddress == "" {
363                 macAddress = generateMac()
364                 isStaticIP = true
365         }
366
367         if isStaticIP {
368                 out, stderr, err = RunOVNNbctl("--may-exist", "lsp-add",
369                         logicalSwitch, portName, "--", "lsp-set-addresses", portName,
370                         fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists",
371                         "clear", "logical_switch_port", portName, "dynamic_addresses", "--", "set",
372                         "logical_switch_port", portName,
373                         "external-ids:namespace="+pod.Namespace,
374                         "external-ids:logical_switch="+logicalSwitch,
375                         "external-ids:pod=true")
376                 if err != nil {
377                         log.Error(err, "Failed to add logical port to switch", "out", out, "stderr", stderr)
378                         return
379                 }
380         } else {
381                 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
382                         "--may-exist", "lsp-add", logicalSwitch, portName,
383                         "--", "lsp-set-addresses",
384                         portName, "dynamic", "--", "set",
385                         "logical_switch_port", portName,
386                         "external-ids:namespace="+pod.Namespace,
387                         "external-ids:logical_switch="+logicalSwitch,
388                         "external-ids:pod=true")
389                 if err != nil {
390                         log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
391                         return
392                 }
393         }
394
395         count := 30
396         for count > 0 {
397                 if isStaticIP {
398                         out, stderr, err = RunOVNNbctl("get",
399                                 "logical_switch_port", portName, "addresses")
400                 } else {
401                         out, stderr, err = RunOVNNbctl("get",
402                                 "logical_switch_port", portName, "dynamic_addresses")
403                 }
404                 if err == nil && out != "[]" {
405                         break
406                 }
407                 if err != nil {
408                         log.Error(err, "Error while obtaining addresses for", "portName", portName)
409                         return
410                 }
411                 time.Sleep(time.Second)
412                 count--
413         }
414         if count == 0 {
415                 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
416                 return
417         }
418
419         // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
420         // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
421         outStr := strings.TrimLeft(out, `[`)
422         outStr = strings.TrimRight(outStr, `]`)
423         outStr = strings.Trim(outStr, `"`)
424         addresses := strings.Split(outStr, " ")
425         if len(addresses) != 2 {
426                 log.Info("Error while obtaining addresses for", "portName", portName)
427                 return
428         }
429
430         gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
431         if err != nil {
432                 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
433                 return
434         }
435         annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)
436
437         return annotation
438 }