7 "ovn4nfv-k8s-plugin/internal/pkg/config"
8 k8sv1alpha1 "ovn4nfv-k8s-plugin/pkg/apis/k8s/v1alpha1"
12 "github.com/mitchellh/mapstructure"
13 kapi "k8s.io/api/core/v1"
14 kexec "k8s.io/utils/exec"
17 type Controller struct {
18 gatewayCache map[string]string
21 type OVNNetworkConf struct {
28 ovn4nfvRouterName = "ovn4nfv-master"
29 // Ovn4nfvAnnotationTag tag on already processed Pods
30 Ovn4nfvAnnotationTag = "k8s.plugin.opnfv.org/ovnInterfaces"
31 // OVN Default Network name
32 Ovn4nfvDefaultNw = "ovn4nfvk8s-default-nw"
35 var ovnConf *OVNNetworkConf
37 //GetOvnNetConf return error
38 func GetOvnNetConf() error {
39 ovnConf = &OVNNetworkConf{}
41 ovnConf.Subnet = os.Getenv("OVN_SUBNET")
42 if ovnConf.Subnet == "" {
43 return fmt.Errorf("OVN subnet is not set in nfn-operator configmap env")
46 ovnConf.GatewayIP = os.Getenv("OVN_GATEWAYIP")
47 if ovnConf.GatewayIP == "" {
48 log.Info("No Gateway IP address provided - 1st IP address of the subnet range will be used as Gateway", "Subnet", ovnConf.Subnet)
51 ovnConf.ExcludeIPs = os.Getenv("OVN_EXCLUDEIPS")
52 if ovnConf.ExcludeIPs == "" {
53 log.Info("No IP addresses are excluded in the subnet range", "Subnet", ovnConf.Subnet)
59 type netInterface struct {
68 var ovnCtl *Controller
70 // NewOvnController creates a new OVN controller for creating logical networks
71 func NewOvnController(exec kexec.Interface) (*Controller, error) {
76 if err := SetExec(exec); err != nil {
77 log.Error(err, "Failed to initialize exec helper")
81 if err := GetOvnNetConf(); err != nil {
82 log.Error(err, "nfn-operator OVN Network configmap is not set")
85 if err := SetupOvnUtils(); err != nil {
86 log.Error(err, "Failed to initialize OVN State")
91 gatewayCache: make(map[string]string),
96 // GetOvnController returns OVN controller for creating logical networks
97 func GetOvnController() (*Controller, error) {
101 return nil, fmt.Errorf("OVN Controller not initialized")
104 func (oc *Controller) AddNodeLogicalPorts(node string) (ipAddr, macAddr string, err error) {
105 nodeName := strings.ToLower(node)
106 portName := config.GetNodeIntfName(nodeName)
108 log.V(1).Info("Creating Node logical port", "node", nodeName, "portName", portName)
110 ipAddr, macmacAddr, err := oc.addNodeLogicalPortWithSwitch(Ovn4nfvDefaultNw, portName)
115 return ipAddr, macmacAddr, nil
118 // AddLogicalPorts adds ports to the Pod
119 func (oc *Controller) AddLogicalPorts(pod *kapi.Pod, ovnNetObjs []map[string]interface{}) (key, value string) {
121 if pod.Spec.HostNetwork {
125 if _, ok := pod.Annotations[Ovn4nfvAnnotationTag]; ok {
126 log.V(1).Info("AddLogicalPorts : Pod annotation found")
130 var ovnString, outStr string
131 var defaultInterface bool
135 for _, net := range ovnNetObjs {
136 err := mapstructure.Decode(net, &ns)
138 log.Error(err, "mapstruct error", "network", net)
141 if !oc.FindLogicalSwitch(ns.Name) {
142 log.Info("Logical Switch not found")
145 if ns.Name == Ovn4nfvDefaultNw {
146 defaultInterface = true
148 if ns.Interface == "" && ns.Name != Ovn4nfvDefaultNw {
149 log.Info("Interface name must be provided")
152 if ns.DefaultGateway == "" {
153 ns.DefaultGateway = "false"
156 if ns.Interface != "" {
157 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, ns.Interface)
159 portName = fmt.Sprintf("%s_%s", pod.Namespace, pod.Name)
162 outStr = oc.addLogicalPortWithSwitch(pod, ns.Name, ns.IPAddress, ns.MacAddress, portName)
166 last := len(outStr) - 1
167 tmpString := outStr[:last]
168 tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + ns.DefaultGateway + "\\\""
169 tmpString += "," + "\\\"interface\\\":" + "\\\"" + ns.Interface + "\\\"}"
170 ovnString += tmpString
174 if defaultInterface == false {
175 // Add Default interface
176 portName := fmt.Sprintf("%s_%s", pod.Namespace, pod.Name)
177 outStr = oc.addLogicalPortWithSwitch(pod, Ovn4nfvDefaultNw, "", "", portName)
181 last := len(outStr) - 1
182 tmpString := outStr[:last]
183 tmpString += "," + "\\\"interface\\\":" + "\\\"" + "*" + "\\\"}"
184 ovnString += tmpString
187 last = len(ovnString) - 1
188 ovnString = ovnString[:last]
190 key = Ovn4nfvAnnotationTag
195 // DeleteLogicalPorts deletes the OVN ports for the pod
196 func (oc *Controller) DeleteLogicalPorts(name, namespace string) {
198 log.Info("DeleteLogicalPorts")
199 logicalPort := fmt.Sprintf("%s_%s", namespace, name)
201 // get the list of logical ports from OVN
202 stdout, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
203 "--columns=name", "find", "logical_switch_port", "external_ids:pod=true")
205 log.Error(err, "Error in obtaining list of logical ports ", "stdout", stdout, "stderr", stderr)
208 existingLogicalPorts := strings.Fields(stdout)
209 for _, existingPort := range existingLogicalPorts {
210 if strings.Contains(existingPort, logicalPort) {
211 // found, delete this logical port
212 log.Info("Deleting", "Port", existingPort)
213 stdout, stderr, err := RunOVNNbctl("--if-exists", "lsp-del",
216 log.Error(err, "Error in deleting pod's logical port ", "stdout", stdout, "stderr", stderr)
223 // CreateNetwork in OVN controller
224 func (oc *Controller) CreateNetwork(cr *k8sv1alpha1.Network) error {
225 var stdout, stderr string
227 // Currently only these fields are supported
229 subnet := cr.Spec.Ipv4Subnets[0].Subnet
230 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
231 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
233 gatewayIPMask, err := createOvnLS(name, subnet, gatewayIP, excludeIps)
238 routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtos-"+name, "mac")
240 log.Error(err, "Failed to get logical router port", "stderr", stderr)
245 newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
246 routerMac = fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
249 _, stderr, err = RunOVNNbctl("--wait=hv", "--may-exist", "lrp-add", ovn4nfvRouterName, "rtos-"+name, routerMac, gatewayIPMask)
251 log.Error(err, "Failed to add logical port to router", "stderr", stderr)
255 // Connect the switch to the router.
256 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+"\"")
258 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
265 // DeleteNetwork in OVN controller
266 func (oc *Controller) DeleteNetwork(cr *k8sv1alpha1.Network) error {
269 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "lrp-del", "rtos-"+name)
271 log.Error(err, "Failed to delete router port", "name", name, "stdout", stdout, "stderr", stderr)
274 stdout, stderr, err = RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
276 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
282 // CreateProviderNetwork in OVN controller
283 func (oc *Controller) CreateProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
284 var stdout, stderr string
286 // Currently only these fields are supported
288 subnet := cr.Spec.Ipv4Subnets[0].Subnet
289 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
290 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
291 _, err := createOvnLS(name, subnet, gatewayIP, excludeIps)
296 // Add localnet port.
297 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "lsp-add", name, "server-localnet_"+name, "--",
298 "lsp-set-addresses", "server-localnet_"+name, "unknown", "--",
299 "lsp-set-type", "server-localnet_"+name, "localnet", "--",
300 "lsp-set-options", "server-localnet_"+name, "network_name=nw_"+name)
302 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
309 // DeleteProviderNetwork in OVN controller
310 func (oc *Controller) DeleteProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
313 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
315 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
321 // FindLogicalSwitch returns true if switch exists
322 func (oc *Controller) FindLogicalSwitch(name string) bool {
323 // get logical switch from OVN
324 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
325 "--columns=name", "find", "logical_switch", "name="+name)
327 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
330 if strings.Compare(name, output) == 0 {
336 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
337 var gatewayIPMaskStr, stderr string
340 if gatewayIPMaskStr, ok = oc.gatewayCache[logicalSwitch]; !ok {
341 gatewayIPMaskStr, stderr, err = RunOVNNbctl("--if-exists",
342 "get", "logical_switch", logicalSwitch,
343 "external_ids:gateway_ip")
345 log.Error(err, "Failed to get gateway IP", "stderr", stderr, "gatewayIPMaskStr", gatewayIPMaskStr)
348 if gatewayIPMaskStr == "" {
349 return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
352 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
354 gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
355 if len(gatewayIPMask) != 2 {
356 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR: %s",
359 gatewayIP := gatewayIPMask[0]
360 mask := gatewayIPMask[1]
361 return gatewayIP, mask, nil
364 func (oc *Controller) addNodeLogicalPortWithSwitch(logicalSwitch, portName string) (ipAddr, macAddr string, r error) {
365 var out, stderr string
368 log.V(1).Info("Creating Node logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
370 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
371 "--may-exist", "lsp-add", logicalSwitch, portName,
372 "--", "lsp-set-addresses",
375 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
381 out, stderr, err = RunOVNNbctl("get",
382 "logical_switch_port", portName, "dynamic_addresses")
384 if err == nil && out != "[]" {
388 log.Error(err, "Error while obtaining addresses for", "portName", portName)
391 time.Sleep(time.Second)
395 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
399 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
400 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
401 outStr := strings.TrimLeft(out, `[`)
402 outStr = strings.TrimRight(outStr, `]`)
403 outStr = strings.Trim(outStr, `"`)
404 addresses := strings.Split(outStr, " ")
405 if len(addresses) != 2 {
406 log.Info("Error while obtaining addresses for", "portName", portName)
410 _, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
412 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
416 ipAddr = fmt.Sprintf("%s/%s", addresses[1], mask)
417 macAddr = fmt.Sprintf("%s", addresses[0])
419 return ipAddr, macAddr, nil
422 func (oc *Controller) getNodeLogicalPortIPAddr(pod *kapi.Pod) (ipAddress string, r error) {
423 var out, stderr, nodeName, portName string
426 nodeName = strings.ToLower(pod.Spec.NodeName)
427 portName = config.GetNodeIntfName(nodeName)
429 log.V(1).Info("Get Node logical port", "pod", pod.GetName(), "node", nodeName, "portName", portName)
433 out, stderr, err = RunOVNNbctl("get",
434 "logical_switch_port", portName, "dynamic_addresses")
436 if err == nil && out != "[]" {
440 log.Error(err, "Error while obtaining addresses for", "portName", portName)
443 time.Sleep(time.Second)
447 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
451 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
452 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
453 outStr := strings.TrimLeft(out, `[`)
454 outStr = strings.TrimRight(outStr, `]`)
455 outStr = strings.Trim(outStr, `"`)
456 addresses := strings.Split(outStr, " ")
457 if len(addresses) != 2 {
458 log.Info("Error while obtaining addresses for", "portName", portName)
462 ipAddr := fmt.Sprintf("%s", addresses[1])
463 log.V(1).Info("Get Node logical port", "pod", pod.GetName(), "node", nodeName, "portName", portName, "Node port IP", ipAddr)
468 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, portName string) (annotation string) {
469 var out, stderr string
472 if pod.Spec.HostNetwork {
476 log.V(1).Info("Creating logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
478 if ipAddress != "" && macAddress != "" {
481 if ipAddress != "" && macAddress == "" {
482 macAddress = generateMac()
487 out, stderr, err = RunOVNNbctl("--may-exist", "lsp-add",
488 logicalSwitch, portName, "--", "lsp-set-addresses", portName,
489 fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists",
490 "clear", "logical_switch_port", portName, "dynamic_addresses", "--", "set",
491 "logical_switch_port", portName,
492 "external-ids:namespace="+pod.Namespace,
493 "external-ids:logical_switch="+logicalSwitch,
494 "external-ids:pod=true")
496 log.Error(err, "Failed to add logical port to switch", "out", out, "stderr", stderr)
500 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
501 "--may-exist", "lsp-add", logicalSwitch, portName,
502 "--", "lsp-set-addresses",
503 portName, "dynamic", "--", "set",
504 "logical_switch_port", portName,
505 "external-ids:namespace="+pod.Namespace,
506 "external-ids:logical_switch="+logicalSwitch,
507 "external-ids:pod=true")
509 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
517 out, stderr, err = RunOVNNbctl("get",
518 "logical_switch_port", portName, "addresses")
520 out, stderr, err = RunOVNNbctl("get",
521 "logical_switch_port", portName, "dynamic_addresses")
523 if err == nil && out != "[]" {
527 log.Error(err, "Error while obtaining addresses for", "portName", portName)
530 time.Sleep(time.Second)
534 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
538 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
539 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
540 outStr := strings.TrimLeft(out, `[`)
541 outStr = strings.TrimRight(outStr, `]`)
542 outStr = strings.Trim(outStr, `"`)
543 addresses := strings.Split(outStr, " ")
544 if len(addresses) != 2 {
545 log.Info("Error while obtaining addresses for", "portName", portName)
549 _, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
551 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
555 gatewayIP, err := oc.getNodeLogicalPortIPAddr(pod)
557 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
561 annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)