Fix Patch Individual VNF instance

In the condition when the vnf instance hasn't been instantiated,
an error occurs when Patching Individual VNF instance.

1. Fix the exception caused by the NULL value of vnf's
updated_at field.
2. Fix the exception happened when one of its attributes is not
existed in method to_dict in Class VnfInfoModifications.
3. Fix the problem that vnfInstanceName and vnfInstanceDescription
are not displayed in notification when vnfdId is included in the
fields which need modified.
4. In the API reference, modify the method of modifying an
"Individual VNF instance" resource from POST to PATCH.

Closes-Bug: #1915100
Change-Id: Iddb5bb540b817d4fd24e2c7c12f885bd39f8662b
This commit is contained in:
Yi Feng 2021-06-07 13:37:16 +09:00
parent a98cd4eaa9
commit a9cb385452
6 changed files with 98 additions and 24 deletions

View File

@ -616,7 +616,7 @@ Request Example
Modify a VNF instance
========================
.. rest_method:: POST /vnflcm/v1/vnf_instances/{vnfInstanceId}
.. rest_method:: PATCH /vnflcm/v1/vnf_instances/{vnfInstanceId}
This method modifies an "Individual VNF instance" resource.

View File

@ -802,7 +802,7 @@ class VnfLcmController(wsgi.Controller):
return self._view_builder.show_lcm_op_occs(vnf_lcm_op_occs)
@wsgi.response(http_client.OK)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def update(self, request, id, body):
context = request.environ['tacker.context']
@ -906,7 +906,7 @@ class VnfLcmController(wsgi.Controller):
vnf_lcm_opoccs = {
'vnf_instance_id': id,
'id': op_occs_uuid,
'state_entered_time': vnf_data.get('updated_at'),
'state_entered_time': timeutils.utcnow(),
'operationParams': str(body)}
self.rpc_api.update(

View File

@ -2113,7 +2113,7 @@ class Conductor(manager.Manager):
lcm_op_obj.operation = fields.InstanceOperation.MODIFY_INFO
lcm_op_obj.is_automatic_invocation = 0
lcm_op_obj.is_cancel_pending = 0
lcm_op_obj.operationParams = vnf_lcm_opoccs.get('operationParams')
lcm_op_obj.operation_params = vnf_lcm_opoccs.get('operationParams')
try:
lcm_op_obj.create()
@ -2161,7 +2161,9 @@ class Conductor(manager.Manager):
vnfd_pkg_data)
else:
changed_info = objects.vnf_lcm_op_occs.VnfInfoModifications()
if body_data.get('vnf_instance_name'):
changed_info.vnf_instance_name = body_data.get('vnf_instance_name')
if body_data.get('vnf_instance_description'):
changed_info.vnf_instance_description = body_data.get(
'vnf_instance_description')

View File

