Example #1
0
    def get_profiles(self):
        """
        Gets information about every profile in etcd. Returns a generator of
        ``Profile`` objects.
        """
        LOG.info("Scanning etcd for all profiles")

        try:
            result = self.client.read(
                PROFILE_DIR, recursive=True, timeout=ETCD_TIMEOUT
            )
        except etcd.EtcdKeyNotFound:
            # No key yet, which is totally fine: just exit.
            LOG.info("No profiles key present")
            return

        nodes = result.children

        tag_indices = {}
        rules_indices = {}

        for node in nodes:
            # All groups have both tags and rules, and we need the
            # modifiedIndex for both.
            tags_match = TAGS_KEY_RE.match(node.key)
            rules_match = RULES_KEY_RE.match(node.key)
            if tags_match:
                profile_id = tags_match.group('profile_id')
                tag_indices[profile_id] = node.modifiedIndex
            elif rules_match:
                profile_id = rules_match.group('profile_id')
                rules_indices[profile_id] = node.modifiedIndex
            else:
                continue

            # Check whether we have a complete set. If we do, remove them and
            # yield.
            if profile_id in tag_indices and profile_id in rules_indices:
                tag_modified = tag_indices.pop(profile_id)
                rules_modified = rules_indices.pop(profile_id)

                LOG.debug("Found profile id %s", profile_id)
                yield Profile(
                    id=profile_id,
                    tags_modified_index=tag_modified,
                    rules_modified_index=rules_modified,
                    tags_data=None,
                    rules_data=None,
                )

        # Quickly confirm that the tag and rule indices are empty (they should
        # be).
        if tag_indices or rules_indices:
            LOG.warning(
                "Imbalanced profile tags and rules! "
                "Extra tags %s, extra rules %s", tag_indices, rules_indices
            )
Example #2
0
    def resync_security_groups(self):
        # Get all current security groups from the OpenStack database and key
        # them on security group ID.
        with self._sgs_semaphore:
            self.sgs.clear()
            for sg in self.driver.get_security_groups():
                self.sgs[sg['id']] = sg

        # As we look at the etcd data, accumulate a set of profile IDs that
        # already have correct data.
        correct_profiles = set()

        # Read all etcd keys directly under /calico/v1/policy/profile.
        try:
            children = self.client.read(PROFILE_DIR, recursive=True).children
        except etcd.EtcdKeyNotFound:
            children = []
        for child in children:
            LOG.debug("etcd key: %s" % child.key)
            m = TAGS_KEY_RE.match(child.key)
            if m:
                # If there are no policies, then read returns the top level
                # node, so we need to check that this really is a profile ID.
                profile_id = m.group("profile_id")
                LOG.debug("Existing etcd profile data for %s" % profile_id)
                try:
                    if profile_id in self.needed_profiles:
                        # This is a profile that we want.  Let's read its rules and
                        # tags, and compare those against the current OpenStack data.
                        rules_key = key_for_profile_rules(profile_id)
                        rules = json_decoder.decode(
                            self.client.read(rules_key).value)
                        tags_key = key_for_profile_tags(profile_id)
                        tags = json_decoder.decode(
                            self.client.read(tags_key).value)

                        if (rules == self.profile_rules(profile_id) and
                            tags == self.profile_tags(profile_id)):
                            # The existing etcd data for this profile is completely
                            # correct.  Remember the profile_id so that we don't
                            # unnecessarily write out its (unchanged) data again below.
                            LOG.debug("Existing etcd profile data is correct")
                            correct_profiles.add(profile_id)
                    else:
                        # We don't want this profile any more, so delete the key.
                        LOG.debug("Existing etcd profile key is now invalid")
                        profile_key = key_for_profile(profile_id)
                        self.client.delete(profile_key, recursive=True)
                except etcd.EtcdKeyNotFound:
                    LOG.info("Etcd data appears to have been reset")

        # Now write etcd data for each profile that we need and that we don't
        # already know to be correct.
        for profile_id in self.needed_profiles.difference(correct_profiles):
            self.write_profile_to_etcd(profile_id)
Example #3
0
def parse_if_tags(etcd_node):
    m = TAGS_KEY_RE.match(etcd_node.key)
    if m:
        # Got some tags.
        profile_id = m.group("profile_id")
        if etcd_node.action == "delete":
            tags = None
        else:
            tags = parse_tags(profile_id, etcd_node.value)
        return intern(profile_id.encode("utf8")), tags
    return None, None
Example #4
0
def parse_if_tags(etcd_node):
    m = TAGS_KEY_RE.match(etcd_node.key)
    if m:
        # Got some tags.
        profile_id = m.group("profile_id")
        if etcd_node.action == "delete":
            tags = None
        else:
            tags = parse_tags(profile_id, etcd_node.value)
        return intern(profile_id.encode("utf8")), tags
    return None, None
Example #5
0
    def resync_security_groups(self):
        # Get all current security groups from the OpenStack database and key
        # them on security group ID.
        self.sgs = {}
        for sg in self.driver.get_security_groups():
            self.sgs[sg['id']] = sg

        # As we look at the etcd data, accumulate a set of profile IDs that
        # already have correct data.
        correct_profiles = set()

        # Read all etcd keys directly under /calico/v1/policy/profile.
        try:
            children = self.client.read(PROFILE_DIR, recursive=True).children
        except etcd.EtcdKeyNotFound:
            children = []
        for child in children:
            LOG.debug("etcd key: %s" % child.key)
            m = TAGS_KEY_RE.match(child.key)
            if m:
                # If there are no policies, then read returns the top level
                # node, so we need to check that this really is a profile ID.
                profile_id = m.group("profile_id")
                LOG.debug("Existing etcd profile data for %s" % profile_id)
                try:
                    if profile_id in self.needed_profiles:
                        # This is a profile that we want.  Let's read its rules and
                        # tags, and compare those against the current OpenStack data.
                        rules_key = key_for_profile_rules(profile_id)
                        rules = json_decoder.decode(
                            self.client.read(rules_key).value)
                        tags_key = key_for_profile_tags(profile_id)
                        tags = json_decoder.decode(
                            self.client.read(tags_key).value)

                        if (rules == self.profile_rules(profile_id) and
                            tags == self.profile_tags(profile_id)):
                            # The existing etcd data for this profile is completely
                            # correct.  Remember the profile_id so that we don't
                            # unnecessarily write out its (unchanged) data again below.
                            LOG.debug("Existing etcd profile data is correct")
                            correct_profiles.add(profile_id)
                    else:
                        # We don't want this profile any more, so delete the key.
                        LOG.debug("Existing etcd profile key is now invalid")
                        profile_key = key_for_profile(profile_id)
                        self.client.delete(profile_key, recursive=True)
                except etcd.EtcdKeyNotFound:
                    LOG.info("Etcd data appears to have been reset")

        # Now write etcd data for each profile that we need and that we don't
        # already know to be correct.
        for profile_id in self.needed_profiles.difference(correct_profiles):
            self.write_profile_to_etcd(profile_id)
Example #6
0
def parse_if_tags(etcd_node):
    m = TAGS_KEY_RE.match(etcd_node.key)
    if m:
        # Got some tags.
        profile_id = m.group("profile_id")
        if etcd_node.action == "delete":
            tags = None
        else:
            tags = json_decoder.decode(etcd_node.value)
            try:
                validate_tags(tags)
            except ValidationFailed:
                _log.exception("Validation failed for profile %s tags : %s",
                               profile_id, tags)
                return profile_id, None

        _log.debug("Found tags for profile %s : %s", profile_id, tags)

        return profile_id, tags
    return None, None