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 if ns.NetType == "" || ns.NetType != "provider" {
103 ns.NetType = "virtual"
105 if ns.NetType == "provider" {
106 if ns.IPAddress == "" {
107 log.Info("ipAddress must be provided for netType Provider")
110 if ns.DefaultGateway == "true" {
111 log.Info("defaultGateway not supported for provider network - Use ovnNetworkRoutes to add routes")
116 outStr = oc.addLogicalPortWithSwitch(pod, ns.Name, ns.IPAddress, ns.MacAddress, ns.Interface, ns.NetType)
120 last := len(outStr) - 1
121 tmpString := outStr[:last]
122 tmpString += "," + "\\\"defaultGateway\\\":" + "\\\"" + ns.DefaultGateway + "\\\""
123 tmpString += "," + "\\\"interface\\\":" + "\\\"" + ns.Interface + "\\\"}"
124 ovnString += tmpString
127 last := len(ovnString) - 1
128 ovnString = ovnString[:last]
130 key = Ovn4nfvAnnotationTag
135 // DeleteLogicalPorts deletes the OVN ports for the pod
136 func (oc *Controller) DeleteLogicalPorts(name, namespace string) {
138 logicalPort := fmt.Sprintf("%s_%s", namespace, name)
140 // get the list of logical ports from OVN
141 stdout, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
142 "--columns=name", "find", "logical_switch_port", "external_ids:pod=true")
144 log.Error(err, "Error in obtaining list of logical ports ", "stdout", stdout, "stderr", stderr)
147 existingLogicalPorts := strings.Fields(stdout)
148 for _, existingPort := range existingLogicalPorts {
149 if strings.Contains(existingPort, logicalPort) {
150 // found, delete this logical port
151 log.V(1).Info("Deleting", "Port", existingPort)
152 stdout, stderr, err := RunOVNNbctl("--if-exists", "lsp-del",
155 log.Error(err, "Error in deleting pod's logical port ", "stdout", stdout, "stderr", stderr)
162 // CreateNetwork in OVN controller
163 func (oc *Controller) CreateNetwork(cr *k8sv1alpha1.Network) error {
164 var stdout, stderr string
166 // Currently only these fields are supported
168 subnet := cr.Spec.Ipv4Subnets[0].Subnet
169 gatewayIP := cr.Spec.Ipv4Subnets[0].Gateway
170 excludeIps := cr.Spec.Ipv4Subnets[0].ExcludeIps
172 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
173 "--columns=name", "find", "logical_switch", "name="+name)
175 log.Error(err, "Error in reading logical switch", "stderr", stderr)
179 if strings.Compare(name, output) == 0 {
180 log.V(1).Info("Logical Switch already exists, delete first to update/recreate", "name", name)
184 _, cidr, err := net.ParseCIDR(subnet)
186 log.Error(err, "ovnNetwork '%s' invalid subnet CIDR", "name", name)
190 firstIP := NextIP(cidr.IP)
191 n, _ := cidr.Mask.Size()
193 var gatewayIPMask string
196 gwIP, _, err = net.ParseCIDR(gatewayIP)
198 // Check if this is a valid IP address
199 gwIP = net.ParseIP(gatewayIP)
202 // If no valid Gateway use the first IP address for GatewayIP
204 gatewayIPMask = fmt.Sprintf("%s/%d", firstIP.String(), n)
206 gatewayIPMask = fmt.Sprintf("%s/%d", gwIP.String(), n)
209 // Create a logical switch and set its subnet.
210 if excludeIps != "" {
211 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)
213 stdout, stderr, err = RunOVNNbctl("--wait=hv", "--", "--may-exist", "ls-add", name, "--", "set", "logical_switch", name, "other-config:subnet="+subnet, "external-ids:gateway_ip="+gatewayIPMask)
216 log.Error(err, "Failed to create a logical switch", "name", name, "stdout", stdout, "stderr", stderr)
220 routerMac, stderr, err := RunOVNNbctl("--if-exist", "get", "logical_router_port", "rtos-"+name, "mac")
222 log.Error(err, "Failed to get logical router port", "stderr", stderr)
227 newRand := rand.New(rand.NewSource(time.Now().UnixNano()))
228 routerMac = fmt.Sprintf("%s:%02x:%02x:%02x", prefix, newRand.Intn(255), newRand.Intn(255), newRand.Intn(255))
231 _, stderr, err = RunOVNNbctl("--wait=hv", "--may-exist", "lrp-add", ovn4nfvRouterName, "rtos-"+name, routerMac, gatewayIPMask)
233 log.Error(err, "Failed to add logical port to router", "stderr", stderr)
237 // Connect the switch to the router.
238 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+"\"")
240 log.Error(err, "Failed to add logical port to switch", "stderr", stderr, "stdout", stdout)
247 // DeleteNetwork in OVN controller
248 func (oc *Controller) DeleteNetwork(cr *k8sv1alpha1.Network) error {
251 stdout, stderr, err := RunOVNNbctl("--if-exist", "--wait=hv", "lrp-del", "rtos-"+name)
253 log.Error(err, "Failed to delete router port", "name", name, "stdout", stdout, "stderr", stderr)
256 stdout, stderr, err = RunOVNNbctl("--if-exist", "--wait=hv", "ls-del", name)
258 log.Error(err, "Failed to delete switch", "name", name, "stdout", stdout, "stderr", stderr)
264 // FindLogicalSwitch returns true if switch exists
265 func (oc *Controller) FindLogicalSwitch(name string) bool {
266 // get logical switch from OVN
267 output, stderr, err := RunOVNNbctl("--data=bare", "--no-heading",
268 "--columns=name", "find", "logical_switch", "name="+name)
270 log.Error(err, "Error in obtaining list of logical switch", "stderr", stderr)
273 if strings.Compare(name, output) == 0 {
279 func (oc *Controller) getGatewayFromSwitch(logicalSwitch string) (string, string, error) {
280 var gatewayIPMaskStr, stderr string
283 log.V(1).Info("getGatewayFromSwitch", "logicalSwitch", logicalSwitch)
284 if gatewayIPMaskStr, ok = oc.gatewayCache[logicalSwitch]; !ok {
285 gatewayIPMaskStr, stderr, err = RunOVNNbctl("--if-exists",
286 "get", "logical_switch", logicalSwitch,
287 "external_ids:gateway_ip")
289 log.Error(err, "Failed to get gateway IP", "stderr", stderr, "gatewayIPMaskStr", gatewayIPMaskStr)
292 if gatewayIPMaskStr == "" {
293 return "", "", fmt.Errorf("Empty gateway IP in logical switch %s",
296 oc.gatewayCache[logicalSwitch] = gatewayIPMaskStr
298 gatewayIPMask := strings.Split(gatewayIPMaskStr, "/")
299 if len(gatewayIPMask) != 2 {
300 return "", "", fmt.Errorf("Failed to get IP and Mask from gateway CIDR: %s",
303 gatewayIP := gatewayIPMask[0]
304 mask := gatewayIPMask[1]
305 return gatewayIP, mask, nil
308 func (oc *Controller) addLogicalPortWithSwitch(pod *kapi.Pod, logicalSwitch, ipAddress, macAddress, interfaceName, netType string) (annotation string) {
309 var out, stderr string
312 if pod.Spec.HostNetwork {
317 if interfaceName != "" {
318 portName = fmt.Sprintf("%s_%s_%s", pod.Namespace, pod.Name, interfaceName)
323 log.V(1).Info("Creating logical port for on switch", "portName", portName, "logicalSwitch", logicalSwitch)
325 if ipAddress != "" && macAddress != "" {
328 if ipAddress != "" && macAddress == "" {
329 macAddress = generateMac()
334 out, stderr, err = RunOVNNbctl("--may-exist", "lsp-add",
335 logicalSwitch, portName, "--", "lsp-set-addresses", portName,
336 fmt.Sprintf("%s %s", macAddress, ipAddress), "--", "--if-exists",
337 "clear", "logical_switch_port", portName, "dynamic_addresses", "--", "set",
338 "logical_switch_port", portName,
339 "external-ids:namespace="+pod.Namespace,
340 "external-ids:logical_switch="+logicalSwitch,
341 "external-ids:pod=true")
343 log.Error(err, "Failed to add logical port to switch", "out", out, "stderr", stderr)
347 out, stderr, err = RunOVNNbctl("--wait=sb", "--",
348 "--may-exist", "lsp-add", logicalSwitch, portName,
349 "--", "lsp-set-addresses",
350 portName, "dynamic", "--", "set",
351 "logical_switch_port", portName,
352 "external-ids:namespace="+pod.Namespace,
353 "external-ids:logical_switch="+logicalSwitch,
354 "external-ids:pod=true")
356 log.Error(err, "Error while creating logical port %s ", "portName", portName, "stdout", out, "stderr", stderr)
364 out, stderr, err = RunOVNNbctl("get",
365 "logical_switch_port", portName, "addresses")
367 out, stderr, err = RunOVNNbctl("get",
368 "logical_switch_port", portName, "dynamic_addresses")
370 if err == nil && out != "[]" {
374 log.Error(err, "Error while obtaining addresses for", "portName", portName)
377 time.Sleep(time.Second)
381 log.Error(err, "Error while obtaining addresses for", "portName", portName, "stdout", out, "stderr", stderr)
385 // static addresses have format ["0a:00:00:00:00:01 192.168.1.3"], while
386 // dynamic addresses have format "0a:00:00:00:00:01 192.168.1.3".
387 outStr := strings.TrimLeft(out, `[`)
388 outStr = strings.TrimRight(outStr, `]`)
389 outStr = strings.Trim(outStr, `"`)
390 addresses := strings.Split(outStr, " ")
391 if len(addresses) != 2 {
392 log.Info("Error while obtaining addresses for", "portName", portName)
396 if netType == "virtual" {
397 gatewayIP, mask, err := oc.getGatewayFromSwitch(logicalSwitch)
399 log.Error(err, "Error obtaining gateway address for switch", "logicalSwitch", logicalSwitch)
402 annotation = fmt.Sprintf(`{\"ip_address\":\"%s/%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], mask, addresses[0], gatewayIP)
404 annotation = fmt.Sprintf(`{\"ip_address\":\"%s\", \"mac_address\":\"%s\", \"gateway_ip\": \"%s\"}`, addresses[1], addresses[0], "")