Fix servernotification metadata issue

This patch fixes these problems about metadata:
- vnfcResourceInfo[].metadata.server_notification
  value is not stored in LCM operations.
- instantiatedVnfInfo.metadata.ServerNotifierFaultID
  value should be a list, not a string.

Closes-Bug: #2004097
Change-Id: I06ea7ced6d9a33c067be3e24ab2c4163bef3574c
This commit is contained in:
Koji Shimizu 2023-01-29 15:31:12 +09:00
parent 844de7fb20
commit f951a43ced
8 changed files with 118 additions and 70 deletions

View File

@ -124,22 +124,40 @@ The ``additionalParams`` must be set when using FaultNotification.
- Additional VNF-specific attributes that - Additional VNF-specific attributes that
provide the current values of the configurable provide the current values of the configurable
properties of the VNF instance. properties of the VNF instance.
* - >isAutohealEnabled: * - additionalParams
- KeyValuePairs
- 0..1
- Additional input parameters for the instantiation process,
specific to the VNF being instantiated.
.. list-table::
:header-rows: 1
:widths: 18 18 10 50
* - Attribute name (vnfConfigurableProperties)
- Data type
- Cardinality
- Description
* - isAutohealEnabled:
- boolean - boolean
- 0..1 - 0..1
- If present, the VNF supports auto-healing. If set to - If present, the VNF supports auto-healing. If set to
true, auto-healing is currently enabled. true, auto-healing is currently enabled.
If set to false, autohealing is currently disabled. If set to false, autohealing is currently disabled.
* - additionalParams
- KeyValuePairs (inlined) .. list-table::
- 0..1 :header-rows: 1
- Additional input parameters for the instantiation process, :widths: 18 18 10 50
specific to the VNF being instantiated.
* - >ServerNotifierUri * - Attribute name (additionalParams)
- Data type
- Cardinality
- Description
* - ServerNotifierUri
- String - String
- 1 - 1
- Base Uri for ServerNotifier. - Base Uri for ServerNotifier.
* - >ServerNotifierFaultID * - ServerNotifierFaultID
- String - String
- 1..N - 1..N
- List of string that indicates which type of alarms to detect. - List of string that indicates which type of alarms to detect.
@ -159,11 +177,12 @@ with vnflcm show command. For example:
| | .... | | | .... |
| | "metadata": { | | | "metadata": { |
| | "ServerNotifierUri": "http://localhost:9990/server_notification", | | | "ServerNotifierUri": "http://localhost:9990/server_notification", |
| | "ServerNotifierFaultID": "1234" | | | "ServerNotifierFaultID": ["1111", "1234"] |
| | } | | | } |
| | .... | | | .... |
| VNF Configurable Properties | isAutohealEnabled=True | +-----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | VNF Configurable Properties | isAutohealEnabled=True |
| | .... | | | .... |
+-----------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Auto Healing Auto Healing
------------ ------------

View File

