Merge "Fix missing modificationsTriggeredByVnfPkgChange"

This commit is contained in:
Zuul 2023-07-31 15:09:27 +00:00 committed by Gerrit Code Review
commit 72573d2f42
10 changed files with 279 additions and 53 deletions

View File

@ -300,21 +300,24 @@ def _check_modification(inst_saved, inst, attr):
return False
def _check_modification_inst(obj, inst_saved, inst):
obj.vnfdId = inst.vnfdId
if inst_saved.vnfProvider != inst.vnfProvider:
obj.vnfProvider = inst.vnfProvider
if inst_saved.vnfProductName != inst.vnfProductName:
obj.vnfProductName = inst.vnfProductName
if inst_saved.vnfSoftwareVersion != inst.vnfSoftwareVersion:
obj.vnfSoftwareVersion = inst.vnfSoftwareVersion
if inst_saved.vnfdVersion != inst.vnfdVersion:
obj.vnfdVersion = inst.vnfdVersion
def _change_vnf_info(lcmocc, inst_saved, inst):
vnf_info_modify = objects.VnfInfoModificationsV2()
# vnfdid is required, don't check the existence of the value.
if inst_saved.vnfdId != inst.vnfdId:
vnf_info_modify.vnfdId = inst.vnfdId
if inst_saved.vnfProvider != inst.vnfProvider:
vnf_info_modify.vnfProvider = inst.vnfProvider
if inst_saved.vnfProductName != inst.vnfProductName:
vnf_info_modify.vnfProductName = inst.vnfProductName
if inst_saved.vnfSoftwareVersion != inst.vnfSoftwareVersion:
vnf_info_modify.vnfSoftwareVersion = inst.vnfSoftwareVersion
if inst_saved.vnfdVersion != inst.vnfdVersion:
vnf_info_modify.vnfdVersion = inst.vnfdVersion
_check_modification_inst(vnf_info_modify, inst_saved, inst)
attrs = ['vnfInstanceName', 'vnfInstanceDescription',
'vnfConfigurableProperties', 'metadata', 'extensions']
@ -365,6 +368,18 @@ def _change_vnf_info(lcmocc, inst_saved, inst):
lcmocc.changedInfo = vnf_info_modify
def _create_modifications_triggered_by_vnfpkg_change(lcmocc, inst_saved, inst):
vnfpkg_modifications = objects.ModificationsTriggeredByVnfPkgChangeV2()
_check_modification_inst(vnfpkg_modifications, inst_saved, inst)
attrs = ['vnfConfigurableProperties', 'metadata', 'extensions']
for attr in attrs:
if _check_modification(inst_saved, inst, attr):
setattr(vnfpkg_modifications, attr, getattr(inst, attr))
lcmocc.modificationsTriggeredByVnfPkgChange = vnfpkg_modifications
def update_lcmocc(lcmocc, inst_saved, inst):
# if operation is MODIFY_INFO, make changedInfo of lcmocc.
# for other operations, make ResourceChanges of lcmocc
@ -440,6 +455,9 @@ def update_lcmocc(lcmocc, inst_saved, inst):
affected_vnfcs.append(
_make_affected_vnfc_modified(vnfc, vnfc_saved))
_create_modifications_triggered_by_vnfpkg_change(
lcmocc, inst_saved, inst)
removed_vls, added_vls, common_vls = _calc_diff(
'vnfVirtualLinkResourceInfo')
affected_vls = []

View File

@ -16,6 +16,8 @@
from oslo_log import log as logging
from tacker.sol_refactored.api.schemas import common_types
from tacker.sol_refactored.api import validator
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored import objects
@ -79,3 +81,32 @@ def select_vim_info(vim_connection_info):
if vim_info.vimType == 'kubernetes':
vim_info.vimType = 'ETSINFV.KUBERNETES.V_1'
return vim_info
def check_metadata_format(metadata):
"""Check VnfInstance.metadata format"""
# NOTE: This method checks keys which Tacker supports originally.
# The key supporting is only 'VDU_VNFc_mapping' for the moment.
_vdu_vnfc_mapping = {
'type': 'object',
'patternProperties': {
'^.*$': {
'type': 'array',
'items': common_types.IdentifierInVnf
}
}
}
if 'VDU_VNFc_mapping' in metadata:
schema_validator = validator.SolSchemaValidator(_vdu_vnfc_mapping)
schema_validator.validate(metadata['VDU_VNFc_mapping'])
all_vnfc_info_ids = list()
for vnfc_info_ids in metadata['VDU_VNFc_mapping'].values():
all_vnfc_info_ids.extend(vnfc_info_ids)
if len(all_vnfc_info_ids) > len(set(all_vnfc_info_ids)):
raise sol_ex.SolValidationError(
detail="Duplicated vnfcInfo ids found in "
"metadata['VDU_VNFc_mapping'].")

