5 "github.com/mitchellh/mapstructure"
6 kapi "k8s.io/api/core/v1"
7 kexec "k8s.io/utils/exec"
10 k8sv1alpha1 "ovn4nfv-k8s-plugin/pkg/apis/k8s/v1alpha1"
15 type Controller struct {
16 gatewayCache map[string]string
20 ovn4nfvRouterName = "ovn4nfv-master"
21 // Ovn4nfvAnnotationTag tag on already processed Pods
22 Ovn4nfvAnnotationTag = "k8s.plugin.opnfv.org/ovnInterfaces"
25 type netInterface struct {
34 var ovnCtl *Controller
36 // NewOvnController creates a new OVN controller for creating logical networks
37 func NewOvnController(exec kexec.Interface) (*Controller, error) {
42 if err := SetExec(exec); err != nil {
43 log.Error(err, "Failed to initialize exec helper")
46 if err := SetupOvnUtils(); err != nil {
47 log.Error(err, "Failed to initialize OVN State")
51 gatewayCache: make(map[string]string),
56 // GetOvnController returns OVN controller for creating logical networks
57 func GetOvnController() (*Controller, error) {
61 return nil, fmt.Errorf("OVN Controller not initialized")
64 // AddLogicalPorts adds ports to the Pod
65 func (oc *Controller) AddLogicalPorts(pod *kapi.Pod, ovnNetObjs []map[string]interface{}) (key, value string) {
67 if ovnNetObjs == nil {
71 if pod.Spec.HostNetwork {
75 if _, ok := pod.Annotations[Ovn4nfvAnnotationTag]; ok {
76 log.V(1).Info("AddLogicalPorts : Pod annotation found")
80 var ovnString, outStr string
83 for _, net := range ovnNetObjs {
85 err := mapstructure.Decode(net, &ns)
87 log.Error(err, "mapstruct error", "network", net)
91 if !oc.FindLogicalSwitch(ns.Name) {
92 log.Info("Logical Switch not found")
95 if ns.Interface == "" {
96 log.Info("Interface name must be provided")
99 if ns.DefaultGateway == "" {
100 ns.DefaultGateway = "false"
102 outStr = oc.addLogicalPortWithSwitch(pod, ns.Name, ns.IPAddress, ns.MacAddress, ns.Interface, ns.NetType)
106 last := len(outStr) - 1
107 tmpString := outStr[:last]
108 tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + ns.DefaultGateway + "\\\""
109 tmpString += "," + "\\\"interface\\\":" + "\\\"" + ns.Interface + "\\\"}"
110 ovnString += tmpString
113 last := len(ovnString) - 1
114 ovnString = ovnString[:last]
116 key = Ovn4nfvAnnotationTag
121 // DeleteLogicalPorts deletes the OVN ports for the pod
122 func (oc *Controller) DeleteLogicalPorts(name, namespace string) {
124 logicalPort := fmt.Sprintf("%s_%s", namespace, name)
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")
130 log.Error(err, "Error in obtaining list of logical ports ", "stdout", stdout, "stderr", stderr)
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",
141 log.Error(err, "Error in deleting pod's logical port ", "stdout", stdout, "stderr", stderr)
148 // CreateNetwork in OVN controller
149 func (oc *Controller) createOvnLS(name, subnet, gatewayIP, excludeIps string) (gatewayIPMask string, err error) {
150 var stdout, stderr string
152 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
153 "--columns=name", "find", "logical_switch", "name="+name)
155 log.Error(err, "Error in reading logical switch", "stderr", stderr)
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")
164 _, cidr, err := net.ParseCIDR(subnet)
166 log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
170 firstIP := NextIP(cidr.IP)
171 n, _ := cidr.Mask.Size()
175 gwIP, _, err = net.ParseCIDR(gatewayIP)
177 // Check if this is a valid IP address
178 gwIP = net.ParseIP(gatewayIP)
181 // If no valid Gateway use the first IP address for GatewayIP
183 gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
185 gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
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)
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)
195 log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
201 // CreateNetwork in OVN controller
202 func (oc *Controller) CreateNetwork(cr *k8sv1alpha1.Network) error {
203 var stdout, stderr string
205 // Currently only these fields are supported
207 subnet := cr.Spec.Ipv4Subnets[0].Subnet
208 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
209 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
211 gatewayIPMask, err := oc.createOvnLS(name, subnet, gatewayIP, excludeIps)
216 routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtos-"+name, "mac")
218 log.Error(err, "Failed to get logical router port", "stderr", stderr)
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))
227 _, stderr, err = RunOVNNbctl("--wait=hv", "--may-exist", "lrp-add", ovn4nfvRouterName, "rtos-"+name, routerMac, gatewayIPMask)
229 log.Error(err, "Failed to add logical port to router", "stderr", stderr)
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+"\"")
236 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
243 // DeleteNetwork in OVN controller
244 func (oc *Controller) DeleteNetwork(cr *k8sv1alpha1.Network) error {
247 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "lrp-del", "rtos-"+name)
249 log.Error(err, "Failed to delete router port", "name", name, "stdout", stdout, "stderr", stderr)
252 stdout, stderr, err = RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
254 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
260 // CreateProviderNetwork in OVN controller
261 func (oc *Controller) CreateProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
262 var stdout, stderr string
264 // Currently only these fields are supported
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)
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)
280 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
287 // DeleteProviderNetwork in OVN controller
288 func (oc *Controller) DeleteProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
291 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
293 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
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)
305 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
308 if strings.Compare(name, output) == 0 {
314 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
315 var gatewayIPMaskStr, stderr string
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")
323 log.Error(err, "Failed to get gateway IP", "stderr", stderr, "gatewayIPMaskStr", gatewayIPMaskStr)
326 if gatewayIPMaskStr == "" {
327 return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
330 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
332 gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
333 if len(gatewayIPMask) != 2 {
334 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR: %s",
337 gatewayIP := gatewayIPMask[0]
338 mask := gatewayIPMask[1]
339 return gatewayIP, mask, nil
342 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType string) (annotation string) {
343 var out, stderr string
346 if pod.Spec.HostNetwork {
351 if interfaceName != "" {
352 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, interfaceName)
357 log.V(1).Info("Creating logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
359 if ipAddress != "" && macAddress != "" {
362 if ipAddress != "" && macAddress == "" {
363 macAddress = generateMac()
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")
377 log.Error(err, "Failed to add logical port to switch", "out", out, "stderr", stderr)
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")
390 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
398 out, stderr, err = RunOVNNbctl("get",
399 "logical_switch_port", portName, "addresses")
401 out, stderr, err = RunOVNNbctl("get",
402 "logical_switch_port", portName, "dynamic_addresses")
404 if err == nil && out != "[]" {
408 log.Error(err, "Error while obtaining addresses for", "portName", portName)
411 time.Sleep(time.Second)
415 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
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)
430 gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
432 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
435 annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)