@ -854,19 +854,24 @@ class VnfInfoModifications(base.TackerObject,
return obj
def to_dict(self):
return {
'vnf_instance_name': self.vnf_instance_name,
'vnf_instance_description': self.vnf_instance_description,
'metadata': self.metadata,
'vim_connection_info': self.vim_connection_info,
'vim_connection_info_delete_ids':
self.vim_connection_info_delete_ids,
'vnf_pkg_id': self.vnf_pkg_id,
'vnfd_id': self.vnfd_id,
'vnf_provider': self.vnf_provider,
'vnf_product_name': self.vnf_product_name,
'vnf_software_version': self.vnf_software_version,
'vnfd_version': self.vnfd_version}
"""For the attributes of this class, if an attribute exists and is not
null, it means that the attribute has been modified. This method
returns a dictionary containing the modified attributes.
"""
dct = {}
for field in self.fields:
if field in self and getattr(self, field):
value = getattr(self, field)
# Since the type of vim_connection_info is ListOfObjectsField,
# the objects in vim_connection_info also need to be converted
# into dictionary, otherwise an error will occur
# when serialized.
if field == "vim_connection_info":
value = [vim_conn.to_dict() for vim_conn in value]
dct[field] = value
return dct
@base.TackerObjectRegistry.register

View File

@ -158,8 +158,8 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
'state_entered_time': datetime.datetime(
1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'operationParams': {
"key": "value"}}
'operationParams': 'operationParams'
}
return vnf_lcm_opoccs
def _create_vnfd_pkg_data(self):
@ -2974,6 +2974,50 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
self.vnfd_pkg_data,
vnfd_id)
@mock.patch.object(conductor_server, 'revert_update_lcm')
@mock.patch.object(t_context.get_admin_context().session, "add")
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save")
@mock.patch.object(objects.VnfInstance, "update")
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create")
def test_update_lcm_without_vnf_instance_name(self, mock_create,
mock_update, mock_save,
mock_add, mock_revert):
mock_create.return_value = "OK"
mock_update.return_value = datetime.datetime(
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
mock_add.return_value = "OK"
mock_save.return_value = "OK"
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
self.body_data.pop('vnf_instance_name')
self.conductor.update(
self.context,
self.vnf_lcm_opoccs,
self.body_data,
self.vnfd_pkg_data,
vnfd_id)
@mock.patch.object(conductor_server, 'revert_update_lcm')
@mock.patch.object(t_context.get_admin_context().session, "add")
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save")
@mock.patch.object(objects.VnfInstance, "update")
@mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create")
def test_update_lcm_without_vnf_instance_description(self, mock_create,
mock_update, mock_save,
mock_add, mock_revert):
mock_create.return_value = "OK"
mock_update.return_value = datetime.datetime(
1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
mock_add.return_value = "OK"
mock_save.return_value = "OK"
vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600"
self.body_data.pop('vnf_instance_description')
self.conductor.update(
self.context,
self.vnf_lcm_opoccs,
self.body_data,
self.vnfd_pkg_data,
vnfd_id)
def test_update_vim(self):
vim_id = uuidsentinel.vim_id
status = "REACHABLE"

View File

@ -968,9 +968,18 @@ def vnflcm_fail_check_added_params(error_point=7):
vim_level_resource_type="OS::Neutron::Net"),
ext_link_ports=[ext_link_port_info]
)
vim_connection_info = {"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_type": 'openstack',
"access_info": {"key1": 'value1', "key2": 'value2'}}
changed_info_values = objects.VnfInfoModifications(
vnf_instance_name="fake_name",
vnf_instance_description="fake_vnf_instance_description",
metadata={'key': 'value'},
vim_connection_info=[VimConnectionInfo(**vim_connection_info)],
vim_connection_info_delete_ids=[
'f8c35bd0-4d67-4436-9f11-14b8a84c92bb'],
vnf_pkg_id='f26f181d-7891-4720-b022-b074ec1733ef',
vnfd_id="f26f181d-7891-4720-b022-b074ec1733ef",
vnf_provider="fake_vnf_provider",
vnf_product_name="fake_vnf_product_name",
@ -1413,10 +1422,15 @@ VNFLCMOPOCC_RESPONSE = {
}]
},
'changedInfo': {
'metadata': {},
'vimConnectionInfo': [],
'vimConnectionInfoDeleteIds': [],
'vnfPkgId': None,
'metadata': {'key': 'value'},
'vimConnectionInfo': [
{"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vimId": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vimType": 'openstack',
'interfaceInfo': {},
"accessInfo": {"key1": 'value1', "key2": 'value2'}}],
'vimConnectionInfoDeleteIds': ['f8c35bd0-4d67-4436-9f11-14b8a84c92bb'],
'vnfPkgId': 'f26f181d-7891-4720-b022-b074ec1733ef',
'vnfInstanceName': 'fake_name',
'vnfInstanceDescription': "fake_vnf_instance_description",
'vnfdId': 'f26f181d-7891-4720-b022-b074ec1733ef',
@ -1528,11 +1542,20 @@ def fake_vnf_lcm_op_occs():
}
resource_changes_obj = objects.ResourceChanges(**resource_changes)
vim_connection_info = {
"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vim_type": 'openstack',
"access_info": {"key1": 'value1', "key2": 'value2'}}
changed_info = {
"vnf_instance_name": "fake_name",
"vnf_instance_description":
"fake_vnf_instance_description",
"metadata": {},
'metadata': {'key': 'value'},
'vim_connection_info': [VimConnectionInfo(**vim_connection_info)],
'vim_connection_info_delete_ids': [
'f8c35bd0-4d67-4436-9f11-14b8a84c92bb'],
'vnf_pkg_id': 'f26f181d-7891-4720-b022-b074ec1733ef',
"vnfd_id": "f26f181d-7891-4720-b022-b074ec1733ef",
"vnf_provider": "fake_vnf_provider",
"vnf_product_name": "fake_vnf_product_name",