View File

@ -768,10 +768,9 @@ class VnfLcmDriverV2(object):
def _modify_from_vnfd_prop(self, inst, vnfd_prop, attr):
if vnfd_prop.get(attr):
setattr(inst, attr, vnfd_prop[attr])
elif inst.obj_attr_is_set(attr):
# set {} since attribute deletion is not supported.
setattr(inst, attr, {})
base = getattr(inst, attr) if inst.obj_attr_is_set(attr) else {}
setattr(inst, attr, inst_utils.json_merge_patch(
base, vnfd_prop[attr]))
def _modify_from_req(self, inst, req, attr):
if req.obj_attr_is_set(attr):
@ -779,6 +778,21 @@ class VnfLcmDriverV2(object):
setattr(inst, attr, inst_utils.json_merge_patch(
base, getattr(req, attr)))
def _modify_inst_vnfd_id(self, context, inst, vnfd):
pkg_info = self.nfvo_client.get_vnf_package_info_vnfd(
context, inst.vnfdId)
vnfd_prop = vnfd.get_vnfd_properties()
inst.vnfProvider = pkg_info.vnfProvider
inst.vnfProductName = pkg_info.vnfProductName
inst.vnfSoftwareVersion = pkg_info.vnfSoftwareVersion
inst.vnfdVersion = pkg_info.vnfdVersion
attrs = ['vnfConfigurableProperties', 'metadata', 'extensions']
for attr in attrs:
self._modify_from_vnfd_prop(inst, vnfd_prop, attr)
def _merge_vim_connection_info(self, inst, req):
# used by MODIFY_INFO and CHANGE_EXT_CONN
# inst.vimConnectionInfo exists since req.vimConnectionInfo
@ -804,26 +818,16 @@ class VnfLcmDriverV2(object):
# NOTE: When vnfdId is changed, the values of attributes
# in the VnfInstance needs update from new VNFD.
inst.vnfdId = req.vnfdId
pkg_info = self.nfvo_client.get_vnf_package_info_vnfd(
context, inst.vnfdId)
new_vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId)
new_vnfd_prop = new_vnfd.get_vnfd_properties()
inst.vnfProvider = pkg_info.vnfProvider
inst.vnfProductName = pkg_info.vnfProductName
inst.vnfSoftwareVersion = pkg_info.vnfSoftwareVersion
inst.vnfdVersion = pkg_info.vnfdVersion
attrs = ['vnfConfigurableProperties', 'metadata', 'extensions']
for attr in attrs:
self._modify_from_vnfd_prop(inst, new_vnfd_prop, attr)
self._modify_inst_vnfd_id(context, inst, new_vnfd)
attrs = ['vnfConfigurableProperties', 'metadata', 'extensions']
for attr in attrs:
self._modify_from_req(inst, req, attr)
if inst.obj_attr_is_set('metadata'):
inst_utils.check_metadata_format(inst.metadata)
if req.obj_attr_is_set('vimConnectionInfo'):
self._merge_vim_connection_info(inst, req)
@ -1127,6 +1131,11 @@ class VnfLcmDriverV2(object):
raise sol_ex.SolException(sol_detail='not support vim type')
inst.vnfdId = grant_req.dstVnfdId
self._modify_inst_vnfd_id(context, inst, vnfd)
attrs = ['vnfConfigurableProperties', 'extensions']
for attr in attrs:
self._modify_from_req(inst, req, attr)
def change_vnfpkg_rollback(
self, context, lcmocc, inst, grant_req, grant, vnfd):

View File

