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
示例#2
0
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']))
示例#3
0
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
示例#4
0
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']))