Add support for Provider network interface
[ovn4nfv-k8s-plugin.git] / internal / pkg / ovn / pods.go
1 package ovn
2
3 import (
4         "fmt"
5         "strings"
6         "time"
7
8         "github.com/sirupsen/logrus"
9         kapi "k8s.io/api/core/v1"
10         "ovn4nfv-k8s-plugin/internal/pkg/util"
11 )
12
13 func (oc *Controller) syncPods(pods []interface{}) {
14 }
15 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
16         var gatewayIPMaskStr, stderr string
17         var ok bool
18         var err error
19         logrus.Infof("getGatewayFromSwitch: %s", logicalSwitch)
20         if gatewayIPMaskStr, ok = oc.gatewayCache[logicalSwitch]; !ok {
21                 gatewayIPMaskStr, stderr, err = util.RunOVNNbctlUnix("--if-exists",
22                         "get", "logical_switch", logicalSwitch,
23                         "external_ids:gateway_ip")
24                 if err != nil {
25                         logrus.Errorf("Failed to get gateway IP:  %s, stderr: %q, %v",
26                                 gatewayIPMaskStr, stderr, err)
27                         return "", "", err
28                 }
29                 if gatewayIPMaskStr == "" {
30                         return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
31                                 logicalSwitch)
32                 }
33                 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
34         }
35         gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
36         if len(gatewayIPMask) != 2 {
37                 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR:  %s",
38                         gatewayIPMaskStr)
39         }
40         gatewayIP := gatewayIPMask[0]
41         mask := gatewayIPMask[1]
42         return gatewayIP, mask, nil
43 }
44
45 func (oc *Controller) deleteLogicalPort(pod *kapi.Pod) {
46
47         if pod.Spec.HostNetwork {
48                 return
49         }
50
51         logrus.Infof("Deleting pod: %s", pod.Name)
52         logicalPort := fmt.Sprintf("%s_%s", pod.Namespace, pod.Name)
53
54         // get the list of logical ports from OVN
55         output, stderr, err := util.RunOVNNbctlUnix("--data=bare", "--no-heading",
56                 "--columns=name", "find", "logical_switch_port", "external_ids:pod=true")
57         if err != nil {
58                 logrus.Errorf("Error in obtaining list of logical ports, "+
59                         "stderr: %q, err: %v",
60                         stderr, err)
61                 return
62         }
63         logrus.Infof("Exising Ports : %s. ", output)
64         existingLogicalPorts := strings.Fields(output)
65         for _, existingPort := range existingLogicalPorts {
66                 if strings.Contains(existingPort, logicalPort) {
67                         // found, delete this logical port
68                         logrus.Infof("Deleting: %s. ", existingPort)
69                         out, stderr, err := util.RunOVNNbctlUnix("--if-exists", "lsp-del",
70                                 existingPort)
71                         if err != nil {
72                                 logrus.Errorf("Error in deleting pod's logical port "+
73                                         "stdout: %q, stderr: %q err: %v",
74                                         out, stderr, err)
75                         } else {
76                                 delete(oc.logicalPortCache, existingPort)
77                         }
78                 }
79         }
80         return
81 }
82
83 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType string) (annotation string) {
84         var out, stderr string
85         var err error
86         var isStaticIP bool
87         if pod.Spec.HostNetwork {
88                 return
89         }
90
91         if !oc.logicalSwitchCache[logicalSwitch] {
92                 oc.logicalSwitchCache[logicalSwitch] = true
93         }
94         var portName string
95         if interfaceName != "" {
96                 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, interfaceName)
97         } else {
98                 return
99         }
100
101         logrus.Infof("Creating logical port for %s on switch %s", portName, logicalSwitch)
102
103         if ipAddress != "" && macAddress != "" {
104                 isStaticIP = true
105         }
106         if ipAddress != "" && macAddress == "" {
107                 macAddress = util.GenerateMac()
108                 isStaticIP = true
109         }
110
111         if isStaticIP {
112                 out, stderr, err = util.RunOVNNbctlUnix("--may-exist", "lsp-add",
113                         logicalSwitch, portName, "--", "lsp-set-addresses", portName,
114                         fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists",
115                         "clear", "logical_switch_port", portName, "dynamic_addresses")
116                 if err != nil {
117                         logrus.Errorf("Failed to add logical port to switch "+
118                                 "stdout: %q, stderr: %q (%v)",
119                                 out, stderr, err)
120                         return
121                 }
122         } else {
123                 out, stderr, err = util.RunOVNNbctlUnix("--wait=sb", "--",
124                         "--may-exist", "lsp-add", logicalSwitch, portName,
125                         "--", "lsp-set-addresses",
126                         portName, "dynamic", "--", "set",
127                         "logical_switch_port", portName,
128                         "external-ids:namespace="+pod.Namespace,
129                         "external-ids:logical_switch="+logicalSwitch,
130                         "external-ids:pod=true")
131                 if err != nil {
132                         logrus.Errorf("Error while creating logical port %s "+
133                                 "stdout: %q, stderr: %q (%v)",
134                                 portName, out, stderr, err)
135                         return
136                 }
137         }
138         oc.logicalPortCache[portName] = logicalSwitch
139
140         count := 30
141         for count > 0 {
142                 if isStaticIP {
143                         out, stderr, err = util.RunOVNNbctlUnix("get",
144                                 "logical_switch_port", portName, "addresses")
145                 } else {
146                         out, stderr, err = util.RunOVNNbctlUnix("get",
147                                 "logical_switch_port", portName, "dynamic_addresses")
148                 }
149                 if err == nil && out != "[]" {
150                         break
151                 }
152                 if err != nil {
153                         logrus.Errorf("Error while obtaining addresses for %s - %v", portName,
154                                 err)
155                         return
156                 }
157                 time.Sleep(time.Second)
158                 count--
159         }
160         if count == 0 {
161                 logrus.Errorf("Error while obtaining addresses for %s "+
162                         "stdout: %q, stderr: %q, (%v)", portName, out, stderr, err)
163                 return
164         }
165
166         // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
167         // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
168         outStr := strings.TrimLeft(out, `[`)
169         outStr = strings.TrimRight(outStr, `]`)
170         outStr = strings.Trim(outStr, `"`)
171         addresses := strings.Split(outStr, " ")
172         if len(addresses) != 2 {
173                 logrus.Errorf("Error while obtaining addresses for %s", portName)
174                 return
175         }
176
177         if netType == "virtual" {
178                 gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
179                 if err != nil {
180                         logrus.Errorf("Error obtaining gateway address for switch %s: %s", logicalSwitch, err)
181                         return
182                 }
183                 annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)
184         } else {
185                 annotation = fmt.Sprintf(`{\"ip_address\":\"%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], addresses[0], "")
186         }
187
188         return annotation
189 }
190
191 func findLogicalSwitch(name string) bool {
192         // get logical switch from OVN
193         output, stderr, err := util.RunOVNNbctlUnix("--data=bare", "--no-heading",
194                 "--columns=name", "find", "logical_switch", "name="+name)
195         if err != nil {
196                 logrus.Errorf("Error in obtaining list of logical switch, "+
197                         "stderr: %q, err: %v",
198                         stderr, err)
199                 return false
200         }
201
202         if strings.Compare(name, output) == 0 {
203                 return true
204         } else {
205                 logrus.Errorf("Error finding Switch %v", name)
206                 return false
207         }
208 }
209
210 func (oc *Controller) addLogicalPort(pod *kapi.Pod) {
211         var logicalSwitch string
212         var ipAddress, macAddress, interfaceName, defaultGateway, netType string
213
214         annotation := pod.Annotations["ovnNetwork"]
215
216         if annotation != "" {
217                 ovnNetObjs, err := oc.parseOvnNetworkObject(annotation)
218                 if err != nil {
219                         logrus.Errorf("addLogicalPort : Error Parsing OvnNetwork List")
220                         return
221                 }
222                 var ovnString, outStr string
223                 ovnString = "["
224                 for _, net := range ovnNetObjs {
225                         logicalSwitch = net["name"].(string)
226                         if !findLogicalSwitch(logicalSwitch) {
227                                 logrus.Errorf("Logical Switch not found")
228                                 return
229                         }
230                         if _, ok := net["interface"]; ok {
231                                 interfaceName = net["interface"].(string)
232                         } else {
233                                 logrus.Errorf("Interface name must be provided")
234                                 return
235                         }
236                         if _, ok := net["ipAddress"]; ok {
237                                 ipAddress = net["ipAddress"].(string)
238                         } else {
239                                 ipAddress = ""
240                         }
241                         if _, ok := net["macAddress"]; ok {
242                                 macAddress = net["macAddress"].(string)
243                         } else {
244                                 macAddress = ""
245                         }
246                         if _, ok := net["defaultGateway"]; ok {
247                                 defaultGateway = net["defaultGateway"].(string)
248                         } else {
249                                 defaultGateway = "false"
250                         }
251                         if _, ok := net["netType"]; ok {
252                                 netType = net["netType"].(string)
253                         } else {
254                                 netType = "virtual"
255                         }
256                         if netType != "provider" && netType != "virtual" {
257                                 logrus.Errorf("netType is not supported")
258                                 return
259                         }
260                         if netType == "provider" && ipAddress == "" {
261                                 logrus.Errorf("ipAddress must be provided for netType Provider")
262                                 return
263                         }
264                         if netType == "provider" && defaultGateway == "true" {
265                                 logrus.Errorf("defaultGateway not supported for provider network - Use ovnNetworkRoutes to add routes")
266                                 return
267                         }
268                         outStr = oc.addLogicalPortWithSwitch(pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType)
269                         if outStr == "" {
270                                 return
271                         }
272                         last := len(outStr) - 1
273                         tmpString := outStr[:last]
274                         tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + defaultGateway + "\\\""
275                         tmpString += "," + "\\\"interface\\\":" + "\\\"" + interfaceName + "\\\"}"
276                         ovnString += tmpString
277                         ovnString += ","
278                 }
279                 last := len(ovnString) - 1
280                 ovnString = ovnString[:last]
281                 ovnString += "]"
282                 logrus.Debugf("ovnIfaceList - %v", ovnString)
283                 err = oc.kube.SetAnnotationOnPod(pod, "ovnIfaceList", ovnString)
284                 if err != nil {
285                         logrus.Errorf("Failed to set annotation on pod %s - %v", pod.Name, err)
286                 }
287         }
288 }