@ -76,6 +76,7 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
metadata = vnfd_prop['metadata']
if 'metadata' in body:
metadata = inst_utils.json_merge_patch(metadata, body['metadata'])
inst_utils.check_metadata_format(metadata)
inst = objects.VnfInstanceV2(
# required fields

View File

@ -1302,12 +1302,10 @@ class Openstack(object):
def _make_vnfc_info_id(self, inst, vnfc_res_info):
vdu_idx = vnfc_res_info.metadata.get('vdu_idx')
if (vdu_idx is not None and inst.obj_attr_is_set('metadata') and
'VDU_VNFc_mapping' in inst.metadata and
isinstance(inst.metadata['VDU_VNFc_mapping'], dict)):
'VDU_VNFc_mapping' in inst.metadata):
vnfc_info_ids = inst.metadata['VDU_VNFc_mapping'].get(
vnfc_res_info.vduId)
if (isinstance(vnfc_info_ids, list) and
len(vnfc_info_ids) > vdu_idx):
if vnfc_info_ids is not None and len(vnfc_info_ids) > vdu_idx:
return vnfc_info_ids[vdu_idx]
# default vnfc_id
@ -1544,6 +1542,9 @@ class Openstack(object):
# re-use current object since
# vnfcConfigurableProperties may be set.
vnfc_info = old_vnfc_info
# metadata['VDU_VNFc_mapping'] may be changed
vnfc_info.id = self._make_vnfc_info_id(inst,
vnfc_res_info)
break
if vnfc_info is None:
vnfc_info_id = self._make_vnfc_info_id(inst, vnfc_res_info)

View File

@ -35,10 +35,6 @@ class ChangeCurrentVnfPkgRequest(base.TackerObject,
'vimConnectionInfo': fields.DictOfObjectsField(
'VimConnectionInfo', nullable=True),
'additionalParams': fields.KeyValuePairsField(nullable=True),
# NOTE: 'extensions' is not supported.
# It can be specified but make no effect at all.
'extensions': fields.KeyValuePairsField(nullable=True),
# NOTE: 'vnfConfigurableProperties' is not supported.
# It can be specified but make no effect at all.
'vnfConfigurableProperties': fields.KeyValuePairsField(nullable=True),
}

View File

@ -2110,6 +2110,17 @@ _expected_resource_changes_change_vnfpkg = {
]
}
_expected_modifications_triggered_by_vnfpkg_change = {
'vnfdId': 'new_vnfd_id',
'metadata': {
'VDU_VNFc_mapping': {
'VDU1': ['a-001', 'a-010', 'a-011'],
'VDU2': ['b-001'],
'VDU3': ['c-001']
}
},
}
# lcmocc_info_example
_lcmocc_modify_value = {
'id': 'test-1',
@ -2327,12 +2338,36 @@ class TestLcmOpOccUtils(base.BaseTestCase):
def test_update_lcmocc_change_vnfpkg(self):
# prepare
inst_saved = objects.VnfInstanceV2()
inst_saved = objects.VnfInstanceV2(
vnfdId="old_vnfd_id",
vnfProvider="Company",
vnfProductName="Sample VNF",
vnfSoftwareVersion="1.0",
vnfdVersion="1.0",
metadata={
'VDU_VNFc_mapping': {
'VDU1': ['a-001', 'a-010', 'a-011'],
'VDU2': ['b-0']
}
}
)
inst_saved.instantiatedVnfInfo = (
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_inst_info_example_1))
inst_saved.vnfdId = 'old_vnfd_id'
inst = objects.VnfInstanceV2()
inst = objects.VnfInstanceV2(
vnfdId="new_vnfd_id",
vnfProvider="Company",
vnfProductName="Sample VNF",
vnfSoftwareVersion="1.0",
vnfdVersion="1.0",
metadata={
'VDU_VNFc_mapping': {
'VDU1': ['a-001', 'a-010', 'a-011'],
'VDU2': ['b-001'],
'VDU3': ['c-001']
}
}
)
inst.instantiatedVnfInfo = (
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_inst_info_example_5))
@ -2346,6 +2381,9 @@ class TestLcmOpOccUtils(base.BaseTestCase):
self.assertEqual(
_expected_resource_changes_change_vnfpkg,
self._sort_resource_changes(lcmocc['resourceChanges']))
self.assertEqual(
_expected_modifications_triggered_by_vnfpkg_change,
lcmocc['modificationsTriggeredByVnfPkgChange'])
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_lcmocc(self, mock_lcmocc):

View File

