def sync_pods(self, pods): expected_logical_ports = set() pods = pods.get('items', []) for pod in pods: pod_name = pod['metadata']['name'] namespace = pod['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) expected_logical_ports.add(logical_port) try: existing_logical_ports = ovn_nbctl( "--data=bare", "--no-heading", "--columns=name", "find", "logical_switch_port", "external_id:pod=true").split() existing_logical_ports = set(existing_logical_ports) except Exception as e: vlog.err("sync_pods: find failed %s" % (str(e))) return for logical_port in existing_logical_ports - expected_logical_ports: try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception as e: vlog.err("sync_pods: failed to delete logical_port %s" % (logical_port)) continue vlog.info("sync_pods: Deleted logical port %s" % (logical_port))
def _create_load_balancer_vip(self, load_balancer, service_ip, ips, port, target_port, protocol): # With service_ip:port as a VIP, create an entry in 'load_balancer' vlog.dbg( "received event to create/modify load_balancer (%s) vip " "service_ip=%s, ips=%s, port=%s, target_port=%s, protocol=%s" % (load_balancer, service_ip, ips, port, target_port, protocol)) if not port or not target_port or not protocol or not load_balancer: return # key is of the form "IP:port" (with quotes around) key = "\"" + service_ip + ":" + str(port) + "\"" if not ips: try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", key) except Exception as e: vlog.err("_create_load_balancer_vip remove: (%s)" % (str(e))) return # target is of the form "IP1:port, IP2:port, IP3:port" target_endpoints = ",".join( ["%s:%s" % (ip, target_port) for ip in ips]) target = "\"" + target_endpoints + "\"" try: ovn_nbctl("set", "load_balancer", load_balancer, "vips:" + key + "=" + target) except Exception as e: vlog.err("_create_load_balancer_vip add: (%s)" % (str(e)))
def sync_pods(self, pods): expected_logical_ports = set() pods = pods.get('items', []) for pod in pods: pod_name = pod['metadata']['name'] namespace = pod['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) expected_logical_ports.add(logical_port) try: existing_logical_ports = ovn_nbctl("--data=bare", "--no-heading", "--columns=name", "find", "logical_switch_port", "external_id:pod=true").split() existing_logical_ports = set(existing_logical_ports) except Exception as e: vlog.err("sync_pods: find failed %s" % (str(e))) return for logical_port in existing_logical_ports - expected_logical_ports: try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception as e: vlog.err("sync_pods: failed to delete logical_port %s" % (logical_port)) continue vlog.info("sync_pods: Deleted logical port %s" % (logical_port))
def sync_pods(self, pods): expected_logical_ports = set() pods = pods.get('items', []) for pod in pods: pod_name = pod['metadata']['name'] namespace = pod['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) annotations = pod['metadata']['annotations'] expected_logical_ports.add(logical_port) # We should sync the container port names as there are no # guarantees that a endpoint creation event will become after # all the pods creation events. ip_address = self._get_ip_address_from_annotations(annotations) if ip_address: self._add_k8s_l4_port_name_cache(pod, ip_address) try: existing_logical_ports = ovn_nbctl( "--data=bare", "--no-heading", "--columns=name", "find", "logical_switch_port", "external-ids:pod=true").split() existing_logical_ports = set(existing_logical_ports) except Exception as e: vlog.err("sync_pods: find failed %s" % (str(e))) return for logical_port in existing_logical_ports - expected_logical_ports: try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception as e: vlog.err("sync_pods: failed to delete logical_port %s" % (logical_port)) continue vlog.info("sync_pods: Deleted logical port %s" % (logical_port))
def _delete_load_balancer_vip(self, load_balancer, vip): # Remove the 'vip' from the 'load_balancer'. try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", vip) vlog.info("deleted vip %s from %s" % (vip, load_balancer)) except Exception as e: vlog.err("_delete_load_balancer_vip: failed to remove vip %s " " from %s (%s)" % (vip, load_balancer, str(e)))
def _create_load_balancer_vip(self, namespace, load_balancer, service_ip, ips, port, target_port, protocol): # With service_ip:port as a VIP, create an entry in 'load_balancer' vlog.dbg( "received event to create/modify load_balancer (%s) vip " "service_ip=%s, ips=%s, port=%s, target_port=%s, protocol=%s" % (load_balancer, service_ip, ips, port, target_port, protocol)) if not port or not target_port or not protocol or not load_balancer: return # key is of the form "IP:port" (with quotes around) key = "\"" + service_ip + ":" + str(port) + "\"" if not ips: try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", key) except Exception as e: vlog.err("_create_load_balancer_vip remove: (%s)" % (str(e))) return if target_port.isdigit(): # target is of the form "IP1:port, IP2:port, IP3:port" target_endpoints = ",".join( ["%s:%s" % (ip, target_port) for ip in ips]) else: # 'target_port' is a string. We should get its number # from the cache. if not self.port_name_cache.get(namespace): vlog.warn("targetPort of %s in ns %s does not have an " "associated port. Ignoring endpoint creation." % (target_port, namespace)) return target_endpoint_list = [] for ip in ips: if not self.port_name_cache[namespace].get(ip): continue num_port = self.port_name_cache[namespace][ip].get(target_port) if not num_port: continue target_endpoint_list.append("%s:%s" % (ip, num_port)) if not target_endpoint_list: vlog.warn("targetPort of %s in ns %s does not have any " "associated ports. Ignoring endpoint creation." % (target_port, namespace)) return target_endpoints = ",".join(target_endpoint_list) target = "\"" + target_endpoints + "\"" try: ovn_nbctl("set", "load_balancer", load_balancer, "vips:" + key + "=" + target) except Exception as e: vlog.err("_create_load_balancer_vip add: (%s)" % (str(e)))
def _create_load_balancer_vip(self, namespace, load_balancer, service_ip, ips, port, target_port, protocol): # With service_ip:port as a VIP, create an entry in 'load_balancer' vlog.dbg("received event to create/modify load_balancer (%s) vip " "service_ip=%s, ips=%s, port=%s, target_port=%s, protocol=%s" % (load_balancer, service_ip, ips, port, target_port, protocol)) if not port or not target_port or not protocol or not load_balancer: return # key is of the form "IP:port" (with quotes around) key = "\"" + service_ip + ":" + str(port) + "\"" if not ips: try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", key) except Exception as e: vlog.err("_create_load_balancer_vip remove: (%s)" % (str(e))) return if target_port.isdigit(): # target is of the form "IP1:port, IP2:port, IP3:port" target_endpoints = ",".join(["%s:%s" % (ip, target_port) for ip in ips]) else: # 'target_port' is a string. We should get its number # from the cache. if not self.port_name_cache.get(namespace): vlog.warn("targetPort of %s in ns %s does not have an " "associated port. Ignoring endpoint creation." % (target_port, namespace)) return target_endpoint_list = [] for ip in ips: if not self.port_name_cache[namespace].get(ip): continue num_port = self.port_name_cache[namespace][ip].get(target_port) if not num_port: continue target_endpoint_list.append("%s:%s" % (ip, num_port)) if not target_endpoint_list: vlog.warn("targetPort of %s in ns %s does not have any " "associated ports. Ignoring endpoint creation." % (target_port, namespace)) return target_endpoints = ",".join(target_endpoint_list) target = "\"" + target_endpoints + "\"" try: ovn_nbctl("set", "load_balancer", load_balancer, "vips:" + key + "=" + target) except Exception as e: vlog.err("_create_load_balancer_vip add: (%s)" % (str(e)))
def ovn_init_overlay(): OVN_NB = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:ovn-nb").strip('"') if not OVN_NB: sys.exit("OVN central database's ip address not set") variables.OVN_NB = OVN_NB K8S_API_SERVER = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:k8s-api-server").strip('"') if not K8S_API_SERVER: sys.exit("K8S_API_SERVER not set") if not K8S_API_SERVER.startswith("http"): variables.K8S_API_SERVER = "http://%s" % K8S_API_SERVER else: variables.K8S_API_SERVER = K8S_API_SERVER K8S_CLUSTER_ROUTER = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "logical_router", "external_ids:k8s-cluster-router=yes", ) if not K8S_CLUSTER_ROUTER: sys.exit("K8S_CLUSTER_ROUTER not set") variables.K8S_CLUSTER_ROUTER = K8S_CLUSTER_ROUTER K8S_CLUSTER_LB_TCP = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-tcp=yes" ) if not K8S_CLUSTER_LB_TCP: sys.exit("K8S_CLUSTER_LB_TCP not set") variables.K8S_CLUSTER_LB_TCP = K8S_CLUSTER_LB_TCP K8S_CLUSTER_LB_UDP = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-udp=yes" ) if not K8S_CLUSTER_LB_UDP: sys.exit("K8S_CLUSTER_LB_UDP not set") variables.K8S_CLUSTER_LB_UDP = K8S_CLUSTER_LB_UDP K8S_NS_LB_TCP = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-ns-lb-tcp=yes" ) variables.K8S_NS_LB_TCP = K8S_NS_LB_TCP K8S_NS_LB_UDP = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-ns-lb-udp=yes" ) variables.K8S_NS_LB_UDP = K8S_NS_LB_UDP variables.OVN_MODE = "overlay"
def ovn_init_overlay(): OVN_NB = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:ovn-nb").strip('"') if not OVN_NB: sys.exit("OVN central database's ip address not set") variables.OVN_NB = OVN_NB K8S_API_SERVER = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:k8s-api-server").strip('"') if not K8S_API_SERVER: sys.exit("K8S_API_SERVER not set") if not K8S_API_SERVER.startswith("http"): variables.K8S_API_SERVER = "http://%s" % K8S_API_SERVER else: variables.K8S_API_SERVER = K8S_API_SERVER K8S_CLUSTER_ROUTER = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "logical_router", "external_ids:k8s-cluster-router=yes") if not K8S_CLUSTER_ROUTER: sys.exit("K8S_CLUSTER_ROUTER not set") variables.K8S_CLUSTER_ROUTER = K8S_CLUSTER_ROUTER K8S_CLUSTER_LB_TCP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-tcp=yes") if not K8S_CLUSTER_LB_TCP: sys.exit("K8S_CLUSTER_LB_TCP not set") variables.K8S_CLUSTER_LB_TCP = K8S_CLUSTER_LB_TCP K8S_CLUSTER_LB_UDP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-udp=yes") if not K8S_CLUSTER_LB_UDP: sys.exit("K8S_CLUSTER_LB_UDP not set") variables.K8S_CLUSTER_LB_UDP = K8S_CLUSTER_LB_UDP K8S_NS_LB_TCP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-ns-lb-tcp=yes") variables.K8S_NS_LB_TCP = K8S_NS_LB_TCP K8S_NS_LB_UDP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-ns-lb-udp=yes") variables.K8S_NS_LB_UDP = K8S_NS_LB_UDP variables.OVN_MODE = "overlay"
def _get_switch_gateway_ip(self, logical_switch): cached_logical_switch = self.logical_switch_cache.get(logical_switch, {}) if cached_logical_switch: gateway_ip_mask = cached_logical_switch.get('gateway_ip_mask') else: try: gateway_ip_mask = ovn_nbctl("--if-exists", "get", "logical_switch", logical_switch, "external_ids:gateway_ip" ).strip('"') except Exception as e: vlog.err("_get_switch_gateway_ip: failed to get gateway_ip %s" % (str(e))) return (None, None) try: (gateway_ip, mask) = gateway_ip_mask.split('/') except Exception as e: vlog.err("_get_switch_gateway_ip: failed to split ip/mask %s" % (gateway_ip_mask)) return (None, None) if not cached_logical_switch: self.logical_switch_cache[logical_switch] = {'gateway_ip_mask': gateway_ip_mask} return (gateway_ip, mask)
def _create_external_vip(self, external_ip, ips, port, target_port, protocol): # With external_ip:port as the VIP, create an entry in a gateway # load-balancer. # Get the gateway where we can add external_ip:port as a VIP. physical_gateway = self._get_ovn_external_ip_gateway() if not physical_gateway: return try: # Get the load-balancer instantiated in the gateway. external_id_key = protocol + "_lb_gateway_router" load_balancer = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:" + external_id_key + "=" + physical_gateway).strip('"') except Exception as e: vlog.err("_create_external_vip: get failed for" " %s (%s)" % (physical_gateway, str(e))) return if not load_balancer: vlog.warn("physical gateway %s does not have a load_balancer" % (physical_gateway)) # With external_ip:port as VIP, add an entry in 'load_balancer'. self._create_load_balancer_vip(load_balancer, external_ip, ips, port, target_port, protocol)
def _create_external_vip(self, namespace, external_ip, ips, port, target_port, protocol): # With external_ip:port as the VIP, create an entry in a gateway # load-balancer. # Get the gateway where we can add external_ip:port as a VIP. physical_gateway = self._get_ovn_external_ip_gateway() if not physical_gateway: return try: # Get the load-balancer instantiated in the gateway. external_id_key = protocol + "_lb_gateway_router" load_balancer = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:" + external_id_key + "=" + physical_gateway).strip('"') except Exception as e: vlog.err("_create_external_vip: get failed for" " %s (%s)" % (physical_gateway, str(e))) return if not load_balancer: vlog.warn("physical gateway %s does not have a load_balancer" % (physical_gateway)) # With external_ip:port as VIP, add an entry in 'load_balancer'. self._create_load_balancer_vip(namespace, load_balancer, external_ip, ips, port, target_port, protocol)
def _get_switch_gateway_ip(self, logical_switch): cached_logical_switch = self.logical_switch_cache.get( logical_switch, {}) if cached_logical_switch: gateway_ip_mask = cached_logical_switch.get('gateway_ip_mask') else: try: gateway_ip_mask = ovn_nbctl( "--if-exists", "get", "logical_switch", logical_switch, "external_ids:gateway_ip").strip('"') except Exception as e: vlog.err( "_get_switch_gateway_ip: failed to get gateway_ip %s" % (str(e))) return (None, None) try: (gateway_ip, mask) = gateway_ip_mask.split('/') except Exception as e: vlog.err("_get_switch_gateway_ip: failed to split ip/mask %s" % (gateway_ip_mask)) return (None, None) if not cached_logical_switch: self.logical_switch_cache[logical_switch] = { 'gateway_ip_mask': gateway_ip_mask } return (gateway_ip, mask)
def _create_gateways_vip(self, namespace, ips, port, target_port, protocol): # Each gateway has a separate load-balancer for N/S traffic physical_gateways = self._get_ovn_gateways() if not physical_gateways: return for physical_gateway in physical_gateways: # Go through each gateway to get its physical_ip and load-balancer. try: physical_ip = ovn_nbctl( "get", "logical_router", physical_gateway, "external_ids:physical_ip").strip('"') except Exception as e: vlog.err("_create_gateways_vip: get failed for" " %s (%s)" % (physical_gateway, str(e))) continue if not physical_ip: vlog.warn("physical gateway %s does not have physical ip" % (physical_ip)) continue try: external_id_key = protocol + "_lb_gateway_router" load_balancer = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:" + external_id_key + "=" + physical_gateway ).strip('"') except Exception as e: vlog.err("_create_gateways_vip: find failed for" " %s (%s)" % (physical_gateway, str(e))) continue if not load_balancer: vlog.warn("physical gateway %s does not have load_balancer" % (physical_gateway)) continue # With the physical_ip:port as the VIP, add an entry in # 'load_balancer'. self._create_load_balancer_vip(namespace, load_balancer, physical_ip, ips, port, target_port, protocol)
def delete_logical_port(self, event): data = event.metadata pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not pod_name: vlog.err("absent pod name in pod %s. " "unable to delete logical port" % data) return try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception: vlog.exception("failure in delete_logical_port: lsp-del") return vlog.info("deleted logical port %s" % logical_port)
def delete_logical_port(self, event): data = event.metadata pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not logical_port: vlog.err("absent pod name in pod %s. " "Not creating logical port" % (data)) return try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception as e: vlog.err("_delete_logical_port: lsp-add (%s)" % (str(e))) return vlog.dbg("deleted logical port %s" % (logical_port))
def _create_load_balancer_vip(self, service_type, service_ip, ips, port, target_port, protocol): vlog.dbg("received event to create load_balancer vip") if not port or not target_port or not protocol or not service_type: return load_balancer = "" if protocol == "TCP" and service_type == "ClusterIP": load_balancer = variables.K8S_CLUSTER_LB_TCP if protocol == "UDP" and service_type == "ClusterIP": load_balancer = variables.K8S_CLUSTER_LB_UDP if protocol == "TCP" and service_type == "NodePort": load_balancer = variables.K8S_NS_LB_TCP if protocol == "UDP" and service_type == "NodePort": load_balancer = variables.K8S_NS_LB_UDP if not load_balancer: return # key is of the form "IP:port" (with quotes around) key = "\"" + service_ip + ":" + str(port) + "\"" if not ips: try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", key) except Exception as e: vlog.err("_create_load_balancer_vip remove: (%s)" % (str(e))) return # target is of the form "IP1:port, IP2:port, IP3:port" target = "" for ip in ips: target = target + ip + ":" + str(target_port) + "," target = target[:-1] target = "\"" + target + "\"" try: ovn_nbctl("set", "load_balancer", load_balancer, "vips:" + key + "=" + target) except Exception as e: vlog.err("_create_load_balancer_vip add: (%s)" % (str(e)))
def _get_load_balancer_vips(self, load_balancer): try: vips = ovn_nbctl("--data=bare", "--no-heading", "get", "load_balancer", load_balancer, "vips").replace('=', ":") return ast.literal_eval(vips) except Exception as e: vlog.err("_get_load_balancer_vips: failed to get vips for %s (%s)" % (load_balancer, str(e))) return None
def _create_load_balancer_vip(self, service_type, service_ip, ips, port, target_port, protocol): vlog.dbg("received event to create/modify load_balancer vip with " "service_type=%s, service_ip=%s, ips=%s, port=%s," "target_port=%s, protocol=%s" % (service_type, service_ip, ips, port, target_port, protocol)) if not port or not target_port or not protocol or not service_type: return load_balancer = "" if protocol == "TCP" and service_type == "ClusterIP": load_balancer = variables.K8S_CLUSTER_LB_TCP elif protocol == "UDP" and service_type == "ClusterIP": load_balancer = variables.K8S_CLUSTER_LB_UDP elif protocol == "TCP" and service_type == "NodePort": load_balancer = variables.K8S_NS_LB_TCP elif protocol == "UDP" and service_type == "NodePort": load_balancer = variables.K8S_NS_LB_UDP if not load_balancer: return # key is of the form "IP:port" (with quotes around) key = "\"" + service_ip + ":" + str(port) + "\"" if not ips: try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", key) except Exception as e: vlog.err("_create_load_balancer_vip remove: (%s)" % (str(e))) return # target is of the form "IP1:port, IP2:port, IP3:port" target_endpoints = ",".join(["%s:%s" % (ip, target_port) for ip in ips]) target = "\"" + target_endpoints + "\"" try: ovn_nbctl("set", "load_balancer", load_balancer, "vips:" + key + "=" + target) except Exception as e: vlog.err("_create_load_balancer_vip add: (%s)" % (str(e)))
def _get_ovn_gateways(self): # Return all created gateways. physical_gateways = [] try: physical_gateways = ovn_nbctl("--data=bare", "--no-heading", "--columns=name", "find", "logical_router", "options:chassis!=null").split() except Exception as e: vlog.err("_get_ovn_gateways: find failed %s" % (str(e))) return physical_gateways
def delete_logical_port(self, event): data = event.metadata pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not pod_name: vlog.err("absent pod name in pod %s. " "unable to delete logical port" % data) return annotations = data['metadata']['annotations'] ip_address = self._get_ip_address_from_annotations(annotations) if ip_address: self._delete_k8s_l4_port_name_cache(data, ip_address) try: ovn_nbctl("--if-exists", "lsp-del", logical_port) except Exception: vlog.exception("failure in delete_logical_port: lsp-del") return vlog.info("deleted logical port %s" % logical_port)
def _get_ovn_gateways(self): # Return all created gateways. physical_gateways = [] try: physical_gateways = ovn_nbctl( "--data=bare", "--no-heading", "--columns=name", "find", "logical_router", "options:chassis!=null").split() except Exception as e: vlog.err("_get_ovn_gateways: find failed %s" % (str(e))) return physical_gateways
def ovn_init_overlay(): if os.path.exists(UNIX_SOCKET): OVN_NB = "unix:%s" % UNIX_SOCKET else: sys.exit("OVN NB database does not have unix socket") variables.OVN_NB = OVN_NB K8S_API_SERVER = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:k8s-api-server").strip('"') if not K8S_API_SERVER: sys.exit("K8S_API_SERVER not set") if not K8S_API_SERVER.startswith("http"): variables.K8S_API_SERVER = "http://%s" % K8S_API_SERVER else: variables.K8S_API_SERVER = K8S_API_SERVER K8S_CLUSTER_ROUTER = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "logical_router", "external_ids:k8s-cluster-router=yes") if not K8S_CLUSTER_ROUTER: sys.exit("K8S_CLUSTER_ROUTER not set") variables.K8S_CLUSTER_ROUTER = K8S_CLUSTER_ROUTER K8S_CLUSTER_LB_TCP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-tcp=yes") if not K8S_CLUSTER_LB_TCP: sys.exit("K8S_CLUSTER_LB_TCP not set") variables.K8S_CLUSTER_LB_TCP = K8S_CLUSTER_LB_TCP K8S_CLUSTER_LB_UDP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-udp=yes") if not K8S_CLUSTER_LB_UDP: sys.exit("K8S_CLUSTER_LB_UDP not set") variables.K8S_CLUSTER_LB_UDP = K8S_CLUSTER_LB_UDP variables.OVN_MODE = "overlay"
def _get_ovn_external_ip_gateway(self): # XXX: In case of K8S 'external_ips', we can only expose it in one # gateway to prevent duplicate ARP responses. We currently allocate # the first gateway to handle all 'external_ips'. try: physical_gateway = ovn_nbctl( "--data=bare", "--no-heading", "--columns=name", "find", "logical_router", "external_ids:first_gateway=yes").split() if not physical_gateway: return None except Exception as e: vlog.err("_get_ovn_external_ip_gateway: find failed %s" % (str(e))) return physical_gateway[0]
def ovn_init_overlay(): if os.path.exists(UNIX_SOCKET): OVN_NB = "unix:%s" % UNIX_SOCKET else: sys.exit("OVN NB database does not have unix socket") variables.OVN_NB = OVN_NB K8S_API_SERVER = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".", "external_ids:k8s-api-server").strip('"') if not K8S_API_SERVER: sys.exit("K8S_API_SERVER not set") if not K8S_API_SERVER.startswith("http"): variables.K8S_API_SERVER = "http://%s" % K8S_API_SERVER else: variables.K8S_API_SERVER = K8S_API_SERVER K8S_CLUSTER_ROUTER = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "logical_router", "external_ids:k8s-cluster-router=yes") if not K8S_CLUSTER_ROUTER: sys.exit("K8S_CLUSTER_ROUTER not set") variables.K8S_CLUSTER_ROUTER = K8S_CLUSTER_ROUTER K8S_CLUSTER_LB_TCP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-tcp=yes") if not K8S_CLUSTER_LB_TCP: sys.exit("K8S_CLUSTER_LB_TCP not set") variables.K8S_CLUSTER_LB_TCP = K8S_CLUSTER_LB_TCP K8S_CLUSTER_LB_UDP = ovn_nbctl("--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:k8s-cluster-lb-udp=yes") if not K8S_CLUSTER_LB_UDP: sys.exit("K8S_CLUSTER_LB_UDP not set") variables.K8S_CLUSTER_LB_UDP = K8S_CLUSTER_LB_UDP
def _get_physical_gateway_ips(self): if self.physical_gateway_ips: return self.physical_gateway_ips try: physical_gateway_ip_networks = ovn_nbctl( "--data=bare", "--no-heading", "--columns=network", "find", "logical_router_port", "external_ids:gateway-physical-ip=yes").split() except Exception as e: vlog.err("_populate_gateway_ip: find failed %s" % (str(e))) for physical_gateway_ip_network in physical_gateway_ip_networks: (ip, mask) = physical_gateway_ip_network.split('/') self.physical_gateway_ips.append(ip) return self.physical_gateway_ips
def _get_physical_gateway_ips(self): if self.physical_gateway_ips: return self.physical_gateway_ips try: physical_gateway_ip_networks = ovn_nbctl( "--data=bare", "--no-heading", "--columns=network", "find", "logical_router_port", "external_ids:gateway-physical-ip=yes").split() except Exception as e: vlog.err("_populate_gateway_ip: find failed %s" % (str(e))) for physical_gateway_ip_network in physical_gateway_ip_networks: ip, _mask = physical_gateway_ip_network.split('/') self.physical_gateway_ips.append(ip) return self.physical_gateway_ips
def create_logical_port(self, event): data = event.metadata logical_switch = data['spec']['nodeName'] pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not logical_switch or not pod_name: vlog.err("absent node name or pod name in pod %s. " "Not creating logical port" % (data)) return (gateway_ip, mask) = self._get_switch_gateway_ip(logical_switch) if not gateway_ip or not mask: vlog.err("_create_logical_port: failed to get gateway_ip") return try: ovn_nbctl("--wait=sb", "--", "--may-exist", "lsp-add", logical_switch, logical_port, "--", "lsp-set-addresses", logical_port, "dynamic", "--", "set", "logical_switch_port", logical_port, "external-ids:namespace=" + namespace, "external-ids:pod=true") except Exception as e: vlog.err("_create_logical_port: lsp-add (%s)" % (str(e))) return try: ret = ovn_nbctl("get", "logical_switch_port", logical_port, "dynamic_addresses") addresses = ast.literal_eval(ret) except Exception as e: vlog.err("_create_logical_port: get dynamic_addresses (%s)" % (str(e))) if not len(addresses): vlog.err("_create_logical_port: failed to get dynamic address") return (mac_address, ip_address) = addresses.split() namespace = data['metadata']['namespace'] pod_name = data['metadata']['name'] ip_address_mask = "%s/%s" % (ip_address, mask) annotation = { 'ip_address': ip_address_mask, 'mac_address': mac_address, 'gateway_ip': gateway_ip } try: kubernetes.set_pod_annotation(variables.K8S_API_SERVER, namespace, pod_name, "ovn", str(annotation)) except Exception as e: vlog.err( "_create_logical_port: failed to annotate addresses (%s)" % (str(e))) return vlog.info("created logical port %s" % (logical_port)) self._add_k8s_l4_port_name_cache(data, ip_address)
def create_logical_port(self, event): data = event.metadata logical_switch = data['spec']['nodeName'] pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not logical_switch or not pod_name: vlog.err("absent node name or pod name in pod %s. " "Not creating logical port" % (data)) return (gateway_ip, mask) = self._get_switch_gateway_ip(logical_switch) if not gateway_ip or not mask: vlog.err("_create_logical_port: failed to get gateway_ip") return try: ovn_nbctl("--wait=sb", "--", "--may-exist", "lsp-add", logical_switch, logical_port, "--", "lsp-set-addresses", logical_port, "dynamic", "--", "set", "logical_switch_port", logical_port, "external-ids:namespace=" + namespace, "external-ids:pod=true") except Exception as e: vlog.err("_create_logical_port: lsp-add (%s)" % (str(e))) return try: ret = ovn_nbctl("get", "logical_switch_port", logical_port, "dynamic_addresses") addresses = ast.literal_eval(ret) except Exception as e: vlog.err("_create_logical_port: get dynamic_addresses (%s)" % (str(e))) if not len(addresses): vlog.err("_create_logical_port: failed to get dynamic address") return (mac_address, ip_address) = addresses.split() namespace = data['metadata']['namespace'] pod_name = data['metadata']['name'] ip_address_mask = "%s/%s" % (ip_address, mask) annotation = {'ip_address': str(ip_address_mask), 'mac_address': str(mac_address), 'gateway_ip': str(gateway_ip)} try: kubernetes.set_pod_annotation(variables.K8S_API_SERVER, namespace, pod_name, "ovn", json.dumps(annotation)) except Exception as e: vlog.err("_create_logical_port: failed to annotate addresses (%s)" % (str(e))) return vlog.info("created logical port %s" % (logical_port)) self._add_k8s_l4_port_name_cache(data, ip_address)
def create_logical_port(self, event): data = event.metadata logical_switch = data['spec']['nodeName'] pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not logical_switch or not logical_port: vlog.err("absent node name or pod name in pod %s. " "Not creating logical port" % (data)) return (gateway_ip, mask) = self._get_switch_gateway_ip(logical_switch) if not gateway_ip or not mask: vlog.err("_create_logical_port: failed to get gateway_ip") return try: ovn_nbctl("--", "--if-exists", "lsp-del", logical_port, "--", "lsp-add", logical_switch, logical_port, "--", "lsp-set-addresses", logical_port, "dynamic") except Exception as e: vlog.err("_create_logical_port: lsp-add (%s)" % (str(e))) return # We wait for a maximum of 3 seconds to get the dynamic addresses in # intervals of 0.1 seconds. addresses = "" counter = 30 while counter != 0: try: ret = ovn_nbctl("get", "logical_switch_port", logical_port, "dynamic_addresses") addresses = ast.literal_eval(ret) if len(addresses): break except Exception as e: vlog.err("_create_logical_port: get dynamic_addresses (%s)" % (str(e))) time.sleep(0.1) counter = counter - 1 if not len(addresses): vlog.err("_create_logical_port: failed to get addresses after " "multiple retries.") return (mac_address, ip_address) = addresses.split() namespace = data['metadata']['namespace'] pod_name = data['metadata']['name'] ip_address_mask = "%s/%s" % (ip_address, mask) annotation = { 'ip_address': ip_address_mask, 'mac_address': mac_address, 'gateway_ip': gateway_ip } try: kubernetes.set_pod_annotation(variables.K8S_API_SERVER, namespace, pod_name, "ovn", str(annotation)) except Exception as e: vlog.err( "_create_logical_port: failed to annotate addresses (%s)" % (str(e))) return vlog.info("created logical port %s" % (logical_port))
def sync_services(self, services): # For all the services, we will populate the below lists with # IP:port that act as VIP in the OVN load-balancers. tcp_nodeport_services = [] udp_nodeport_services = [] tcp_services = [] udp_services = [] services = services.get('items', []) for service in services: service_type = service['spec'].get('type') if service_type != "ClusterIP" and service_type != "NodePort": continue service_ip = service['spec'].get('clusterIP') if not service_ip: continue service_ports = service['spec'].get('ports') if not service_ports: continue external_ips = service['spec'].get('externalIPs') for service_port in service_ports: if service_type == "NodePort": port = service_port.get('nodePort') else: port = service_port.get('port') if not port: continue protocol = service_port.get('protocol', 'TCP') if service_type == "NodePort": physical_gateway_ips = self._get_physical_gateway_ips() for gateway_ip in physical_gateway_ips: key = "%s:%s" % (gateway_ip, port) if protocol == "TCP": tcp_nodeport_services.append(key) else: udp_nodeport_services.append(key) elif service_type == "ClusterIP": key = "%s:%s" % (service_ip, port) if protocol == "TCP": tcp_services.append(key) else: udp_services.append(key) if external_ips: for external_ip in external_ips: key = "%s:%s" % (external_ip, port) if protocol == "TCP": tcp_nodeport_services.append(key) else: udp_nodeport_services.append(key) # For each of the OVN load-balancer, if the VIP that exists in # the load balancer is not seen in current k8s services, we # delete it. load_balancers = {variables.K8S_CLUSTER_LB_TCP: tcp_services, variables.K8S_CLUSTER_LB_UDP: udp_services, variables.K8S_NS_LB_TCP: tcp_nodeport_services, variables.K8S_NS_LB_UDP: udp_nodeport_services} for load_balancer, k8s_services in load_balancers.items(): vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: if vip not in k8s_services: vip = "\"" + vip + "\"" try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", vip) vlog.info("sync_services: deleted vip %s from %s" % (vip, load_balancer)) except Exception as e: vlog.err("sync_services: failed to remove vip %s" "from %s (%s)" % (vip, load_balancer, str(e)))
def sync_services(self, services): # For all the 'clusterIP' services, we will populate the below lists # (inside dict) with IP:port. cluster_services = {'TCP': [], 'UDP': []} # For all the NodePort services, we will populate the below lists with # just nodeport or 'external_ip:port'. nodeport_services = {'TCP': [], 'UDP': []} services = services.get('items', []) for service in services: service_type = service['spec'].get('type') if service_type != "ClusterIP" and service_type != "NodePort": continue service_ip = service['spec'].get('clusterIP') if not service_ip: continue service_ports = service['spec'].get('ports') if not service_ports: continue external_ips = service['spec'].get('externalIPs') for service_port in service_ports: if service_type == "NodePort": port = service_port.get('nodePort') else: port = service_port.get('port') if not port: continue protocol = service_port.get('protocol', 'TCP') if service_type == "NodePort": if protocol == "TCP": nodeport_services['TCP'].append(str(port)) else: nodeport_services['UDP'].append(str(port)) elif service_type == "ClusterIP": key = "%s:%s" % (service_ip, port) if protocol == "TCP": cluster_services['TCP'].append(key) else: cluster_services['UDP'].append(key) if external_ips: for external_ip in external_ips: key = "%s:%s" % (external_ip, port) if protocol == "TCP": nodeport_services['TCP'].append(key) else: nodeport_services['UDP'].append(key) # For OVN cluster load-balancer if the VIP that exists in the # OVN load balancer is not seen in current k8s services, we delete it. load_balancers = { variables.K8S_CLUSTER_LB_TCP: cluster_services['TCP'], variables.K8S_CLUSTER_LB_UDP: cluster_services['UDP'], } for load_balancer, k8s_services in load_balancers.items(): vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: if vip not in k8s_services: vip = "\"" + vip + "\"" self._delete_load_balancer_vip(load_balancer, vip) # For each gateway, remove any VIP that does not exist in # 'nodeport_services' physical_gateways = self._get_ovn_gateways() for physical_gateway in physical_gateways: for protocol, service in nodeport_services.items(): try: external_id_key = protocol + "_lb_gateway_router" load_balancer = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:" + external_id_key + "=" + physical_gateway ).strip('"') except Exception as e: vlog.err("sync_services: get failed for" " %s (%s)" % (physical_gateway, str(e))) continue if not load_balancer: continue # Get the OVN load-balancer VIPs. vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: vip_and_port = vip.split(":") if len(vip_and_port) == 1: # In a OVN load-balancer, we should always have # vip:port. In the unlikely event that it is not # the case, skip it. continue # An example 'service' is ["3892", "10.1.1.20:8880"] if vip_and_port[1] in service or vip in service: continue vip = "\"" + vip + "\"" self._delete_load_balancer_vip(load_balancer, vip)
def sync_services(self, services): # For all the services, we will populate the below lists with # IP:port that act as VIP in the OVN load-balancers. tcp_nodeport_services = [] udp_nodeport_services = [] tcp_services = [] udp_services = [] services = services.get('items', []) for service in services: service_type = service['spec'].get('type') if service_type != "ClusterIP" and service_type != "NodePort": continue service_ip = service['spec'].get('clusterIP') if not service_ip: continue service_ports = service['spec'].get('ports') if not service_ports: continue external_ips = service['spec'].get('externalIPs') for service_port in service_ports: if service_type == "NodePort": port = service_port.get('nodePort') else: port = service_port.get('port') if not port: continue protocol = service_port.get('protocol', 'TCP') if service_type == "NodePort": physical_gateway_ips = self._get_physical_gateway_ips() for gateway_ip in physical_gateway_ips: key = "%s:%s" % (gateway_ip, port) if protocol == "TCP": tcp_nodeport_services.append(key) else: udp_nodeport_services.append(key) elif service_type == "ClusterIP": key = "%s:%s" % (service_ip, port) if protocol == "TCP": tcp_services.append(key) else: udp_services.append(key) if external_ips: for external_ip in external_ips: key = "%s:%s" % (external_ip, port) if protocol == "TCP": tcp_nodeport_services.append(key) else: udp_nodeport_services.append(key) # For each of the OVN load-balancer, if the VIP that exists in # the load balancer is not seen in current k8s services, we # delete it. load_balancers = { variables.K8S_CLUSTER_LB_TCP: tcp_services, variables.K8S_CLUSTER_LB_UDP: udp_services, variables.K8S_NS_LB_TCP: tcp_nodeport_services, variables.K8S_NS_LB_UDP: udp_nodeport_services } for load_balancer, k8s_services in load_balancers.items(): vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: if vip not in k8s_services: vip = "\"" + vip + "\"" try: ovn_nbctl("remove", "load_balancer", load_balancer, "vips", vip) vlog.info("sync_services: deleted vip %s from %s" % (vip, load_balancer)) except Exception as e: vlog.err("sync_services: failed to remove vip %s" "from %s (%s)" % (vip, load_balancer, str(e)))
def sync_services(self, services): # For all the 'clusterIP' services, we will populate the below lists # (inside dict) with IP:port. cluster_services = {'TCP': [], 'UDP': []} # For all the NodePort services, we will populate the below lists with # just nodeport or 'external_ip:port'. nodeport_services = {'TCP': [], 'UDP': []} services = services.get('items', []) for service in services: service_type = service['spec'].get('type') if service_type != "ClusterIP" and service_type != "NodePort": continue service_ip = service['spec'].get('clusterIP') if not service_ip: continue service_ports = service['spec'].get('ports') if not service_ports: continue external_ips = service['spec'].get('externalIPs') for service_port in service_ports: if service_type == "NodePort": port = service_port.get('nodePort') else: port = service_port.get('port') if not port: continue protocol = service_port.get('protocol', 'TCP') if service_type == "NodePort": if protocol == "TCP": nodeport_services['TCP'].append(str(port)) else: nodeport_services['UDP'].append(str(port)) elif service_type == "ClusterIP": key = "%s:%s" % (service_ip, port) if protocol == "TCP": cluster_services['TCP'].append(key) else: cluster_services['UDP'].append(key) if external_ips: for external_ip in external_ips: key = "%s:%s" % (external_ip, port) if protocol == "TCP": nodeport_services['TCP'].append(key) else: nodeport_services['UDP'].append(key) # For OVN cluster load-balancer if the VIP that exists in the # OVN load balancer is not seen in current k8s services, we delete it. load_balancers = { variables.K8S_CLUSTER_LB_TCP: cluster_services['TCP'], variables.K8S_CLUSTER_LB_UDP: cluster_services['UDP'], } for load_balancer, k8s_services in load_balancers.items(): vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: if vip not in k8s_services: vip = "\"" + vip + "\"" self._delete_load_balancer_vip(load_balancer, vip) # For each gateway, remove any VIP that does not exist in # 'nodeport_services' physical_gateways = self._get_ovn_gateways() for physical_gateway in physical_gateways: for protocol, service in nodeport_services.items(): try: external_id_key = protocol + "_lb_gateway_router" load_balancer = ovn_nbctl( "--data=bare", "--no-heading", "--columns=_uuid", "find", "load_balancer", "external_ids:" + external_id_key + "=" + physical_gateway).strip('"') except Exception as e: vlog.err("sync_services: get failed for" " %s (%s)" % (physical_gateway, str(e))) continue if not load_balancer: continue # Get the OVN load-balancer VIPs. vips = self._get_load_balancer_vips(load_balancer) if not vips: continue for vip in vips: vip_and_port = vip.split(":") if len(vip_and_port) == 1: # In a OVN load-balancer, we should always have # vip:port. In the unlikely event that it is not # the case, skip it. continue # An example 'service' is ["3892", "10.1.1.20:8880"] if vip_and_port[1] in service or vip in service: continue vip = "\"" + vip + "\"" self._delete_load_balancer_vip(load_balancer, vip)
def create_logical_port(self, event): data = event.metadata logical_switch = data['spec']['nodeName'] pod_name = data['metadata']['name'] namespace = data['metadata']['namespace'] logical_port = "%s_%s" % (namespace, pod_name) if not logical_switch or not pod_name: vlog.err("absent node name or pod name in pod %s. " "Not creating logical port" % (data)) return (gateway_ip, mask) = self._get_switch_gateway_ip(logical_switch) if not gateway_ip or not mask: vlog.err("_create_logical_port: failed to get gateway_ip") return try: ovn_nbctl("--", "--may-exist", "lsp-add", logical_switch, logical_port, "--", "lsp-set-addresses", logical_port, "dynamic", "--", "set", "logical_switch_port", logical_port, "external-ids:namespace=" + namespace, "external-ids:pod=true") except Exception as e: vlog.err("_create_logical_port: lsp-add (%s)" % (str(e))) return # We wait for a maximum of 3 seconds to get the dynamic addresses in # intervals of 0.1 seconds. addresses = "" counter = 30 while counter != 0: try: ret = ovn_nbctl("get", "logical_switch_port", logical_port, "dynamic_addresses") addresses = ast.literal_eval(ret) if len(addresses): break except Exception as e: vlog.err("_create_logical_port: get dynamic_addresses (%s)" % (str(e))) time.sleep(0.1) counter = counter - 1 if not len(addresses): vlog.err("_create_logical_port: failed to get addresses after " "multiple retries.") return (mac_address, ip_address) = addresses.split() namespace = data['metadata']['namespace'] pod_name = data['metadata']['name'] ip_address_mask = "%s/%s" % (ip_address, mask) annotation = {'ip_address': ip_address_mask, 'mac_address': mac_address, 'gateway_ip': gateway_ip} try: kubernetes.set_pod_annotation(variables.K8S_API_SERVER, namespace, pod_name, "ovn", str(annotation)) except Exception as e: vlog.err("_create_logical_port: failed to annotate addresses (%s)" % (str(e))) return vlog.info("created logical port %s" % (logical_port))