From 0e4c0dc8df0abb2af4a081522f54f6c11b5d5a98 Mon Sep 17 00:00:00 2001 From: Kent Wu Date: Fri, 10 Mar 2017 10:35:40 -0800 Subject: [PATCH] Implement the 'gbp purge ' CLI This basically follows what 'neutron purge' CLI is doing. And here is the neutron client patch for that: https://review.openstack.org/#/c/276541 Some special care has to be done to take care of auto_ptg/l2p/l3p created under implicit workflow otherwise the CLI will report failures while deleting those. Also publish the purge() API thru gbpclient interface. Change-Id: Ib1c515f0b66cf4b958472b8f56ba1a4e574e8431 --- gbpclient/gbp/v2_0/purge.py | 112 +++++++++++++++++++++++ gbpclient/gbpshell.py | 2 + gbpclient/tests/unit/test_cli20_purge.py | 27 ++++++ gbpclient/v2_0/client.py | 5 + 4 files changed, 146 insertions(+) create mode 100644 gbpclient/gbp/v2_0/purge.py create mode 100644 gbpclient/tests/unit/test_cli20_purge.py diff --git a/gbpclient/gbp/v2_0/purge.py b/gbpclient/gbp/v2_0/purge.py new file mode 100644 index 0000000..bfebcda --- /dev/null +++ b/gbpclient/gbp/v2_0/purge.py @@ -0,0 +1,112 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import re +import sys + +from neutronclient.common import exceptions as nexc +from neutronclient.neutron.v2_0 import purge as n_purge + +AUTO_PTG_REGEX = 'auto[0-9a-f]{32}\Z' + + +class Purge(n_purge.Purge): + """Delete all resources that belong to a given tenant.""" + + def _pluralize(self, string): + if re.search('_policy$', string): + return re.sub('_policy$', '_policies', string) + return string + 's' + + def _get_resources(self, neutron_client, resource_types, tenant_id): + resources = super(Purge, self)._get_resources(neutron_client, + resource_types, + tenant_id) + # exclude auto_ptg as it was created by implicit workflow + if 'policy_target_group' in resource_types: + index = resource_types.index('policy_target_group') + for resource in list(resources[index]): + if re.match(AUTO_PTG_REGEX, resource['id']): + resources[index].remove(resource) + self.total_resources -= 1 + return resources + + def _purge_resources(self, neutron_client, resource_types, + tenant_resources): + deleted = {} + failed = {} + failures = False + for index, resources in enumerate(tenant_resources): + resource_type = resource_types[index] + failed[resource_type] = 0 + deleted[resource_type] = 0 + for resource in resources: + try: + self._delete_resource(neutron_client, resource_type, + resource) + deleted[resource_type] += 1 + self.deleted_resources += 1 + except nexc.NotFound: + # this is for l2p/l3p created under the + # implicit workflow. + deleted[resource_type] += 1 + self.deleted_resources += 1 + except Exception: + failures = True + failed[resource_type] += 1 + self.total_resources -= 1 + percent_complete = 100 + if self.total_resources > 0: + percent_complete = (self.deleted_resources / + float(self.total_resources)) * 100 + sys.stdout.write("\rPurging resources: %d%% complete." % + percent_complete) + sys.stdout.flush() + return (deleted, failed, failures) + + def take_action(self, parsed_args): + neutron_client = self.get_client() + + self.any_failures = False + + # A list of the types of resources supported in the order in which + # they should be deleted. + resource_types = ['policy_target', 'policy_target_group', 'l2_policy', + 'l3_policy', 'external_policy', 'nat_pool', + 'external_segment', 'policy_rule_set', + 'policy_rule', 'policy_classifier', + 'policy_action', 'network_service_policy', + 'servicechain_instance', 'servicechain_spec', + 'servicechain_node', 'service_profile'] + deleted = {} + failed = {} + self.total_resources = 0 + self.deleted_resources = 0 + resources = self._get_resources(neutron_client, resource_types, + parsed_args.tenant) + deleted, failed, failures = self._purge_resources(neutron_client, + resource_types, + resources) + print('\n%s' % self._build_message(deleted, failed, failures)) + + # TODO(Kent): clean up Neutron resources also + # super(Purge, self).take_action(parsed_args) + + +class PurgeAPI(Purge): + def __init__(self, app, app_args, gbp_client): + self.gbp_client = gbp_client + super(PurgeAPI, self).__init__(app, app_args) + + def get_client(self): + return self.gbp_client diff --git a/gbpclient/gbpshell.py b/gbpclient/gbpshell.py index 56b3fdb..add47ab 100644 --- a/gbpclient/gbpshell.py +++ b/gbpclient/gbpshell.py @@ -39,6 +39,7 @@ from neutronclient.i18n import _ from neutronclient.version import __version__ from gbpclient.gbp.v2_0 import groupbasedpolicy as gbp +from gbpclient.gbp.v2_0 import purge from gbpclient.gbp.v2_0 import servicechain VERSION = '2.0' @@ -152,6 +153,7 @@ COMMAND_V2 = { 'policy-rule-set-update': gbp.UpdatePolicyRuleSet, 'policy-rule-set-list': gbp.ListPolicyRuleSet, 'policy-rule-set-show': gbp.ShowPolicyRuleSet, + 'purge': purge.Purge, 'service-profile-list': servicechain.ListServiceProfile, 'service-profile-show': servicechain.ShowServiceProfile, 'service-profile-create': servicechain.CreateServiceProfile, diff --git a/gbpclient/tests/unit/test_cli20_purge.py b/gbpclient/tests/unit/test_cli20_purge.py new file mode 100644 index 0000000..8f49fdc --- /dev/null +++ b/gbpclient/tests/unit/test_cli20_purge.py @@ -0,0 +1,27 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from neutronclient.tests.unit import test_cli20_purge + + +class CLITestV20Purge(test_cli20_purge.CLITestV20Purge): + def setUp(self): + super(CLITestV20Purge, self).setUp() + self.resource_types = ['policy_target', 'policy_target_group', + 'l2_policy', 'l3_policy', 'external_policy', + 'nat_pool', 'external_segment', + 'policy_rule_set', 'policy_rule', + 'policy_classifier', 'policy_action', + 'network_service_policy', + 'servicechain_instance', 'servicechain_spec', + 'servicechain_node', 'service_profile'] diff --git a/gbpclient/v2_0/client.py b/gbpclient/v2_0/client.py index e87407b..434df4a 100644 --- a/gbpclient/v2_0/client.py +++ b/gbpclient/v2_0/client.py @@ -14,6 +14,7 @@ import logging import time +from gbpclient.gbp.v2_0 import purge as gbpclient_purge from neutronclient import client from neutronclient.common import exceptions from neutronclient.common import serializer @@ -685,6 +686,10 @@ class Client(object): return self.delete(self.servicechain_instance_path % (servicechain_instance)) + def purge(self, tenant_id): + purge_obj = gbpclient_purge.PurgeAPI(None, None, self) + purge_obj.take_action(tenant_id) + def __init__(self, **kwargs): """Initialize a new client for the GBP v2.0 API.""" super(Client, self).__init__()