@ -135,3 +135,33 @@ class TestVnfInstanceUtils(base.BaseTestCase):
self.assertEqual(
'ETSINFV.HELM.V_3', inst.vimConnectionInfo['vim1'].vimType)
self.assertEqual('ETSINFV.HELM.V_3', result.vimType)
def test_check_metadata(self):
# VDU_VNFc_mapping is not dict
metadata = {
"VDU_VNFc_mapping": "a-001"
}
self.assertRaises(sol_ex.SolValidationError,
inst_utils.check_metadata_format, metadata)
# VDU_VNFc_mapping dict value is not list.
metadata = {
"VDU_VNFc_mapping": {
"VDU1": "a-001"
}
}
self.assertRaises(sol_ex.SolValidationError,
inst_utils.check_metadata_format, metadata)
# Duplicate vnfcInfo id.
metadata = {
"VDU_VNFc_mapping": {
"VDU1": ["a-001", "a-002", "a-003"],
"VDU2": ["a-002"]
}
}
ex = self.assertRaises(sol_ex.SolValidationError,
inst_utils.check_metadata_format, metadata)
expected_detail = ("Duplicated vnfcInfo ids found in "
"metadata['VDU_VNFc_mapping'].")
self.assertEqual(expected_detail, ex.detail)

View File

