diff --git a/doc/source/command-objects/ec2-credentials.rst b/doc/source/command-objects/ec2-credentials.rst index 6748422e14..caf7fd6901 100644 --- a/doc/source/command-objects/ec2-credentials.rst +++ b/doc/source/command-objects/ec2-credentials.rst @@ -55,7 +55,7 @@ Delete EC2 credentials os ec2 credentials delete [--user ] [--user-domain ] - + [ ...] .. option:: --user @@ -71,7 +71,7 @@ Delete EC2 credentials .. _ec2_credentials_delete-access-key: .. describe:: access-key - Credentials access key + Credentials access key(s) The :option:`--user` option is typically only useful for admin users, but may be allowed for other users depending on the policy of the cloud and diff --git a/doc/source/command-objects/endpoint.rst b/doc/source/command-objects/endpoint.rst index 817ad49143..c058c8444e 100644 --- a/doc/source/command-objects/endpoint.rst +++ b/doc/source/command-objects/endpoint.rst @@ -81,18 +81,18 @@ Create new endpoint endpoint delete --------------- -Delete endpoint +Delete endpoint(s) .. program:: endpoint delete .. code:: bash os endpoint delete - + [ ...] .. _endpoint_delete-endpoint: .. describe:: - Endpoint to delete (ID only) + Endpoint(s) to delete (ID only) endpoint list ------------- diff --git a/doc/source/command-objects/service.rst b/doc/source/command-objects/service.rst index 421a298b31..b5ec2d9962 100644 --- a/doc/source/command-objects/service.rst +++ b/doc/source/command-objects/service.rst @@ -46,18 +46,18 @@ Create new service service delete -------------- -Delete service +Delete service(s) .. program:: service delete .. code-block:: bash os service delete - + [ ...] .. _service_delete-type: .. describe:: - Service to delete (type, name or ID) + Service(s) to delete (type, name or ID) service list ------------ diff --git a/functional/tests/identity/v2/test_ec2_credentials.py b/functional/tests/identity/v2/test_ec2_credentials.py index 319bd11a64..7a8ee35d7e 100644 --- a/functional/tests/identity/v2/test_ec2_credentials.py +++ b/functional/tests/identity/v2/test_ec2_credentials.py @@ -25,6 +25,14 @@ class EC2CredentialsTests(common.IdentityTests): ) self.assertEqual(0, len(raw_output)) + def test_ec2_credentials_multi_delete(self): + access_key_1 = self._create_dummy_ec2_credentials(add_clean_up=False) + access_key_2 = self._create_dummy_ec2_credentials(add_clean_up=False) + raw_output = self.openstack( + 'ec2 credentials delete ' + access_key_1 + ' ' + access_key_2 + ) + self.assertEqual(0, len(raw_output)) + def test_ec2_credentials_list(self): self._create_dummy_ec2_credentials() raw_output = self.openstack('ec2 credentials list') diff --git a/functional/tests/identity/v2/test_endpoint.py b/functional/tests/identity/v2/test_endpoint.py index 0682e6b476..34888c0b50 100644 --- a/functional/tests/identity/v2/test_endpoint.py +++ b/functional/tests/identity/v2/test_endpoint.py @@ -24,6 +24,13 @@ class EndpointTests(common.IdentityTests): 'endpoint delete %s' % endpoint_id) self.assertEqual(0, len(raw_output)) + def test_endpoint_multi_delete(self): + endpoint_id_1 = self._create_dummy_endpoint(add_clean_up=False) + endpoint_id_2 = self._create_dummy_endpoint(add_clean_up=False) + raw_output = self.openstack( + 'endpoint delete ' + endpoint_id_1 + ' ' + endpoint_id_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/v2/test_service.py b/functional/tests/identity/v2/test_service.py index 219ed33f07..9dcb6bea07 100644 --- a/functional/tests/identity/v2/test_service.py +++ b/functional/tests/identity/v2/test_service.py @@ -23,6 +23,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_name_1 = self._create_dummy_service(add_clean_up=False) + service_name_2 = self._create_dummy_service(add_clean_up=False) + raw_output = self.openstack( + 'service delete ' + service_name_1 + ' ' + service_name_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/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 78f65824a6..058b5772d8 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -16,13 +16,19 @@ """Identity v2 EC2 Credentials 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 CreateEC2Creds(command.ShowOne): """Create EC2 credentials""" @@ -85,9 +91,10 @@ class DeleteEC2Creds(command.Command): def get_parser(self, prog_name): parser = super(DeleteEC2Creds, self).get_parser(prog_name) parser.add_argument( - 'access_key', + 'access_keys', metavar='', - help=_('Credentials access key'), + nargs='+', + help=_('Credentials access keys'), ) parser.add_argument( '--user', @@ -108,7 +115,21 @@ class DeleteEC2Creds(command.Command): # Get the user from the current auth user = self.app.client_manager.auth_ref.user_id - identity_client.ec2.delete(user, parsed_args.access_key) + result = 0 + for access_key in parsed_args.access_keys: + try: + identity_client.ec2.delete(user, access_key) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete EC2 keys with " + "access key '%(access_key)s': %(e)s") + % {'access_key': access_key, 'e': e}) + + if result > 0: + total = len(parsed_args.access_keys) + 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/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index ee2bab6ff0..5a3b3186fb 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -15,7 +15,10 @@ """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__) + + class CreateEndpoint(command.ShowOne): """Create new endpoint""" @@ -74,20 +80,36 @@ 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', + 'endpoints', 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 - identity_client.endpoints.delete(parsed_args.endpoint) + + result = 0 + for endpoint in parsed_args.endpoints: + try: + identity_client.endpoints.delete(endpoint) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete endpoint with " + "ID '%(endpoint)s': %(e)s") + % {'endpoint': endpoint, 'e': e}) + + if result > 0: + total = len(parsed_args.endpoints) + 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/v2_0/service.py b/openstackclient/identity/v2_0/service.py index df332eb64d..e318643c17 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -91,21 +91,37 @@ 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', + 'services', 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 - service = common.find_service(identity_client, parsed_args.service) - identity_client.services.delete(service.id) + + result = 0 + for service in parsed_args.services: + try: + service = common.find_service(identity_client, service) + identity_client.services.delete(service.id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete service with " + "name or ID '%(service)s': %(e)s") + % {'service': service, 'e': e}) + + if result > 0: + total = len(parsed_args.services) + 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/tests/identity/v2_0/test_endpoint.py b/openstackclient/tests/identity/v2_0/test_endpoint.py index e8fb926290..0b539702be 100644 --- a/openstackclient/tests/identity/v2_0/test_endpoint.py +++ b/openstackclient/tests/identity/v2_0/test_endpoint.py @@ -112,7 +112,7 @@ class TestEndpointDelete(TestEndpoint): self.fake_endpoint.id, ] verifylist = [ - ('endpoint', self.fake_endpoint.id), + ('endpoints', [self.fake_endpoint.id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/openstackclient/tests/identity/v2_0/test_service.py b/openstackclient/tests/identity/v2_0/test_service.py index dc0fbcd122..ba976f4c85 100644 --- a/openstackclient/tests/identity/v2_0/test_service.py +++ b/openstackclient/tests/identity/v2_0/test_service.py @@ -190,7 +190,7 @@ class TestServiceDelete(TestService): identity_fakes.service_name, ] verifylist = [ - ('service', identity_fakes.service_name), + ('services', [identity_fakes.service_name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) diff --git a/releasenotes/notes/bug-1592906-a5604ec5abe77507.yaml b/releasenotes/notes/bug-1592906-a5604ec5abe77507.yaml new file mode 100644 index 0000000000..a9b0606fda --- /dev/null +++ b/releasenotes/notes/bug-1592906-a5604ec5abe77507.yaml @@ -0,0 +1,5 @@ +--- +features: + - Support bulk deletion for ``ec2 credentials delete``, ``endpoint delete``, + ``service delete`` in identity V2.0 . + [Bug `1592906 `_]