@ -71,16 +71,17 @@ class ServerNotification(mon_base.MonitoringPlugin):
'vnfcInfo')): 'vnfcInfo')):
raise sol_ex.ServerNotificationValidationError( raise sol_ex.ServerNotificationValidationError(
detail="access info not found in the vnf instance.") detail="access info not found in the vnf instance.")
if fault_id != vnf_instance.instantiatedVnfInfo.metadata.get( if fault_id not in vnf_instance.instantiatedVnfInfo.metadata.get(
'ServerNotifierFaultID'): 'ServerNotifierFaultID', []):
raise sol_ex.ServerNotificationValidationError( raise sol_ex.ServerNotificationValidationError(
detail="fault_id does not match.") detail="fault_id does not match.")
# Get the list of instantiatedVnfInfo.vnfcInfo[x].id where # Get the list of instantiatedVnfInfo.vnfcInfo[x].id where
# vnfcInfo[x].vnfcResourceInfoId == vnfcResourceInfo[y].id and # vnfcInfo[x].vnfcResourceInfoId = vnfcResourceInfo[y].id and
# vnfcResourceInfo[y].metadata.alarmId == alarm_id # vnfcResourceInfo[y].metadata.server_notification.alarmId = alarm_id
rsc_info = filter(lambda x: ('metadata' in x and rsc_info = filter(lambda x: ('metadata' in x and
alarm_id == x['metadata'].get('alarmId')), alarm_id == x['metadata'].get(
'server_notification', {}).get('alarmId')),
vnf_instance.instantiatedVnfInfo.vnfcResourceInfo) vnf_instance.instantiatedVnfInfo.vnfcResourceInfo)
rsc_ids = list(map(lambda x: x['id'], rsc_info)) rsc_ids = list(map(lambda x: x['id'], rsc_info))
vnfc_info = filter(lambda x: vnfc_info = filter(lambda x:

View File

@ -1196,7 +1196,7 @@ class Openstack(object):
return metadata return metadata
def _update_vnfc_metadata(self, vnfc_res_infos, storage_infos, def _update_vnfc_metadata(self, vnfc_res_infos, storage_infos,
heat_client, nfv_dict): heat_client, nfv_dict, inst):
for vnfc_res_info in vnfc_res_infos: for vnfc_res_info in vnfc_res_infos:
metadata = vnfc_res_info.metadata metadata = vnfc_res_info.metadata
if 'parent_stack_id' in metadata: if 'parent_stack_id' in metadata:
@ -1224,6 +1224,9 @@ class Openstack(object):
metadata['zone'] = zone metadata['zone'] = zone
vnfc_res_info.zoneId = zone vnfc_res_info.zoneId = zone
self._add_extra_metadata_from_inst(metadata, vnfc_res_info.id,
inst)
def _make_vnfc_info_id(self, inst, vnfc_res_info): def _make_vnfc_info_id(self, inst, vnfc_res_info):
vdu_idx = vnfc_res_info.metadata.get('vdu_idx') vdu_idx = vnfc_res_info.metadata.get('vdu_idx')
if (vdu_idx is not None and inst.obj_attr_is_set('metadata') and if (vdu_idx is not None and inst.obj_attr_is_set('metadata') and
@ -1238,6 +1241,20 @@ class Openstack(object):
# default vnfc_id # default vnfc_id
return _make_combination_id(vnfc_res_info.vduId, vnfc_res_info.id) return _make_combination_id(vnfc_res_info.vduId, vnfc_res_info.id)
def _add_extra_metadata_from_inst(self, metadata, vnfc_res_id, inst):
if (not inst.obj_attr_is_set('instantiatedVnfInfo') or
not inst.instantiatedVnfInfo.obj_attr_is_set(
'vnfcResourceInfo')):
return
extra_keys = ['server_notification']
for vnfc_res_info in inst.instantiatedVnfInfo.vnfcResourceInfo:
if vnfc_res_info.id == vnfc_res_id and vnfc_res_info.metadata:
for key in extra_keys:
if key in vnfc_res_info.metadata:
metadata[key] = vnfc_res_info.metadata[key]
return
def _make_instantiated_vnf_info(self, req, inst, grant_req, grant, vnfd, def _make_instantiated_vnf_info(self, req, inst, grant_req, grant, vnfd,
heat_client, is_rollback=False, stack_id=None): heat_client, is_rollback=False, stack_id=None):
# get heat resources # get heat resources
@ -1320,7 +1337,7 @@ class Openstack(object):
vnfc_res_info.vnfcCpInfo = cp_infos vnfc_res_info.vnfcCpInfo = cp_infos
self._update_vnfc_metadata(vnfc_res_infos, storage_infos, self._update_vnfc_metadata(vnfc_res_infos, storage_infos,
heat_client, nfv_dict) heat_client, nfv_dict, inst)
vnf_vl_res_infos = [ vnf_vl_res_infos = [
objects.VnfVirtualLinkResourceInfoV2( objects.VnfVirtualLinkResourceInfoV2(

View File

@ -78,11 +78,11 @@ class ServerNotificationMgmtDriver(object):
res_ids = vnfc_res_ids if vnfc_res_ids else [] res_ids = vnfc_res_ids if vnfc_res_ids else []
for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']: for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if ('metadata' in rsc and if ('metadata' in rsc and
'alarmId' in rsc['metadata'] and (isall or 'server_notification' in rsc['metadata'] and (isall or
rsc['computeResource']['resourceId'] in res_ids)): rsc['computeResource']['resourceId'] in res_ids)):
found = True found = True
alarm_id = rsc['metadata']['alarmId'] alarm_id = rsc['metadata']['server_notification']['alarmId']
del rsc['metadata']['alarmId'] del rsc['metadata']['server_notification']
server_id = rsc['computeResource']['resourceId'] server_id = rsc['computeResource']['resourceId']
self._request_unregister( self._request_unregister(
server_notifier_uri, tenant, server_id, alarm_id) server_notifier_uri, tenant, server_id, alarm_id)
@ -91,11 +91,13 @@ class ServerNotificationMgmtDriver(object):
def request_register_cancel(self, rsc_list): def request_register_cancel(self, rsc_list):
server_notifier_uri, _, tenant = self.get_params() server_notifier_uri, _, tenant = self.get_params()
for rsc in rsc_list: for rsc in rsc_list:
alarm_id = rsc['metadata']['alarmId'] if ('metadata' in rsc and
del rsc['metadata']['alarmId'] 'server_notification' in rsc['metadata']):
server_id = rsc['computeResource']['resourceId'] alarm_id = rsc['metadata']['server_notification']['alarmId']
self._request_unregister( del rsc['metadata']['server_notification']
server_notifier_uri, tenant, server_id, alarm_id) server_id = rsc['computeResource']['resourceId']
self._request_unregister(
server_notifier_uri, tenant, server_id, alarm_id)
def _request_register(self, vnfc_resource): def _request_register(self, vnfc_resource):
server_id = vnfc_resource['computeResource']['resourceId'] server_id = vnfc_resource['computeResource']['resourceId']
@ -118,7 +120,8 @@ class ServerNotificationMgmtDriver(object):
raise common_ex.MgmtDriverOtherError(error_message=msg) raise common_ex.MgmtDriverOtherError(error_message=msg)
if 'metadata' not in vnfc_resource: if 'metadata' not in vnfc_resource:
vnfc_resource['metadata'] = {} vnfc_resource['metadata'] = {}
vnfc_resource['metadata']['alarmId'] = res_body['alarm_id'] vnfc_resource['metadata']['server_notification'] = {
'alarmId': res_body['alarm_id']}
LOG.debug( LOG.debug(
"server_notification registration is processed: %d. " "server_notification registration is processed: %d. "
"alarm_id: %s", resp.status_code, res_body['alarm_id']) "alarm_id: %s", resp.status_code, res_body['alarm_id'])
@ -128,7 +131,7 @@ class ServerNotificationMgmtDriver(object):
rsc_list = [] rsc_list = []
for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']: for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if ('metadata' not in rsc or if ('metadata' not in rsc or
'alarmId' not in rsc['metadata']): 'server_notification' not in rsc['metadata']):
try: try:
self._request_register(rsc) self._request_register(rsc)
rsc_list.append(rsc) rsc_list.append(rsc)

View File

@ -23,6 +23,8 @@ from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
test_count = 0 test_count = 0
RETRY_LIMIT = 10
RETRY_TIMEOUT = 3
def make_alarm_id(header, body): def make_alarm_id(header, body):
@ -133,6 +135,18 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
- 8. LCM-Delete - 8. LCM-Delete
""" """
# Retrying LCM function in case that
# the lcmocc is completed but the lock is still remaining.
def _lcm_retry(func, *args):
retry = RETRY_LIMIT
while retry > 0:
resp, body = func(*args)
if 409 != resp.status_code:
break
time.sleep(RETRY_TIMEOUT)
retry -= 1
return resp, body
is_nfvo = False is_nfvo = False
# 0. Pre setting # 0. Pre setting
create_req = paramgen.create_vnf_min(self.svn_id) create_req = paramgen.create_vnf_min(self.svn_id)
@ -195,7 +209,7 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
instantiate_req = paramgen.instantiate_vnf_min() instantiate_req = paramgen.instantiate_vnf_min()
instantiate_req['additionalParams'] = { instantiate_req['additionalParams'] = {
'ServerNotifierUri': server_notification_uri, 'ServerNotifierUri': server_notification_uri,
'ServerNotifierFaultID': '1234' 'ServerNotifierFaultID': ['1111', '1234']
} }
instantiate_req['vnfConfigurableProperties'] = { instantiate_req['vnfConfigurableProperties'] = {
'isAutohealEnabled': is_autoheal_enabled 'isAutohealEnabled': is_autoheal_enabled
@ -264,11 +278,7 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
(stack['resource_name'] == 'VDU2')][0] (stack['resource_name'] == 'VDU2')][0]
heal_req = paramgen.heal_vnf_all_min() heal_req = paramgen.heal_vnf_all_min()
while True: resp, body = _lcm_retry(self.heal_vnf_instance, inst_id, heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
if 409 != resp.status_code:
break
time.sleep(3)
self.assertEqual(202, resp.status_code) self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp) self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location']) lcmocc_id = os.path.basename(resp.headers['Location'])
@ -336,11 +346,7 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
self.assertIsNotNone(vnfc_id) self.assertIsNotNone(vnfc_id)
heal_req = paramgen.heal_vnf_vnfc_min(vnfc_id) heal_req = paramgen.heal_vnf_vnfc_min(vnfc_id)
while True: resp, body = _lcm_retry(self.heal_vnf_instance, inst_id, heal_req)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
if 409 != resp.status_code:
break
time.sleep(3)
self.assertEqual(202, resp.status_code) self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp) self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location']) lcmocc_id = os.path.basename(resp.headers['Location'])
@ -381,11 +387,8 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
nested_stacks = self.heat_client.get_resources(stack_name) nested_stacks = self.heat_client.get_resources(stack_name)
count_before_scaleout = len(nested_stacks) count_before_scaleout = len(nested_stacks)
scaleout_req = paramgen.scaleout_vnf_min() scaleout_req = paramgen.scaleout_vnf_min()
while True: resp, body = _lcm_retry(
resp, body = self.scale_vnf_instance(inst_id, scaleout_req) self.scale_vnf_instance, inst_id, scaleout_req)
if 409 != resp.status_code:
break
time.sleep(3)
self.assertEqual(202, resp.status_code) self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp) self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location']) lcmocc_id = os.path.basename(resp.headers['Location'])
@ -415,11 +418,8 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# 6. LCM-Scale (SCALE_IN) # 6. LCM-Scale (SCALE_IN)
scalein_req = paramgen.scalein_vnf_min() scalein_req = paramgen.scalein_vnf_min()
while True: resp, body = _lcm_retry(
resp, body = self.scale_vnf_instance(inst_id, scalein_req) self.scale_vnf_instance, inst_id, scalein_req)
if 409 != resp.status_code:
break
time.sleep(3)
self.assertEqual(202, resp.status_code) self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp) self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location']) lcmocc_id = os.path.basename(resp.headers['Location'])
@ -434,11 +434,8 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# 7. LCM-Terminate # 7. LCM-Terminate
terminate_req = paramgen.terminate_vnf_min() terminate_req = paramgen.terminate_vnf_min()
while True: resp, body = _lcm_retry(
resp, body = self.terminate_vnf_instance(inst_id, terminate_req) self.terminate_vnf_instance, inst_id, terminate_req)
if 409 != resp.status_code:
break
time.sleep(3)
self.assertEqual(202, resp.status_code) self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp) self.check_resp_headers_in_operation_task(resp)