@ -694,6 +694,10 @@ _modify_inst_example = {
},
"metadata": {
"metadata": "example",
"VDU_VNFc_mapping": {
"VDU1": ["a-001", "a-002", "a-003"],
"VDU2": ["b-001"]
}
},
"vimConnectionInfo": {
"vim1": {
@ -1251,7 +1255,10 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
},
"metadata": {
"metadata_1": "example_1",
"metadata_2": None
"metadata_2": None,
"VDU_VNFc_mapping": {
"VDU2": ["b-010"]
}
},
"extensions": {
"extensions": "example_1"
@ -1293,7 +1300,8 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
]
}
)
inst = objects.VnfInstanceV2.from_dict(_modify_inst_example)
inst_example = copy.deepcopy(_modify_inst_example)
inst = objects.VnfInstanceV2.from_dict(inst_example)
vnfc_info = objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_modify_vnfc_info_example)
inst.instantiatedVnfInfo = vnfc_info
@ -1327,7 +1335,11 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
},
"metadata": {
"metadata": "example",
"metadata_1": "example_1"
"metadata_1": "example_1",
"VDU_VNFc_mapping": {
"VDU1": ["a-001", "a-002", "a-003"],
"VDU2": ["b-010"]
}
},
"extensions": {
"extensions": "example_1"
@ -1389,7 +1401,11 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
vnfdVersion="vnfd version",
operationalState="ENABLED"
)
new_vnfd_prop = {}
new_vnfd_prop = {
'vnfConfigurableProperties': {},
'extensions': {},
'metadata': {}
}
mocked_get_vnf_package_info_vnfd.return_value = pkg_info
mocked_get_vnfd.return_value = vnfd_utils.Vnfd(new_vnfd_id)
@ -1418,7 +1434,8 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
}
}
)
inst = objects.VnfInstanceV2.from_dict(_modify_inst_example)
inst_example = copy.deepcopy(_modify_inst_example)
inst = objects.VnfInstanceV2.from_dict(inst_example)
lcmocc = objects.VnfLcmOpOccV2(
# required fields
id=uuidutils.generate_uuid(),
@ -1443,8 +1460,16 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
"vnfProductName": "product_1",
"vnfSoftwareVersion": "software version",
"vnfdVersion": "vnfd version",
"vnfConfigurableProperties": {},
"metadata": {},
"vnfConfigurableProperties": {
"vnfproperties": "example"
},
"metadata": {
"metadata": "example",
"VDU_VNFc_mapping": {
"VDU1": ["a-001", "a-002", "a-003"],
"VDU2": ["b-001"]
}
},
"vimConnectionInfo": {
"vim1": {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
@ -2092,8 +2117,10 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
self.assertEqual(expected_res_ids[key][name], ids)
@mock.patch.object(kubernetes.Kubernetes, 'change_vnfpkg')
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
def test_cnf_change_vnfpkg(self, mock_vnfd, mock_change_vnfpkg):
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnf_package_info_vnfd')
@mock.patch.object(vnfd_utils.Vnfd, 'get_vnfd_properties')
def test_cnf_change_vnfpkg(self, mock_get_vnfd_properties,
mock_get_vnf_package_info_vnfd, mock_change_vnfpkg):
# prepare
req_inst = objects.InstantiateVnfRequest.from_dict(
_inst_cnf_req_example)
@ -2132,7 +2159,21 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
isAutomaticInvocation=False,
isCancelPending=False,
operationParams=req)
mock_vnfd.return_value = self.vnfd_2
pkg_info = objects.VnfPkgInfoV2(
id=uuidutils.generate_uuid(),
vnfProvider="provider",
vnfProductName="product name",
vnfSoftwareVersion="software version",
vnfdVersion="vnfd version",
operationalState="ENABLED"
)
vnfd_prop = {
"vnfConfigurableProperties": {},
"metadata": {},
"extensions": {}
}
mock_get_vnf_package_info_vnfd.return_value = pkg_info
mock_get_vnfd_properties.return_value = vnfd_prop
self.driver.change_vnfpkg_process(
self.context, lcmocc, inst, grant_req, grant, self.vnfd_3)
@ -3102,8 +3143,11 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
self.assertEqual(2, len(grant_req.addResources))
self.assertEqual(2, len(grant_req.removeResources))
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnf_package_info_vnfd')
@mock.patch.object(vnfd_utils.Vnfd, 'get_vnfd_properties')
@mock.patch.object(openstack.Openstack, 'change_vnfpkg')
def test_change_vnfpkg_process(self, mock_change_vnfpkg):
def test_change_vnfpkg_process(self, mock_change_vnfpkg,
mock_get_vnfd_properties, mock_get_vnf_package_info_vnfd):
# openstack
req_inst = objects.InstantiateVnfRequest.from_dict(_inst_req_example)
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
@ -3120,10 +3164,20 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_inst_info_example))
_inst_info_example)),
vnfConfigurableProperties={
"vnf_config_prop_key": "vnf_config_prop_value"
},
metadata={
'VDU_VNFc_mapping': {
'VDU1': ['a-001', 'a-010', 'a-011'],
'VDU2': ['b-0']
}
}
)
NEW_VNFD_ID = "8557e855-09e3-4f4c-9e4f-bfe5e2025700"
grant_req = objects.GrantRequestV1(
dstVnfdId=SAMPLE_VNFD_ID,
dstVnfdId=NEW_VNFD_ID,
operation=fields.LcmOperationType.CHANGE_VNFPKG
)
grant = objects.GrantV1()
@ -3138,9 +3192,51 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
isAutomaticInvocation=False,
isCancelPending=False,
operationParams=req)
pkg_info = objects.VnfPkgInfoV2(
id=uuidutils.generate_uuid(),
vnfProvider="provider",
vnfProductName="product name",
vnfSoftwareVersion="software version",
vnfdVersion="vnfd version",
operationalState="ENABLED"
)
vnfd_prop = {
'vnfConfigurableProperties': {},
'extensions': {
'extensions_key': 'extensions_value'
},
'metadata': {
'VDU_VNFc_mapping': {
'VDU3': ['c-0']
}
}
}
mock_get_vnf_package_info_vnfd.return_value = pkg_info
mock_get_vnfd_properties.return_value = vnfd_prop
self.driver.change_vnfpkg_process(
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
inst = inst.to_dict()
expected_vnfconfigurableproperties_result = {
'vnf_config_prop_key': 'vnf_config_prop_value'
}
expected_extensions_result = {
'extensions_key': 'extensions_value'
}
expected_metadata_result = {
'VDU_VNFc_mapping': {
'VDU1': ['a-001', 'a-010', 'a-011'],
'VDU2': ['b-0'],
'VDU3': ['c-0']
}
}
self.assertEqual(expected_vnfconfigurableproperties_result,
inst['vnfConfigurableProperties'])
self.assertEqual(expected_extensions_result, inst['extensions'])
self.assertEqual(expected_metadata_result, inst['metadata'])
# error
inst = objects.VnfInstanceV2(
# required fields

View File

@ -707,7 +707,13 @@ class TestVnflcmV2(db_base.SqlTestCase):
"vnfdId": vnfd_id,
"vnfInstanceName": "test",
"vnfInstanceDescription": "test",
"metadata": {"key": "value"}
"metadata": {
"key": "value",
"VDU_VNFc_mapping": {
"VDU1": ["a-001", "a-002", "a-003"],
"VDU2": ["b-001"]
}
}
}
result = self.controller.create(request=self.request, body=body)
self.assertEqual(201, result.status)