class VncSecurityPolicy(VncCommon): default_policy_management_name = 'default-policy-management' vnc_lib = None cluster_aps_uuid = None get_tags_fn = None vnc_security_policy_instance = None allow_all_fw_policy_uuid = None deny_all_fw_policy_uuid = None ingress_svc_fw_policy_uuid = None name = 'VncSecurityPolicy' def __init__(self, vnc_lib, get_tags_fn): self._k8s_event_type = VncSecurityPolicy.name VncSecurityPolicy.vnc_lib = vnc_lib self._logger = vnc_kube_config.logger() self._labels = XLabelCache(self._k8s_event_type) self.reset_resources() # Init FW Rule constructs. FWRule.default_policy_management_name = self.default_policy_management_name FWRule.vnc_lib = vnc_lib VncSecurityPolicy.get_tags_fn = get_tags_fn super(VncSecurityPolicy, self).__init__(self._k8s_event_type) VncSecurityPolicy.vnc_security_policy_instance = self def reset_resources(self): self._labels.reset_resource() VncSecurityPolicy.allow_all_fw_policy_uuid = None VncSecurityPolicy.deny_all_fw_policy_uuid = None VncSecurityPolicy.ingress_svc_fw_policy_uuid = None @staticmethod def construct_sequence_number(seq_num): snum_list = str(float(seq_num)).split('.') constructed_snum = "%s.%s" % (snum_list[0].zfill(5), snum_list[1]) return FirewallSequence(sequence=constructed_snum) @classmethod def create_application_policy_set(cls, name, parent_obj=None): if not parent_obj: pm_obj = PolicyManagement(cls.default_policy_management_name) try: parent_uuid = cls.vnc_lib.policy_management_create(pm_obj) except RefsExistError: pass pm_obj = cls.vnc_lib.policy_management_read( fq_name=pm_obj.get_fq_name()) PolicyManagementKM.locate(pm_obj.get_uuid()) else: pm_obj = parent_obj aps_obj = ApplicationPolicySet(name=name, parent_obj=pm_obj) try: aps_uuid = cls.vnc_lib.application_policy_set_create(aps_obj) except RefsExistError: cls.vnc_lib.application_policy_set_update(aps_obj) aps_uuid = aps_obj.get_uuid() # Update application policy set in our cache. ApplicationPolicySetKM.locate(aps_uuid) cls.cluster_aps_uuid = aps_uuid return aps_uuid @classmethod def tag_cluster_application_policy_set(cls): aps_uuid = cls.cluster_aps_uuid aps_obj = cls.vnc_lib.application_policy_set_read(id=aps_uuid) cls.vnc_security_policy_instance._labels.process( aps_uuid, cls.vnc_security_policy_instance._labels.get_cluster_label( vnc_kube_config.cluster_name())) cls.vnc_lib.set_tags( aps_obj, cls.vnc_security_policy_instance._labels.get_labels_dict(aps_uuid)) @classmethod def get_firewall_policy_name(cls, name, namespace, is_global): if is_global: policy_name = name else: policy_name = "-".join([namespace, name]) # Always prepend firewall policy name with cluster name. return "-".join([vnc_kube_config.cluster_name(), policy_name]) @classmethod def create_firewall_policy(cls, name, namespace, spec, tag_last=False, is_global=False, k8s_uuid=None): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_obj = FirewallPolicy( cls.get_firewall_policy_name(name, namespace, is_global), pm_obj) custom_ann_kwargs = {} custom_ann_kwargs['k8s_uuid'] = k8s_uuid curr_fw_policy = None fw_rules_del_candidates = set() # If this firewall policy already exists, get its uuid. fw_policy_uuid = VncSecurityPolicy.get_firewall_policy_uuid( name, namespace, is_global) if fw_policy_uuid: # # FW policy exists. # Check for modidifcation to its spec. # If not modifications are found, return the uuid of policy. # curr_fw_policy = FirewallPolicyKM.locate(fw_policy_uuid) if curr_fw_policy and curr_fw_policy.spec: if curr_fw_policy.spec == json.dumps(spec): # Input spec is same as existing spec. Nothing to do. # Just return the uuid. return fw_policy_uuid # Get the current firewall rules on this policy. # All rules are delete candidates as any of them could have # changed. fw_rules_del_candidates = curr_fw_policy.firewall_rules # Annotate the FW policy object with input spec. # This will be used later to identify and validate subsequent modify # or add (i.e post restart) events. custom_ann_kwargs['spec'] = json.dumps(spec) # Check if we are being asked to place this firewall policy in the end # of fw policy list in its Application Policy Set. # If yes, tag accordingly. if tag_last: custom_ann_kwargs['tail'] = "True" # Parse input spec and construct the list of rules for this FW policy. fw_rules = [] deny_all_rule_uuid = None egress_deny_all_rule_uuid = None if spec is not None: fw_rules, deny_all_rule_uuid, egress_deny_all_rule_uuid =\ FWRule.parser(name, namespace, pm_obj, spec) for rule in fw_rules: try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() # The rule is in use and needs to stay. # Remove it from delete candidate collection. if fw_rules_del_candidates and\ rule_uuid in fw_rules_del_candidates: fw_rules_del_candidates.remove(rule_uuid) rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) fw_policy_obj.add_firewall_rule( rule_obj, cls.construct_sequence_number(fw_rules.index(rule))) if deny_all_rule_uuid: VncSecurityPolicy.add_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, deny_all_rule_uuid) custom_ann_kwargs['deny_all_rule_uuid'] = deny_all_rule_uuid if egress_deny_all_rule_uuid: VncSecurityPolicy.add_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, egress_deny_all_rule_uuid) custom_ann_kwargs['egress_deny_all_rule_uuid'] =\ egress_deny_all_rule_uuid FirewallPolicyKM.add_annotations( VncSecurityPolicy.vnc_security_policy_instance, fw_policy_obj, namespace, name, None, **custom_ann_kwargs) try: fw_policy_uuid = cls.vnc_lib.firewall_policy_create(fw_policy_obj) except RefsExistError: # Remove existing firewall rule refs on this fw policy. # Once existing firewall rules are remove, firewall policy will # be updated with rules correspoinding to current input spec. for rule in fw_rules_del_candidates: cls.delete_firewall_rule(fw_policy_uuid, rule) cls.vnc_lib.firewall_policy_update(fw_policy_obj) fw_policy_uuid = fw_policy_obj.get_uuid() fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) FirewallPolicyKM.locate(fw_policy_uuid) return fw_policy_uuid @classmethod def delete_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return fw_policy = FirewallPolicyKM.locate(fw_policy_uuid) fw_policy_rules = fw_policy.firewall_rules # Remove deny all firewall rule, if any. if fw_policy.deny_all_rule_uuid: VncSecurityPolicy.delete_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, fw_policy.deny_all_rule_uuid) # Remove egress deny all firewall rule, if any. if fw_policy.egress_deny_all_rule_uuid: VncSecurityPolicy.delete_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, fw_policy.egress_deny_all_rule_uuid) for rule_uuid in fw_policy_rules: try: VncSecurityPolicy.delete_firewall_rule(fw_policy_uuid, rule_uuid) except: raise cls.remove_firewall_policy(name, namespace) try: cls.vnc_lib.firewall_policy_delete(id=fw_policy_uuid) FirewallPolicyKM.delete(fw_policy_uuid) except: raise @classmethod def create_firewall_rule_allow_all(cls, rule_name, labels_dict, src_labels_dict=None): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise tags = VncSecurityPolicy.get_tags_fn(labels_dict, True) if src_labels_dict: src_tags = VncSecurityPolicy.get_tags_fn(src_labels_dict, True) else: src_tags = None protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.PASS.value ep1 = FWRuleEndpoint.get(src_tags) ep2 = FWRuleEndpoint.get(tags) service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def create_firewall_rule_deny_all(cls, rule_name, tags): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.DENY.value ep1 = FWRuleEndpoint.get() ep2 = FWRuleEndpoint.get(tags) service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def create_firewall_rule_egress_deny_all(cls, name, namespace, tags): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise rule_name = "-".join( [FWRule.get_egress_rule_name(name, namespace), "default-deny-all"]) protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.DENY.value ep1 = FWRuleEndpoint.get(tags) ep2 = FWRuleEndpoint.get() service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def _move_trailing_firewall_policies(cls, aps_obj, tail_sequence): sequence_num = float(tail_sequence.get_sequence()) if cls.deny_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.deny_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 if cls.allow_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.allow_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 cls.vnc_lib.application_policy_set_update(aps_obj) return cls.construct_sequence_number(sequence_num) @classmethod def add_firewall_policy(cls, fw_policy_uuid, append_after_tail=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: new_fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy_uuid) except NoIdError: raise last_obj = False last_entry_sequence = None last_k8s_obj = None last_k8s_obj_sequence = None fw_policy_refs = aps_obj.get_firewall_policy_refs() for fw_policy in fw_policy_refs if fw_policy_refs else []: try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy['uuid']) except NoIdError: # TBD Error handling. pass # Return if the firewall policy is already found on this APS. if new_fw_policy_obj.get_fq_name() == fw_policy_obj.get_fq_name(): return k8s_obj = False last_obj = False annotations = fw_policy_obj.get_annotations() if annotations: for kvp in annotations.get_key_value_pair() or []: if kvp.key == 'owner' and kvp.value == 'k8s': k8s_obj = True elif kvp.key == 'tail' and kvp.value == 'True': last_obj = True if k8s_obj and last_obj: last_k8s_obj = fw_policy_obj last_k8s_obj_sequence = fw_policy['attr'].get_sequence() if not last_entry_sequence: last_entry_sequence = fw_policy['attr'].get_sequence() elif float(last_entry_sequence) < float( fw_policy['attr'].get_sequence()): last_entry_sequence = fw_policy['attr'].get_sequence() # # Determine the sequence number. # # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if len(fw_policy_refs if fw_policy_refs else []): last_k8s_fw_policy_sequence = \ cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) if last_k8s_obj_sequence: tail_sequence = cls._move_trailing_firewall_policies( aps_obj, last_k8s_fw_policy_sequence) if append_after_tail: sequence = cls.construct_sequence_number( float(tail_sequence.get_sequence())) else: sequence = FirewallSequence(sequence=last_k8s_obj_sequence) # Move the existing last k8s FW policy to the end of the list. else: sequence = last_k8s_fw_policy_sequence aps_obj.add_firewall_policy(new_fw_policy_obj, sequence) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def remove_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise aps_obj.del_firewall_policy(fw_policy_obj) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def add_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: raise last_entry_sequence = None rule_refs = fw_policy_obj.get_firewall_rule_refs() for rule in rule_refs if rule_refs else []: if fw_rule_uuid == rule['uuid']: return if last_entry_sequence < rule['attr'].get_sequence(): last_entry_sequence = rule['attr'].get_sequence() # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if last_entry_sequence: sequence = cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) fw_policy_obj.add_firewall_rule(fw_rule_obj, sequence) cls.vnc_lib.firewall_policy_update(fw_policy_obj) FirewallPolicyKM.locate(fw_policy_obj.get_uuid()) @classmethod def delete_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): # If policy or rule info is not provided, then there is nothing to do. if not fw_policy_uuid or not fw_rule_uuid: return try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: return addr_grp_refs = fw_rule_obj.get_address_group_refs() fw_policy_obj.del_firewall_rule(fw_rule_obj) cls.vnc_lib.firewall_policy_update(fw_policy_obj) FirewallPolicyKM.locate(fw_policy_obj.get_uuid()) # Delete the rule. cls.vnc_lib.firewall_rule_delete(id=fw_rule_uuid) # Try to delete address groups allocated for this FW rule. for addr_grp in addr_grp_refs if addr_grp_refs else []: FWRule.delete_address_group(addr_grp['uuid']) @classmethod def create_allow_all_security_policy(cls): if not cls.allow_all_fw_policy_uuid: allow_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy("allowall", None, None, is_global=True) VncSecurityPolicy.add_firewall_policy(allow_all_fw_policy_uuid, append_after_tail=True) cls.allow_all_fw_policy_uuid = allow_all_fw_policy_uuid @classmethod def create_deny_all_security_policy(cls): if not cls.deny_all_fw_policy_uuid: cls.deny_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy("denyall", None, None, tag_last=True, is_global=True) VncSecurityPolicy.add_firewall_policy(cls.deny_all_fw_policy_uuid) @classmethod def get_firewall_rule_uuid(cls, rule_name): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) pm = PolicyManagementKM.locate(aps.parent_uuid) rule_fq_name = pm.fq_name + [rule_name] rule_uuid = FirewallRuleKM.get_fq_name_to_uuid(rule_fq_name) return rule_uuid @classmethod def get_firewall_policy_uuid(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) if not aps or not aps.parent_uuid: return None pm = PolicyManagementKM.locate(aps.parent_uuid) fw_policy_fq_name = pm.fq_name +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) return fw_policy_uuid @classmethod def validate_cluster_security_policy(cls): # If APS does not exist for this cluster, then there is nothing to do. if not cls.cluster_aps_uuid: return True aps = ApplicationPolicySetKM.find_by_name_or_uuid(cls.cluster_aps_uuid) # If we are not able to local APS in cache, then there is nothing to do. if not aps: return True # If APS does not match this cluster name, then there is nothing to do. if aps.name != vnc_kube_config.cluster_name(): return True # Update the APS, so we have the latest state. aps.update() fw_policy_uuids = aps.get_firewall_policies() # If there are no firewall policies on this APS yet, there is nothing # to verify. if not fw_policy_uuids: if cls.ingress_svc_fw_policy_uuid and\ cls.deny_all_fw_policy_uuid and\ cls.allow_all_fw_policy_uuid: return False else: return True # Validate that ingress firewall policy is the first policy of the # cluster owned firewall policies in the APS. if cls.ingress_svc_fw_policy_uuid: for fw_policy_uuid in fw_policy_uuids: fw_policy = FirewallPolicyKM.find_by_name_or_uuid( fw_policy_uuid) if not fw_policy: continue # Filter out policies not owned by this cluster. if fw_policy.cluster_name != vnc_kube_config.cluster_name(): continue # The first policy to reach here should be ingress policy. # Else return validation failure. if cls.ingress_svc_fw_policy_uuid == fw_policy_uuid: break vnc_kube_config.logger().error( "%s - Ingress FW Policy [%s] not the first policy on APS [%s]"\ %(cls.name, cls.ingress_svc_fw_policy_uuid, aps.name)) return False # Validate that deny and allow policies of this cluster are found on # on this APS. # The allow policy should follow the deny policy. deny_all_fw_policy_index = None allow_all_fw_policy_index = None if cls.deny_all_fw_policy_uuid and cls.allow_all_fw_policy_uuid: for index, fw_policy_uuid in enumerate(fw_policy_uuids): fw_policy = FirewallPolicyKM.find_by_name_or_uuid( fw_policy_uuid) if not fw_policy: continue # Filter out policies not owned by this cluster. if fw_policy.cluster_name != vnc_kube_config.cluster_name(): continue # Allow policy should follow the deny policy. # If not, return validation failure. if deny_all_fw_policy_index: if cls.allow_all_fw_policy_uuid == fw_policy_uuid: allow_all_fw_policy_index = index break elif cls.deny_all_fw_policy_uuid == fw_policy_uuid: deny_all_fw_policy_index = index # If we are unable to locate deny or allow policy, return validation # failure. if not deny_all_fw_policy_index or not allow_all_fw_policy_index: if cls.deny_all_fw_policy_uuid and not deny_all_fw_policy_index: vnc_kube_config.logger().error( "%s - deny-all FW Policy [%s] not found on APS [%s]"\ %(cls.name, cls.deny_all_fw_policy_uuid, aps.name)) if cls.allow_all_fw_policy_uuid and not allow_all_fw_policy_index: vnc_kube_config.logger().error( "%s - allow-all FW Policy [%s] not found (or not found"\ " after deny-all policy) on APS [%s]"\ %(cls.name, cls.allow_all_fw_policy_uuid, aps.name)) return False # Validation succeeded. All is well. return True @classmethod def recreate_cluster_security_policy(cls): # If APS does not exist for this cluster, then there is nothing to do. if not cls.cluster_aps_uuid: return aps = ApplicationPolicySetKM.find_by_name_or_uuid(cls.cluster_aps_uuid) # If APS does not match this cluster name, then there is nothing to do. if aps.name != vnc_kube_config.cluster_name(): return # Update the APS, so we have the latest state. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) aps.update() vnc_kube_config.logger().debug( "%s - Remove existing firewall policies of cluster from APS [%s]"\ %(cls.name, aps.name)) # To begin with, remove all existing firewall policies of this cluster # from the APS. fw_policy_uuids = aps.get_firewall_policies() removed_firewall_policies = [] for fw_policy_uuid in fw_policy_uuids if fw_policy_uuids else []: fw_policy = FirewallPolicyKM.find_by_name_or_uuid(fw_policy_uuid) # Filter out policies not owned by this cluster. if fw_policy.cluster_name != vnc_kube_config.cluster_name(): continue # De-link the firewall policy from APS. try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy_uuid) except NoIdError: raise aps_obj.del_firewall_policy(fw_policy_obj) removed_firewall_policies.append(fw_policy_uuid) # If we need to remove some policies, update the object accordingly. if removed_firewall_policies: cls.vnc_lib.application_policy_set_update(aps_obj) aps.update() # Derive the sequence number we can use to start recreating firewall # policies. If there are existing policies that dont belong and are # not managed by the cluster, recreate the cluster firewall policies # to the tail. fw_policy_refs = aps.get_firewall_policy_refs_sorted() # Lets begin with the assumption that we are the first policy. sequence = cls.construct_sequence_number('1.0') if fw_policy_refs: # Get the sequence number of the last policy on this APS. last_entry_sequence = fw_policy_refs[-1]['attr'].get_sequence() # Construct the next sequence number to use. sequence = cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) # Filter our infra created firewall policies. try: removed_firewall_policies.remove(cls.ingress_svc_fw_policy_uuid) except ValueError: pass try: removed_firewall_policies.remove(cls.deny_all_fw_policy_uuid) except ValueError: pass try: removed_firewall_policies.remove(cls.allow_all_fw_policy_uuid) except ValueError: pass # Reconstruct the policies in the order we want them to be. add_firewall_policies = [cls.ingress_svc_fw_policy_uuid] +\ removed_firewall_policies+\ [cls.deny_all_fw_policy_uuid]+\ [cls.allow_all_fw_policy_uuid] # Attach the policies to the APS. for fw_policy_uuid in add_firewall_policies: vnc_kube_config.logger().debug( "%s - Recreate FW policy [%s] on APS [%s] at sequence [%s]"\ %(cls.name, fw_policy_uuid, aps.name, sequence.get_sequence())) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence = cls.construct_sequence_number( float(sequence.get_sequence()) + float('1.0')) # Update the APS. cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def sync_cluster_security_policy(cls): """ Synchronize K8s network policies with Contrail Security policy. Expects that FW policies on the APS are in proper order. Returns a list of orphaned or invalid firewall policies. """ # If APS does not exist for this cluster, then there is nothing to do. if not cls.cluster_aps_uuid: return [] aps = ApplicationPolicySetKM.find_by_name_or_uuid(cls.cluster_aps_uuid) if not aps: return [] # If APS does not match this cluster name, then there is nothing to do. if aps.name != vnc_kube_config.cluster_name(): return [] # Get the current list of firewall policies on the APS. fw_policy_uuids = aps.get_firewall_policies() # Construct list of firewall policies that belong to the cluster. cluster_firewall_policies = [] for fw_policy_uuid in fw_policy_uuids: fw_policy = FirewallPolicyKM.find_by_name_or_uuid(fw_policy_uuid) if fw_policy.cluster_name != vnc_kube_config.cluster_name(): continue cluster_firewall_policies.append(fw_policy_uuid) # We are interested only in policies created by k8s user via network # policy. These policies are sequenced between the infra created ingress # policy and infra created deny-all policy. try: start_index = cluster_firewall_policies.index( cls.ingress_svc_fw_policy_uuid) end_index = cluster_firewall_policies.index( cls.deny_all_fw_policy_uuid) curr_user_firewall_policies =\ cluster_firewall_policies[start_index+1:end_index] except ValueError: return [] # Get list of user created network policies. configured_network_policies = NetworkPolicyKM.get_configured_policies() for nw_policy_uuid in configured_network_policies: np = NetworkPolicyKM.find_by_name_or_uuid(nw_policy_uuid) if not np or not np.get_vnc_fq_name(): continue # Decipher the firewall policy corresponding to the network policy. fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( np.get_vnc_fq_name().split(":")) if not fw_policy_uuid: # We are yet to process this network policy. continue # A firewall policy was found but it is not inbetween the infra # created policies as expected. Add it again so it will be inserted # in the right place. if fw_policy_uuid not in curr_user_firewall_policies: cls.add_firewall_policy(fw_policy_uuid) else: # Filter out processed policies. curr_user_firewall_policies.remove(fw_policy_uuid) # Return orphaned firewall policies that could not be validated against # user created network policy. headless_fw_policy_uuids = curr_user_firewall_policies return headless_fw_policy_uuids
class VncService(VncCommon): def __init__(self, ingress_mgr): self._k8s_event_type = 'Service' super(VncService, self).__init__(self._k8s_event_type) self._name = type(self).__name__ self._ingress_mgr = ingress_mgr self._vnc_lib = vnc_kube_config.vnc_lib() self._label_cache = vnc_kube_config.label_cache() self._labels = XLabelCache(self._k8s_event_type) self._labels.reset_resource() self._args = vnc_kube_config.args() self.logger = vnc_kube_config.logger() self._queue = vnc_kube_config.queue() self.kube = vnc_kube_config.kube() self._fip_pool_obj = None # Cache kubernetes API server params. self._kubernetes_api_server = self._args.kubernetes_api_server self._kubernetes_api_secure_port =\ int(self._args.kubernetes_api_secure_port) # Cache kuberneter service name. self._kubernetes_service_name = self._args.kubernetes_service_name # Config knob to control enable/disable of link local service. if self._args.api_service_link_local == 'True': api_service_ll_enable = True else: api_service_ll_enable = False # If Kubernetes API server info is incomplete, disable link-local create, # as create is not possible. if not self._kubernetes_api_server: self._create_linklocal = False elif vnc_kube_config.is_cluster_network_configured() and\ DBBaseKM.is_nested(): # In nested mode, if cluster network is configured, then the k8s api # server is in the same network as the k8s cluster. So there is no # need for link local. self._create_linklocal = False else: self._create_linklocal = api_service_ll_enable self.service_lb_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbManager') self.service_ll_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbListenerManager') self.service_lb_pool_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbPoolManager') self.service_lb_member_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbMemberManager') def _get_project(self, service_namespace): proj_fq_name =\ vnc_kube_config.cluster_project_fq_name(service_namespace) try: proj_obj = self._vnc_lib.project_read(fq_name=proj_fq_name) return proj_obj except NoIdError: return None @staticmethod def _get_namespace(service_namespace): return NamespaceKM.find_by_name_or_uuid(service_namespace) def _get_annotated_ns_fip_pool(self, service_namespace): fip_pool_obj = None ns = self._get_namespace(service_namespace) try: if ns.get_annotated_ns_fip_pool_fq_name() != None: fip_pool_obj = self._vnc_lib.floating_ip_pool_read( fq_name=ns.get_annotated_ns_fip_pool_fq_name()) except NoIdError: return None return fip_pool_obj def _get_cluster_service_network(self, service_namespace): ns = self._get_namespace(service_namespace) if ns and ns.is_isolated(): vn_fq_name = ns.get_isolated_service_network_fq_name() else: vn_fq_name = vnc_kube_config.cluster_default_service_network_fq_name( ) try: vn_obj = self._vnc_lib.virtual_network_read(fq_name=vn_fq_name) except NoIdError: return None return vn_obj def _get_service_ipam_subnet_uuid(self, vn_obj): service_ipam_subnet_uuid = None fq_name = vnc_kube_config.service_ipam_fq_name() vn = VirtualNetworkKM.find_by_name_or_uuid(vn_obj.get_uuid()) if vn: service_ipam_subnet_uuid = vn.get_ipam_subnet_uuid(fq_name) if service_ipam_subnet_uuid is None: self.logger.error("%s - %s Not Found" % (self._name, fq_name)) return service_ipam_subnet_uuid def _get_specified_fip_pool(self, specified_fip_pool_fq_name_str): if specified_fip_pool_fq_name_str == None: return None fip_pool_fq_name = get_fip_pool_fq_name_from_dict_string( specified_fip_pool_fq_name_str) try: fip_pool_obj = self._vnc_lib.floating_ip_pool_read( fq_name=fip_pool_fq_name) except NoIdError: self.logger.notice("FIP Pool %s not found. " "Floating IP will not be available " "until FIP pool is configured." % (specified_fip_pool_fq_name_str)) return None return fip_pool_obj def _get_public_fip_pool(self): if self._fip_pool_obj: return self._fip_pool_obj if not vnc_kube_config.is_public_fip_pool_configured(): return None fip_pool_fq_name = get_fip_pool_fq_name_from_dict_string( self._args.public_fip_pool) try: fip_pool_obj = self._vnc_lib.floating_ip_pool_read( fq_name=fip_pool_fq_name) except NoIdError: self.logger.notice("Public FIP Pool not found. " "Floating IP will not be available " "until FIP pool is configured.") return None self._fip_pool_obj = fip_pool_obj return fip_pool_obj def _get_virtualmachine(self, id): try: vm_obj = self._vnc_lib.virtual_machine_read(id=id) except NoIdError: return None obj = self._vnc_lib.virtual_machine_read( id=id, fields=['virtual_machine_interface_back_refs']) back_refs = getattr(obj, 'virtual_machine_interface_back_refs', None) vm_obj.virtual_machine_interface_back_refs = back_refs return vm_obj def check_service_selectors_actions(self, selectors, service_id, ports): for selector in list(selectors.items()): key = self._label_cache._get_key(selector) self._label_cache._locate_label( key, self._label_cache.service_selector_cache, selector, service_id) pod_ids = self._label_cache.pod_label_cache.get(key, []) if len(pod_ids): self.add_pods_to_service(service_id, pod_ids, ports) def _vnc_create_pool(self, namespace, ll, port): proj_obj = self._get_project(namespace) ll_obj = self.service_ll_mgr.read(ll.uuid) pool_obj = self.service_lb_pool_mgr.create(ll_obj, proj_obj, port) return pool_obj def _vnc_create_listener(self, namespace, lb, port): proj_obj = self._get_project(namespace) lb_obj = self.service_lb_mgr.read(lb.uuid) ll_obj = self.service_ll_mgr.create(lb_obj, proj_obj, port) return ll_obj def _create_listeners(self, namespace, lb, ports): for port in ports: listener_found = False for ll_id in lb.loadbalancer_listeners: ll = LoadbalancerListenerKM.get(ll_id) if not ll: continue if not ll.params['protocol_port']: continue if not ll.params['protocol']: continue if ll.params['protocol_port'] == port['port'] and \ ll.params['protocol'] == port['protocol']: listener_found = True break if not listener_found: ll_obj = self._vnc_create_listener(namespace, lb, port) ll = LoadbalancerListenerKM.locate(ll_obj._uuid) pool_id = ll.loadbalancer_pool if pool_id: pool = LoadbalancerPoolKM.get(pool_id) # SAS FIXME: If pool_id present, check for targetPort value if not pool_id or not pool: pool_obj = self._vnc_create_pool(namespace, ll, port) LoadbalancerPoolKM.locate(pool_obj._uuid) def _create_link_local_service(self, svc_name, svc_ns, svc_ip, ports): # Create link local service only if enabled. if self._create_linklocal: # Create link local service, one for each port. for port in ports: try: ll_mgr.create_link_local_service_entry( self._vnc_lib, name=svc_name + '-' + port['port'].__str__(), k8s_ns=svc_ns, service_ip=svc_ip, service_port=port['port'], fabric_ip=self._kubernetes_api_server, fabric_port=self._kubernetes_api_secure_port) except: self.logger.error("Create link-local service failed for" " service " + svc_name + " port " + port['port'].__str__()) def _delete_link_local_service(self, svc_name, svc_ns, ports): # Delete link local service only if enabled. if self._create_linklocal: # Delete link local service, one for each port. for port in ports: try: ll_mgr.delete_link_local_service_entry( self._vnc_lib, svc_name + '-' + port['port'].__str__(), svc_ns) except: self.logger.error("Delete link local service failed for" " service " + svc_name + " port " + port['port'].__str__()) def _vnc_create_lb(self, service_id, service_name, service_namespace, service_ip): proj_obj = self._get_project(service_namespace) vn_obj = self._get_cluster_service_network(service_namespace) service_ipam_subnet_uuid = self._get_service_ipam_subnet_uuid(vn_obj) lb_obj = self.service_lb_mgr.create(self._k8s_event_type, service_namespace, service_id, service_name, proj_obj, vn_obj, service_ip, service_ipam_subnet_uuid) return lb_obj def _lb_create(self, service_id, service_name, service_namespace, service_ip, ports): lb = LoadbalancerKM.get(service_id) if not lb: lb_obj = self._vnc_create_lb(service_id, service_name, service_namespace, service_ip) if not lb_obj: raise NoIdError lb = LoadbalancerKM.locate(service_id) self._create_listeners(service_namespace, lb, ports) def _read_allocated_floating_ips(self, service_id): floating_ips = set() lb = LoadbalancerKM.get(service_id) if not lb: return vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found = False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found = True break if interface_found is False: return fip_ids = vmi.floating_ips if fip_ids is None: return None for fip_id in list(fip_ids): fip = FloatingIpKM.get(fip_id) if fip is not None: floating_ips.add(fip.address) return floating_ips def _allocate_floating_ips(self, service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips=set()): lb = LoadbalancerKM.get(service_id) if not lb: return None vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found = False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found = True break if interface_found is False: return vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=vmi_id) if vmi_obj is None: return None fip_pool = None if specified_fip_pool_fq_name_str != None: fip_pool = self._get_specified_fip_pool( specified_fip_pool_fq_name_str) if fip_pool is None and self._get_annotated_ns_fip_pool( service_namespace) != None: fip_pool = self._get_annotated_ns_fip_pool(service_namespace) if fip_pool is None: fip_pool = self._get_public_fip_pool() if fip_pool is None: self.logger.warning("public_fip_pool doesn't exists") return None def _check_ip_with_fip_pool(external_ip, fip_pool_obj): try: vn_obj = self._vnc_lib.virtual_network_read( id=fip_pool_obj.parent_uuid) except NoIdError: return True ipam_refs = vn_obj.__dict__.get('network_ipam_refs', []) for ipam_ref in ipam_refs: vnsn_data = ipam_ref['attr'].__dict__ ipam_subnets = vnsn_data.get('ipam_subnets', []) for ipam_subnet in ipam_subnets: subnet_dict = ipam_subnet.__dict__.get('subnet', {}) if 'ip_prefix' in subnet_dict.__dict__: ip_subnet_str = subnet_dict.__dict__.get('ip_prefix','')+'/' \ +str(subnet_dict.__dict__.get('ip_prefix_len')) if IPAddress(external_ip) in IPNetwork(ip_subnet_str): return True self.logger.error("external_ip not in fip_pool subnet") return False def _allocate_floating_ip(lb, vmi, fip_pool, external_ip=None): fip_obj = FloatingIp(lb.name + str(external_ip) + "-externalIP", fip_pool) fip_obj.set_virtual_machine_interface(vmi_obj) if external_ip: if not (_check_ip_with_fip_pool(external_ip, fip_pool)): err_str = "external_ip " + external_ip + " not in fip_pool subnet" self.logger.error(err_str) return None fip_obj.set_floating_ip_address(external_ip) project = self._vnc_lib.project_read(id=lb.parent_uuid) fip_obj.set_project(project) try: self._vnc_lib.floating_ip_create(fip_obj) except RefsExistError as e: string_buf = StringIO() cgitb_hook(file=string_buf, format="text") err_msg = string_buf.getvalue() self.logger.error("%s" % (err_msg)) except: string_buf = StringIO() cgitb_hook(file=string_buf, format="text") err_msg = string_buf.getvalue() self.logger.error("%s" % (err_msg)) fip = FloatingIpKM.locate(fip_obj.uuid) self.logger.notice("floating ip allocated : %s for Service (%s)" % (fip.address, service_id)) return (fip.address) fips = set() if len(external_ips) is 0: fip_addr = _allocate_floating_ip(lb, vmi, fip_pool) if fip_addr: fips.add(fip_addr) return fips for external_ip in external_ips: fip_addr = _allocate_floating_ip(lb, vmi, fip_pool, external_ip) if fip_addr: fips.add(fip_addr) return fips def _deallocate_floating_ips(self, service_id): lb = LoadbalancerKM.get(service_id) if not lb: return vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found = False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found = True break if interface_found is False: return fip_ids = vmi.floating_ips.copy() for fip_id in fip_ids: try: self._vnc_lib.floating_ip_delete(id=fip_id) except NoIdError: pass def _update_service_external_ip(self, service_namespace, service_name, external_ips): merge_patch = {'spec': {'externalIPs': [', '.join(external_ips)]}} self.kube.patch_resource(resource_type="services", resource_name=service_name, namespace=service_namespace, merge_patch=merge_patch) self.logger.notice("Service (%s, %s) updated with EXTERNAL-IP (%s)" % (service_namespace, service_name, external_ips)) def _update_service_public_ip(self, service_id, service_name, service_namespace, service_type, external_ips, loadBalancerIp, specified_fip_pool_fq_name_str): allocated_fips = self._read_allocated_floating_ips(service_id) if service_type in ["LoadBalancer"]: if allocated_fips is not None and len(allocated_fips) is 0: # Allocate floating-ip from public-pool, if none exists. # if "loadBalancerIp" if specified in Service definition, allocate # loadBalancerIp as floating-ip. # if external ips specified, allocate external_ips as floating-ips. # if None specficied, then let contrail allocate a floating-ip and # update the allocated fip to kubernetes if loadBalancerIp: allocated_fip = self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, set([loadBalancerIp])) if allocated_fip: self._update_service_external_ip( service_namespace, service_name, allocated_fip) elif external_ips: allocated_fips = self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) else: allocated_fip = self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace) if allocated_fip: self._update_service_external_ip( service_namespace, service_name, allocated_fip) return if allocated_fips is not None and len(allocated_fips): if loadBalancerIp and loadBalancerIp in allocated_fips: self._deallocate_floating_ips(service_id) self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, set([loadBalancerIp])) self._update_service_external_ip(service_namespace, service_name, loadBalancerIp) return if external_ips and external_ips != allocated_fips: # If Service's EXTERNAL-IP is not same as allocated floating-ip, # update kube-api server with allocated fip as the EXTERNAL-IP self._deallocate_floating_ips(service_id) self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) return if not external_ips: self._update_service_external_ip(service_namespace, service_name, allocated_fips) return return if service_type in ["ClusterIP"]: if allocated_fips: if not external_ips: self._deallocate_floating_ips(service_id) else: if allocated_fips != external_ips: self._deallocate_floating_ips(service_id) self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) else: #allocated_fip is None if external_ips: self._allocate_floating_ips( service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) return def _check_service_uuid_change(self, svc_uuid, svc_name, svc_namespace, ports): proj_fq_name = vnc_kube_config.cluster_project_fq_name(svc_namespace) lb_fq_name = proj_fq_name + [svc_name] lb_uuid = LoadbalancerKM.get_fq_name_to_uuid(lb_fq_name) if lb_uuid is None: return if svc_uuid != lb_uuid: self.vnc_service_delete(lb_uuid, svc_name, svc_namespace, ports) self.logger.notice("Uuid change detected for service %s. " "Deleteing old service" % lb_fq_name) def vnc_service_add(self, service_id, service_name, service_namespace, service_ip, selectors, ports, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str): ingress_update = False lb = LoadbalancerKM.get(service_id) if not lb: ingress_update = True self._check_service_uuid_change(service_id, service_name, service_namespace, ports) self._lb_create(service_id, service_name, service_namespace, service_ip, ports) # "kubernetes" service needs a link-local service to be created. # This link-local service will steer traffic destined for # "kubernetes" service from slave (compute) nodes to kube-api server # running on master (control) node. if service_name == self._kubernetes_service_name: self._create_link_local_service(service_name, service_namespace, service_ip, ports) self._update_service_public_ip(service_id, service_name, service_namespace, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str) if ingress_update: self._ingress_mgr.update_ingress_backend(service_namespace, service_name, 'ADD') def _vnc_delete_pool(self, pool_id): self.service_lb_pool_mgr.delete(pool_id) def _vnc_delete_listener(self, ll_id): self.service_ll_mgr.delete(ll_id) def _vnc_delete_listeners(self, lb): listeners = lb.loadbalancer_listeners.copy() for ll_id in listeners or []: ll = LoadbalancerListenerKM.get(ll_id) if not ll: continue pool_id = ll.loadbalancer_pool if pool_id: pool = LoadbalancerPoolKM.get(pool_id) if pool: members = pool.members.copy() for member_id in members or []: member = LoadbalancerMemberKM.get(member_id) if member: self.service_lb_member_mgr.delete(member_id) self.logger.debug("Deleting LB member %s" % member.name) LoadbalancerMemberKM.delete(member_id) self._vnc_delete_pool(pool_id) self.logger.debug("Deleting LB pool %s" % pool.name) LoadbalancerPoolKM.delete(pool_id) self.logger.debug("Deleting LB listener %s" % ll.name) self._vnc_delete_listener(ll_id) LoadbalancerListenerKM.delete(ll_id) def _vnc_delete_lb(self, lb_id): self.service_lb_mgr.delete(lb_id) def _lb_delete(self, service_id, service_name, service_namespace): lb = LoadbalancerKM.get(service_id) if not lb: self.logger.debug( "LB doesnot exist for (%s,%s) in cfg db, return" % (service_namespace, service_name)) return self._vnc_delete_listeners(lb) self._vnc_delete_lb(service_id) LoadbalancerKM.delete(service_id) def vnc_service_delete(self, service_id, service_name, service_namespace, ports): self._deallocate_floating_ips(service_id) self._lb_delete(service_id, service_name, service_namespace) # Delete link local service that would have been allocated for # kubernetes service. if service_name == self._kubernetes_service_name: self._delete_link_local_service(service_name, service_namespace, ports) self._ingress_mgr.update_ingress_backend(service_namespace, service_name, 'DELETE') def _create_service_event(self, event_type, service_id, lb): event = {} object = {} object['kind'] = 'Service' object['spec'] = {} object['metadata'] = {} object['metadata']['uid'] = service_id if event_type == 'delete': event['type'] = 'DELETED' event['object'] = object self._queue.put(event) return def _sync_service_lb(self): lb_uuid_set = set(LoadbalancerKM.keys()) service_uuid_set = set(ServiceKM.keys()) deleted_uuid_set = lb_uuid_set - service_uuid_set for uuid in deleted_uuid_set: lb = LoadbalancerKM.get(uuid) if not lb: continue if not lb.annotations: continue owner = None kind = None cluster = None for kvp in lb.annotations['key_value_pair'] or []: if kvp['key'] == 'cluster': cluster = kvp['value'] elif kvp['key'] == 'owner': owner = kvp['value'] elif kvp['key'] == 'kind': kind = kvp['value'] if cluster == vnc_kube_config.cluster_name() and \ owner == 'k8s' and \ kind == self._k8s_event_type: self._create_service_event('delete', uuid, lb) break return def service_timer(self): self._sync_service_lb() return def process(self, event): event_type = event['type'] kind = event['object'].get('kind') service_namespace = event['object']['metadata'].get('namespace') service_name = event['object']['metadata'].get('name') service_id = event['object']['metadata'].get('uid') service_ip = event['object']['spec'].get('clusterIP') selectors = event['object']['spec'].get('selector', None) ports = event['object']['spec'].get('ports') service_type = event['object']['spec'].get('type') loadBalancerIp = event['object']['spec'].get('loadBalancerIP', None) externalIps = event['object']['spec'].get('externalIPs', []) annotations = event['object']['metadata'].get('annotations') specified_fip_pool_fq_name_str = None if annotations: if 'opencontrail.org/fip-pool' in annotations: specified_fip_pool_fq_name_str = annotations[ 'opencontrail.org/fip-pool'] print("%s - Got %s %s %s:%s:%s" % (self._name, event_type, kind, service_namespace, service_name, service_id)) self.logger.debug("%s - Got %s %s %s:%s:%s" % (self._name, event_type, kind, service_namespace, service_name, service_id)) # We dont need to do anything for Headless Service if (service_ip == 'None'): self.logger.warning( "%s - Headless Service %s:%s:%s" % (self._name, service_namespace, service_name, service_id)) return if event['type'] == 'ADDED' or event['type'] == 'MODIFIED': # Add a service label for this service. labels = self._labels.get_service_label(service_name) self._labels.process(service_id, labels) self.vnc_service_add(service_id, service_name, service_namespace, service_ip, selectors, ports, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str) elif event['type'] == 'DELETED': self.vnc_service_delete(service_id, service_name, service_namespace, ports) self._labels.process(service_id) else: self.logger.warning('Unknown event type: "{}" Ignoring'.format( event['type']))
class VncSecurityPolicy(VncCommon): default_policy_management_name = 'default-policy-management' vnc_lib = None cluster_aps_uuid = None get_tags_fn = None vnc_security_policy_instance = None allow_all_fw_policy_uuid = None deny_all_fw_policy_uuid = None ingress_svc_fw_policy_uuid = None def __init__(self, vnc_lib, get_tags_fn): self._k8s_event_type = 'VncSecurityPolicy' VncSecurityPolicy.vnc_lib = vnc_lib self._labels = XLabelCache(self._k8s_event_type) self.reset_resources() # Init FW Rule constructs. FWRule.default_policy_management_name = self.default_policy_management_name FWRule.vnc_lib = vnc_lib VncSecurityPolicy.get_tags_fn = get_tags_fn super(VncSecurityPolicy, self).__init__(self._k8s_event_type) VncSecurityPolicy.vnc_security_policy_instance = self def reset_resources(self): self._labels.reset_resource() VncSecurityPolicy.allow_all_fw_policy_uuid = None VncSecurityPolicy.deny_all_fw_policy_uuid = None VncSecurityPolicy.ingress_svc_fw_policy_uuid = None @staticmethod def construct_sequence_number(seq_num): snum_list = str(float(seq_num)).split('.') constructed_snum = "%s.%s" % (snum_list[0].zfill(5), snum_list[1]) return FirewallSequence(sequence=constructed_snum) @classmethod def create_application_policy_set(cls, name, parent_obj=None): if not parent_obj: pm_obj = PolicyManagement(cls.default_policy_management_name) try: parent_uuid = cls.vnc_lib.policy_management_create(pm_obj) except RefsExistError: pass pm_obj = cls.vnc_lib.policy_management_read( fq_name=pm_obj.get_fq_name()) PolicyManagementKM.locate(pm_obj.get_uuid()) else: pm_obj = parent_obj aps_obj = ApplicationPolicySet(name=name, parent_obj=pm_obj) try: aps_uuid = cls.vnc_lib.application_policy_set_create(aps_obj) except RefsExistError: cls.vnc_lib.application_policy_set_update(aps_obj) aps_uuid = aps_obj.get_uuid() # Update application policy set in our cache. ApplicationPolicySetKM.locate(aps_uuid) cls.cluster_aps_uuid = aps_uuid return aps_uuid @classmethod def tag_cluster_application_policy_set(cls): aps_uuid = cls.cluster_aps_uuid aps_obj = cls.vnc_lib.application_policy_set_read(id=aps_uuid) cls.vnc_security_policy_instance._labels.process(aps_uuid, cls.vnc_security_policy_instance._labels.get_cluster_label( vnc_kube_config.cluster_name())) cls.vnc_lib.set_tags(aps_obj, cls.vnc_security_policy_instance._labels.get_labels_dict(aps_uuid)) @classmethod def get_firewall_policy_name(cls, name, namespace, is_global): if is_global: return name else: return "-".join([namespace, name]) @classmethod def create_firewall_policy(cls, name, namespace, spec, tag_last=False, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_obj = FirewallPolicy( cls.get_firewall_policy_name(name, namespace, is_global), pm_obj) custom_ann_kwargs = {} if tag_last: custom_ann_kwargs['tail'] = "True" # Parse input spec and construct the list of rules for this FW policy. fw_rules = [] deny_all_rule_uuid = None if spec is not None: fw_rules, deny_all_rule_uuid = FWRule.parser(name, namespace, pm_obj, spec) for rule in fw_rules: try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) #FirewallSequence( # sequence=cls.construct_sequence_number(fw_rules.index(rule))) fw_policy_obj.add_firewall_rule(rule_obj, cls.construct_sequence_number(fw_rules.index(rule))) if deny_all_rule_uuid: VncSecurityPolicy.add_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, deny_all_rule_uuid) custom_ann_kwargs['deny_all_rule_uuid'] = deny_all_rule_uuid FirewallPolicyKM.add_annotations( VncSecurityPolicy.vnc_security_policy_instance, fw_policy_obj, namespace, name, None, **custom_ann_kwargs) try: fw_policy_uuid = cls.vnc_lib.firewall_policy_create(fw_policy_obj) except RefsExistError: cls.vnc_lib.firewall_policy_update(fw_policy_obj) fw_policy_uuid = fw_policy_obj.get_uuid() fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) FirewallPolicyKM.locate(fw_policy_uuid) return fw_policy_uuid @classmethod def delete_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid(fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return fw_policy = FirewallPolicyKM.locate(fw_policy_uuid) fw_policy_rules = fw_policy.firewall_rules # Remove deny all firewall rule, if any. if fw_policy.deny_all_rule_uuid: VncSecurityPolicy.delete_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, fw_policy.deny_all_rule_uuid) for rule_uuid in fw_policy_rules: try: VncSecurityPolicy.delete_firewall_rule(fw_policy_uuid, rule_uuid) except: raise cls.remove_firewall_policy(name, namespace) try: cls.vnc_lib.firewall_policy_delete(id=fw_policy_uuid) FirewallPolicyKM.delete(fw_policy_uuid) except: raise @classmethod def create_firewall_rule_allow_all(cls, rule_name, labels_dict, src_labels_dict=None): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise tags = VncSecurityPolicy.get_tags_fn(labels_dict, True) if src_labels_dict: src_tags = VncSecurityPolicy.get_tags_fn(src_labels_dict, True) else: src_tags = None protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.PASS.value ep1 = FWRuleEndpoint.get(src_tags) ep2 = FWRuleEndpoint.get(tags) service=FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule( name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value ) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def create_firewall_rule_deny_all(cls, rule_name, tags): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.DENY.value ep1 = FWRuleEndpoint.get() ep2 = FWRuleEndpoint.get(tags) service=FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule( name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value ) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def _move_trailing_firewall_policies(cls, aps_obj, tail_sequence): sequence_num = float(tail_sequence.get_sequence()) if cls.deny_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.deny_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 if cls.allow_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.allow_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 cls.vnc_lib.application_policy_set_update(aps_obj) return cls.construct_sequence_number(sequence_num) @classmethod def add_firewall_policy(cls, fw_policy_uuid, append_after_tail=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: new_fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy_uuid) except NoIdError: raise last_obj = False last_entry_sequence = None last_k8s_obj = None last_k8s_obj_sequence = None fw_policy_refs = aps_obj.get_firewall_policy_refs() for fw_policy in fw_policy_refs if fw_policy_refs else []: try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy['uuid']) except NoIdError: # TBD Error handling. pass # Return if the firewall policy is already found on this APS. if new_fw_policy_obj.get_fq_name() == fw_policy_obj.get_fq_name(): return k8s_obj = False last_obj = False annotations = fw_policy_obj.get_annotations() if annotations: for kvp in annotations.get_key_value_pair() or []: if kvp.key == 'owner' and kvp.value == 'k8s': k8s_obj = True elif kvp.key == 'tail' and kvp.value == 'True': last_obj = True if k8s_obj and last_obj: last_k8s_obj = fw_policy_obj last_k8s_obj_sequence = fw_policy['attr'].get_sequence() if not last_entry_sequence: last_entry_sequence = fw_policy['attr'].get_sequence() elif float(last_entry_sequence) < float(fw_policy['attr'].get_sequence()): last_entry_sequence = fw_policy['attr'].get_sequence() # # Determine the sequence number. # # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if len(fw_policy_refs if fw_policy_refs else []): last_k8s_fw_policy_sequence = \ cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) if last_k8s_obj_sequence: tail_sequence = cls._move_trailing_firewall_policies(aps_obj, last_k8s_fw_policy_sequence) if append_after_tail: sequence = cls.construct_sequence_number( float(tail_sequence.get_sequence())) else: sequence = FirewallSequence(sequence=last_k8s_obj_sequence) # Move the existing last k8s FW policy to the end of the list. else: sequence = last_k8s_fw_policy_sequence aps_obj.add_firewall_policy(new_fw_policy_obj, sequence) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def remove_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid(fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise aps_obj.del_firewall_policy(fw_policy_obj) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def add_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: raise last_entry_sequence = None rule_refs = fw_policy_obj.get_firewall_rule_refs() for rule in rule_refs if rule_refs else []: if fw_rule_uuid == rule['uuid']: return if last_entry_sequence < rule['attr'].get_sequence(): last_entry_sequence = rule['attr'].get_sequence() # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if last_entry_sequence: sequence = cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) fw_policy_obj.add_firewall_rule(fw_rule_obj, sequence) cls.vnc_lib.firewall_policy_update(fw_policy_obj) @classmethod def delete_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: raise fw_policy_obj.del_firewall_rule(fw_rule_obj) cls.vnc_lib.firewall_policy_update(fw_policy_obj) cls.vnc_lib.firewall_rule_delete(id=fw_rule_uuid) @classmethod def create_allow_all_security_policy(cls): if not cls.allow_all_fw_policy_uuid: allow_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy( "-".join([vnc_kube_config.cluster_name(), "allowall"]), None, None, is_global=True) VncSecurityPolicy.add_firewall_policy(allow_all_fw_policy_uuid, append_after_tail=True) cls.allow_all_fw_policy_uuid = allow_all_fw_policy_uuid @classmethod def create_deny_all_security_policy(cls): if not cls.deny_all_fw_policy_uuid: cls.deny_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy( "-".join([vnc_kube_config.cluster_name(), "denyall"]), None, None, tag_last=True, is_global=True) VncSecurityPolicy.add_firewall_policy(cls.deny_all_fw_policy_uuid) @classmethod def get_firewall_rule_uuid(cls, rule_name): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) pm = PolicyManagementKM.locate(aps.parent_uuid) rule_fq_name = pm.fq_name + [rule_name] rule_uuid = FirewallRuleKM.get_fq_name_to_uuid(rule_fq_name) return rule_uuid @classmethod def get_firewall_policy_rule_uuid(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) pm = PolicyManagementKM.locate(aps.parent_uuid) fw_policy_fq_name = pm.fq_name +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid(fw_policy_fq_name) return fw_policy_uuid
class VncSecurityPolicy(VncCommon): default_policy_management_name = 'default-policy-management' vnc_lib = None cluster_aps_uuid = None get_tags_fn = None vnc_security_policy_instance = None allow_all_fw_policy_uuid = None deny_all_fw_policy_uuid = None ingress_svc_fw_policy_uuid = None def __init__(self, vnc_lib, get_tags_fn): self._k8s_event_type = 'VncSecurityPolicy' VncSecurityPolicy.vnc_lib = vnc_lib self._labels = XLabelCache(self._k8s_event_type) self.reset_resources() # Init FW Rule constructs. FWRule.default_policy_management_name = self.default_policy_management_name FWRule.vnc_lib = vnc_lib VncSecurityPolicy.get_tags_fn = get_tags_fn super(VncSecurityPolicy, self).__init__(self._k8s_event_type) VncSecurityPolicy.vnc_security_policy_instance = self def reset_resources(self): self._labels.reset_resource() VncSecurityPolicy.allow_all_fw_policy_uuid = None VncSecurityPolicy.deny_all_fw_policy_uuid = None VncSecurityPolicy.ingress_svc_fw_policy_uuid = None @staticmethod def construct_sequence_number(seq_num): snum_list = str(float(seq_num)).split('.') constructed_snum = "%s.%s" % (snum_list[0].zfill(5), snum_list[1]) return FirewallSequence(sequence=constructed_snum) @classmethod def create_application_policy_set(cls, name, parent_obj=None): if not parent_obj: pm_obj = PolicyManagement(cls.default_policy_management_name) try: parent_uuid = cls.vnc_lib.policy_management_create(pm_obj) except RefsExistError: pass pm_obj = cls.vnc_lib.policy_management_read( fq_name=pm_obj.get_fq_name()) PolicyManagementKM.locate(pm_obj.get_uuid()) else: pm_obj = parent_obj aps_obj = ApplicationPolicySet(name=name, parent_obj=pm_obj) try: aps_uuid = cls.vnc_lib.application_policy_set_create(aps_obj) except RefsExistError: cls.vnc_lib.application_policy_set_update(aps_obj) aps_uuid = aps_obj.get_uuid() # Update application policy set in our cache. ApplicationPolicySetKM.locate(aps_uuid) cls.cluster_aps_uuid = aps_uuid return aps_uuid @classmethod def tag_cluster_application_policy_set(cls): aps_uuid = cls.cluster_aps_uuid aps_obj = cls.vnc_lib.application_policy_set_read(id=aps_uuid) cls.vnc_security_policy_instance._labels.process( aps_uuid, cls.vnc_security_policy_instance._labels.get_cluster_label( vnc_kube_config.cluster_name())) cls.vnc_lib.set_tags( aps_obj, cls.vnc_security_policy_instance._labels.get_labels_dict(aps_uuid)) @classmethod def get_firewall_policy_name(cls, name, namespace, is_global): if is_global: return name else: return "-".join([namespace, name]) @classmethod def create_firewall_policy(cls, name, namespace, spec, tag_last=False, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_obj = FirewallPolicy( cls.get_firewall_policy_name(name, namespace, is_global), pm_obj) custom_ann_kwargs = {} curr_fw_policy = None fw_rules_del_candidates = set() # If this firewall policy already exists, get its uuid. fw_policy_uuid = VncSecurityPolicy.get_firewall_policy_uuid( name, namespace, is_global) if fw_policy_uuid: # # FW policy exists. # Check for modidifcation to its spec. # If not modifications are found, return the uuid of policy. # curr_fw_policy = FirewallPolicyKM.locate(fw_policy_uuid) if curr_fw_policy and curr_fw_policy.spec: if curr_fw_policy.spec == json.dumps(spec): # Input spec is same as existing spec. Nothing to do. # Just return the uuid. return fw_policy_uuid # Get the current firewall rules on this policy. # All rules are delete candidates as any of them could have # changed. fw_rules_del_candidates = curr_fw_policy.firewall_rules # Annotate the FW policy object with input spec. # This will be used later to identify and validate subsequent modify # or add (i.e post restart) events. custom_ann_kwargs['spec'] = json.dumps(spec) # Check if we are being asked to place this firewall policy in the end # of fw policy list in its Application Policy Set. # If yes, tag accordingly. if tag_last: custom_ann_kwargs['tail'] = "True" # Parse input spec and construct the list of rules for this FW policy. fw_rules = [] deny_all_rule_uuid = None egress_deny_all_rule_uuid = None if spec is not None: fw_rules, deny_all_rule_uuid, egress_deny_all_rule_uuid =\ FWRule.parser(name, namespace, pm_obj, spec) for rule in fw_rules: try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() # The rule is in use and needs to stay. # Remove it from delete candidate collection. if fw_rules_del_candidates and\ rule_uuid in fw_rules_del_candidates: fw_rules_del_candidates.remove(rule_uuid) rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) fw_policy_obj.add_firewall_rule( rule_obj, cls.construct_sequence_number(fw_rules.index(rule))) if deny_all_rule_uuid: VncSecurityPolicy.add_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, deny_all_rule_uuid) custom_ann_kwargs['deny_all_rule_uuid'] = deny_all_rule_uuid if egress_deny_all_rule_uuid: VncSecurityPolicy.add_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, egress_deny_all_rule_uuid) custom_ann_kwargs['egress_deny_all_rule_uuid'] =\ egress_deny_all_rule_uuid FirewallPolicyKM.add_annotations( VncSecurityPolicy.vnc_security_policy_instance, fw_policy_obj, namespace, name, None, **custom_ann_kwargs) try: fw_policy_uuid = cls.vnc_lib.firewall_policy_create(fw_policy_obj) except RefsExistError: # Remove existing firewall rule refs on this fw policy. # Once existing firewall rules are remove, firewall policy will # be updated with rules correspoinding to current input spec. for rule in fw_rules_del_candidates: cls.delete_firewall_rule(fw_policy_uuid, rule) cls.vnc_lib.firewall_policy_update(fw_policy_obj) fw_policy_uuid = fw_policy_obj.get_uuid() fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) FirewallPolicyKM.locate(fw_policy_uuid) return fw_policy_uuid @classmethod def delete_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return fw_policy = FirewallPolicyKM.locate(fw_policy_uuid) fw_policy_rules = fw_policy.firewall_rules # Remove deny all firewall rule, if any. if fw_policy.deny_all_rule_uuid: VncSecurityPolicy.delete_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, fw_policy.deny_all_rule_uuid) # Remove egress deny all firewall rule, if any. if fw_policy.egress_deny_all_rule_uuid: VncSecurityPolicy.delete_firewall_rule( VncSecurityPolicy.deny_all_fw_policy_uuid, fw_policy.egress_deny_all_rule_uuid) for rule_uuid in fw_policy_rules: try: VncSecurityPolicy.delete_firewall_rule(fw_policy_uuid, rule_uuid) except: raise cls.remove_firewall_policy(name, namespace) try: cls.vnc_lib.firewall_policy_delete(id=fw_policy_uuid) FirewallPolicyKM.delete(fw_policy_uuid) except: raise @classmethod def create_firewall_rule_allow_all(cls, rule_name, labels_dict, src_labels_dict=None): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise tags = VncSecurityPolicy.get_tags_fn(labels_dict, True) if src_labels_dict: src_tags = VncSecurityPolicy.get_tags_fn(src_labels_dict, True) else: src_tags = None protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.PASS.value ep1 = FWRuleEndpoint.get(src_tags) ep2 = FWRuleEndpoint.get(tags) service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() rule_obj = cls.vnc_lib.firewall_rule_read(id=rule_uuid) FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def create_firewall_rule_deny_all(cls, rule_name, tags): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.DENY.value ep1 = FWRuleEndpoint.get() ep2 = FWRuleEndpoint.get(tags) service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def create_firewall_rule_egress_deny_all(cls, name, namespace, tags): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") # Get parent object for this firewall policy. aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise rule_name = "-".join( [FWRule.get_egress_rule_name(name, namespace), "default-deny-all"]) protocol = FWDefaultProtoPort.PROTOCOL.value port_start = FWDefaultProtoPort.START_PORT.value port_end = FWDefaultProtoPort.END_PORT.value action = FWSimpleAction.DENY.value ep1 = FWRuleEndpoint.get(tags) ep2 = FWRuleEndpoint.get() service = FWService.get(protocol, dst_start_port=port_start, dst_end_port=port_end) rule = FirewallRule(name='%s' % rule_name, parent_obj=pm_obj, action_list=action, service=service, endpoint_1=ep1, endpoint_2=ep2, direction=FWDirection.TO.value) try: rule_uuid = cls.vnc_lib.firewall_rule_create(rule) except RefsExistError: cls.vnc_lib.firewall_rule_update(rule) rule_uuid = rule.get_uuid() FirewallRuleKM.locate(rule_uuid) return rule_uuid @classmethod def _move_trailing_firewall_policies(cls, aps_obj, tail_sequence): sequence_num = float(tail_sequence.get_sequence()) if cls.deny_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.deny_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 if cls.allow_all_fw_policy_uuid: sequence = cls.construct_sequence_number(sequence_num) try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=cls.allow_all_fw_policy_uuid) except NoIdError: raise aps_obj.add_firewall_policy(fw_policy_obj, sequence) sequence_num += 1 cls.vnc_lib.application_policy_set_update(aps_obj) return cls.construct_sequence_number(sequence_num) @classmethod def add_firewall_policy(cls, fw_policy_uuid, append_after_tail=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: new_fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy_uuid) except NoIdError: raise last_obj = False last_entry_sequence = None last_k8s_obj = None last_k8s_obj_sequence = None fw_policy_refs = aps_obj.get_firewall_policy_refs() for fw_policy in fw_policy_refs if fw_policy_refs else []: try: fw_policy_obj = cls.vnc_lib.firewall_policy_read( id=fw_policy['uuid']) except NoIdError: # TBD Error handling. pass # Return if the firewall policy is already found on this APS. if new_fw_policy_obj.get_fq_name() == fw_policy_obj.get_fq_name(): return k8s_obj = False last_obj = False annotations = fw_policy_obj.get_annotations() if annotations: for kvp in annotations.get_key_value_pair() or []: if kvp.key == 'owner' and kvp.value == 'k8s': k8s_obj = True elif kvp.key == 'tail' and kvp.value == 'True': last_obj = True if k8s_obj and last_obj: last_k8s_obj = fw_policy_obj last_k8s_obj_sequence = fw_policy['attr'].get_sequence() if not last_entry_sequence: last_entry_sequence = fw_policy['attr'].get_sequence() elif float(last_entry_sequence) < float( fw_policy['attr'].get_sequence()): last_entry_sequence = fw_policy['attr'].get_sequence() # # Determine the sequence number. # # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if len(fw_policy_refs if fw_policy_refs else []): last_k8s_fw_policy_sequence = \ cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) if last_k8s_obj_sequence: tail_sequence = cls._move_trailing_firewall_policies( aps_obj, last_k8s_fw_policy_sequence) if append_after_tail: sequence = cls.construct_sequence_number( float(tail_sequence.get_sequence())) else: sequence = FirewallSequence(sequence=last_k8s_obj_sequence) # Move the existing last k8s FW policy to the end of the list. else: sequence = last_k8s_fw_policy_sequence aps_obj.add_firewall_policy(new_fw_policy_obj, sequence) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def remove_firewall_policy(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps_obj = cls.vnc_lib.application_policy_set_read( id=cls.cluster_aps_uuid) try: pm_obj = cls.vnc_lib.policy_management_read( fq_name=aps_obj.get_parent_fq_name()) except NoIdError: raise fw_policy_fq_name = pm_obj.get_fq_name() +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) if not fw_policy_uuid: # We are not aware of this firewall policy. return try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise aps_obj.del_firewall_policy(fw_policy_obj) cls.vnc_lib.application_policy_set_update(aps_obj) @classmethod def add_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: raise last_entry_sequence = None rule_refs = fw_policy_obj.get_firewall_rule_refs() for rule in rule_refs if rule_refs else []: if fw_rule_uuid == rule['uuid']: return if last_entry_sequence < rule['attr'].get_sequence(): last_entry_sequence = rule['attr'].get_sequence() # Start with presumption that this is the first. sequence = cls.construct_sequence_number('1.0') if last_entry_sequence: sequence = cls.construct_sequence_number( float(last_entry_sequence) + float('1.0')) fw_policy_obj.add_firewall_rule(fw_rule_obj, sequence) cls.vnc_lib.firewall_policy_update(fw_policy_obj) FirewallPolicyKM.locate(fw_policy_obj.get_uuid()) @classmethod def delete_firewall_rule(cls, fw_policy_uuid, fw_rule_uuid): # If policy or rule info is not provided, then there is nothing to do. if not fw_policy_uuid or not fw_rule_uuid: return try: fw_policy_obj = cls.vnc_lib.firewall_policy_read(id=fw_policy_uuid) except NoIdError: raise try: fw_rule_obj = cls.vnc_lib.firewall_rule_read(id=fw_rule_uuid) except NoIdError: return addr_grp_refs = fw_rule_obj.get_address_group_refs() fw_policy_obj.del_firewall_rule(fw_rule_obj) cls.vnc_lib.firewall_policy_update(fw_policy_obj) FirewallPolicyKM.locate(fw_policy_obj.get_uuid()) # Delete the rule. cls.vnc_lib.firewall_rule_delete(id=fw_rule_uuid) # Try to delete address groups allocated for this FW rule. for addr_grp in addr_grp_refs if addr_grp_refs else []: FWRule.delete_address_group(addr_grp['uuid']) @classmethod def create_allow_all_security_policy(cls): if not cls.allow_all_fw_policy_uuid: allow_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy( "-".join([vnc_kube_config.cluster_name(), "allowall"]), None, None, is_global=True) VncSecurityPolicy.add_firewall_policy(allow_all_fw_policy_uuid, append_after_tail=True) cls.allow_all_fw_policy_uuid = allow_all_fw_policy_uuid @classmethod def create_deny_all_security_policy(cls): if not cls.deny_all_fw_policy_uuid: cls.deny_all_fw_policy_uuid =\ VncSecurityPolicy.create_firewall_policy( "-".join([vnc_kube_config.cluster_name(), "denyall"]), None, None, tag_last=True, is_global=True) VncSecurityPolicy.add_firewall_policy(cls.deny_all_fw_policy_uuid) @classmethod def get_firewall_rule_uuid(cls, rule_name): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) pm = PolicyManagementKM.locate(aps.parent_uuid) rule_fq_name = pm.fq_name + [rule_name] rule_uuid = FirewallRuleKM.get_fq_name_to_uuid(rule_fq_name) return rule_uuid @classmethod def get_firewall_policy_uuid(cls, name, namespace, is_global=False): if not cls.cluster_aps_uuid: raise Exception("Cluster Application Policy Set not available.") aps = ApplicationPolicySetKM.locate(cls.cluster_aps_uuid) if not aps or not aps.parent_uuid: return None pm = PolicyManagementKM.locate(aps.parent_uuid) fw_policy_fq_name = pm.fq_name +\ [cls.get_firewall_policy_name(name, namespace, is_global)] fw_policy_uuid = FirewallPolicyKM.get_fq_name_to_uuid( fw_policy_fq_name) return fw_policy_uuid
class VncService(VncCommon): def __init__(self, ingress_mgr): self._k8s_event_type = 'Service' super(VncService,self).__init__(self._k8s_event_type) self._name = type(self).__name__ self._ingress_mgr = ingress_mgr self._vnc_lib = vnc_kube_config.vnc_lib() self._label_cache = vnc_kube_config.label_cache() self._labels = XLabelCache(self._k8s_event_type) self._labels.reset_resource() self._args = vnc_kube_config.args() self.logger = vnc_kube_config.logger() self._queue = vnc_kube_config.queue() self.kube = vnc_kube_config.kube() self._fip_pool_obj = None # Cache kubernetes API server params. self._kubernetes_api_server = self._args.kubernetes_api_server self._kubernetes_api_secure_port =\ int(self._args.kubernetes_api_secure_port) # Cache kuberneter service name. self._kubernetes_service_name = self._args.kubernetes_service_name # Config knob to control enable/disable of link local service. if self._args.api_service_link_local == 'True': api_service_ll_enable = True else: api_service_ll_enable = False # If Kubernetes API server info is incomplete, disable link-local create, # as create is not possible. if not self._kubernetes_api_server: self._create_linklocal = False elif vnc_kube_config.is_cluster_network_configured() and\ DBBaseKM.is_nested(): # In nested mode, if cluster network is configured, then the k8s api # server is in the same network as the k8s cluster. So there is no # need for link local. self._create_linklocal = False else: self._create_linklocal = api_service_ll_enable self.service_lb_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbManager') self.service_ll_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbListenerManager') self.service_lb_pool_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbPoolManager') self.service_lb_member_mgr = importutils.import_object( 'kube_manager.vnc.loadbalancer.ServiceLbMemberManager') def _get_project(self, service_namespace): proj_fq_name =\ vnc_kube_config.cluster_project_fq_name(service_namespace) try: proj_obj = self._vnc_lib.project_read(fq_name=proj_fq_name) return proj_obj except NoIdError: return None @staticmethod def _get_namespace(service_namespace): return NamespaceKM.find_by_name_or_uuid(service_namespace) def _get_annotated_ns_fip_pool(self, service_namespace): fip_pool_obj = None ns = self._get_namespace(service_namespace) try: if ns.get_annotated_ns_fip_pool_fq_name() != None: fip_pool_obj = self._vnc_lib.floating_ip_pool_read(fq_name=ns.get_annotated_ns_fip_pool_fq_name()) except NoIdError: return None return fip_pool_obj def _get_cluster_service_network(self, service_namespace): ns = self._get_namespace(service_namespace) if ns and ns.is_isolated(): vn_fq_name = ns.get_isolated_service_network_fq_name() else: vn_fq_name = vnc_kube_config.cluster_default_service_network_fq_name() try: vn_obj = self._vnc_lib.virtual_network_read(fq_name=vn_fq_name) except NoIdError: return None return vn_obj def _get_service_ipam_subnet_uuid(self, vn_obj): service_ipam_subnet_uuid = None fq_name = vnc_kube_config.service_ipam_fq_name() vn = VirtualNetworkKM.find_by_name_or_uuid(vn_obj.get_uuid()) if vn: service_ipam_subnet_uuid = vn.get_ipam_subnet_uuid(fq_name) if service_ipam_subnet_uuid is None: self.logger.error("%s - %s Not Found" %(self._name, fq_name)) return service_ipam_subnet_uuid def _get_specified_fip_pool(self, specified_fip_pool_fq_name_str): if specified_fip_pool_fq_name_str == None: return None fip_pool_fq_name = get_fip_pool_fq_name_from_dict_string( specified_fip_pool_fq_name_str) try: fip_pool_obj = self._vnc_lib.floating_ip_pool_read(fq_name=fip_pool_fq_name) except NoIdError: self.logger.notice("FIP Pool %s not found. " "Floating IP will not be available " "until FIP pool is configured." % (specified_fip_pool_fq_name_str)); return None return fip_pool_obj def _get_public_fip_pool(self): if self._fip_pool_obj: return self._fip_pool_obj if not vnc_kube_config.is_public_fip_pool_configured(): return None fip_pool_fq_name = get_fip_pool_fq_name_from_dict_string( self._args.public_fip_pool) try: fip_pool_obj = self._vnc_lib.floating_ip_pool_read(fq_name=fip_pool_fq_name) except NoIdError: self.logger.notice("Public FIP Pool not found. " "Floating IP will not be available " "until FIP pool is configured."); return None self._fip_pool_obj = fip_pool_obj return fip_pool_obj def _get_virtualmachine(self, id): try: vm_obj = self._vnc_lib.virtual_machine_read(id=id) except NoIdError: return None obj = self._vnc_lib.virtual_machine_read(id = id, fields = ['virtual_machine_interface_back_refs']) back_refs = getattr(obj, 'virtual_machine_interface_back_refs', None) vm_obj.virtual_machine_interface_back_refs = back_refs return vm_obj def check_service_selectors_actions(self, selectors, service_id, ports): for selector in selectors.items(): key = self._label_cache._get_key(selector) self._label_cache._locate_label(key, self._label_cache.service_selector_cache, selector, service_id) pod_ids = self._label_cache.pod_label_cache.get(key, []) if len(pod_ids): self.add_pods_to_service(service_id, pod_ids, ports) def _vnc_create_pool(self, namespace, ll, port): proj_obj = self._get_project(namespace) ll_obj = self.service_ll_mgr.read(ll.uuid) pool_obj = self.service_lb_pool_mgr.create(ll_obj, proj_obj, port) return pool_obj def _vnc_create_listener(self, namespace, lb, port): proj_obj = self._get_project(namespace) lb_obj = self.service_lb_mgr.read(lb.uuid) ll_obj = self.service_ll_mgr.create(lb_obj, proj_obj, port) return ll_obj def _create_listeners(self, namespace, lb, ports): for port in ports: listener_found = False for ll_id in lb.loadbalancer_listeners: ll = LoadbalancerListenerKM.get(ll_id) if not ll: continue if not ll.params['protocol_port']: continue if not ll.params['protocol']: continue if ll.params['protocol_port'] == port['port'] and \ ll.params['protocol'] == port['protocol']: listener_found = True break if not listener_found: ll_obj = self._vnc_create_listener(namespace, lb, port) ll = LoadbalancerListenerKM.locate(ll_obj._uuid) pool_id = ll.loadbalancer_pool if pool_id: pool = LoadbalancerPoolKM.get(pool_id) # SAS FIXME: If pool_id present, check for targetPort value if not pool_id or not pool: pool_obj = self._vnc_create_pool(namespace, ll, port) LoadbalancerPoolKM.locate(pool_obj._uuid) def _create_link_local_service(self, svc_name, svc_ns, svc_ip, ports): # Create link local service only if enabled. if self._create_linklocal: # Create link local service, one for each port. for port in ports: try: ll_mgr.create_link_local_service_entry(self._vnc_lib, name=svc_name + '-' + port['port'].__str__(), k8s_ns=svc_ns, service_ip=svc_ip, service_port=port['port'], fabric_ip=self._kubernetes_api_server, fabric_port=self._kubernetes_api_secure_port) except: self.logger.error("Create link-local service failed for" " service " + svc_name + " port " + port['port'].__str__()) def _delete_link_local_service(self, svc_name, svc_ns, ports): # Delete link local service only if enabled. if self._create_linklocal: # Delete link local service, one for each port. for port in ports: try: ll_mgr.delete_link_local_service_entry(self._vnc_lib, svc_name + '-' + port['port'].__str__(), svc_ns) except: self.logger.error("Delete link local service failed for" " service " + svc_name + " port " + port['port'].__str__()) def _vnc_create_lb(self, service_id, service_name, service_namespace, service_ip): proj_obj = self._get_project(service_namespace) vn_obj = self._get_cluster_service_network(service_namespace) service_ipam_subnet_uuid = self._get_service_ipam_subnet_uuid(vn_obj) lb_obj = self.service_lb_mgr.create(self._k8s_event_type, service_namespace, service_id, service_name, proj_obj, vn_obj, service_ip, service_ipam_subnet_uuid) return lb_obj def _lb_create(self, service_id, service_name, service_namespace, service_ip, ports): lb = LoadbalancerKM.get(service_id) if not lb: lb_obj = self._vnc_create_lb(service_id, service_name, service_namespace, service_ip) if not lb_obj: raise NoIdError lb = LoadbalancerKM.locate(service_id) self._create_listeners(service_namespace, lb, ports) def _read_allocated_floating_ips(self, service_id): floating_ips = set() lb = LoadbalancerKM.get(service_id) if not lb: return vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found=False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found=True break if interface_found is False: return fip_ids = vmi.floating_ips if fip_ids is None: return None for fip_id in list(fip_ids): fip = FloatingIpKM.get(fip_id) if fip is not None: floating_ips.add(fip.address) return floating_ips def _allocate_floating_ips(self, service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips=set()): lb = LoadbalancerKM.get(service_id) if not lb: return None vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found=False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found=True break if interface_found is False: return vmi_obj = self._vnc_lib.virtual_machine_interface_read(id=vmi_id) if vmi_obj is None: return None fip_pool = None if specified_fip_pool_fq_name_str != None: fip_pool = self._get_specified_fip_pool(specified_fip_pool_fq_name_str) if fip_pool is None and self._get_annotated_ns_fip_pool(service_namespace) != None: fip_pool = self._get_annotated_ns_fip_pool(service_namespace) if fip_pool is None: fip_pool = self._get_public_fip_pool() if fip_pool is None: self.logger.warning("public_fip_pool doesn't exists") return None def _check_ip_with_fip_pool(external_ip, fip_pool_obj): try: vn_obj = self._vnc_lib.virtual_network_read(id=fip_pool_obj.parent_uuid) except NoIdError: return True ipam_refs = vn_obj.__dict__.get('network_ipam_refs', []) for ipam_ref in ipam_refs: vnsn_data = ipam_ref['attr'].__dict__ ipam_subnets = vnsn_data.get('ipam_subnets', []) for ipam_subnet in ipam_subnets: subnet_dict = ipam_subnet.__dict__.get('subnet', {}) if 'ip_prefix' in subnet_dict.__dict__: ip_subnet_str = subnet_dict.__dict__.get('ip_prefix','')+'/' \ +str(subnet_dict.__dict__.get('ip_prefix_len')) if IPAddress(external_ip) in IPNetwork(ip_subnet_str): return True self.logger.error("external_ip not in fip_pool subnet") return False def _allocate_floating_ip(lb, vmi, fip_pool, external_ip=None): fip_obj = FloatingIp(lb.name + str(external_ip) + "-externalIP", fip_pool) fip_obj.set_virtual_machine_interface(vmi_obj) if external_ip: if not(_check_ip_with_fip_pool(external_ip, fip_pool)): err_str = "external_ip " + external_ip + " not in fip_pool subnet" self.logger.error(err_str) return None fip_obj.set_floating_ip_address(external_ip) project = self._vnc_lib.project_read(id=lb.parent_uuid) fip_obj.set_project(project) try: self._vnc_lib.floating_ip_create(fip_obj) except RefsExistError as e: string_buf = StringIO() cgitb_hook(file=string_buf, format="text") err_msg = string_buf.getvalue() self.logger.error("%s" %(err_msg)) except: string_buf = StringIO() cgitb_hook(file=string_buf, format="text") err_msg = string_buf.getvalue() self.logger.error("%s" %(err_msg)) fip = FloatingIpKM.locate(fip_obj.uuid) self.logger.notice("floating ip allocated : %s for Service (%s)" % (fip.address, service_id)) return(fip.address) fips = set() if len(external_ips) is 0: fip_addr = _allocate_floating_ip(lb, vmi, fip_pool) if fip_addr: fips.add(fip_addr) return fips for external_ip in external_ips: fip_addr = _allocate_floating_ip(lb, vmi, fip_pool, external_ip) if fip_addr: fips.add(fip_addr) return fips def _deallocate_floating_ips(self, service_id): lb = LoadbalancerKM.get(service_id) if not lb: return vmi_ids = lb.virtual_machine_interfaces if vmi_ids is None: return None interface_found=False for vmi_id in vmi_ids: vmi = VirtualMachineInterfaceKM.get(vmi_id) if vmi is not None: interface_found=True break if interface_found is False: return fip_ids = vmi.floating_ips.copy() for fip_id in fip_ids: try: self._vnc_lib.floating_ip_delete(id=fip_id) except NoIdError: pass def _update_service_external_ip(self, service_namespace, service_name, external_ips): merge_patch = {'spec': {'externalIPs': [', '.join(external_ips)]}} self.kube.patch_resource(resource_type="services", resource_name=service_name, namespace=service_namespace, merge_patch=merge_patch) self.logger.notice("Service (%s, %s) updated with EXTERNAL-IP (%s)" % (service_namespace, service_name, external_ips)); def _update_service_public_ip(self, service_id, service_name, service_namespace, service_type, external_ips, loadBalancerIp, specified_fip_pool_fq_name_str): allocated_fips = self._read_allocated_floating_ips(service_id) if service_type in ["LoadBalancer"]: if allocated_fips is not None and len(allocated_fips) is 0: # Allocate floating-ip from public-pool, if none exists. # if "loadBalancerIp" if specified in Service definition, allocate # loadBalancerIp as floating-ip. # if external ips specified, allocate external_ips as floating-ips. # if None specficied, then let contrail allocate a floating-ip and # update the allocated fip to kubernetes if loadBalancerIp: allocated_fip = self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, set([loadBalancerIp])) if allocated_fip: self._update_service_external_ip(service_namespace, service_name, allocated_fip) elif external_ips: allocated_fips = self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) else: allocated_fip = self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace) if allocated_fip: self._update_service_external_ip(service_namespace, service_name, allocated_fip) return if allocated_fips is not None and len(allocated_fips): if loadBalancerIp and loadBalancerIp in allocated_fips: self._deallocate_floating_ips(service_id) self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, set([loadBalancerIp])) self._update_service_external_ip(service_namespace, service_name, loadBalancerIp) return if external_ips and external_ips != allocated_fips: # If Service's EXTERNAL-IP is not same as allocated floating-ip, # update kube-api server with allocated fip as the EXTERNAL-IP self._deallocate_floating_ips(service_id) self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) return if not external_ips : self._update_service_external_ip(service_namespace, service_name, allocated_fips) return return if service_type in ["ClusterIP"]: if allocated_fips: if not external_ips : self._deallocate_floating_ips(service_id) else: if allocated_fips != external_ips: self._deallocate_floating_ips(service_id) self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) else: #allocated_fip is None if external_ips: self._allocate_floating_ips(service_id, specified_fip_pool_fq_name_str, service_namespace, external_ips) return def _check_service_uuid_change(self, svc_uuid, svc_name, svc_namespace, ports): proj_fq_name = vnc_kube_config.cluster_project_fq_name(svc_namespace) lb_fq_name = proj_fq_name + [svc_name] lb_uuid = LoadbalancerKM.get_fq_name_to_uuid(lb_fq_name) if lb_uuid is None: return if svc_uuid != lb_uuid: self.vnc_service_delete(lb_uuid, svc_name, svc_namespace, ports) self.logger.notice("Uuid change detected for service %s. " "Deleteing old service" % lb_fq_name); def vnc_service_add(self, service_id, service_name, service_namespace, service_ip, selectors, ports, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str): ingress_update = False lb = LoadbalancerKM.get(service_id) if not lb: ingress_update = True self._check_service_uuid_change(service_id, service_name, service_namespace, ports) self._lb_create(service_id, service_name, service_namespace, service_ip, ports) # "kubernetes" service needs a link-local service to be created. # This link-local service will steer traffic destined for # "kubernetes" service from slave (compute) nodes to kube-api server # running on master (control) node. if service_name == self._kubernetes_service_name: self._create_link_local_service(service_name, service_namespace, service_ip, ports) self._update_service_public_ip(service_id, service_name, service_namespace, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str) if ingress_update: self._ingress_mgr.update_ingress_backend( service_namespace, service_name, 'ADD') def _vnc_delete_pool(self, pool_id): self.service_lb_pool_mgr.delete(pool_id) def _vnc_delete_listener(self, ll_id): self.service_ll_mgr.delete(ll_id) def _vnc_delete_listeners(self, lb): listeners = lb.loadbalancer_listeners.copy() for ll_id in listeners or []: ll = LoadbalancerListenerKM.get(ll_id) if not ll: continue pool_id = ll.loadbalancer_pool if pool_id: pool = LoadbalancerPoolKM.get(pool_id) if pool: members = pool.members.copy() for member_id in members or []: member = LoadbalancerMemberKM.get(member_id) if member: self.service_lb_member_mgr.delete(member_id) self.logger.debug("Deleting LB member %s" % member.name) LoadbalancerMemberKM.delete(member_id) self._vnc_delete_pool(pool_id) self.logger.debug("Deleting LB pool %s" % pool.name) LoadbalancerPoolKM.delete(pool_id) self.logger.debug("Deleting LB listener %s" % ll.name) self._vnc_delete_listener(ll_id) LoadbalancerListenerKM.delete(ll_id) def _vnc_delete_lb(self, lb_id): self.service_lb_mgr.delete(lb_id) def _lb_delete(self, service_id, service_name, service_namespace): lb = LoadbalancerKM.get(service_id) if not lb: self.logger.debug("LB doesnot exist for (%s,%s) in cfg db, return" % (service_namespace, service_name)) return self._vnc_delete_listeners(lb) self._vnc_delete_lb(service_id) LoadbalancerKM.delete(service_id) def vnc_service_delete(self, service_id, service_name, service_namespace, ports): self._deallocate_floating_ips(service_id) self._lb_delete(service_id, service_name, service_namespace) # Delete link local service that would have been allocated for # kubernetes service. if service_name == self._kubernetes_service_name: self._delete_link_local_service(service_name, service_namespace, ports) self._ingress_mgr.update_ingress_backend( service_namespace, service_name, 'DELETE') def _create_service_event(self, event_type, service_id, lb): event = {} object = {} object['kind'] = 'Service' object['spec'] = {} object['metadata'] = {} object['metadata']['uid'] = service_id if event_type == 'delete': event['type'] = 'DELETED' event['object'] = object self._queue.put(event) return def _sync_service_lb(self): lb_uuid_set = set(LoadbalancerKM.keys()) service_uuid_set = set(ServiceKM.keys()) deleted_uuid_set = lb_uuid_set - service_uuid_set for uuid in deleted_uuid_set: lb = LoadbalancerKM.get(uuid) if not lb: continue if not lb.annotations: continue owner = None kind = None cluster = None for kvp in lb.annotations['key_value_pair'] or []: if kvp['key'] == 'cluster': cluster = kvp['value'] elif kvp['key'] == 'owner': owner = kvp['value'] elif kvp['key'] == 'kind': kind = kvp['value'] if cluster == vnc_kube_config.cluster_name() and \ owner == 'k8s' and \ kind == self._k8s_event_type: self._create_service_event('delete', uuid, lb) break return def service_timer(self): self._sync_service_lb() return def process(self, event): event_type = event['type'] kind = event['object'].get('kind') service_namespace = event['object']['metadata'].get('namespace') service_name = event['object']['metadata'].get('name') service_id = event['object']['metadata'].get('uid') service_ip = event['object']['spec'].get('clusterIP') selectors = event['object']['spec'].get('selector', None) ports = event['object']['spec'].get('ports') service_type = event['object']['spec'].get('type') loadBalancerIp = event['object']['spec'].get('loadBalancerIP', None) externalIps = event['object']['spec'].get('externalIPs', []) annotations = event['object']['metadata'].get('annotations') specified_fip_pool_fq_name_str = None if annotations: if 'opencontrail.org/fip-pool' in annotations: specified_fip_pool_fq_name_str = annotations['opencontrail.org/fip-pool'] print("%s - Got %s %s %s:%s:%s" %(self._name, event_type, kind, service_namespace, service_name, service_id)) self.logger.debug("%s - Got %s %s %s:%s:%s" %(self._name, event_type, kind, service_namespace, service_name, service_id)) if event['type'] == 'ADDED' or event['type'] == 'MODIFIED': # Add a service label for this service. labels = self._labels.get_service_label(service_name) self._labels.process(service_id, labels) self.vnc_service_add(service_id, service_name, service_namespace, service_ip, selectors, ports, service_type, externalIps, loadBalancerIp, specified_fip_pool_fq_name_str) elif event['type'] == 'DELETED': self.vnc_service_delete(service_id, service_name, service_namespace, ports) self._labels.process(service_id) else: self.logger.warning( 'Unknown event type: "{}" Ignoring'.format(event['type']))