From 840d754822344e625c65a47a9eb39aabca7f489b Mon Sep 17 00:00:00 2001 From: tpatil Date: Thu, 5 Dec 2019 07:47:47 +0000 Subject: [PATCH] Add delete vnf instance API Implemented delete vnf instance API. * DELETE /vnflcm/v1/vnf_instances/{vnf_instance_id} Co-authored-By: Ajay Parja Co-authored-By: Niraj Singh Blueprint: support-etsi-nfv-specs Change-Id: I66040705a2f2c15e3aa84112226ce77ab5a7e18d --- tacker/api/vnflcm/v1/controller.py | 14 +++- tacker/policies/vnf_lcm.py | 11 +++ tacker/tests/unit/vnflcm/test_controller.py | 85 +++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) diff --git a/tacker/api/vnflcm/v1/controller.py b/tacker/api/vnflcm/v1/controller.py index 411cf940b..7c8bbcd21 100644 --- a/tacker/api/vnflcm/v1/controller.py +++ b/tacker/api/vnflcm/v1/controller.py @@ -209,8 +209,20 @@ class VnfLcmController(wsgi.Controller): vnf_instances = objects.VnfInstanceList.get_all(context) return self._view_builder.index(vnf_instances) + @check_vnf_state(action="delete", + instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED], + task_state=[None]) + def _delete(self, context, vnf_instance): + vnf_instance.destroy(context) + + @wsgi.response(http_client.NO_CONTENT) + @wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND, + http_client.CONFLICT)) def delete(self, request, id): - raise webob.exc.HTTPNotImplemented() + context = request.environ['tacker.context'] + + vnf_instance = self._get_vnf_instance(context, id) + self._delete(context, vnf_instance) @check_vnf_state(action="instantiate", instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED], diff --git a/tacker/policies/vnf_lcm.py b/tacker/policies/vnf_lcm.py index 411a30895..cb9c39815 100644 --- a/tacker/policies/vnf_lcm.py +++ b/tacker/policies/vnf_lcm.py @@ -88,6 +88,17 @@ rules = [ } ] ), + policy.DocumentedRuleDefault( + name=VNFLCM % 'delete', + check_str=base.RULE_ADMIN_OR_OWNER, + description="Delete an Individual VNF instance.", + operations=[ + { + 'method': 'DELETE', + 'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}' + } + ] + ), ] diff --git a/tacker/tests/unit/vnflcm/test_controller.py b/tacker/tests/unit/vnflcm/test_controller.py index 4a84ef51f..a1b7f8a2c 100644 --- a/tacker/tests/unit/vnflcm/test_controller.py +++ b/tacker/tests/unit/vnflcm/test_controller.py @@ -1004,3 +1004,88 @@ class TestController(base.TestCase): req.method = method resp = req.get_response(self.app) self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code) + + @mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id") + @mock.patch.object(objects.vnf_instance, '_destroy_vnf_instance') + def test_delete(self, mock_destroy_vnf_instance, mock_vnf_by_id): + req = fake_request.HTTPRequest.blank( + '/vnf_instances/%s' % uuidsentinel.vnf_instance_id) + req.method = 'DELETE' + mock_vnf_by_id.return_value = fakes.return_vnf_instance() + req.headers['Content-Type'] = 'application/json' + + # Call delete API + resp = req.get_response(self.app) + + self.assertEqual(http_client.NO_CONTENT, resp.status_code) + mock_destroy_vnf_instance.assert_called_once() + + @mock.patch.object(objects.VnfInstance, "get_by_id") + def test_delete_with_non_existing_vnf_instance(self, mock_vnf_by_id): + req = fake_request.HTTPRequest.blank( + '/vnf_instances/%s' % uuidsentinel.vnf_instance_id) + req.method = 'DELETE' + + mock_vnf_by_id.side_effect = exceptions.VnfInstanceNotFound + + # Call delete API + resp = req.get_response(self.app) + + self.assertEqual(http_client.NOT_FOUND, resp.status_code) + self.assertEqual("Can not find requested vnf instance: %s" % + uuidsentinel.vnf_instance_id, + resp.json['itemNotFound']['message']) + + def test_delete_with_invalid_uuid(self): + req = fake_request.HTTPRequest.blank( + '/vnf_instances/%s' % constants.INVALID_UUID) + req.method = 'DELETE' + + # Call delete API + resp = req.get_response(self.app) + + self.assertEqual(http_client.NOT_FOUND, resp.status_code) + self.assertEqual("Can not find requested vnf instance: %s" % + constants.INVALID_UUID, + resp.json['itemNotFound']['message']) + + @mock.patch.object(objects.VnfInstance, "get_by_id") + def test_delete_with_incorrect_instantiation_state(self, mock_vnf_by_id): + req = fake_request.HTTPRequest.blank( + '/vnf_instances/%s' % uuidsentinel.vnf_instance_id) + req.method = 'DELETE' + + vnf_instance = fakes.return_vnf_instance( + fields.VnfInstanceState.INSTANTIATED) + mock_vnf_by_id.return_value = vnf_instance + + # Call delete API + resp = req.get_response(self.app) + + self.assertEqual(http_client.CONFLICT, resp.status_code) + expected_msg = ("Vnf instance %s in instantiation_state " + "INSTANTIATED. Cannot delete while the vnf instance " + "is in this state.") + self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, + resp.json['conflictingRequest']['message']) + + @mock.patch.object(objects.VnfInstance, "get_by_id") + def test_delete_with_incorrect_task_state(self, mock_vnf_by_id): + req = fake_request.HTTPRequest.blank( + '/vnf_instances/%s' % uuidsentinel.vnf_instance_id) + req.method = 'DELETE' + + vnf_instance = fakes.return_vnf_instance( + fields.VnfInstanceState.NOT_INSTANTIATED, + task_state=fields.VnfInstanceTaskState.ERROR) + mock_vnf_by_id.return_value = vnf_instance + + # Call delete API + resp = req.get_response(self.app) + + self.assertEqual(http_client.CONFLICT, resp.status_code) + expected_msg = ("Vnf instance %s in task_state ERROR. " + "Cannot delete while the vnf instance " + "is in this state.") + self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id, + resp.json['conflictingRequest']['message'])