Skip to content

Commit c33a213

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Support bulk deletion for delete commands in networkv2"
2 parents 5d23a12 + 041ea49 commit c33a213

8 files changed

Lines changed: 215 additions & 31 deletions

File tree

doc/source/command-objects/subnet-pool.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,18 @@ Create subnet pool
8181
subnet pool delete
8282
------------------
8383
84-
Delete subnet pool
84+
Delete subnet pool(s)
8585
8686
.. program:: subnet pool delete
8787
.. code:: bash
8888
8989
os subnet pool delete
90-
<subnet-pool>
90+
<subnet-pool> [<subnet-pool> ...]
9191
9292
.. _subnet_pool_delete-subnet-pool:
9393
.. describe:: <subnet-pool>
9494
95-
Subnet pool to delete (name or ID)
95+
Subnet pool(s) to delete (name or ID)
9696
9797
subnet pool list
9898
----------------

doc/source/command-objects/subnet.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,18 @@ Create new subnet
119119
subnet delete
120120
-------------
121121
122-
Delete a subnet
122+
Delete subnet(s)
123123
124124
.. program:: subnet delete
125125
.. code:: bash
126126
127127
os subnet delete
128-
<subnet>
128+
<subnet> [<subnet> ...]
129129
130130
.. _subnet_delete-subnet:
131131
.. describe:: <subnet>
132132
133-
Subnet to delete (name or ID)
133+
Subnet(s) to delete (name or ID)
134134
135135
subnet list
136136
-----------

