Support bulk deletion for delete commands in networkv2

This patch support bulk deletion for delete commands below:
1.subnet delete
2.subnet pool delete

Up to now, all the delete commands in networkv2 support bulk deletion.

Change-Id: I63f6d1d02bad1fcc26e72b7028b53958a68ce2dc
Partially-Implements: blueprint multi-argument-network
Partial-Bug: #1592906
This commit is contained in:
Huanxuan Ao 2016-06-16 13:09:27 +08:00 committed by Steve Martinelli
parent 114eeeb023
commit 041ea4978b
8 changed files with 215 additions and 31 deletions

View File

@ -81,18 +81,18 @@ Create subnet pool
subnet pool delete subnet pool delete
------------------ ------------------
Delete subnet pool Delete subnet pool(s)
.. program:: subnet pool delete .. program:: subnet pool delete
.. code:: bash .. code:: bash
os subnet pool delete os subnet pool delete
<subnet-pool> <subnet-pool> [<subnet-pool> ...]
.. _subnet_pool_delete-subnet-pool: .. _subnet_pool_delete-subnet-pool:
.. describe:: <subnet-pool> .. describe:: <subnet-pool>
Subnet pool to delete (name or ID) Subnet pool(s) to delete (name or ID)
subnet pool list subnet pool list
---------------- ----------------

View File

@ -119,18 +119,18 @@ Create new subnet
subnet delete subnet delete
------------- -------------
Delete a subnet Delete subnet(s)
.. program:: subnet delete .. program:: subnet delete
.. code:: bash .. code:: bash
os subnet delete os subnet delete
<subnet> <subnet> [<subnet> ...]
.. _subnet_delete-subnet: .. _subnet_delete-subnet:
.. describe:: <subnet> .. describe:: <subnet>
Subnet to delete (name or ID) Subnet(s) to delete (name or ID)
subnet list subnet list
----------- -----------

View File

