Merge "Support bulk deletion for delete commands in networkv2"
This commit is contained in:
commit
c33a213cf1
@ -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
|
||||||
----------------
|
----------------
|
||||||
|
@ -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
|
||||||
-----------
|
-----------
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
for pool in parsed_args.subnet_pool:
|
||||||
|
try:
|
||||||
|
obj = client.find_subnet_pool(pool, ignore_missing=False)
|
||||||
client.delete_subnet_pool(obj)
|
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):
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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>`_]
|
||||||
|
Loading…
Reference in New Issue
Block a user