View File

@ -78,11 +78,11 @@ class ServerNotificationMgmtDriver(object):
res_ids = vnfc_res_ids if vnfc_res_ids else [] res_ids = vnfc_res_ids if vnfc_res_ids else []
for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']: for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if ('metadata' in rsc and if ('metadata' in rsc and
'alarmId' in rsc['metadata'] and (isall or 'server_notification' in rsc['metadata'] and (isall or
rsc['computeResource']['resourceId'] in res_ids)): rsc['computeResource']['resourceId'] in res_ids)):
found = True found = True
alarm_id = rsc['metadata']['alarmId'] alarm_id = rsc['metadata']['server_notification']['alarmId']
del rsc['metadata']['alarmId'] del rsc['metadata']['server_notification']
server_id = rsc['computeResource']['resourceId'] server_id = rsc['computeResource']['resourceId']
self._request_unregister( self._request_unregister(
server_notifier_uri, tenant, server_id, alarm_id) server_notifier_uri, tenant, server_id, alarm_id)
@ -91,11 +91,13 @@ class ServerNotificationMgmtDriver(object):
def request_register_cancel(self, rsc_list): def request_register_cancel(self, rsc_list):
server_notifier_uri, _, tenant = self.get_params() server_notifier_uri, _, tenant = self.get_params()
for rsc in rsc_list: for rsc in rsc_list:
alarm_id = rsc['metadata']['alarmId'] if ('metadata' in rsc and
del rsc['metadata']['alarmId'] 'server_notification' in rsc['metadata']):
server_id = rsc['computeResource']['resourceId'] alarm_id = rsc['metadata']['server_notification']['alarmId']
self._request_unregister( del rsc['metadata']['server_notification']
server_notifier_uri, tenant, server_id, alarm_id) server_id = rsc['computeResource']['resourceId']
self._request_unregister(
server_notifier_uri, tenant, server_id, alarm_id)
def _request_register(self, vnfc_resource): def _request_register(self, vnfc_resource):
server_id = vnfc_resource['computeResource']['resourceId'] server_id = vnfc_resource['computeResource']['resourceId']
@ -118,7 +120,8 @@ class ServerNotificationMgmtDriver(object):
raise common_ex.MgmtDriverOtherError(error_message=msg) raise common_ex.MgmtDriverOtherError(error_message=msg)
if 'metadata' not in vnfc_resource: if 'metadata' not in vnfc_resource:
vnfc_resource['metadata'] = {} vnfc_resource['metadata'] = {}
vnfc_resource['metadata']['alarmId'] = res_body['alarm_id'] vnfc_resource['metadata']['server_notification'] = {
'alarmId': res_body['alarm_id']}
LOG.debug( LOG.debug(
"server_notification registration is processed: %d. " "server_notification registration is processed: %d. "
"alarm_id: %s", resp.status_code, res_body['alarm_id']) "alarm_id: %s", resp.status_code, res_body['alarm_id'])
@ -128,7 +131,7 @@ class ServerNotificationMgmtDriver(object):
rsc_list = [] rsc_list = []
for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']: for rsc in self.inst['instantiatedVnfInfo']['vnfcResourceInfo']:
if ('metadata' not in rsc or if ('metadata' not in rsc or
'alarmId' not in rsc['metadata']): 'server_notification' not in rsc['metadata']):
try: try:
self._request_register(rsc) self._request_register(rsc)
rsc_list.append(rsc) rsc_list.append(rsc)

