From 537f5cbe8ac9439351afe365360eeb38fb340fcb Mon Sep 17 00:00:00 2001 From: Huanxuan Ao Date: Sat, 28 May 2016 12:50:14 +0800 Subject: [PATCH] Support deleting multi address scopes in networkv2 This patch adds support for deleting multi address scopes by using "address scope delete" command. Change-Id: Ic8d3ebc17db44ca5d42c336d2c4d5633f70d4e8b Partially-Implements: blueprint multi-argument-network --- doc/source/command-objects/address-scope.rst | 6 +- openstackclient/network/v2/address_scope.py | 24 +++++-- openstackclient/tests/network/v2/fakes.py | 19 +++++ .../tests/network/v2/test_address_scope.py | 70 ++++++++++++++++--- 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/doc/source/command-objects/address-scope.rst b/doc/source/command-objects/address-scope.rst index a2ab566c82..7481ed53de 100644 --- a/doc/source/command-objects/address-scope.rst +++ b/doc/source/command-objects/address-scope.rst @@ -50,18 +50,18 @@ Create new address scope address scope delete -------------------- -Delete an address scope +Delete address scope(s) .. program:: address scope delete .. code:: bash os address scope delete - + [ ...] .. _address_scope_delete-address-scope: .. describe:: - Address scope to delete (name or ID) + Address scope(s) to delete (name or ID) address scope list ------------------ diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index 614900c92e..dbc6865fb4 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -98,22 +98,38 @@ class CreateAddressScope(command.ShowOne): class DeleteAddressScope(command.Command): - """Delete an address scope""" + """Delete address scope(s)""" def get_parser(self, prog_name): parser = super(DeleteAddressScope, self).get_parser(prog_name) parser.add_argument( 'address_scope', metavar="", - help=_("Address scope to delete (name or ID)") + nargs='+', + help=_("Address scope(s) to delete (name or ID)") ) return parser def take_action(self, parsed_args): client = self.app.client_manager.network - obj = client.find_address_scope(parsed_args.address_scope) - client.delete_address_scope(obj) + result = 0 + + for scope in parsed_args.address_scope: + try: + obj = client.find_address_scope(scope, ignore_missing=False) + client.delete_address_scope(obj) + except Exception as e: + result += 1 + self.app.log.error(_("Failed to delete address scope with " + "name or ID '%(scope)s': %(e)s") + % {'scope': scope, 'e': e}) + + if result > 0: + total = len(parsed_args.address_scope) + msg = (_("%(result)s of %(total)s address scopes failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) class ListAddressScope(command.Lister): diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 417cf26ee7..ce6f63e8c9 100644 --- a/openstackclient/tests/network/v2/fakes.py +++ b/openstackclient/tests/network/v2/fakes.py @@ -127,6 +127,25 @@ class FakeAddressScope(object): return address_scopes + @staticmethod + def get_address_scopes(address_scopes=None, count=2): + """Get an iterable MagicMock object with a list of faked address scopes. + + If address scopes list is provided, then initialize the Mock object + with the list. Otherwise create one. + + :param List address scopes: + A list of FakeResource objects faking address scopes + :param int count: + The number of address scopes to fake + :return: + An iterable Mock object with side_effect set to a list of faked + address scopes + """ + if address_scopes is None: + address_scopes = FakeAddressScope.create_address_scopes(count) + return mock.MagicMock(side_effect=address_scopes) + class FakeAvailabilityZone(object): """Fake one or more network availability zones (AZs).""" diff --git a/openstackclient/tests/network/v2/test_address_scope.py b/openstackclient/tests/network/v2/test_address_scope.py index ac94b489b1..b4f4fa885f 100644 --- a/openstackclient/tests/network/v2/test_address_scope.py +++ b/openstackclient/tests/network/v2/test_address_scope.py @@ -14,6 +14,7 @@ import copy import mock +from mock import call from openstackclient.common import exceptions from openstackclient.network.v2 import address_scope from openstackclient.tests import fakes @@ -168,33 +169,86 @@ class TestCreateAddressScope(TestAddressScope): class TestDeleteAddressScope(TestAddressScope): # The address scope to delete. - _address_scope = ( - network_fakes.FakeAddressScope.create_one_address_scope()) + _address_scopes = ( + network_fakes.FakeAddressScope.create_address_scopes(count=2)) def setUp(self): super(TestDeleteAddressScope, self).setUp() self.network.delete_address_scope = mock.Mock(return_value=None) - self.network.find_address_scope = mock.Mock( - return_value=self._address_scope) + self.network.find_address_scope = ( + network_fakes.FakeAddressScope.get_address_scopes( + address_scopes=self._address_scopes) + ) # Get the command object to test self.cmd = address_scope.DeleteAddressScope(self.app, self.namespace) - def test_delete(self): + def test_address_scope_delete(self): arglist = [ - self._address_scope.name, + self._address_scopes[0].name, ] verifylist = [ - ('address_scope', self._address_scope.name), + ('address_scope', [self._address_scopes[0].name]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) + self.network.find_address_scope.assert_called_once_with( + self._address_scopes[0].name, ignore_missing=False) self.network.delete_address_scope.assert_called_once_with( - self._address_scope) + self._address_scopes[0]) self.assertIsNone(result) + def test_multi_address_scopes_delete(self): + arglist = [] + verifylist = [] + + for a in self._address_scopes: + arglist.append(a.name) + verifylist = [ + ('address_scope', arglist), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + calls = [] + for a in self._address_scopes: + calls.append(call(a)) + self.network.delete_address_scope.assert_has_calls(calls) + self.assertIsNone(result) + + def test_multi_address_scopes_delete_with_exception(self): + arglist = [ + self._address_scopes[0].name, + 'unexist_address_scope', + ] + verifylist = [ + ('address_scope', + [self._address_scopes[0].name, 'unexist_address_scope']), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self._address_scopes[0], exceptions.CommandError] + self.network.find_address_scope = ( + mock.MagicMock(side_effect=find_mock_result) + ) + + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 address scopes failed to delete.', str(e)) + + self.network.find_address_scope.assert_any_call( + self._address_scopes[0].name, ignore_missing=False) + self.network.find_address_scope.assert_any_call( + 'unexist_address_scope', ignore_missing=False) + self.network.delete_address_scope.assert_called_once_with( + self._address_scopes[0] + ) + class TestListAddressScope(TestAddressScope):