From 60639d76a742852e18f9e2889c480be95596c268 Mon Sep 17 00:00:00 2001 From: Huanxuan Ao Date: Tue, 12 Jul 2016 12:44:55 +0800 Subject: [PATCH] Support bulk deletion for delete commands in identityv3 Support bulk deletion for delete commands in the list below identity/v3/consumer identity/v3/credential identity/v3/domain identity/v3/ec2creds identity/v3/endpoint identity/v3/federation_protocol identity/v3/identity_provider identity/v3/mapping identity/v3/policy identity/v3/region identity/v3/service_provider identity/v3/service The unit test in identityv3 need to be refactored, so I add some functional tests instead. I will add all unit tests at one time after the refactor completed. Change-Id: I82367570f59817b47c87b6c7bfeae95ccfe5c50e Closes-Bug: #1592906 --- doc/source/command-objects/consumer.rst | 6 ++-- doc/source/command-objects/domain.rst | 6 ++-- .../command-objects/federation-protocol.rst | 6 ++-- .../command-objects/identity-provider.rst | 6 ++-- doc/source/command-objects/mapping.rst | 6 ++-- doc/source/command-objects/policy.rst | 6 ++-- doc/source/command-objects/region.rst | 6 ++-- .../command-objects/service-provider.rst | 6 ++-- functional/tests/identity/v3/test_domain.py | 12 +++++++ functional/tests/identity/v3/test_endpoint.py | 7 +++++ functional/tests/identity/v3/test_idp.py | 7 +++++ functional/tests/identity/v3/test_region.py | 7 +++++ functional/tests/identity/v3/test_service.py | 7 +++++ .../identity/v3/test_service_provider.py | 7 +++++ openstackclient/identity/v3/consumer.py | 31 ++++++++++++++++--- openstackclient/identity/v3/credential.py | 27 ++++++++++++++-- openstackclient/identity/v3/domain.py | 25 ++++++++++++--- openstackclient/identity/v3/ec2creds.py | 25 +++++++++++++-- openstackclient/identity/v3/endpoint.py | 31 ++++++++++++++++--- .../identity/v3/federation_protocol.py | 24 +++++++++++--- .../identity/v3/identity_provider.py | 23 +++++++++++--- openstackclient/identity/v3/mapping.py | 20 ++++++++++-- openstackclient/identity/v3/policy.py | 27 ++++++++++++++-- openstackclient/identity/v3/region.py | 26 ++++++++++++++-- openstackclient/identity/v3/service.py | 29 ++++++++++++++--- .../identity/v3/service_provider.py | 28 ++++++++++++++--- .../tests/identity/v3/test_consumer.py | 2 +- .../tests/identity/v3/test_domain.py | 2 +- .../tests/identity/v3/test_endpoint.py | 2 +- .../identity/v3/test_identity_provider.py | 2 +- .../tests/identity/v3/test_mappings.py | 2 +- .../tests/identity/v3/test_protocol.py | 2 +- .../tests/identity/v3/test_region.py | 2 +- .../tests/identity/v3/test_service.py | 2 +- .../identity/v3/test_service_provider.py | 2 +- .../notes/bug-1592906-ad67ce8736f3cd48.yaml | 8 +++++ 36 files changed, 358 insertions(+), 79 deletions(-) create mode 100644 releasenotes/notes/bug-1592906-ad67ce8736f3cd48.yaml diff --git a/doc/source/command-objects/consumer.rst b/doc/source/command-objects/consumer.rst index 91294fa20..335eaea32 100644 --- a/doc/source/command-objects/consumer.rst +++ b/doc/source/command-objects/consumer.rst @@ -24,17 +24,17 @@ Create new consumer consumer delete --------------- -Delete consumer +Delete consumer(s) .. program:: consumer delete .. code:: bash os consumer delete - + [ ...] .. describe:: - Consumer to delete + Consumer(s) to delete consumer list ------------- diff --git a/doc/source/command-objects/domain.rst b/doc/source/command-objects/domain.rst index 94473570d..573a78cb2 100644 --- a/doc/source/command-objects/domain.rst +++ b/doc/source/command-objects/domain.rst @@ -43,17 +43,17 @@ Create new domain domain delete ------------- -Delete domain +Delete domain(s) .. program:: domain delete .. code:: bash os domain delete - + [ ...] .. describe:: - Domain to delete (name or ID) + Domain(s) to delete (name or ID) domain list ----------- diff --git a/doc/source/command-objects/federation-protocol.rst b/doc/source/command-objects/federation-protocol.rst index 5b4ea48ac..911d4e99c 100644 --- a/doc/source/command-objects/federation-protocol.rst +++ b/doc/source/command-objects/federation-protocol.rst @@ -34,14 +34,14 @@ Create new federation protocol federation protocol delete -------------------------- -Delete federation protocol +Delete federation protocol(s) .. program:: federation protocol delete .. code:: bash os federation protocol delete --identity-provider - + [ ...] .. option:: --identity-provider @@ -49,7 +49,7 @@ Delete federation protocol .. describe:: - Federation protocol to delete (name or ID) + Federation protocol(s) to delete (name or ID) federation protocol list ------------------------ diff --git a/doc/source/command-objects/identity-provider.rst b/doc/source/command-objects/identity-provider.rst index ca773d811..f772511d5 100644 --- a/doc/source/command-objects/identity-provider.rst +++ b/doc/source/command-objects/identity-provider.rst @@ -49,17 +49,17 @@ Create new identity provider identity provider delete ------------------------ -Delete identity provider +Delete identity provider(s) .. program:: identity provider delete .. code:: bash os identity provider delete - + [ ...] .. describe:: - Identity provider to delete + Identity provider(s) to delete identity provider list ---------------------- diff --git a/doc/source/command-objects/mapping.rst b/doc/source/command-objects/mapping.rst index 25af47406..7f61366e5 100644 --- a/doc/source/command-objects/mapping.rst +++ b/doc/source/command-objects/mapping.rst @@ -30,18 +30,18 @@ Create new mapping mapping delete -------------- -Delete mapping +Delete mapping(s) .. program:: mapping delete .. code:: bash os mapping delete - + [ ...] .. _mapping_delete-mapping: .. describe:: - Mapping to delete + Mapping(s) to delete mapping list ------------ diff --git a/doc/source/command-objects/policy.rst b/doc/source/command-objects/policy.rst index 195a89f25..757b1c53a 100644 --- a/doc/source/command-objects/policy.rst +++ b/doc/source/command-objects/policy.rst @@ -27,17 +27,17 @@ Create new policy policy delete ------------- -Delete policy +Delete policy(s) .. program:: policy delete .. code:: bash os policy delete - + [ ...] .. describe:: - Policy to delete + Policy(s) to delete policy list ----------- diff --git a/doc/source/command-objects/region.rst b/doc/source/command-objects/region.rst index 1892fc244..b74821a9d 100644 --- a/doc/source/command-objects/region.rst +++ b/doc/source/command-objects/region.rst @@ -33,18 +33,18 @@ Create new region region delete ------------- -Delete region +Delete region(s) .. program:: region delete .. code:: bash os region delete - + [ ...] .. _region_delete-region-id: .. describe:: - Region ID to delete + Region ID(s) to delete region list ----------- diff --git a/doc/source/command-objects/service-provider.rst b/doc/source/command-objects/service-provider.rst index 963493b4a..e0fbba2fe 100644 --- a/doc/source/command-objects/service-provider.rst +++ b/doc/source/command-objects/service-provider.rst @@ -48,17 +48,17 @@ Create new service provider service provider delete ----------------------- -Delete service provider +Delete service provider(s) .. program:: service provider delete .. code:: bash os service provider delete - + [ ...] .. describe:: - Service provider to delete + Service provider(s) to delete service provider list --------------------- diff --git a/functional/tests/identity/v3/test_domain.py b/functional/tests/identity/v3/test_domain.py index 305ed58da..3f514b580 100644 --- a/functional/tests/identity/v3/test_domain.py +++ b/functional/tests/identity/v3/test_domain.py @@ -43,6 +43,18 @@ class DomainTests(common.IdentityTests): raw_output = self.openstack('domain delete %s' % domain_name) self.assertEqual(0, len(raw_output)) + def test_domain_multi_delete(self): + domain_1 = self._create_dummy_domain(add_clean_up=False) + domain_2 = self._create_dummy_domain(add_clean_up=False) + # cannot delete enabled domain, disable it first + raw_output = self.openstack('domain set --disable %s' % domain_1) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack('domain set --disable %s' % domain_2) + self.assertEqual(0, len(raw_output)) + raw_output = self.openstack( + 'domain delete %s %s' % (domain_1, domain_2)) + self.assertEqual(0, len(raw_output)) + def test_domain_delete_failure(self): domain_name = self._create_dummy_domain() # cannot delete enabled domain diff --git a/functional/tests/identity/v3/test_endpoint.py b/functional/tests/identity/v3/test_endpoint.py index 162c9e58c..e0afab233 100644 --- a/functional/tests/identity/v3/test_endpoint.py +++ b/functional/tests/identity/v3/test_endpoint.py @@ -28,6 +28,13 @@ class EndpointTests(common.IdentityTests): 'endpoint delete %s' % endpoint_id) self.assertEqual(0, len(raw_output)) + def test_endpoint_multi_delete(self): + endpoint_1 = self._create_dummy_endpoint(add_clean_up=False) + endpoint_2 = self._create_dummy_endpoint(add_clean_up=False) + raw_output = self.openstack( + 'endpoint delete %s %s' % (endpoint_1, endpoint_2)) + self.assertEqual(0, len(raw_output)) + def test_endpoint_list(self): endpoint_id = self._create_dummy_endpoint() raw_output = self.openstack('endpoint list') diff --git a/functional/tests/identity/v3/test_idp.py b/functional/tests/identity/v3/test_idp.py index 8d8647124..bc9690f77 100644 --- a/functional/tests/identity/v3/test_idp.py +++ b/functional/tests/identity/v3/test_idp.py @@ -26,6 +26,13 @@ class IdentityProviderTests(common.IdentityTests): % identity_provider) self.assertEqual(0, len(raw_output)) + def test_idp_multi_delete(self): + idp_1 = self._create_dummy_idp(add_clean_up=False) + idp_2 = self._create_dummy_idp(add_clean_up=False) + raw_output = self.openstack( + 'identity provider delete %s %s' % (idp_1, idp_2)) + self.assertEqual(0, len(raw_output)) + def test_idp_show(self): identity_provider = self._create_dummy_idp(add_clean_up=True) raw_output = self.openstack('identity provider show %s' diff --git a/functional/tests/identity/v3/test_region.py b/functional/tests/identity/v3/test_region.py index 5ba0c231c..2ebc0e597 100644 --- a/functional/tests/identity/v3/test_region.py +++ b/functional/tests/identity/v3/test_region.py @@ -27,6 +27,13 @@ class RegionTests(common.IdentityTests): raw_output = self.openstack('region delete %s' % region_id) self.assertEqual(0, len(raw_output)) + def test_region_multi_delete(self): + region_1 = self._create_dummy_region(add_clean_up=False) + region_2 = self._create_dummy_region(add_clean_up=False) + raw_output = self.openstack( + 'region delete %s %s' % (region_1, region_2)) + self.assertEqual(0, len(raw_output)) + def test_region_list(self): raw_output = self.openstack('region list') items = self.parse_listing(raw_output) diff --git a/functional/tests/identity/v3/test_service.py b/functional/tests/identity/v3/test_service.py index c757d914e..79a63dc8a 100644 --- a/functional/tests/identity/v3/test_service.py +++ b/functional/tests/identity/v3/test_service.py @@ -25,6 +25,13 @@ class ServiceTests(common.IdentityTests): raw_output = self.openstack('service delete %s' % service_name) self.assertEqual(0, len(raw_output)) + def test_service_multi_delete(self): + service_1 = self._create_dummy_service(add_clean_up=False) + service_2 = self._create_dummy_service(add_clean_up=False) + raw_output = self.openstack( + 'service delete %s %s' % (service_1, service_2)) + self.assertEqual(0, len(raw_output)) + def test_service_list(self): self._create_dummy_service() raw_output = self.openstack('service list') diff --git a/functional/tests/identity/v3/test_service_provider.py b/functional/tests/identity/v3/test_service_provider.py index 6cfa7e56d..458c2ae65 100644 --- a/functional/tests/identity/v3/test_service_provider.py +++ b/functional/tests/identity/v3/test_service_provider.py @@ -26,6 +26,13 @@ class ServiceProviderTests(common.IdentityTests): % service_provider) self.assertEqual(0, len(raw_output)) + def test_sp_multi_delete(self): + sp1 = self._create_dummy_sp(add_clean_up=False) + sp2 = self._create_dummy_sp(add_clean_up=False) + raw_output = self.openstack( + 'service provider delete %s %s' % (sp1, sp2)) + self.assertEqual(0, len(raw_output)) + def test_sp_show(self): service_provider = self._create_dummy_sp(add_clean_up=True) raw_output = self.openstack('service provider show %s' diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index 65bf657fe..b41a37ca2 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -15,13 +15,19 @@ """Identity v3 Consumer action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateConsumer(command.ShowOne): """Create new consumer""" @@ -44,22 +50,37 @@ class CreateConsumer(command.ShowOne): class DeleteConsumer(command.Command): - """Delete consumer""" + """Delete consumer(s)""" def get_parser(self, prog_name): parser = super(DeleteConsumer, self).get_parser(prog_name) parser.add_argument( 'consumer', metavar='', - help=_('Consumer to delete'), + nargs='+', + help=_('Consumer(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - consumer = utils.find_resource( - identity_client.oauth1.consumers, parsed_args.consumer) - identity_client.oauth1.consumers.delete(consumer.id) + result = 0 + for i in parsed_args.consumer: + try: + consumer = utils.find_resource( + identity_client.oauth1.consumers, i) + identity_client.oauth1.consumers.delete(consumer.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consumer with name or " + "ID '%(consumer)s': %(e)s") + % {'consumer': i, 'e': e}) + + if result > 0: + total = len(parsed_args.consumer) + msg = (_("%(result)s of %(total)s consumers failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListConsumer(command.Lister): diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index eeeddfa55..0ea29abaa 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -15,13 +15,19 @@ """Identity v3 Credential action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateCredential(command.ShowOne): """Create credential command""" @@ -72,20 +78,35 @@ class CreateCredential(command.ShowOne): class DeleteCredential(command.Command): - """Delete credential command""" + """Delete credential(s)""" def get_parser(self, prog_name): parser = super(DeleteCredential, self).get_parser(prog_name) parser.add_argument( 'credential', metavar='', - help=_('ID of credential to delete'), + nargs='+', + help=_('ID of credential(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.credentials.delete(parsed_args.credential) + result = 0 + for i in parsed_args.credential: + try: + identity_client.credentials.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete credentials with " + "ID '%(credential)s': %(e)s") + % {'credential': i, 'e': e}) + + if result > 0: + total = len(parsed_args.credential) + msg = (_("%(result)s of %(total)s credential failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListCredential(command.Lister): diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 3e9bcf634..76e47d32e 100755 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -19,6 +19,7 @@ import logging from keystoneauth1 import exceptions as ks_exc from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -88,22 +89,36 @@ class CreateDomain(command.ShowOne): class DeleteDomain(command.Command): - """Delete domain""" + """Delete domain(s)""" def get_parser(self, prog_name): parser = super(DeleteDomain, self).get_parser(prog_name) parser.add_argument( 'domain', metavar='', - help=_('Domain to delete (name or ID)'), + nargs='+', + help=_('Domain(s) to delete (name or ID)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - domain = utils.find_resource(identity_client.domains, - parsed_args.domain) - identity_client.domains.delete(domain.id) + result = 0 + for i in parsed_args.domain: + try: + domain = utils.find_resource(identity_client.domains, i) + identity_client.domains.delete(domain.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete domain with name or " + "ID '%(domain)s': %(e)s") + % {'domain': i, 'e': e}) + + if result > 0: + total = len(parsed_args.domain) + msg = (_("%(result)s of %(total)s domains failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListDomain(command.Lister): diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 835fe96fd..7ad017190 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -12,7 +12,10 @@ """Identity v3 EC2 Credentials action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -20,6 +23,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + def _determine_ec2_user(parsed_args, client_manager): """Determine a user several different ways. @@ -113,7 +119,8 @@ class DeleteEC2Creds(command.Command): parser.add_argument( 'access_key', metavar='', - help=_('Credentials access key'), + nargs='+', + help=_('Credentials access key(s)'), ) parser.add_argument( '--user', @@ -126,7 +133,21 @@ class DeleteEC2Creds(command.Command): def take_action(self, parsed_args): client_manager = self.app.client_manager user = _determine_ec2_user(parsed_args, client_manager) - client_manager.identity.ec2.delete(user, parsed_args.access_key) + result = 0 + for i in parsed_args.access_key: + try: + client_manager.identity.ec2.delete(user, i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete EC2 credentials with " + "access key '%(access_key)s': %(e)s") + % {'access_key': i, 'e': e}) + + if result > 0: + total = len(parsed_args.access_key) + msg = (_("%(result)s of %(total)s EC2 keys failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListEC2Creds(command.Lister): diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index bd2df361d..73b37a436 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -15,7 +15,10 @@ """Identity v3 Endpoint action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -23,6 +26,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + def get_service_name(service): if hasattr(service, 'name'): return service.name @@ -93,22 +99,37 @@ class CreateEndpoint(command.ShowOne): class DeleteEndpoint(command.Command): - """Delete endpoint""" + """Delete endpoint(s)""" def get_parser(self, prog_name): parser = super(DeleteEndpoint, self).get_parser(prog_name) parser.add_argument( 'endpoint', metavar='', - help=_('Endpoint to delete (ID only)'), + nargs='+', + help=_('Endpoint(s) to delete (ID only)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - endpoint_id = utils.find_resource(identity_client.endpoints, - parsed_args.endpoint).id - identity_client.endpoints.delete(endpoint_id) + result = 0 + for i in parsed_args.endpoint: + try: + endpoint_id = utils.find_resource( + identity_client.endpoints, i).id + identity_client.endpoints.delete(endpoint_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete endpoint with " + "ID '%(endpoint)s': %(e)s") + % {'endpoint': i, 'e': e}) + + if result > 0: + total = len(parsed_args.endpoint) + msg = (_("%(result)s of %(total)s endpoints failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListEndpoint(command.Lister): diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 0369bc3d0..3fde9027e 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -17,6 +17,7 @@ import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -71,14 +72,15 @@ class CreateProtocol(command.ShowOne): class DeleteProtocol(command.Command): - """Delete federation protocol""" + """Delete federation protocol(s)""" def get_parser(self, prog_name): parser = super(DeleteProtocol, self).get_parser(prog_name) parser.add_argument( 'federation_protocol', metavar='', - help=_('Federation protocol to delete (name or ID)'), + nargs='+', + help=_('Federation protocol(s) to delete (name or ID)'), ) parser.add_argument( '--identity-provider', @@ -92,8 +94,22 @@ class DeleteProtocol(command.Command): def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.federation.protocols.delete( - parsed_args.identity_provider, parsed_args.federation_protocol) + result = 0 + for i in parsed_args.federation_protocol: + try: + identity_client.federation.protocols.delete( + parsed_args.identity_provider, i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete federation protocol " + "with name or ID '%(protocol)s': %(e)s") + % {'protocol': i, 'e': e}) + + if result > 0: + total = len(parsed_args.federation_protocol) + msg = (_("%(result)s of %(total)s federation protocols failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListProtocols(command.Lister): diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index 6fc9b13ce..0453e8883 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -16,6 +16,7 @@ import logging from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -93,21 +94,35 @@ class CreateIdentityProvider(command.ShowOne): class DeleteIdentityProvider(command.Command): - """Delete identity provider""" + """Delete identity provider(s)""" def get_parser(self, prog_name): parser = super(DeleteIdentityProvider, self).get_parser(prog_name) parser.add_argument( 'identity_provider', metavar='', - help=_('Identity provider to delete'), + nargs='+', + help=_('Identity provider(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.federation.identity_providers.delete( - parsed_args.identity_provider) + result = 0 + for i in parsed_args.identity_provider: + try: + identity_client.federation.identity_providers.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete identity providers with " + "name or ID '%(provider)s': %(e)s") + % {'provider': i, 'e': e}) + + if result > 0: + total = len(parsed_args.identity_provider) + msg = (_("%(result)s of %(total)s identity providers failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListIdentityProvider(command.Lister): diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 69c141b12..09181a0ba 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -111,21 +111,35 @@ class CreateMapping(command.ShowOne, _RulesReader): class DeleteMapping(command.Command): - """Delete mapping""" + """Delete mapping(s)""" def get_parser(self, prog_name): parser = super(DeleteMapping, self).get_parser(prog_name) parser.add_argument( 'mapping', metavar='', - help=_('Mapping to delete'), + nargs='+', + help=_('Mapping(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + result = 0 + for i in parsed_args.mapping: + try: + identity_client.federation.mappings.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete mapping with name or " + "ID '%(mapping)s': %(e)s") + % {'mapping': i, 'e': e}) - identity_client.federation.mappings.delete(parsed_args.mapping) + if result > 0: + total = len(parsed_args.mapping) + msg = (_("%(result)s of %(total)s mappings failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListMapping(command.Lister): diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 79215cab4..596eae018 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -15,13 +15,19 @@ """Identity v3 Policy action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreatePolicy(command.ShowOne): """Create new policy""" @@ -55,20 +61,35 @@ class CreatePolicy(command.ShowOne): class DeletePolicy(command.Command): - """Delete policy""" + """Delete policy(s)""" def get_parser(self, prog_name): parser = super(DeletePolicy, self).get_parser(prog_name) parser.add_argument( 'policy', metavar='', - help=_('Policy to delete'), + nargs='+', + help=_('Policy(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity - identity_client.policies.delete(parsed_args.policy) + result = 0 + for i in parsed_args.policy: + try: + identity_client.policies.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete policy with name or " + "ID '%(policy)s': %(e)s") + % {'policy': i, 'e': e}) + + if result > 0: + total = len(parsed_args.policy) + msg = (_("%(result)s of %(total)s policys failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListPolicy(command.Lister): diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index d714cd05e..b7c51f931 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -13,13 +13,19 @@ """Identity v3 Region action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateRegion(command.ShowOne): """Create new region""" @@ -60,21 +66,35 @@ class CreateRegion(command.ShowOne): class DeleteRegion(command.Command): - """Delete region""" + """Delete region(s)""" def get_parser(self, prog_name): parser = super(DeleteRegion, self).get_parser(prog_name) parser.add_argument( 'region', metavar='', - help=_('Region ID to delete'), + nargs='+', + help=_('Region ID(s) to delete'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + result = 0 + for i in parsed_args.region: + try: + identity_client.regions.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete region with " + "ID '%(region)s': %(e)s") + % {'region': i, 'e': e}) - identity_client.regions.delete(parsed_args.region) + if result > 0: + total = len(parsed_args.region) + msg = (_("%(result)s of %(total)s regions failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListRegion(command.Lister): diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 7b23ae299..97e64dc60 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -15,7 +15,10 @@ """Identity v3 Service action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six @@ -23,6 +26,9 @@ from openstackclient.i18n import _ from openstackclient.identity import common +LOG = logging.getLogger(__name__) + + class CreateService(command.ShowOne): """Create new service""" @@ -75,23 +81,36 @@ class CreateService(command.ShowOne): class DeleteService(command.Command): - """Delete service""" + """Delete service(s)""" def get_parser(self, prog_name): parser = super(DeleteService, self).get_parser(prog_name) parser.add_argument( 'service', metavar='', - help=_('Service to delete (type, name or ID)'), + nargs='+', + help=_('Service(s) to delete (type, name or ID)'), ) return parser def take_action(self, parsed_args): identity_client = self.app.client_manager.identity + result = 0 + for i in parsed_args.service: + try: + service = common.find_service(identity_client, i) + identity_client.services.delete(service.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consumer with type, " + "name or ID '%(service)s': %(e)s") + % {'service': i, 'e': e}) - service = common.find_service(identity_client, parsed_args.service) - - identity_client.services.delete(service.id) + if result > 0: + total = len(parsed_args.service) + msg = (_("%(result)s of %(total)s services failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListService(command.Lister): diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 1f95def81..440eba409 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -13,13 +13,19 @@ """Service Provider action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + class CreateServiceProvider(command.ShowOne): """Create new service provider""" @@ -81,21 +87,35 @@ class CreateServiceProvider(command.ShowOne): class DeleteServiceProvider(command.Command): - """Delete service provider""" + """Delete service provider(s)""" def get_parser(self, prog_name): parser = super(DeleteServiceProvider, self).get_parser(prog_name) parser.add_argument( 'service_provider', metavar='', - help=_('Service provider to delete'), + nargs='+', + help=_('Service provider(s) to delete'), ) return parser def take_action(self, parsed_args): service_client = self.app.client_manager.identity - service_client.federation.service_providers.delete( - parsed_args.service_provider) + result = 0 + for i in parsed_args.service_provider: + try: + service_client.federation.service_providers.delete(i) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete service provider with " + "name or ID '%(provider)s': %(e)s") + % {'provider': i, 'e': e}) + + if result > 0: + total = len(parsed_args.service_provider) + msg = (_("%(result)s of %(total)s service providers failed" + " to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListServiceProvider(command.Lister): diff --git a/openstackclient/tests/identity/v3/test_consumer.py b/openstackclient/tests/identity/v3/test_consumer.py index 4a8cf0871..d90c7347c 100644 --- a/openstackclient/tests/identity/v3/test_consumer.py +++ b/openstackclient/tests/identity/v3/test_consumer.py @@ -83,7 +83,7 @@ class TestConsumerDelete(TestOAuth1): identity_fakes.consumer_id, ] verifylist = [ - ('consumer', identity_fakes.consumer_id), + ('consumer', [identity_fakes.consumer_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_domain.py b/openstackclient/tests/identity/v3/test_domain.py index 17bcee065..5e0940217 100644 --- a/openstackclient/tests/identity/v3/test_domain.py +++ b/openstackclient/tests/identity/v3/test_domain.py @@ -182,7 +182,7 @@ class TestDomainDelete(TestDomain): self.domain.id, ] verifylist = [ - ('domain', self.domain.id), + ('domain', [self.domain.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_endpoint.py b/openstackclient/tests/identity/v3/test_endpoint.py index 184e14a4c..042763199 100644 --- a/openstackclient/tests/identity/v3/test_endpoint.py +++ b/openstackclient/tests/identity/v3/test_endpoint.py @@ -273,7 +273,7 @@ class TestEndpointDelete(TestEndpoint): identity_fakes.endpoint_id, ] verifylist = [ - ('endpoint', identity_fakes.endpoint_id), + ('endpoint', [identity_fakes.endpoint_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py index 8561fab99..1ec610527 100644 --- a/openstackclient/tests/identity/v3/test_identity_provider.py +++ b/openstackclient/tests/identity/v3/test_identity_provider.py @@ -255,7 +255,7 @@ class TestIdentityProviderDelete(TestIdentityProvider): identity_fakes.idp_id, ] verifylist = [ - ('identity_provider', identity_fakes.idp_id), + ('identity_provider', [identity_fakes.idp_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_mappings.py b/openstackclient/tests/identity/v3/test_mappings.py index af7b135d0..6aa1a6e5d 100644 --- a/openstackclient/tests/identity/v3/test_mappings.py +++ b/openstackclient/tests/identity/v3/test_mappings.py @@ -91,7 +91,7 @@ class TestMappingDelete(TestMapping): identity_fakes.mapping_id ] verifylist = [ - ('mapping', identity_fakes.mapping_id) + ('mapping', [identity_fakes.mapping_id]) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_protocol.py b/openstackclient/tests/identity/v3/test_protocol.py index 238b0ff8f..f718b27bd 100644 --- a/openstackclient/tests/identity/v3/test_protocol.py +++ b/openstackclient/tests/identity/v3/test_protocol.py @@ -88,7 +88,7 @@ class TestProtocolDelete(TestProtocol): identity_fakes.protocol_id ] verifylist = [ - ('federation_protocol', identity_fakes.protocol_id), + ('federation_protocol', [identity_fakes.protocol_id]), ('identity_provider', identity_fakes.idp_id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_region.py b/openstackclient/tests/identity/v3/test_region.py index 44e4814b0..41ee5ce9a 100644 --- a/openstackclient/tests/identity/v3/test_region.py +++ b/openstackclient/tests/identity/v3/test_region.py @@ -153,7 +153,7 @@ class TestRegionDelete(TestRegion): identity_fakes.region_id, ] verifylist = [ - ('region', identity_fakes.region_id), + ('region', [identity_fakes.region_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_service.py b/openstackclient/tests/identity/v3/test_service.py index 1e70383f5..a1f85adc7 100644 --- a/openstackclient/tests/identity/v3/test_service.py +++ b/openstackclient/tests/identity/v3/test_service.py @@ -200,7 +200,7 @@ class TestServiceDelete(TestService): identity_fakes.service_name, ] verifylist = [ - ('service', identity_fakes.service_name), + ('service', [identity_fakes.service_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v3/test_service_provider.py b/openstackclient/tests/identity/v3/test_service_provider.py index 428057949..f5270d839 100644 --- a/openstackclient/tests/identity/v3/test_service_provider.py +++ b/openstackclient/tests/identity/v3/test_service_provider.py @@ -185,7 +185,7 @@ class TestServiceProviderDelete(TestServiceProvider): service_fakes.sp_id, ] verifylist = [ - ('service_provider', service_fakes.sp_id), + ('service_provider', [service_fakes.sp_id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/releasenotes/notes/bug-1592906-ad67ce8736f3cd48.yaml b/releasenotes/notes/bug-1592906-ad67ce8736f3cd48.yaml new file mode 100644 index 000000000..e674a2092 --- /dev/null +++ b/releasenotes/notes/bug-1592906-ad67ce8736f3cd48.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + Support bulk deletion for identity v3 commands: ``consumer``, + ``credential``, ``domain``, ``ec2creds``, ``endpoint``, + ``federation_protocol``, ``identity_provider``, ``mapping``, + ``policy``, ``region``, ``service_provider`` and ``service``. + [Bug `1592906 `_]