View File

@ -40,14 +40,18 @@ _inst1 = {
'vduId': 'vduId', 'vduId': 'vduId',
'computeResource': {}, 'computeResource': {},
'metadata': { 'metadata': {
"alarmId": "alarm_id" "server_notification": {
"alarmId": "alarm_id"
}
} }
}, { }, {
'id': 'vnfc_resource_id2', 'id': 'vnfc_resource_id2',
'vduId': 'vduId2', 'vduId': 'vduId2',
'computeResource': {}, 'computeResource': {},
'metadata': { 'metadata': {
"alarmId": "alarm_id2" "server_notification": {
"alarmId": "alarm_id2"
}
} }
} }
], ],
@ -59,7 +63,7 @@ _inst1 = {
}], }],
'metadata': { 'metadata': {
'ServerNotifierUri': 'ServerNotifierUri', 'ServerNotifierUri': 'ServerNotifierUri',
'ServerNotifierFaultID': '1234' 'ServerNotifierFaultID': ['1111', '1234']
} }
} }
} }

View File

@ -42,14 +42,18 @@ _inst1 = {
'vduId': 'vduId', 'vduId': 'vduId',
'computeResource': {}, 'computeResource': {},
'metadata': { 'metadata': {
"alarmId": "alarm_id" "server_notification": {
"alarmId": "alarm_id"
}
} }
}, { }, {
'id': 'vnfc_resource_id2', 'id': 'vnfc_resource_id2',
'vduId': 'vduId2', 'vduId': 'vduId2',
'computeResource': {}, 'computeResource': {},
'metadata': { 'metadata': {
"alarmId": "alarm_id2" "server_notification": {
"alarmId": "alarm_id2"
}
} }
} }
], ],
@ -61,7 +65,7 @@ _inst1 = {
}], }],
'metadata': { 'metadata': {
'ServerNotifierUri': 'ServerNotifierUri', 'ServerNotifierUri': 'ServerNotifierUri',
'ServerNotifierFaultID': '1234' 'ServerNotifierFaultID': ['1111', '1234']
} }
}, },
'vnfConfigurableProperties': { 'vnfConfigurableProperties': {
@ -177,7 +181,7 @@ class TestServerNotification(base.TestCase):
group='server_notification', server_notification=True) group='server_notification', server_notification=True)
_inst = copy.deepcopy(_inst1) _inst = copy.deepcopy(_inst1)
metadata = _inst['instantiatedVnfInfo']['metadata'] metadata = _inst['instantiatedVnfInfo']['metadata']
metadata['ServerNotifierFaultID'] = '0000' metadata['ServerNotifierFaultID'] = ['0000']
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst) mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst)
self.assertRaises( self.assertRaises(