From 6834454309085bee6f83b5a1549580bdee1b2db8 Mon Sep 17 00:00:00 2001 From: Hiromu Asahina Date: Fri, 5 Nov 2021 00:13:55 +0900 Subject: [PATCH] Support of Cancel VNF command in openstackclient Add ``openstack vnflcm op cancel`` to python-tackerclient. This command can execute Cancel operation [1]. This API makes a transition a LCM that is stopped in the PROCESSING state to the FAILED_TEMP state. [1] https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/03.03.01_60/gs_NFV-SOL003v030301p.pdf Implements: blueprint support-cancel Change-Id: Id957f6a7992f9fd5b04806aff3d76680d8d63a8d Signed-off-by: Hiromu Asahina --- setup.cfg | 1 + tackerclient/osc/v1/vnflcm/vnflcm_op_occs.py | 41 ++++++++++++ .../tests/unit/osc/v1/test_vnflcm_op_occs.py | 65 +++++++++++++++++++ tackerclient/v1_0/client.py | 8 +++ 4 files changed, 115 insertions(+) diff --git a/setup.cfg b/setup.cfg index 1adecf38..7e8bfc1c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -96,6 +96,7 @@ openstack.tackerclient.v1 = vnflcm_scale = tackerclient.osc.v1.vnflcm.vnflcm:ScaleVnfLcm vnflcm_change-ext-conn = tackerclient.osc.v1.vnflcm.vnflcm:ChangeExtConnVnfLcm vnflcm_op_rollback = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RollbackVnfLcmOp + vnflcm_op_cancel = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:CancelVnfLcmOp vnflcm_op_fail = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:FailVnfLcmOp vnflcm_op_retry = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RetryVnfLcmOp vnflcm_op_list = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:ListVnfLcmOp diff --git a/tackerclient/osc/v1/vnflcm/vnflcm_op_occs.py b/tackerclient/osc/v1/vnflcm/vnflcm_op_occs.py index 39ed7be3..c7223f88 100644 --- a/tackerclient/osc/v1/vnflcm/vnflcm_op_occs.py +++ b/tackerclient/osc/v1/vnflcm/vnflcm_op_occs.py @@ -101,6 +101,47 @@ class RollbackVnfLcmOp(command.Command): ' accepted') % {'id': parsed_args.vnf_lcm_op_occ_id})) +class CancelVnfLcmOp(command.ShowOne): + _description = _("Cancel VNF Instance") + + def get_parser(self, prog_name): + """Add arguments to parser. + + Args: + prog_name ([type]): program name + + Returns: + parser([ArgumentParser]): + """ + parser = super(CancelVnfLcmOp, self).get_parser(prog_name) + parser.add_argument( + _VNF_LCM_OP_OCC_ID, + metavar="", + help=_('VNF lifecycle management operation occurrence ID.')) + parser.add_argument( + "--cancel-mode", + default='GRACEFUL', + metavar="", + choices=['GRACEFUL', 'FORCEFUL'], + help=_("Cancel mode can be 'GRACEFUL' or 'FORCEFUL'. " + "Default is 'GRACEFUL'")) + return parser + + def take_action(self, parsed_args): + """Execute cancel_vnf_instance and output comment. + + Args: + parsed_args ([Namespace]): arguments of CLI. + """ + client = self.app.client_manager.tackerclient + result = client.cancel_vnf_instance( + parsed_args.vnf_lcm_op_occ_id, + {'cancelMode': parsed_args.cancel_mode}) + if not result: + print((_('Cancel request for LCM operation %(id)s has been' + ' accepted') % {'id': parsed_args.vnf_lcm_op_occ_id})) + + class FailVnfLcmOp(command.ShowOne): _description = _("Fail VNF Instance") diff --git a/tackerclient/tests/unit/osc/v1/test_vnflcm_op_occs.py b/tackerclient/tests/unit/osc/v1/test_vnflcm_op_occs.py index 68e769e8..59ef4562 100644 --- a/tackerclient/tests/unit/osc/v1/test_vnflcm_op_occs.py +++ b/tackerclient/tests/unit/osc/v1/test_vnflcm_op_occs.py @@ -14,6 +14,7 @@ from io import StringIO import os import sys +import ddt from oslo_utils.fixture import uuidsentinel from unittest import mock @@ -57,6 +58,70 @@ class TestVnfLcm(base.FixturedTestCase): self.app.client_manager.tackerclient = self.client_manager +@ddt.ddt +class TestCancelVnfLcmOp(TestVnfLcm): + + def setUp(self): + super(TestCancelVnfLcmOp, self).setUp() + self.cancel_vnf_lcm = vnflcm_op_occs.CancelVnfLcmOp( + self.app, self.app_args, cmd_name='vnflcm op cancel') + + @ddt.data('GRACEFUL', 'FORCEFUL') + def test_take_action(self, cancel_mode): + """take_action normal system test""" + + arglist = ['--cancel-mode', cancel_mode, + uuidsentinel.vnf_lcm_op_occ_id] + verifylist = [('cancel_mode', cancel_mode), + ('vnf_lcm_op_occ_id', uuidsentinel.vnf_lcm_op_occ_id)] + + parsed_args = self.check_parser( + self.cancel_vnf_lcm, arglist, verifylist) + url = os.path.join( + self.url, + 'vnflcm/v1/vnf_lcm_op_occs', + uuidsentinel.vnf_lcm_op_occ_id, + 'cancel') + self.requests_mock.register_uri( + 'POST', url, headers=self.header, json={}) + + sys.stdout = buffer = StringIO() + self.cancel_vnf_lcm.take_action(parsed_args) + + actual_message = buffer.getvalue().strip() + + expected_message = ( + 'Cancel request for LCM operation ' + + uuidsentinel.vnf_lcm_op_occ_id + + ' has been accepted') + + self.assertEqual(expected_message, actual_message) + + def test_terminate_no_options(self): + self.assertRaises(base.ParserException, self.check_parser, + self.cancel_vnf_lcm, [], []) + + def test_take_action_vnf_lcm_op_occ_id_not_found(self): + """take_action abnomaly system test""" + + arglist = [uuidsentinel.vnf_lcm_op_occ_id] + verifylist = [('vnf_lcm_op_occ_id', uuidsentinel.vnf_lcm_op_occ_id)] + + parsed_args = self.check_parser( + self.cancel_vnf_lcm, arglist, verifylist) + url = os.path.join( + self.url, + 'vnflcm/v1/vnf_lcm_op_occs', + uuidsentinel.vnf_lcm_op_occ_id, + 'cancel') + self.requests_mock.register_uri( + 'POST', url, headers=self.header, status_code=404, json={}) + + self.assertRaises(exceptions.TackerClientException, + self.cancel_vnf_lcm.take_action, + parsed_args) + + class TestRollbackVnfLcmOp(TestVnfLcm): def setUp(self): diff --git a/tackerclient/v1_0/client.py b/tackerclient/v1_0/client.py index b70a4199..487ab4bc 100644 --- a/tackerclient/v1_0/client.py +++ b/tackerclient/v1_0/client.py @@ -928,6 +928,11 @@ class VnfLCMClient(ClientBase): def rollback_vnf_instance(self, occ_id): return self.post((self.vnf_lcm_op_occs_path + "/rollback") % occ_id) + @APIParamsCall + def cancel_vnf_instance(self, occ_id, body): + return self.post((self.vnf_lcm_op_occs_path + "/cancel") % occ_id, + body=body) + @APIParamsCall def fail_vnf_instance(self, occ_id): return self.post((self.vnf_lcm_op_occs_path + "/fail") % occ_id) @@ -1236,6 +1241,9 @@ class Client(object): def rollback_vnf_instance(self, occ_id): return self.vnf_lcm_client.rollback_vnf_instance(occ_id) + def cancel_vnf_instance(self, occ_id, body): + return self.vnf_lcm_client.cancel_vnf_instance(occ_id, body) + def fail_vnf_instance(self, occ_id): return self.vnf_lcm_client.fail_vnf_instance(occ_id)