openstackclient/network/v2/subnet.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""Subnet action implementations"""
1515

1616
import copy
17+
import logging
1718

1819
from osc_lib.cli import parseractions
1920
from osc_lib.command import command
@@ -24,6 +25,9 @@
2425
from openstackclient.identity import common as identity_common
2526

2627

28+
LOG = logging.getLogger(__name__)
29+
30+
2731
def _format_allocation_pools(data):
2832
pool_formatted = ['%s-%s' % (pool.get('start', ''), pool.get('end', ''))
2933
for pool in data]
@@ -270,21 +274,37 @@ def take_action(self, parsed_args):
270274

271275

272276
class DeleteSubnet(command.Command):
273-
"""Delete subnet"""
277+
"""Delete subnet(s)"""
274278

275279
def get_parser(self, prog_name):
276280
parser = super(DeleteSubnet, self).get_parser(prog_name)
277281
parser.add_argument(
278282
'subnet',
279283
metavar="<subnet>",
280-
help=_("Subnet to delete (name or ID)")
284+
nargs='+',
285+
help=_("Subnet(s) to delete (name or ID)")
281286
)
282287
return parser
283288

284289
def take_action(self, parsed_args):
285290
client = self.app.client_manager.network
286-
client.delete_subnet(
287-
client.find_subnet(parsed_args.subnet))
291+
result = 0
292+
293+
for subnet in parsed_args.subnet:
294+
try:
295+
obj = client.find_subnet(subnet, ignore_missing=False)
296+
client.delete_subnet(obj)
297+
except Exception as e:
298+
result += 1
299+
LOG.error(_("Failed to delete subnet with "
300+
"name or ID '%(subnet)s': %(e)s")
301+
% {'subnet': subnet, 'e': e})
302+
303+
if result > 0:
304+
total = len(parsed_args.subnet)
305+
msg = (_("%(result)s of %(total)s subnets failed "
306+
"to delete.") % {'result': result, 'total': total})
307+
raise exceptions.CommandError(msg)
288308

289309

290310
class ListSubnet(command.Lister):

openstackclient/network/v2/subnet_pool.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@
1313

1414
"""Subnet pool action implementations"""
1515

16+
import logging
17+
1618
from osc_lib.cli import parseractions
1719
from osc_lib.command import command
20+
from osc_lib import exceptions
1821
from osc_lib import utils
1922

2023
from openstackclient.i18n import _
2124
from openstackclient.identity import common as identity_common
2225

2326

27+
LOG = logging.getLogger(__name__)
28+
29+
2430
def _get_columns(item):
2531
columns = list(item.keys())
2632
if 'tenant_id' in columns:
@@ -176,21 +182,37 @@ def take_action(self, parsed_args):
176182

177183

178184
class DeleteSubnetPool(command.Command):
179-
"""Delete subnet pool"""
185+
"""Delete subnet pool(s)"""
180186

181187
def get_parser(self, prog_name):
182188
parser = super(DeleteSubnetPool, self).get_parser(prog_name)
183189
parser.add_argument(
184190
'subnet_pool',
185191
metavar='<subnet-pool>',
186-
help=_("Subnet pool to delete (name or ID)")
192+
nargs='+',
193+
help=_("Subnet pool(s) to delete (name or ID)")
187194
)
188195
return parser
189196

190197
def take_action(self, parsed_args):
191198
client = self.app.client_manager.network
192-
obj = client.find_subnet_pool(parsed_args.subnet_pool)
193-
client.delete_subnet_pool(obj)
199+
result = 0
200+
201+
for pool in parsed_args.subnet_pool:
202+
try:
203+
obj = client.find_subnet_pool(pool, ignore_missing=False)
204+
client.delete_subnet_pool(obj)
205+
except Exception as e:
206+
result += 1
207+
LOG.error(_("Failed to delete subnet pool with "
208+
"name or ID '%(pool)s': %(e)s")
209+
% {'pool': pool, 'e': e})
210+
211+
if result > 0:
212+
total = len(parsed_args.subnet_pool)
213+
msg = (_("%(result)s of %(total)s subnet pools failed "
214+
"to delete.") % {'result': result, 'total': total})
215+
raise exceptions.CommandError(msg)
194216

195217

196218
class ListSubnetPool(command.Lister):

openstackclient/tests/network/v2/fakes.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,25 @@ def create_subnets(attrs=None, count=2):
771771

772772
return subnets
773773

774+
@staticmethod
775+
def get_subnets(subnets=None, count=2):
776+
"""Get an iterable MagicMock object with a list of faked subnets.
777+
778+
If subnets list is provided, then initialize the Mock object
779+
with the list. Otherwise create one.
780+
781+
:param List subnets:
782+
A list of FakeResource objects faking subnets
783+
:param int count:
784+
The number of subnets to fake
785+
:return:
786+
An iterable Mock object with side_effect set to a list of faked
787+
subnets
788+
"""
789+
if subnets is None:
790+
subnets = FakeSubnet.create_subnets(count)
791+
return mock.MagicMock(side_effect=subnets)
792+
774793

775794
class FakeFloatingIP(object):
776795
"""Fake one or more floating ip."""
@@ -910,3 +929,22 @@ def create_subnet_pools(attrs=None, count=2):
910929
)
911930

912931
return subnet_pools
932+
933+
@staticmethod
934+
def get_subnet_pools(subnet_pools=None, count=2):
935+
"""Get an iterable MagicMock object with a list of faked subnet pools.
936+
937+
If subnet_pools list is provided, then initialize the Mock object
938+
with the list. Otherwise create one.
939+
940+
:param List subnet pools:
941+
A list of FakeResource objects faking subnet pools
942+
:param int count:
943+
The number of subnet pools to fake
944+
:return:
945+
An iterable Mock object with side_effect set to a list of faked
946+
subnet pools
947+
"""
948+
if subnet_pools is None:
949+
subnet_pools = FakeSubnetPool.create_subnet_pools(count)
950+
return mock.MagicMock(side_effect=subnet_pools)

openstackclient/tests/network/v2/test_subnet.py

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
import copy
1515
import mock
16+
from mock import call
1617

18+
from osc_lib import exceptions
1719
from osc_lib import utils
1820

1921
from openstackclient.network.v2 import subnet as subnet_v2
@@ -361,32 +363,82 @@ def test_create_options_subnet_range_ipv6(self):
361363

362364
class TestDeleteSubnet(TestSubnet):
363365

364-
# The subnet to delete.
365-
_subnet = network_fakes.FakeSubnet.create_one_subnet()
366+
# The subnets to delete.
367+
_subnets = network_fakes.FakeSubnet.create_subnets(count=2)
366368

367369
def setUp(self):
368370
super(TestDeleteSubnet, self).setUp()
369371

370372
self.network.delete_subnet = mock.Mock(return_value=None)
371373

372-
self.network.find_subnet = mock.Mock(return_value=self._subnet)
374+
self.network.find_subnet = (
375+
network_fakes.FakeSubnet.get_subnets(self._subnets))
373376

374377
# Get the command object to test
375378
self.cmd = subnet_v2.DeleteSubnet(self.app, self.namespace)
376379

377-
def test_delete(self):
380+
def test_subnet_delete(self):
378381
arglist = [
379-
self._subnet.name,
382+
self._subnets[0].name,
380383
]
381384
verifylist = [
382-
('subnet', self._subnet.name),
385+
('subnet', [self._subnets[0].name]),
383386
]
384387
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
385388

386389
result = self.cmd.take_action(parsed_args)
387-
self.network.delete_subnet.assert_called_once_with(self._subnet)
390+
self.network.delete_subnet.assert_called_once_with(self._subnets[0])
388391
self.assertIsNone(result)
389392

393+
def test_multi_subnets_delete(self):
394+
arglist = []
395+
verifylist = []
396+
397+
for s in self._subnets:
398+
arglist.append(s.name)
399+
verifylist = [
400+
('subnet', arglist),
401+
]
402+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
403+
404+
result = self.cmd.take_action(parsed_args)
405+
406+
calls = []
407+
for s in self._subnets:
408+
calls.append(call(s))
409+
self.network.delete_subnet.assert_has_calls(calls)
410+
self.assertIsNone(result)
411+
412+
def test_multi_subnets_delete_with_exception(self):
413+
arglist = [
414+
self._subnets[0].name,
415+
'unexist_subnet',
416+
]
417+
verifylist = [
418+
('subnet',
419+
[self._subnets[0].name, 'unexist_subnet']),
420+
]
421+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
422+
423+
find_mock_result = [self._subnets[0], exceptions.CommandError]
424+
self.network.find_subnet = (
425+
mock.MagicMock(side_effect=find_mock_result)
426+
)
427+
428+
try:
429+
self.cmd.take_action(parsed_args)
430+
self.fail('CommandError should be raised.')
431+
except exceptions.CommandError as e:
432+
self.assertEqual('1 of 2 subnets failed to delete.', str(e))
433+
434+
self.network.find_subnet.assert_any_call(
435+
self._subnets[0].name, ignore_missing=False)
436+
self.network.find_subnet.assert_any_call(
437+
'unexist_subnet', ignore_missing=False)
438+
self.network.delete_subnet.assert_called_once_with(
439+
self._subnets[0]
440+
)
441+
390442

391443
class TestListSubnet(TestSubnet):
392444
# The subnets going to be listed up.

0 commit comments

Comments
 (0)