@ -14,6 +14,7 @@
"""Subnet action implementations""" """Subnet action implementations"""
import copy import copy
import logging
from osc_lib.cli import parseractions from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
@ -24,6 +25,9 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
def _format_allocation_pools(data): def _format_allocation_pools(data):
pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', '')) pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
for pool in data] for pool in data]
@ -270,21 +274,37 @@ class CreateSubnet(command.ShowOne):
class DeleteSubnet(command.Command): class DeleteSubnet(command.Command):
"""Delete subnet""" """Delete subnet(s)"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(DeleteSubnet, self).get_parser(prog_name) parser = super(DeleteSubnet, self).get_parser(prog_name)
parser.add_argument( parser.add_argument(
'subnet', 'subnet',
metavar="<subnet>", metavar="<subnet>",
help=_("Subnet to delete (name or ID)") nargs='+',
help=_("Subnet(s) to delete (name or ID)")
) )
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
client = self.app.client_manager.network client = self.app.client_manager.network
client.delete_subnet( result = 0
client.find_subnet(parsed_args.subnet))
for subnet in parsed_args.subnet:
try:
obj = client.find_subnet(subnet, ignore_missing=False)
client.delete_subnet(obj)
except Exception as e:
result += 1
LOG.error(_("Failed to delete subnet with "
"name or ID '%(subnet)s': %(e)s")
% {'subnet': subnet, 'e': e})
if result > 0:
total = len(parsed_args.subnet)
msg = (_("%(result)s of %(total)s subnets failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListSubnet(command.Lister): class ListSubnet(command.Lister):

View File

@ -13,14 +13,20 @@
"""Subnet pool action implementations""" """Subnet pool action implementations"""
import logging
from osc_lib.cli import parseractions from osc_lib.cli import parseractions
from osc_lib.command import command from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
from openstackclient.i18n import _ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
def _get_columns(item): def _get_columns(item):
columns = list(item.keys()) columns = list(item.keys())
if 'tenant_id' in columns: if 'tenant_id' in columns:
@ -176,21 +182,37 @@ class CreateSubnetPool(command.ShowOne):
class DeleteSubnetPool(command.Command): class DeleteSubnetPool(command.Command):
"""Delete subnet pool""" """Delete subnet pool(s)"""
def get_parser(self, prog_name): def get_parser(self, prog_name):
parser = super(DeleteSubnetPool, self).get_parser(prog_name) parser = super(DeleteSubnetPool, self).get_parser(prog_name)
parser.add_argument( parser.add_argument(
'subnet_pool', 'subnet_pool',
metavar='<subnet-pool>', metavar='<subnet-pool>',
help=_("Subnet pool to delete (name or ID)") nargs='+',
help=_("Subnet pool(s) to delete (name or ID)")
) )
return parser return parser
def take_action(self, parsed_args): def take_action(self, parsed_args):
client = self.app.client_manager.network client = self.app.client_manager.network
obj = client.find_subnet_pool(parsed_args.subnet_pool) result = 0
client.delete_subnet_pool(obj)
for pool in parsed_args.subnet_pool:
try:
obj = client.find_subnet_pool(pool, ignore_missing=False)
client.delete_subnet_pool(obj)
except Exception as e:
result += 1
LOG.error(_("Failed to delete subnet pool with "
"name or ID '%(pool)s': %(e)s")
% {'pool': pool, 'e': e})
if result > 0:
total = len(parsed_args.subnet_pool)
msg = (_("%(result)s of %(total)s subnet pools failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListSubnetPool(command.Lister): class ListSubnetPool(command.Lister):

View File

@ -771,6 +771,25 @@ class FakeSubnet(object):
return subnets return subnets
@staticmethod
def get_subnets(subnets=None, count=2):
"""Get an iterable MagicMock object with a list of faked subnets.
If subnets list is provided, then initialize the Mock object
with the list. Otherwise create one.
:param List subnets:
A list of FakeResource objects faking subnets
:param int count:
The number of subnets to fake
:return:
An iterable Mock object with side_effect set to a list of faked
subnets
"""
if subnets is None:
subnets = FakeSubnet.create_subnets(count)
return mock.MagicMock(side_effect=subnets)
class FakeFloatingIP(object): class FakeFloatingIP(object):
"""Fake one or more floating ip.""" """Fake one or more floating ip."""
@ -910,3 +929,22 @@ class FakeSubnetPool(object):
) )
return subnet_pools return subnet_pools
@staticmethod
def get_subnet_pools(subnet_pools=None, count=2):
"""Get an iterable MagicMock object with a list of faked subnet pools.
If subnet_pools list is provided, then initialize the Mock object
with the list. Otherwise create one.
:param List subnet pools:
A list of FakeResource objects faking subnet pools
:param int count:
The number of subnet pools to fake
:return:
An iterable Mock object with side_effect set to a list of faked
subnet pools
"""
if subnet_pools is None:
subnet_pools = FakeSubnetPool.create_subnet_pools(count)
return mock.MagicMock(side_effect=subnet_pools)

View File

@ -13,7 +13,9 @@
import copy import copy
import mock import mock
from mock import call
from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
from openstackclient.network.v2 import subnet as subnet_v2 from openstackclient.network.v2 import subnet as subnet_v2
@ -361,32 +363,82 @@ class TestCreateSubnet(TestSubnet):
class TestDeleteSubnet(TestSubnet): class TestDeleteSubnet(TestSubnet):
# The subnet to delete. # The subnets to delete.
_subnet = network_fakes.FakeSubnet.create_one_subnet() _subnets = network_fakes.FakeSubnet.create_subnets(count=2)
def setUp(self): def setUp(self):
super(TestDeleteSubnet, self).setUp() super(TestDeleteSubnet, self).setUp()
self.network.delete_subnet = mock.Mock(return_value=None) self.network.delete_subnet = mock.Mock(return_value=None)
self.network.find_subnet = mock.Mock(return_value=self._subnet) self.network.find_subnet = (
network_fakes.FakeSubnet.get_subnets(self._subnets))
# Get the command object to test # Get the command object to test
self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace) self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace)
def test_delete(self): def test_subnet_delete(self):
arglist = [ arglist = [
self._subnet.name, self._subnets[0].name,
] ]
verifylist = [ verifylist = [
('subnet', self._subnet.name), ('subnet', [self._subnets[0].name]),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
self.network.delete_subnet.assert_called_once_with(self._subnet) self.network.delete_subnet.assert_called_once_with(self._subnets[0])
self.assertIsNone(result) self.assertIsNone(result)
def test_multi_subnets_delete(self):
arglist = []
verifylist = []
for s in self._subnets:
arglist.append(s.name)
verifylist = [
('subnet', arglist),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for s in self._subnets:
calls.append(call(s))
self.network.delete_subnet.assert_has_calls(calls)
self.assertIsNone(result)
def test_multi_subnets_delete_with_exception(self):
arglist = [
self._subnets[0].name,
'unexist_subnet',
]
verifylist = [
('subnet',
[self._subnets[0].name, 'unexist_subnet']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
find_mock_result = [self._subnets[0], exceptions.CommandError]
self.network.find_subnet = (
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 subnets failed to delete.', str(e))
self.network.find_subnet.assert_any_call(
self._subnets[0].name, ignore_missing=False)
self.network.find_subnet.assert_any_call(
'unexist_subnet', ignore_missing=False)
self.network.delete_subnet.assert_called_once_with(
self._subnets[0]
)
class TestListSubnet(TestSubnet): class TestListSubnet(TestSubnet):
# The subnets going to be listed up. # The subnets going to be listed up.

View File

@ -14,7 +14,9 @@
import argparse import argparse
import copy import copy
import mock import mock
from mock import call
from osc_lib import exceptions
from osc_lib import utils from osc_lib import utils
from openstackclient.network.v2 import subnet_pool from openstackclient.network.v2 import subnet_pool
@ -263,36 +265,85 @@ class TestCreateSubnetPool(TestSubnetPool):
class TestDeleteSubnetPool(TestSubnetPool): class TestDeleteSubnetPool(TestSubnetPool):
# The subnet pool to delete. # The subnet pools to delete.
_subnet_pool = network_fakes.FakeSubnetPool.create_one_subnet_pool() _subnet_pools = network_fakes.FakeSubnetPool.create_subnet_pools(count=2)
def setUp(self): def setUp(self):
super(TestDeleteSubnetPool, self).setUp() super(TestDeleteSubnetPool, self).setUp()
self.network.delete_subnet_pool = mock.Mock(return_value=None) self.network.delete_subnet_pool = mock.Mock(return_value=None)
self.network.find_subnet_pool = mock.Mock( self.network.find_subnet_pool = (
return_value=self._subnet_pool network_fakes.FakeSubnetPool.get_subnet_pools(self._subnet_pools)
) )
# Get the command object to test # Get the command object to test
self.cmd = subnet_pool.DeleteSubnetPool(self.app, self.namespace) self.cmd = subnet_pool.DeleteSubnetPool(self.app, self.namespace)
def test_delete(self): def test_subnet_pool_delete(self):
arglist = [ arglist = [
self._subnet_pool.name, self._subnet_pools[0].name,
] ]
verifylist = [ verifylist = [
('subnet_pool', self._subnet_pool.name), ('subnet_pool', [self._subnet_pools[0].name]),
] ]
parsed_args = self.check_parser(self.cmd, arglist, verifylist) parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args) result = self.cmd.take_action(parsed_args)
self.network.delete_subnet_pool.assert_called_once_with( self.network.delete_subnet_pool.assert_called_once_with(
self._subnet_pool) self._subnet_pools[0])
self.assertIsNone(result) self.assertIsNone(result)
def test_multi_subnet_pools_delete(self):
arglist = []
verifylist = []
for s in self._subnet_pools:
arglist.append(s.name)
verifylist = [
('subnet_pool', arglist),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
calls = []
for s in self._subnet_pools:
calls.append(call(s))
self.network.delete_subnet_pool.assert_has_calls(calls)
self.assertIsNone(result)
def test_multi_subnet_pools_delete_with_exception(self):
arglist = [
self._subnet_pools[0].name,
'unexist_subnet_pool',
]
verifylist = [
('subnet_pool',
[self._subnet_pools[0].name, 'unexist_subnet_pool']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
find_mock_result = [self._subnet_pools[0], exceptions.CommandError]
self.network.find_subnet_pool = (
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 subnet pools failed to delete.', str(e))
self.network.find_subnet_pool.assert_any_call(
self._subnet_pools[0].name, ignore_missing=False)
self.network.find_subnet_pool.assert_any_call(
'unexist_subnet_pool', ignore_missing=False)
self.network.delete_subnet_pool.assert_called_once_with(
self._subnet_pools[0]
)
class TestListSubnetPool(TestSubnetPool): class TestListSubnetPool(TestSubnetPool):
# The subnet pools going to be listed up. # The subnet pools going to be listed up.

View File

@ -1,5 +1,6 @@
--- ---
features: features:
- Support bulk deletion for ``floating ip delete``, ``security group delete``, - Support bulk deletion for ``subnet pool delete``, ``subnet delete``,
and ``security group rule delete`` commands in networkv2. ``floating ip delete``, ``security group delete`` and
``security group rule delete``.
[Blueprint `multi-argument-network <https://blueprints.launchpad.net/python-openstackclient/+spec/multi-argument-network>`_] [Blueprint `multi-argument-network <https://blueprints.launchpad.net/python-openstackclient/+spec/multi-argument-network>`_]