diff --git a/releasenotes/notes/multi-delete-support-in-tacker-acd4a7e86114f0be.yaml b/releasenotes/notes/multi-delete-support-in-tacker-acd4a7e86114f0be.yaml new file mode 100644 index 00000000..567e54be --- /dev/null +++ b/releasenotes/notes/multi-delete-support-in-tacker-acd4a7e86114f0be.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support for multi delete feature for resources. diff --git a/tackerclient/tacker/v1_0/__init__.py b/tackerclient/tacker/v1_0/__init__.py index 59170fcf..6d5047a1 100644 --- a/tackerclient/tacker/v1_0/__init__.py +++ b/tackerclient/tacker/v1_0/__init__.py @@ -500,7 +500,7 @@ class UpdateCommand(TackerCommand): class DeleteCommand(TackerCommand): - """Delete a given resource + """Delete given resource(s) """ @@ -508,34 +508,61 @@ class DeleteCommand(TackerCommand): resource = None log = None allow_names = True + deleted_msg = {} 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') + help_str = _('IDs or names of %s to delete') else: - help_str = _('ID of %s to delete') + help_str = _('IDs of %s to delete') parser.add_argument( - 'id', metavar=self.resource.upper(), + 'ids', nargs='+', + metavar=self.resource.upper(), help=help_str % self.resource) return parser def run(self, parsed_args): - self.log.debug('run(%s)', parsed_args) + failure = False + deleted_ids = [] + failed_items = {} tacker_client = self.get_client() tacker_client.format = parsed_args.request_format obj_deleter = getattr(tacker_client, "delete_%s" % self.resource) - if self.allow_names: - _id = find_resourceid_by_name_or_id(tacker_client, self.resource, - parsed_args.id) + for resource_id in parsed_args.ids: + try: + if self.allow_names: + _id = find_resourceid_by_name_or_id( + tacker_client, self.resource, resource_id) + else: + _id = resource_id + obj_deleter(_id) + deleted_ids.append(resource_id) + except Exception as e: + failure = True + failed_items[resource_id] = e + if failure: + msg = '' + if deleted_ids: + status_msg = self.deleted_msg.get(self.resource, 'deleted') + msg = (_('Successfully %(status_msg)s %(resource)s(s):' + ' %(deleted_list)s') % {'status_msg': status_msg, + 'deleted_list': + ', '.join(deleted_ids), + 'resource': self.resource}) + err_msg = _("\n\nUnable to delete the below" + " %s(s):") % self.resource + for failed_id, error in failed_items.iteritems(): + err_msg += (_('\n Cannot delete %(failed_id)s: %(error)s') + % {'failed_id': failed_id, + 'error': error}) + msg += err_msg + raise exceptions.CommandError(msg) else: - _id = parsed_args.id - obj_deleter(_id) - print((_('Deleted %(resource)s: %(id)s') - % {'id': parsed_args.id, - 'resource': self.resource}), - file=self.app.stdout) + print((_('All %(resource)s(s) %(msg)s successfully') + % {'msg': self.deleted_msg.get(self.resource, 'deleted'), + 'resource': self.resource})) return diff --git a/tackerclient/tacker/v1_0/nfvo/vim.py b/tackerclient/tacker/v1_0/nfvo/vim.py index a9080d99..6d585e1a 100644 --- a/tackerclient/tacker/v1_0/nfvo/vim.py +++ b/tackerclient/tacker/v1_0/nfvo/vim.py @@ -115,5 +115,5 @@ class UpdateVIM(tackerV10.UpdateCommand): class DeleteVIM(tackerV10.DeleteCommand): - """Delete a given VIM.""" + """Delete given VIM(s).""" resource = _VIM diff --git a/tackerclient/tacker/v1_0/vm/vnf.py b/tackerclient/tacker/v1_0/vm/vnf.py index d4b65706..53a9ccee 100644 --- a/tackerclient/tacker/v1_0/vm/vnf.py +++ b/tackerclient/tacker/v1_0/vm/vnf.py @@ -165,9 +165,10 @@ class UpdateVNF(tackerV10.UpdateCommand): class DeleteVNF(tackerV10.DeleteCommand): - """Delete a given VNF.""" + """Delete given VNF(s).""" resource = _VNF + deleted_msg = {'vnf': 'delete initiated'} class ListVNFResources(tackerV10.ListCommand): diff --git a/tackerclient/tacker/v1_0/vm/vnfd.py b/tackerclient/tacker/v1_0/vm/vnfd.py index 2c042a02..5ccfd9be 100644 --- a/tackerclient/tacker/v1_0/vm/vnfd.py +++ b/tackerclient/tacker/v1_0/vm/vnfd.py @@ -80,7 +80,7 @@ class CreateVNFD(tackerV10.CreateCommand): class DeleteVNFD(tackerV10.DeleteCommand): - """Delete a given VNFD.""" + """Delete given VNFD(s).""" resource = _VNFD diff --git a/tackerclient/tests/unit/test_cli10.py b/tackerclient/tests/unit/test_cli10.py index 261bc691..0506d74d 100644 --- a/tackerclient/tests/unit/test_cli10.py +++ b/tackerclient/tests/unit/test_cli10.py @@ -601,6 +601,7 @@ class CLITestV10Base(testtools.TestCase): self.assertIn('myname', _str) def _test_delete_resource(self, resource, cmd, myid, args): + deleted_msg = {'vnf': 'delete initiated'} self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) @@ -617,7 +618,10 @@ class CLITestV10Base(testtools.TestCase): self.mox.VerifyAll() self.mox.UnsetStubs() _str = self.fake_stdout.make_string() - self.assertIn(myid, _str) + msg = 'All %(resource)s(s) %(msg)s successfully\n' % { + 'msg': deleted_msg.get(resource, 'deleted'), + 'resource': resource} + self.assertEqual(msg, _str) def _test_update_resource_action(self, resource, cmd, myid, action, args, body, retval=None): diff --git a/tackerclient/tests/unit/vm/test_cli10_vim.py b/tackerclient/tests/unit/vm/test_cli10_vim.py index f8dab37b..830a5f86 100644 --- a/tackerclient/tests/unit/vm/test_cli10_vim.py +++ b/tackerclient/tests/unit/vm/test_cli10_vim.py @@ -143,3 +143,9 @@ class CLITestV10VIMJSON(test_cli10.CLITestV10Base): my_id = 'my-id' args = [my_id] self._test_delete_resource(self._RESOURCE, cmd, my_id, args) + + def test_multi_delete_vim(self): + cmd = vim.DeleteVIM(test_cli10.MyApp(sys.stdout), None) + vim_ids = 'my-id1 my-id2 my-id3' + args = [vim_ids] + self._test_delete_resource(self._RESOURCE, cmd, vim_ids, args) diff --git a/tackerclient/tests/unit/vm/test_cli10_vnf.py b/tackerclient/tests/unit/vm/test_cli10_vnf.py index 26196257..e4b04f62 100644 --- a/tackerclient/tests/unit/vm/test_cli10_vnf.py +++ b/tackerclient/tests/unit/vm/test_cli10_vnf.py @@ -207,3 +207,9 @@ class CLITestV10VmVNFJSON(test_cli10.CLITestV10Base): self.assertIn('id1', val) self.assertIn('NeutronPort', val) self.assertIn('CP11', val) + + def test_multi_delete_vnf(self): + cmd = vnf.DeleteVNF(test_cli10.MyApp(sys.stdout), None) + vnf_ids = 'vnf1 vnf2 vnf3' + args = [vnf_ids] + self._test_delete_resource(self._RESOURCE, cmd, vnf_ids, args) diff --git a/tackerclient/tests/unit/vm/test_cli10_vnfd.py b/tackerclient/tests/unit/vm/test_cli10_vnfd.py index e8fee88a..6291302b 100644 --- a/tackerclient/tests/unit/vm/test_cli10_vnfd.py +++ b/tackerclient/tests/unit/vm/test_cli10_vnfd.py @@ -95,3 +95,10 @@ class CLITestV10VmVNFDJSON(test_cli10.CLITestV10Base): my_id = 'my-id' args = [my_id] self._test_delete_resource(self._RESOURCE, cmd, my_id, args) + + def test_multi_delete_vnfd(self): + cmd = vnfd.DeleteVNFD( + test_cli10.MyApp(sys.stdout), None) + vnfd_ids = 'my-id1 my-id2 my-id3' + args = [vnfd_ids] + self._test_delete_resource(self._RESOURCE, cmd, vnfd_ids, args)