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 {
69 var ovnCtl *Controller
71 // NewOvnController creates a new OVN controller for creating logical networks
72 func NewOvnController(exec kexec.Interface) (*Controller, error) {
77 if err := SetExec(exec); err != nil {
78 log.Error(err, "Failed to initialize exec helper")
82 if err := GetOvnNetConf(); err != nil {
83 log.Error(err, "nfn-operator OVN Network configmap is not set")
86 if err := SetupOvnUtils(); err != nil {
87 log.Error(err, "Failed to initialize OVN State")
92 gatewayCache: make(map[string]string),
97 // GetOvnController returns OVN controller for creating logical networks
98 func GetOvnController() (*Controller, error) {
102 return nil, fmt.Errorf("OVN Controller not initialized")
105 func (oc *Controller) AddNodeLogicalPorts(node string) (ipAddr, macAddr string, err error) {
106 nodeName := strings.ToLower(node)
107 portName := config.GetNodeIntfName(nodeName)
109 log.V(1).Info("Creating Node logical port", "node", nodeName, "portName", portName)
111 ipAddr, macmacAddr, err := oc.addNodeLogicalPortWithSwitch(Ovn4nfvDefaultNw, portName)
116 return ipAddr, macmacAddr, nil
119 // AddLogicalPorts adds ports to the Pod
120 func (oc *Controller) AddLogicalPorts(pod *kapi.Pod, ovnNetObjs []map[string]interface{}) (key, value string) {
122 if pod.Spec.HostNetwork {
126 if _, ok := pod.Annotations[Ovn4nfvAnnotationTag]; ok {
127 log.V(1).Info("AddLogicalPorts : Pod annotation found")
131 var ovnString, outStr string
132 var defaultInterface bool
136 for _, net := range ovnNetObjs {
137 err := mapstructure.Decode(net, &ns)
139 log.Error(err, "mapstruct error", "network", net)
142 if !oc.FindLogicalSwitch(ns.Name) {
143 log.Info("Logical Switch not found")
146 if ns.Name == Ovn4nfvDefaultNw {
147 defaultInterface = true
149 if ns.Interface == "" && ns.Name != Ovn4nfvDefaultNw {
150 log.Info("Interface name must be provided")
153 if ns.DefaultGateway == "" {
154 ns.DefaultGateway = "false"
157 if ns.Interface != "" {
158 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, ns.Interface)
160 portName = fmt.Sprintf("%s_%s", pod.Namespace, pod.Name)
163 outStr = oc.addLogicalPortWithSwitch(pod, ns.Name, ns.IPAddress, ns.MacAddress, ns.GWIPaddress, portName)
167 last := len(outStr) - 1
168 tmpString := outStr[:last]
169 tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + ns.DefaultGateway + "\\\""
170 tmpString += "," + "\\\"interface\\\":" + "\\\"" + ns.Interface + "\\\"}"
171 ovnString += tmpString
175 if defaultInterface == false {
176 // Add Default interface
177 portName := fmt.Sprintf("%s_%s", pod.Namespace, pod.Name)
178 outStr = oc.addLogicalPortWithSwitch(pod, Ovn4nfvDefaultNw, "", "", "", portName)
182 last := len(outStr) - 1
183 tmpString := outStr[:last]
184 tmpString += "," + "\\\"interface\\\":" + "\\\"" + "*" + "\\\"}"
185 ovnString += tmpString
188 last = len(ovnString) - 1
189 ovnString = ovnString[:last]
191 key = Ovn4nfvAnnotationTag
196 // DeleteLogicalPorts deletes the OVN ports for the pod
197 func (oc *Controller) DeleteLogicalPorts(name, namespace string) {
199 log.Info("DeleteLogicalPorts")
200 logicalPort := fmt.Sprintf("%s_%s", namespace, name)
202 // get the list of logical ports from OVN
203 stdout, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
204 "--columns=name", "find", "logical_switch_port", "external_ids:pod=true")
206 log.Error(err, "Error in obtaining list of logical ports ", "stdout", stdout, "stderr", stderr)
209 existingLogicalPorts := strings.Fields(stdout)
210 for _, existingPort := range existingLogicalPorts {
211 if strings.Contains(existingPort, logicalPort) {
212 // found, delete this logical port
213 log.Info("Deleting", "Port", existingPort)
214 stdout, stderr, err := RunOVNNbctl("--if-exists", "lsp-del",
217 log.Error(err, "Error in deleting pod's logical port ", "stdout", stdout, "stderr", stderr)
224 // CreateNetwork in OVN controller
225 func (oc *Controller) CreateNetwork(cr *k8sv1alpha1.Network) error {
226 var stdout, stderr string
228 // Currently only these fields are supported
230 subnet := cr.Spec.Ipv4Subnets[0].Subnet
231 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
232 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
234 gatewayIPMask, err := createOvnLS(name, subnet, gatewayIP, excludeIps)
239 routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtos-"+name, "mac")
241 log.Error(err, "Failed to get logical router port", "stderr", stderr)
246 newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
247 routerMac = fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
250 _, stderr, err = RunOVNNbctl("--wait=hv", "--may-exist", "lrp-add", ovn4nfvRouterName, "rtos-"+name, routerMac, gatewayIPMask)
252 log.Error(err, "Failed to add logical port to router", "stderr", stderr)
256 // Connect the switch to the router.
257 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+"\"")
259 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
266 // DeleteNetwork in OVN controller
267 func (oc *Controller) DeleteNetwork(cr *k8sv1alpha1.Network) error {
270 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "lrp-del", "rtos-"+name)
272 log.Error(err, "Failed to delete router port", "name", name, "stdout", stdout, "stderr", stderr)
275 stdout, stderr, err = RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
277 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
283 // CreateProviderNetwork in OVN controller
284 func (oc *Controller) CreateProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
285 var stdout, stderr string
287 // Currently only these fields are supported
289 subnet := cr.Spec.Ipv4Subnets[0].Subnet
290 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
291 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
292 _, err := createOvnLS(name, subnet, gatewayIP, excludeIps)
297 // Add localnet port.
298 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "lsp-add", name, "server-localnet_"+name, "--",
299 "lsp-set-addresses", "server-localnet_"+name, "unknown", "--",
300 "lsp-set-type", "server-localnet_"+name, "localnet", "--",
301 "lsp-set-options", "server-localnet_"+name, "network_name=nw_"+name)
303 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
310 // DeleteProviderNetwork in OVN controller
311 func (oc *Controller) DeleteProviderNetwork(cr *k8sv1alpha1.ProviderNetwork) error {
314 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
316 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
322 // FindLogicalSwitch returns true if switch exists
323 func (oc *Controller) FindLogicalSwitch(name string) bool {
324 // get logical switch from OVN
325 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
326 "--columns=name", "find", "logical_switch", "name="+name)
328 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
331 if strings.Compare(name, output) == 0 {
337 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
338 var gatewayIPMaskStr, stderr string
341 if gatewayIPMaskStr, ok = oc.gatewayCache[logicalSwitch]; !ok {
342 gatewayIPMaskStr, stderr, err = RunOVNNbctl("--if-exists",
343 "get", "logical_switch", logicalSwitch,
344 "external_ids:gateway_ip")
346 log.Error(err, "Failed to get gateway IP", "stderr", stderr, "gatewayIPMaskStr", gatewayIPMaskStr)
349 if gatewayIPMaskStr == "" {
350 return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
353 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
355 gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
356 if len(gatewayIPMask) != 2 {
357 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR: %s",
360 gatewayIP := gatewayIPMask[0]
361 mask := gatewayIPMask[1]
362 return gatewayIP, mask, nil
365 func (oc *Controller) addNodeLogicalPortWithSwitch(logicalSwitch, portName string) (ipAddr, macAddr string, r error) {
366 var out, stderr string
369 log.V(1).Info("Creating Node logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
371 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
372 "--may-exist", "lsp-add", logicalSwitch, portName,
373 "--", "lsp-set-addresses",
376 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
382 out, stderr, err = RunOVNNbctl("get",
383 "logical_switch_port", portName, "dynamic_addresses")
385 if err == nil && out != "[]" {
389 log.Error(err, "Error while obtaining addresses for", "portName", portName)
392 time.Sleep(time.Second)
396 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
400 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
401 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
402 outStr := strings.TrimLeft(out, `[`)
403 outStr = strings.TrimRight(outStr, `]`)
404 outStr = strings.Trim(outStr, `"`)
405 addresses := strings.Split(outStr, " ")
406 if len(addresses) != 2 {
407 log.Info("Error while obtaining addresses for", "portName", portName)
411 _, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
413 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
417 ipAddr = fmt.Sprintf("%s/%s", addresses[1], mask)
418 macAddr = fmt.Sprintf("%s", addresses[0])
420 return ipAddr, macAddr, nil
423 func (oc *Controller) getNodeLogicalPortIPAddr(pod *kapi.Pod) (ipAddress string, r error) {
424 var out, stderr, nodeName, portName string
427 nodeName = strings.ToLower(pod.Spec.NodeName)
428 portName = config.GetNodeIntfName(nodeName)
430 log.V(1).Info("Get Node logical port", "pod", pod.GetName(), "node", nodeName, "portName", portName)
434 out, stderr, err = RunOVNNbctl("get",
435 "logical_switch_port", portName, "dynamic_addresses")
437 if err == nil && out != "[]" {
441 log.Error(err, "Error while obtaining addresses for", "portName", portName)
444 time.Sleep(time.Second)
448 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
452 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
453 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
454 outStr := strings.TrimLeft(out, `[`)
455 outStr = strings.TrimRight(outStr, `]`)
456 outStr = strings.Trim(outStr, `"`)
457 addresses := strings.Split(outStr, " ")
458 if len(addresses) != 2 {
459 log.Info("Error while obtaining addresses for", "portName", portName)
463 ipAddr := fmt.Sprintf("%s", addresses[1])
464 log.V(1).Info("Get Node logical port", "pod", pod.GetName(), "node", nodeName, "portName", portName, "Node port IP", ipAddr)
469 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, gwipAddress, portName string) (annotation string) {
470 var out, stderr string
473 if pod.Spec.HostNetwork {
477 log.V(1).Info("Creating logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
479 if ipAddress != "" && macAddress != "" {
482 if ipAddress != "" && macAddress == "" {
483 macAddress = generateMac()
488 out, stderr, err = RunOVNNbctl("--may-exist", "lsp-add",
489 logicalSwitch, portName, "--", "lsp-set-addresses", portName,
490 fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists",
491 "clear", "logical_switch_port", portName, "dynamic_addresses", "--", "set",
492 "logical_switch_port", portName,
493 "external-ids:namespace="+pod.Namespace,
494 "external-ids:logical_switch="+logicalSwitch,
495 "external-ids:pod=true")
497 log.Error(err, "Failed to add logical port to switch", "out", out, "stderr", stderr)
501 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
502 "--may-exist", "lsp-add", logicalSwitch, portName,
503 "--", "lsp-set-addresses",
504 portName, "dynamic", "--", "set",
505 "logical_switch_port", portName,
506 "external-ids:namespace="+pod.Namespace,
507 "external-ids:logical_switch="+logicalSwitch,
508 "external-ids:pod=true")
510 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
518 out, stderr, err = RunOVNNbctl("get",
519 "logical_switch_port", portName, "addresses")
521 out, stderr, err = RunOVNNbctl("get",
522 "logical_switch_port", portName, "dynamic_addresses")
524 if err == nil && out != "[]" {
528 log.Error(err, "Error while obtaining addresses for", "portName", portName)
531 time.Sleep(time.Second)
535 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
539 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
540 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
541 outStr := strings.TrimLeft(out, `[`)
542 outStr = strings.TrimRight(outStr, `]`)
543 outStr = strings.Trim(outStr, `"`)
544 addresses := strings.Split(outStr, " ")
545 if len(addresses) != 2 {
546 log.Info("Error while obtaining addresses for", "portName", portName)
550 _, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
552 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
557 if gwipAddress != "" {
558 gatewayIP = gwipAddress
560 gatewayIP, err = oc.getNodeLogicalPortIPAddr(pod)
562 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
567 annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)