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:
parent
114eeeb023
commit
041ea4978b
@ -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
|
||||
----------------
|
||||
|
@ -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
|
||||
-----------
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
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):
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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>`_]
|
||||
|
Loading…
Reference in New Issue
Block a user