def get_profile_data(self, profile): """get_profile_data Get data for a profile out of etcd. This should be used on profiles returned from functions like ``get_profiles``. :param profile: A ``Profile`` class. :return: A ``Profile`` class with tags and rules data present. """ LOG.debug("Getting profile %s", profile.id) etcd_profile_id = with_openstack_sg_prefix(profile.id) tags_result = self.client.read( datamodel_v1.key_for_profile_tags(etcd_profile_id), timeout=ETCD_TIMEOUT) rules_result = self.client.read( datamodel_v1.key_for_profile_rules(etcd_profile_id), timeout=ETCD_TIMEOUT) return Profile( id=profile.id, tags_modified_index=tags_result.modifiedIndex, rules_modified_index=rules_result.modifiedIndex, tags_data=tags_result.value, rules_data=rules_result.value, )
def get_profile_data(self, profile): """get_profile_data Get data for a profile out of etcd. This should be used on profiles returned from functions like ``get_profiles``. :param profile: A ``Profile`` class. :return: A ``Profile`` class with tags and rules data present. """ LOG.debug("Getting profile %s", profile.id) etcd_profile_id = with_openstack_sg_prefix(profile.id) tags_result = self.client.read( datamodel_v1.key_for_profile_tags(etcd_profile_id), timeout=ETCD_TIMEOUT ) rules_result = self.client.read( datamodel_v1.key_for_profile_rules(etcd_profile_id), timeout=ETCD_TIMEOUT ) return Profile( id=profile.id, tags_modified_index=tags_result.modifiedIndex, rules_modified_index=rules_result.modifiedIndex, tags_data=tags_result.value, rules_data=rules_result.value, )
def write_profile_to_etcd(self, profile, prev_rules_index=None, prev_tags_index=None): """Write a single security profile into etcd.""" LOG.debug("Writing profile %s", profile) etcd_profile_id = with_openstack_sg_prefix(profile.id) # python-etcd is stupid about the prevIndex keyword argument, so we # need to explicitly filter out None-y values ourselves. rules_kwargs = {} if prev_rules_index is not None: rules_kwargs['prevIndex'] = prev_rules_index tags_kwargs = {} if prev_tags_index is not None: tags_kwargs['prevIndex'] = prev_tags_index self.client.write( datamodel_v1.key_for_profile_rules(etcd_profile_id), json.dumps(profile_rules(profile)), **rules_kwargs ) self.client.write( datamodel_v1.key_for_profile_tags(etcd_profile_id), json.dumps(profile_tags(profile)), **tags_kwargs )
def atomic_delete_profile(self, profile): """atomic_delete_profile Atomically delete a profile. This occurs in two stages: first the tag, then the rules. Abort if the first stage fails, as we can assume that someone else is trying to replace the profile. Tolerates attempting to delete keys that are already deleted. This will also attempt to clean up the directory, but isn't overly bothered if that fails. """ LOG.info( "Deleting profile %s, tags modified %s, rules modified %s", profile.id, profile.tags_modified_index, profile.rules_modified_index ) etcd_profile_id = with_openstack_sg_prefix(profile.id) # Try to delete tags and rules. We don't care if we can't, but we # should log in case it's symptomatic of a wider problem. try: self.client.delete( datamodel_v1.key_for_profile_tags(etcd_profile_id), prevIndex=profile.tags_modified_index, timeout=ETCD_TIMEOUT ) except etcd.EtcdKeyNotFound: LOG.info( "Profile %s tags already deleted, nothing to do.", profile.id ) try: self.client.delete( datamodel_v1.key_for_profile_rules(etcd_profile_id), prevIndex=profile.rules_modified_index, timeout=ETCD_TIMEOUT ) except etcd.EtcdKeyNotFound: LOG.info( "Profile %s rules already deleted, nothing to do.", profile.id ) # Strip the rules/tags specific part of the key. profile_key = datamodel_v1.key_for_profile(etcd_profile_id) try: self.client.delete(profile_key, dir=True, timeout=ETCD_TIMEOUT) except etcd.EtcdException as e: LOG.debug("Failed to delete %s (%r), giving up.", profile_key, e)
def atomic_delete_profile(self, profile): """atomic_delete_profile Atomically delete a profile. This occurs in two stages: first the tag, then the rules. Abort if the first stage fails, as we can assume that someone else is trying to replace the profile. Tolerates attempting to delete keys that are already deleted. This will also attempt to clean up the directory, but isn't overly bothered if that fails. """ LOG.info("Deleting profile %s, tags modified %s, rules modified %s", profile.id, profile.tags_modified_index, profile.rules_modified_index) etcd_profile_id = with_openstack_sg_prefix(profile.id) # Try to delete tags and rules. We don't care if we can't, but we # should log in case it's symptomatic of a wider problem. try: self.client.delete( datamodel_v1.key_for_profile_tags(etcd_profile_id), prevIndex=profile.tags_modified_index, timeout=ETCD_TIMEOUT) except etcd.EtcdKeyNotFound: LOG.info("Profile %s tags already deleted, nothing to do.", profile.id) try: self.client.delete( datamodel_v1.key_for_profile_rules(etcd_profile_id), prevIndex=profile.rules_modified_index, timeout=ETCD_TIMEOUT) except etcd.EtcdKeyNotFound: LOG.info("Profile %s rules already deleted, nothing to do.", profile.id) # Strip the rules/tags specific part of the key. profile_key = datamodel_v1.key_for_profile(etcd_profile_id) try: self.client.delete(profile_key, dir=True, timeout=ETCD_TIMEOUT) except etcd.EtcdException as e: LOG.debug("Failed to delete %s (%r), giving up.", profile_key, e)
def write_profile_to_etcd(self, profile, prev_rules_index=None, prev_tags_index=None): """Write a single security profile into etcd.""" LOG.debug("Writing profile %s", profile) etcd_profile_id = with_openstack_sg_prefix(profile.id) # python-etcd is stupid about the prevIndex keyword argument, so we # need to explicitly filter out None-y values ourselves. rules_kwargs = {} if prev_rules_index is not None: rules_kwargs['prevIndex'] = prev_rules_index tags_kwargs = {} if prev_tags_index is not None: tags_kwargs['prevIndex'] = prev_tags_index self.client.write(datamodel_v1.key_for_profile_rules(etcd_profile_id), json.dumps(profile_rules(profile)), **rules_kwargs) self.client.write(datamodel_v1.key_for_profile_tags(etcd_profile_id), json.dumps(profile_tags(profile)), **tags_kwargs)
def test_key_for_profile_rules(self): self.assertEqual(key_for_profile_rules("prof1"), "/calico/v1/policy/profile/prof1/rules")