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
------------------
Delete subnet pool
Delete subnet pool(s)
.. program:: subnet pool delete
.. code:: bash
os subnet pool delete
<subnet-pool>
<subnet-pool> [<subnet-pool> ...]
.. _subnet_pool_delete-subnet-pool:
.. describe:: <subnet-pool>
Subnet pool to delete (name or ID)
Subnet pool(s) to delete (name or ID)
subnet pool list
----------------

View File

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

View File

@ -14,6 +14,7 @@
"""Subnet action implementations"""
import copy
import logging
from osc_lib.cli import parseractions
from osc_lib.command import command
@ -24,6 +25,9 @@ from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
def _format_allocation_pools(data):
pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
for pool in data]
@ -270,21 +274,37 @@ class CreateSubnet(command.ShowOne):
class DeleteSubnet(command.Command):
"""Delete subnet"""
"""Delete subnet(s)"""
def get_parser(self, prog_name):
parser = super(DeleteSubnet, self).get_parser(prog_name)
parser.add_argument(
'subnet',
metavar="<subnet>",
help=_("Subnet to delete (name or ID)")
nargs='+',
help=_("Subnet(s) to delete (name or ID)")
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.network
client.delete_subnet(
client.find_subnet(parsed_args.subnet))
result = 0
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):

View File

@ -13,14 +13,20 @@
"""Subnet pool action implementations"""
import logging
from osc_lib.cli import parseractions
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
from openstackclient.identity import common as identity_common
LOG = logging.getLogger(__name__)
def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
@ -176,21 +182,37 @@ class CreateSubnetPool(command.ShowOne):
class DeleteSubnetPool(command.Command):
"""Delete subnet pool"""
"""Delete subnet pool(s)"""
def get_parser(self, prog_name):
parser = super(DeleteSubnetPool, self).get_parser(prog_name)
parser.add_argument(
'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
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_subnet_pool(parsed_args.subnet_pool)
client.delete_subnet_pool(obj)
result = 0
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):

View File

@ -771,6 +771,25 @@ class FakeSubnet(object):
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):
"""Fake one or more floating ip."""
@ -910,3 +929,22 @@ class FakeSubnetPool(object):
)
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 mock
from mock import call
from osc_lib import exceptions
from osc_lib import utils
from openstackclient.network.v2 import subnet as subnet_v2
@ -361,32 +363,82 @@ class TestCreateSubnet(TestSubnet):
class TestDeleteSubnet(TestSubnet):
# The subnet to delete.
_subnet = network_fakes.FakeSubnet.create_one_subnet()
# The subnets to delete.
_subnets = network_fakes.FakeSubnet.create_subnets(count=2)
def setUp(self):
super(TestDeleteSubnet, self).setUp()
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
self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace)
def test_delete(self):
def test_subnet_delete(self):
arglist = [
self._subnet.name,
self._subnets[0].name,
]
verifylist = [
('subnet', self._subnet.name),
('subnet', [self._subnets[0].name]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
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)
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):
# The subnets going to be listed up.

View File

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

View File

@ -1,5 +1,6 @@
---
features:
- Support bulk deletion for ``floating ip delete``, ``security group delete``,
and ``security group rule delete`` commands in networkv2.
- Support bulk deletion for ``subnet pool delete``, ``subnet delete``,
``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>`_]