Addition of location header field in response

For below VNFInstance's operations "Location header" will be present
1) heal
2) instantiate
3) terminate

Response header will contain the "Location" HTTP header field.
And this "Location" HTTP header will contain "vnfLcmOpOccId".

This patch changes notification parameter based on
SOL013  7.1.4 Type: NotificationLink.

As per SOL003, 5.5.2.17, VnfLcmOperationOccurrenceNotification
represents a VNF lifecycle management operation occurrence
notification, which informs the receiver of changes in the
VNF lifecycle caused by a VNF LCM operation occurrence.

This notification shall be triggered by the VNFM,
when there is a change in the state of a VNF LCM operation
occurrence that changes the VNF lifecycle, including:
• Instantiation of the VNF
• Scaling of the VNF instance (including auto-scaling)
• Healing of the VNF
and others.

URI of a resource referenced from a notification
should be an absolute URI if APIRoot is known.

Co-Authored-By: Sheel Rana <ranasheel2000@gmail.com>

Closes-Bug: #1915107
Closes-Bug: #1911625
Change-Id: I3a325634f88b9521d7fa2f524076934df315742d
This commit is contained in:
spandit 2021-03-30 15:22:39 +00:00 committed by Sheel Rana
parent 6fc64560e2
commit 5a9ae0c858
2 changed files with 73 additions and 15 deletions

View File

@ -193,10 +193,12 @@ class VnfLcmController(wsgi.Controller):
self._view_builder_subscription = vnf_subscription_view.ViewBuilder()
def _get_vnf_instance_href(self, vnf_instance):
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
return '{}vnflcm/v1/vnf_instances/{}'.format(
CONF.vnf_lcm.endpoint_url, vnf_instance.id)
def _get_vnf_lcm_op_occs_href(self, vnf_lcm_op_occs_id):
return '/vnflcm/v1/vnf_lcm_op_occs/%s' % vnf_lcm_op_occs_id
return '{}vnflcm/v1/vnf_lcm_op_occs/{}'.format(
CONF.vnf_lcm.endpoint_url, vnf_lcm_op_occs_id)
def _get_vnf_instance(self, context, id):
# check if id is of type uuid format
@ -643,6 +645,13 @@ class VnfLcmController(wsgi.Controller):
self.rpc_api.instantiate(context, vnf_instance, vnf,
instantiate_vnf_request,
vnf_lcm_op_occs_id)
# set response header
res = webob.Response()
res.status_int = 202
location = ('Location',
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
res.headerlist.append(location)
return res
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
@ -655,7 +664,7 @@ class VnfLcmController(wsgi.Controller):
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._instantiate(context, vnf_instance, vnf, body)
return self._instantiate(context, vnf_instance, vnf, body)
@check_vnf_state(action="terminate",
instantiation_state=[
@ -685,6 +694,13 @@ class VnfLcmController(wsgi.Controller):
if vnf_lcm_op_occs_id:
self.rpc_api.terminate(context, vnf_instance, vnf,
terminate_vnf_req, vnf_lcm_op_occs_id)
# set response header
res = webob.Response()
res.status_int = 202
location = ('Location',
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
res.headerlist.append(location)
return res
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
@ -696,7 +712,7 @@ class VnfLcmController(wsgi.Controller):
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._terminate(context, vnf_instance, vnf, body)
return self._terminate(context, vnf_instance, vnf, body)
@check_vnf_status_and_error_point(action="heal",
status=[constants.ACTIVE])
@ -733,6 +749,14 @@ class VnfLcmController(wsgi.Controller):
self.rpc_api.heal(context, vnf_instance, vnf_dict,
heal_vnf_request, vnf_lcm_op_occs_id)
# set response header
res = webob.Response()
res.status_int = 202
location = ('Location',
self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id))
res.headerlist.append(location)
return res
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
http_client.NOT_FOUND, http_client.CONFLICT))
@ -758,7 +782,7 @@ class VnfLcmController(wsgi.Controller):
state=vnf_instance.task_state,
action='heal')
self._heal(context, vnf_instance, vnf, body)
return self._heal(context, vnf_instance, vnf, body)
@wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))

