Add support for Bulk Delete in NeutronClient

The following patch adds support for BulkDelete in NeutronClient.
Currently, the core existing Neutron CLIs are going to support
Bulk Deletion in NeutronClient. However, if any extension does not
require Bulk Delete, it can be disabled by updating the
class attribute 'bulk_delete'.

DocImpact
Depends-On: Ib23d1e53947b5dffcff8db0dde77cae0a0b31243
Change-Id: I3b8a05698625baad3906784e3ecffb0f0242d660
Closes-Bug: #1495440
This commit is contained in:
reedip 2016-01-05 16:32:36 +09:00 committed by Reedip
parent 389aae7d16
commit b16c9a8d3d
4 changed files with 85 additions and 18 deletions

View File

@ -478,17 +478,19 @@ class DeleteCommand(NeutronCommand):
log = None
allow_names = True
help_resource = None
bulk_delete = True
def get_parser(self, prog_name):
parser = super(DeleteCommand, self).get_parser(prog_name)
if self.allow_names:
help_str = _('ID or name of %s to delete.')
else:
help_str = _('ID of %s to delete.')
if not self.help_resource:
self.help_resource = self.resource
if self.allow_names:
help_str = _('ID(s) or name(s) of %s to delete.')
else:
help_str = _('ID(s) of %s to delete.')
parser.add_argument(
'id', metavar=self.resource.upper(),
nargs='+' if self.bulk_delete else 1,
help=help_str % self.help_resource)
self.add_known_arguments(parser)
return parser
@ -498,24 +500,62 @@ class DeleteCommand(NeutronCommand):
neutron_client = self.get_client()
obj_deleter = getattr(neutron_client,
"delete_%s" % self.cmd_resource)
if self.bulk_delete:
self._bulk_delete(obj_deleter, neutron_client, parsed_args.id)
else:
self.delete_item(obj_deleter, neutron_client, parsed_args.id)
print((_('Deleted %(resource)s: %(id)s')
% {'id': parsed_args.id,
'resource': self.resource}),
file=self.app.stdout)
return
def _bulk_delete(self, obj_deleter, neutron_client, parsed_args_ids):
successful_delete = []
non_existent = []
multiple_ids = []
for item_id in parsed_args_ids:
try:
self.delete_item(obj_deleter, neutron_client, item_id)
successful_delete.append(item_id)
except exceptions.NotFound:
non_existent.append(item_id)
except exceptions.NeutronClientNoUniqueMatch:
multiple_ids.append(item_id)
if successful_delete:
print((_('Deleted %(resource)s(s): %(id)s'))
% {'id': ", ".join(successful_delete),
'resource': self.cmd_resource},
file=self.app.stdout)
if non_existent:
print((_("Unable to find %(resource)s(s) with id(s) "
"'%(id)s'") %
{'resource': self.cmd_resource,
'id': ", ".join(non_existent)}),
file=self.app.stdout)
if multiple_ids:
print((_("Multiple %(resource)s(s) matches found for name(s)"
" '%(id)s'. Please use an ID to be more specific.")) %
{'resource': self.cmd_resource,
'id': ", ".join(multiple_ids)},
file=self.app.stdout)
def delete_item(self, obj_deleter, neutron_client, item_id):
if self.allow_names:
params = {'cmd_resource': self.cmd_resource,
'parent_id': self.parent_id}
_id = find_resourceid_by_name_or_id(neutron_client,
self.resource,
parsed_args.id,
item_id,
**params)
else:
_id = parsed_args.id
_id = item_id
if self.parent_id:
obj_deleter(_id, self.parent_id)
else:
obj_deleter(_id)
print((_('Deleted %(resource)s: %(id)s')
% {'id': parsed_args.id,
'resource': self.resource}),
file=self.app.stdout)
return

View File

@ -504,14 +504,7 @@ class CLITestV20Base(base.BaseTestCase):
self.assertIn(myid, _str)
self.assertIn('myname', _str)
def _test_delete_resource(self, resource, cmd, myid, args,
cmd_resource=None, parent_id=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
if not cmd_resource:
cmd_resource = resource
path = getattr(self.client, cmd_resource + "_path")
def _test_set_path_and_delete(self, path, parent_id, myid):
if parent_id:
path = path % (parent_id, myid)
else:
@ -521,6 +514,20 @@ class CLITestV20Base(base.BaseTestCase):
body=None,
headers=mox.ContainsKeyValue(
'X-Auth-Token', TOKEN)).AndReturn((MyResp(204), None))
def _test_delete_resource(self, resource, cmd, myid, args,
cmd_resource=None, parent_id=None,
extra_ids=None):
self.mox.StubOutWithMock(cmd, "get_client")
self.mox.StubOutWithMock(self.client.httpclient, "request")
cmd.get_client().MultipleTimes().AndReturn(self.client)
if not cmd_resource:
cmd_resource = resource
path = getattr(self.client, cmd_resource + "_path")
self._test_set_path_and_delete(path, parent_id, myid)
# extra_ids is used to test for bulk_delete
if extra_ids:
self._test_set_path_and_delete(path, parent_id, extra_ids)
self.mox.ReplayAll()
cmd_parser = cmd.get_parser("delete_" + cmd_resource)
shell.run_command(cmd, cmd_parser, args)
@ -528,6 +535,8 @@ class CLITestV20Base(base.BaseTestCase):
self.mox.UnsetStubs()
_str = self.fake_stdout.make_string()
self.assertIn(myid, _str)
if extra_ids:
self.assertIn(extra_ids, _str)
def _test_update_resource_action(self, resource, cmd, myid, action, args,
body, retval=None, cmd_resource=None):

View File

@ -588,6 +588,15 @@ class CLITestV20NetworkJSON(test_cli20.CLITestV20Base):
args = [myid]
self._test_delete_resource(resource, cmd, myid, args)
def test_bulk_delete_network(self):
# Delete net: myid1 myid2.
resource = 'network'
cmd = network.DeleteNetwork(test_cli20.MyApp(sys.stdout), None)
myid1 = 'myid1'
myid2 = 'myid2'
args = [myid1, myid2]
self._test_delete_resource(resource, cmd, myid1, args, extra_ids=myid2)
def _test_extend_list(self, mox_calls):
data = [{'id': 'netid%d' % i, 'name': 'net%d' % i,
'subnets': ['mysubid%d' % i]}

View File

@ -0,0 +1,9 @@
---
features:
- |
CLI support for bulk delete.
* By using this feature, multiple resource
can be deleted using a single command.
* Example: ``neutron router-delete router_a router_b``
deletes both router_a and router_b.