View File

@ -182,6 +182,8 @@ class FakeVNFMPlugin(mock.Mock):
@ddt.ddt
class TestController(base.TestCase):
expected_location_prefix = ('http://localhost:9890/'
'vnflcm/v1/vnf_lcm_op_occs/')
def setUp(self):
super(TestController, self).setUp()
@ -298,7 +300,7 @@ class TestController(base.TestCase):
updates = {"vnfInstanceName": "SampleVnf",
"vnfInstanceDescription": "SampleVnf Description"}
expected_vnf = fakes.fake_vnf_instance_response(**updates)
location_header = ('http://localhost/vnflcm/v1/vnf_instances/%s'
location_header = ('http://localhost:9890/vnflcm/v1/vnf_instances/%s'
% resp.json['id'])
self.assertEqual(expected_vnf, resp.json)
@ -599,6 +601,10 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_insta_notfi_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
@ -636,6 +642,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No flavour with id 'invalid'.",
resp.json['badRequest']['message'])
@ -682,6 +689,10 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_insta_notif_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
@ -728,6 +739,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No instantiation level with id "
"'instantiation_level_1'.",
@ -770,6 +782,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No instantiation level with id 'non-existing'.",
resp.json['badRequest']['message'])
@ -821,6 +834,10 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_insta_notif_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
@ -867,6 +884,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("VimConnection id is not found: %s" %
uuidsentinel.vim_id,
@ -915,6 +933,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("Region not found for the VimConnection: %s" %
uuidsentinel.vim_id,
@ -957,6 +976,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("Default VIM is not defined.",
resp.json['badRequest']['message'])
@ -983,6 +1003,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
@ -1008,7 +1029,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state INSTANTIATING. Cannot "
"instantiate while the vnf instance is in this state.")
@ -1069,7 +1090,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("'flavourId' is a required property",
resp.json['badRequest']['message'])
@ -1093,7 +1114,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.INTERNAL_SERVER_ERROR, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
@ -1110,7 +1131,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual(
"Can not find requested vnf: %s" % constants.INVALID_UUID,
@ -1135,7 +1156,7 @@ class TestController(base.TestCase):
# Call Instantiate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
@ -1156,6 +1177,7 @@ class TestController(base.TestCase):
req.headers['Content-Type'] = 'application/json'
req.method = method
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
@ -1261,6 +1283,9 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
expected_location = (self.expected_location_prefix +
str(mock_notification_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_terminate.assert_called_once()
mock_get_vnf.assert_called_once()
@ -1317,6 +1342,7 @@ class TestController(base.TestCase):
# Call terminate API
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("'terminationType' is a required property",
resp.json['badRequest']['message'])
@ -1336,6 +1362,7 @@ class TestController(base.TestCase):
req.headers['Content-Type'] = 'application/json'
req.method = method
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
@ -1356,7 +1383,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
@ -1387,7 +1414,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state TERMINATING. Cannot "
"terminate while the vnf instance is in this state.")
@ -1423,7 +1450,10 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertTrue('Location' in resp.headers.keys())
expected_location = (self.expected_location_prefix +
str(mock_heal_notif_process.return_value))
self.assertEqual(expected_location, resp.headers['location'])
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_rpc_heal.assert_called_once()
@ -1440,6 +1470,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
@ -1468,6 +1499,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in instantiation_state "
"NOT_INSTANTIATED. Cannot heal while the vnf instance "
@ -1498,6 +1530,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state "
"HEALING. Cannot heal while the vnf instance "
@ -1533,6 +1566,7 @@ class TestController(base.TestCase):
req.method = 'POST'
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
expected_msg = "Vnfc id %s not present in vnf instance %s"
self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id,
@ -1553,7 +1587,7 @@ class TestController(base.TestCase):
req.method = method
resp = req.get_response(self.app)
self.assertFalse('Location' in resp.headers.keys())
self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',