Support fault notification for error handling
In the MgmtDriver of fault notification, the error handling operation is supported. This patch solves the problem of Tacker double-logging or canceling alarms and missing alarms on the monitoring server during error handling. Implements: blueprint support-autoheal-queue Change-Id: I658876b8ae8be9a0d7e8660c67e654e6ad7ef5e8
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support error handling for LCM using MgmtDriver in V2 API. We added the
|
||||
``user_script_err_handling_data`` variable to hold the data that needs
|
||||
to be preserved when the LCM fails.
|
||||
|
||||
At the same time, we updated the MgmtDriver of the FaultNotification
|
||||
function to support error handling.
|
||||
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import threading
|
||||
|
||||
from oslo_log import log as logging
|
||||
@@ -99,13 +100,19 @@ class ConductorV2(object):
|
||||
self.nfvo_client.send_lcmocc_notification(context, lcmocc,
|
||||
inst, self.endpoint)
|
||||
|
||||
def _set_lcmocc_error(self, lcmocc, ex):
|
||||
def _set_lcmocc_error(
|
||||
self, lcmocc, ex, user_script_err_handling_data={}):
|
||||
if isinstance(ex, sol_ex.SolException):
|
||||
problem_details = ex.make_problem_details()
|
||||
else:
|
||||
# program bug. it occurs only under development.
|
||||
problem_details = {'status': 500,
|
||||
'detail': str(ex)}
|
||||
|
||||
if user_script_err_handling_data:
|
||||
problem_details[
|
||||
'userScriptErrHandlingData'] = user_script_err_handling_data
|
||||
|
||||
lcmocc.error = objects.ProblemDetails.from_dict(problem_details)
|
||||
|
||||
@log.log
|
||||
@@ -123,6 +130,8 @@ class ConductorV2(object):
|
||||
|
||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||
|
||||
user_script_err_handling_data = {}
|
||||
|
||||
# NOTE: error cannot happen to here basically.
|
||||
# if an error occurred lcmocc.operationState remains STARTING.
|
||||
# see the log of the tacker-conductor to investigate the cause
|
||||
@@ -177,7 +186,8 @@ class ConductorV2(object):
|
||||
|
||||
try:
|
||||
self.vnflcm_driver.process(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
grant, vnfd,
|
||||
user_script_err_handling_data)
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.COMPLETED
|
||||
# update inst and lcmocc at the same time
|
||||
@@ -190,7 +200,7 @@ class ConductorV2(object):
|
||||
except Exception as ex:
|
||||
LOG.exception("PROCESSING %s failed", lcmocc.operation)
|
||||
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
|
||||
self._set_lcmocc_error(lcmocc, ex)
|
||||
self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data)
|
||||
lcmocc.update(context)
|
||||
|
||||
# send notification COMPLETED or FAILED_TEMP
|
||||
@@ -212,6 +222,13 @@ class ConductorV2(object):
|
||||
|
||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||
|
||||
if (lcmocc.obj_attr_is_set('error') and
|
||||
lcmocc.error.obj_attr_is_set('userScriptErrHandlingData')):
|
||||
user_script_err_handling_data = copy.deepcopy(
|
||||
lcmocc.error.userScriptErrHandlingData)
|
||||
else:
|
||||
user_script_err_handling_data = {}
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.PROCESSING
|
||||
lcmocc.update(context)
|
||||
# send notification PROCESSING
|
||||
@@ -230,7 +247,8 @@ class ConductorV2(object):
|
||||
self.vnflcm_driver.post_grant(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
self.vnflcm_driver.process(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
grant, vnfd,
|
||||
user_script_err_handling_data)
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.COMPLETED
|
||||
lcmocc.error = None # clear error
|
||||
@@ -245,7 +263,7 @@ class ConductorV2(object):
|
||||
except Exception as ex:
|
||||
LOG.exception("PROCESSING %s failed", lcmocc.operation)
|
||||
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
|
||||
self._set_lcmocc_error(lcmocc, ex)
|
||||
self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data)
|
||||
lcmocc.update(context)
|
||||
# grant_req and grant are already saved. they are not deleted
|
||||
# while operationState is FAILED_TEMP.
|
||||
@@ -269,6 +287,13 @@ class ConductorV2(object):
|
||||
|
||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||
|
||||
if (lcmocc.obj_attr_is_set('error') and
|
||||
lcmocc.error.obj_attr_is_set('userScriptErrHandlingData')):
|
||||
user_script_err_handling_data = copy.deepcopy(
|
||||
lcmocc.error.userScriptErrHandlingData)
|
||||
else:
|
||||
user_script_err_handling_data = {}
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.ROLLING_BACK
|
||||
lcmocc.update(context)
|
||||
# send notification ROLLING_BACK
|
||||
@@ -283,9 +308,13 @@ class ConductorV2(object):
|
||||
self.vnflcm_driver.post_grant(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
self.vnflcm_driver.rollback(context, lcmocc, inst, grant_req,
|
||||
grant, vnfd)
|
||||
grant, vnfd,
|
||||
user_script_err_handling_data)
|
||||
|
||||
lcmocc.operationState = fields.LcmOperationStateType.ROLLED_BACK
|
||||
if (lcmocc.obj_attr_is_set('error') and
|
||||
lcmocc.error.obj_attr_is_set('userScriptErrHandlingData')):
|
||||
del lcmocc.error.userScriptErrHandlingData
|
||||
with context.session.begin(subtransactions=True):
|
||||
lcmocc.update(context)
|
||||
# NOTE: Basically inst is not changed. But there is a case
|
||||
@@ -302,7 +331,7 @@ class ConductorV2(object):
|
||||
except Exception as ex:
|
||||
LOG.exception("ROLLING_BACK %s failed", lcmocc.operation)
|
||||
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
|
||||
self._set_lcmocc_error(lcmocc, ex)
|
||||
self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data)
|
||||
lcmocc.update(context)
|
||||
# grant_req and grant are already saved. they are not deleted
|
||||
# while operationState is FAILED_TEMP.
|
||||
@@ -325,13 +354,17 @@ class ConductorV2(object):
|
||||
return
|
||||
|
||||
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||
|
||||
user_script_err_handling_data = {}
|
||||
|
||||
# send notification PROCESSING
|
||||
self.nfvo_client.send_lcmocc_notification(context, lcmocc, inst,
|
||||
self.endpoint)
|
||||
|
||||
try:
|
||||
vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId)
|
||||
self.vnflcm_driver.process(context, lcmocc, inst, None, None, vnfd)
|
||||
self.vnflcm_driver.process(context, lcmocc, inst, None, None, vnfd,
|
||||
user_script_err_handling_data)
|
||||
lcmocc.operationState = fields.LcmOperationStateType.COMPLETED
|
||||
# update inst and lcmocc at the same time
|
||||
with context.session.begin(subtransactions=True):
|
||||
@@ -340,7 +373,7 @@ class ConductorV2(object):
|
||||
except Exception as ex:
|
||||
LOG.exception("PROCESSING %s failed", lcmocc.operation)
|
||||
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
|
||||
self._set_lcmocc_error(lcmocc, ex)
|
||||
self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data)
|
||||
lcmocc.update(context)
|
||||
|
||||
# send notification COMPLETED or FAILED_TEMP
|
||||
|
||||
@@ -94,7 +94,8 @@ class VnfLcmDriverV2(object):
|
||||
method(context, lcmocc, inst, grant_req, grant, vnfd)
|
||||
|
||||
def _exec_mgmt_driver_script(self, operation, flavour_id, req, inst,
|
||||
grant_req, grant, vnfd, new_vnfd=None):
|
||||
grant_req, grant, vnfd,
|
||||
user_script_err_handling_data, new_vnfd=None):
|
||||
script = vnfd.get_interface_script(flavour_id, operation)
|
||||
if script is None:
|
||||
return
|
||||
@@ -108,7 +109,8 @@ class VnfLcmDriverV2(object):
|
||||
if grant_req is not None else None),
|
||||
'grant_response': (grant.to_dict()
|
||||
if grant is not None else None),
|
||||
'tmp_csar_dir': tmp_csar_dir
|
||||
'tmp_csar_dir': tmp_csar_dir,
|
||||
'user_script_err_handling_data': user_script_err_handling_data
|
||||
}
|
||||
if new_vnfd:
|
||||
new_csar_dir = new_vnfd.make_tmp_csar_dir()
|
||||
@@ -122,21 +124,25 @@ class VnfLcmDriverV2(object):
|
||||
|
||||
vnfd.remove_tmp_csar_dir(tmp_csar_dir)
|
||||
|
||||
try:
|
||||
output = pickle.loads(out.stdout)
|
||||
if isinstance(output, dict):
|
||||
if 'vnf_instance' in output:
|
||||
_inst = objects.VnfInstanceV2.from_dict(
|
||||
output['vnf_instance'])
|
||||
inst.__dict__.update(_inst.__dict__)
|
||||
if 'user_script_err_handling_data' in output:
|
||||
user_script_err_handling_data.update(output[
|
||||
'user_script_err_handling_data'])
|
||||
except EOFError:
|
||||
pass
|
||||
except pickle.UnpicklingError:
|
||||
pass
|
||||
|
||||
if out.returncode != 0:
|
||||
LOG.debug("execute %s failed: %s", operation, out.stderr)
|
||||
msg = "{} failed: {}".format(operation, out.stderr)
|
||||
raise sol_ex.MgmtDriverExecutionFailed(sol_detail=msg)
|
||||
else:
|
||||
try:
|
||||
output = pickle.loads(out.stdout)
|
||||
if isinstance(output, dict) and 'vnf_instance' in output:
|
||||
_inst = objects.VnfInstanceV2.from_dict(
|
||||
output['vnf_instance'])
|
||||
inst.__dict__.update(_inst.__dict__)
|
||||
except EOFError:
|
||||
pass
|
||||
except pickle.UnpicklingError:
|
||||
pass
|
||||
|
||||
LOG.debug("execute %s of %s success.", operation, script)
|
||||
|
||||
@@ -218,7 +224,8 @@ class VnfLcmDriverV2(object):
|
||||
else:
|
||||
return operation.lower()
|
||||
|
||||
def process(self, context, lcmocc, inst, grant_req, grant, vnfd):
|
||||
def process(self, context, lcmocc, inst, grant_req, grant, vnfd,
|
||||
user_script_err_handling_data):
|
||||
# save inst to use updating lcmocc after process done
|
||||
inst_saved = inst.obj_clone()
|
||||
|
||||
@@ -234,8 +241,9 @@ class VnfLcmDriverV2(object):
|
||||
# NOTE: This is only the case that operation is MODIFY_INFO
|
||||
# and vnf instance is never instantiated after creation.
|
||||
flavour_id = None
|
||||
self._exec_mgmt_driver_script(operation,
|
||||
flavour_id, req, inst, grant_req, grant, vnfd)
|
||||
self._exec_mgmt_driver_script(
|
||||
operation, flavour_id, req, inst, grant_req, grant, vnfd,
|
||||
user_script_err_handling_data)
|
||||
|
||||
# main process
|
||||
method = getattr(self, "%s_%s" % (lcmocc.operation.lower(), 'process'))
|
||||
@@ -249,21 +257,44 @@ class VnfLcmDriverV2(object):
|
||||
context, req.vnfdId, all_contents=True)
|
||||
operation = "{}_end".format(self._script_method_name(lcmocc.operation))
|
||||
self._exec_mgmt_driver_script(
|
||||
operation, flavour_id, req, inst,
|
||||
grant_req, grant, vnfd, new_vnfd=new_vnfd)
|
||||
operation, flavour_id, req, inst, grant_req, grant, vnfd,
|
||||
user_script_err_handling_data, new_vnfd=new_vnfd)
|
||||
|
||||
self._make_inst_info_common(lcmocc, inst_saved, inst, vnfd)
|
||||
lcmocc_utils.update_lcmocc(lcmocc, inst_saved, inst)
|
||||
|
||||
def rollback(self, context, lcmocc, inst, grant_req, grant, vnfd):
|
||||
def rollback(self, context, lcmocc, inst, grant_req, grant, vnfd,
|
||||
user_script_err_handling_data):
|
||||
method = getattr(self,
|
||||
"%s_%s" % (lcmocc.operation.lower(), 'rollback'),
|
||||
None)
|
||||
if method:
|
||||
method(context, lcmocc, inst, grant_req, grant, vnfd)
|
||||
else:
|
||||
if not method:
|
||||
raise sol_ex.RollbackNotSupported(op=lcmocc.operation)
|
||||
|
||||
# perform preamble LCM script
|
||||
req = lcmocc.operationParams
|
||||
operation_name = self._script_method_name(lcmocc.operation)
|
||||
operation = f"{operation_name}_rollback_start"
|
||||
if lcmocc.operation == v2fields.LcmOperationType.INSTANTIATE:
|
||||
flavour_id = req.flavourId
|
||||
elif inst.obj_attr_is_set('instantiatedVnfInfo'):
|
||||
flavour_id = inst.instantiatedVnfInfo.flavourId
|
||||
else:
|
||||
# NOTE: This is only the case that operation is MODIFY_INFO
|
||||
# and vnf instance is never instantiated after creation.
|
||||
flavour_id = None
|
||||
self._exec_mgmt_driver_script(
|
||||
operation, flavour_id, req, inst, grant_req,
|
||||
grant, vnfd, user_script_err_handling_data)
|
||||
|
||||
method(context, lcmocc, inst, grant_req, grant, vnfd)
|
||||
|
||||
# perform postamble LCM script
|
||||
operation = f"{operation_name}_rollback_end"
|
||||
self._exec_mgmt_driver_script(
|
||||
operation, flavour_id, req, inst, grant_req,
|
||||
grant, vnfd, user_script_err_handling_data)
|
||||
|
||||
def _get_link_ports(self, inst_req):
|
||||
names = set()
|
||||
if inst_req.obj_attr_is_set('extVirtualLinks'):
|
||||
|
||||
@@ -20,12 +20,14 @@ import sys
|
||||
|
||||
class SampleScript(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir):
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.user_script_err_handling_data = user_script_err_handling_data
|
||||
|
||||
def instantiate_start(self):
|
||||
pass
|
||||
@@ -49,13 +51,23 @@ def main():
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
user_script_err_handling_data = script_dict[
|
||||
'user_script_err_handling_data']
|
||||
|
||||
script = SampleScript(req, inst, grant_req, grant, csar_dir)
|
||||
script = SampleScript(req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data)
|
||||
try:
|
||||
getattr(script, operation)()
|
||||
except AttributeError:
|
||||
output_dict = {}
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
raise Exception("{} is not included in the script.".format(operation))
|
||||
|
||||
output_dict = {}
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
@@ -32,20 +33,81 @@ CONF = cfg.CONF
|
||||
|
||||
class ServerNotificationMgmtDriver(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir):
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.user_script_err_handling_data = user_script_err_handling_data
|
||||
auth_handle = http_client.NoAuthHandle()
|
||||
self.client = http_client.HttpClient(auth_handle)
|
||||
CONF(project='tacker')
|
||||
rpc.init(CONF)
|
||||
self.rpc = conductor_rpc_v2.VnfLcmRpcApiV2()
|
||||
|
||||
def _merge_recent_register(self):
|
||||
"""Merge alarms information
|
||||
|
||||
This method will compare the information in vnf_instance
|
||||
and user_script_err_handling_data, merging the state of
|
||||
user_script_err_handling_data with the state of vnf_instance
|
||||
to make them consistent.
|
||||
"""
|
||||
|
||||
if self.user_script_err_handling_data.get('serverNotification'):
|
||||
tmp_data = copy.deepcopy(self.user_script_err_handling_data)
|
||||
rm_server_id = []
|
||||
for server_notify in tmp_data.get('serverNotification'):
|
||||
server_id = server_notify.get('serverId')
|
||||
res = [res for res in self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'] if res['computeResource'][
|
||||
'resourceId'] == server_id]
|
||||
if res:
|
||||
alarm_id = server_notify.get('alarmId')
|
||||
if alarm_id:
|
||||
# Since there is registered alarm on the
|
||||
# monitoring server side, add it to vnfcResourceInfo.
|
||||
if 'metadata' not in res[0]:
|
||||
res[0]['metadata'] = {}
|
||||
res[0]['metadata']['server_notification'] = {
|
||||
"alarmId": alarm_id}
|
||||
else:
|
||||
# Since there is no registered alarm on the
|
||||
# monitoring server side, delete it from
|
||||
# vnfcResourceInfo.
|
||||
if res[0]['metadata'].get('server_notification'):
|
||||
del res[0]['metadata']['server_notification']
|
||||
else:
|
||||
# Since the alarm registration remains only on the
|
||||
# monitoring server side, unregister it.
|
||||
server_notifier_uri, _, tenant = self.get_params()
|
||||
if server_notify.get('alarmId'):
|
||||
self._request_unregister(
|
||||
server_notifier_uri, tenant, server_id,
|
||||
server_notify.get('alarmId'))
|
||||
rm_server_id.append(server_id)
|
||||
self.user_script_err_handling_data['serverNotification'] = [
|
||||
data for data in self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data['serverId'] not in rm_server_id
|
||||
]
|
||||
|
||||
def _instantiate_scale_rollback(self):
|
||||
if self.user_script_err_handling_data.get('serverNotification'):
|
||||
tmp_data = copy.deepcopy(self.user_script_err_handling_data)
|
||||
server_notifier_uri, _, tenant = self.get_params()
|
||||
for server_notify in tmp_data.get('serverNotification'):
|
||||
server_id = server_notify.get('serverId')
|
||||
alarm_id = server_notify.get('alarmId')
|
||||
if server_id and alarm_id:
|
||||
self._request_unregister(
|
||||
server_notifier_uri, tenant, server_id, alarm_id)
|
||||
|
||||
def make_output_dict(self):
|
||||
return {'vnf_instance': self.inst}
|
||||
return {'vnf_instance': self.inst, 'user_script_err_handling_data':
|
||||
self.user_script_err_handling_data}
|
||||
|
||||
def request_remove_timer(self, vnf_instance_id):
|
||||
self.rpc.server_notification_remove_timer(
|
||||
@@ -66,6 +128,22 @@ class ServerNotificationMgmtDriver(object):
|
||||
LOG.debug(
|
||||
"server_notification unregistration is processed: %d.",
|
||||
resp.status_code)
|
||||
|
||||
if ('serverNotification' not in
|
||||
self.user_script_err_handling_data):
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification'] = []
|
||||
rm_target = [data for data in
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data.get('serverId')
|
||||
and data['serverId'] == server_id]
|
||||
if rm_target and rm_target[0].get('alarmId'):
|
||||
del rm_target[0]['alarmId']
|
||||
else:
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification'].append({"serverId": server_id})
|
||||
|
||||
except sol_ex.SolException as e:
|
||||
# Even if unregistration is failed for a single alarm_id,
|
||||
# Unregistration should be done for remaining alarm_ids.
|
||||
@@ -125,6 +203,20 @@ class ServerNotificationMgmtDriver(object):
|
||||
LOG.debug(
|
||||
"server_notification registration is processed: %d. "
|
||||
"alarm_id: %s", resp.status_code, res_body['alarm_id'])
|
||||
|
||||
if 'serverNotification' not in self.user_script_err_handling_data:
|
||||
self.user_script_err_handling_data['serverNotification'] = []
|
||||
add_target = [
|
||||
data for data in self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data.get('serverId') and data['serverId'] == server_id
|
||||
]
|
||||
if add_target:
|
||||
add_target[0]['alarmId'] = res_body['alarm_id']
|
||||
else:
|
||||
self.user_script_err_handling_data['serverNotification'].append(
|
||||
{'serverId': server_id, 'alarmId': res_body['alarm_id']})
|
||||
|
||||
return {'alarmId': res_body['alarm_id'], 'serverId': server_id}
|
||||
|
||||
def request_register(self):
|
||||
@@ -147,9 +239,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
fault_id = None
|
||||
tenant = None
|
||||
additional_params = self.req.get('additionalParams', None)
|
||||
if 'instantiatedVnfInfo' not in self.inst:
|
||||
return (None, None, None)
|
||||
vnf_info = self.inst['instantiatedVnfInfo']
|
||||
vnf_info = self.inst.get('instantiatedVnfInfo')
|
||||
|
||||
if (additional_params and
|
||||
'ServerNotifierUri' in additional_params and
|
||||
@@ -172,6 +262,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
return (None, None, None)
|
||||
|
||||
def terminate_start(self):
|
||||
self._merge_recent_register()
|
||||
at_least_one_id_unregistered = self.request_unregister()
|
||||
if at_least_one_id_unregistered:
|
||||
self.request_remove_timer(self.inst['id'])
|
||||
@@ -184,6 +275,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
def scale_start(self):
|
||||
if self.req['type'] != 'SCALE_IN':
|
||||
return
|
||||
self._merge_recent_register()
|
||||
vnfc_res_ids = [res_def['resource']['resourceId']
|
||||
for res_def in self.grant_req['removeResources']
|
||||
if res_def.get('type', None) == 'COMPUTE']
|
||||
@@ -192,6 +284,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
return self.make_output_dict()
|
||||
|
||||
def heal_start(self):
|
||||
self._merge_recent_register()
|
||||
isall = ('additionalParams' in self.req and
|
||||
self.req['additionalParams'].get('all', False) and
|
||||
'vnfcInstanceId' not in self.req)
|
||||
@@ -203,6 +296,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
return self.make_output_dict()
|
||||
|
||||
def instantiate_end(self):
|
||||
self._merge_recent_register()
|
||||
self.request_register()
|
||||
server_notifier_uri, fault_id, _ = self.get_params()
|
||||
vnf_info = self.inst['instantiatedVnfInfo']
|
||||
@@ -215,6 +309,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
def scale_end(self):
|
||||
if self.req['type'] != 'SCALE_OUT':
|
||||
return
|
||||
self._merge_recent_register()
|
||||
self.request_register()
|
||||
return self.make_output_dict()
|
||||
|
||||
@@ -228,6 +323,14 @@ class ServerNotificationMgmtDriver(object):
|
||||
def terminate_end(self):
|
||||
pass
|
||||
|
||||
def instantiate_rollback_start(self):
|
||||
self._instantiate_scale_rollback()
|
||||
return self.make_output_dict()
|
||||
|
||||
def scale_rollback_start(self):
|
||||
self._instantiate_scale_rollback()
|
||||
return self.make_output_dict()
|
||||
|
||||
|
||||
def main():
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
@@ -238,12 +341,23 @@ def main():
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
user_script_err_handling_data = script_dict[
|
||||
'user_script_err_handling_data']
|
||||
|
||||
script = ServerNotificationMgmtDriver(
|
||||
req, inst, grant_req, grant, csar_dir)
|
||||
output_dict = getattr(script, operation)()
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
script = ServerNotificationMgmtDriver(
|
||||
req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data)
|
||||
output_dict = getattr(script, operation)()
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
except Exception as mgmt_ex:
|
||||
output_dict = {
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
raise common_ex.MgmtDriverOtherError(error_message=str(mgmt_ex))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -31,4 +31,7 @@ class ProblemDetails(base.TackerObject, base.TackerObjectDictCompat):
|
||||
'status': fields.IntegerField(nullable=False),
|
||||
'detail': fields.StringField(nullable=False),
|
||||
'instance': fields.UriField(nullable=True),
|
||||
# NOTE: userScriptErrHandlingData is not defined in SOL003.
|
||||
# It is original definition of Tacker.
|
||||
'userScriptErrHandlingData': fields.KeyValuePairsField(nullable=True),
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -161,6 +161,8 @@ class BaseSolV2Test(base_v2.BaseTackerTestV2):
|
||||
def get_server_details(self, server_name):
|
||||
path = "/servers/detail"
|
||||
resp, resp_body = self.nova_client.do_request(path, "GET")
|
||||
if server_name is None:
|
||||
return resp_body.get('servers')
|
||||
|
||||
server_details = None
|
||||
for server in resp_body.get('servers'):
|
||||
|
||||
@@ -60,6 +60,10 @@ topology_template:
|
||||
implementation: sample-script
|
||||
modify_information_start:
|
||||
implementation: sample-script
|
||||
instantiate_rollback_start:
|
||||
implementation: sample-script
|
||||
scale_rollback_start:
|
||||
implementation: sample-script
|
||||
artifacts:
|
||||
sample-script:
|
||||
description: Sample script
|
||||
|
||||
@@ -6,6 +6,34 @@ imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
interface_types:
|
||||
sample.test.Vnflcm:
|
||||
derived_from: tosca.interfaces.nfv.Vnflcm
|
||||
instantiate_start:
|
||||
description: Invoked before instantiate
|
||||
instantiate_end:
|
||||
description: Invoked after instantiate
|
||||
heal_start:
|
||||
description: Invoked before heal
|
||||
heal_end:
|
||||
description: Invoked after heal
|
||||
scale_start:
|
||||
description: Invoked before scale
|
||||
scale_end:
|
||||
description: Invoked after scale
|
||||
terminate_start:
|
||||
description: Invoked before terminate
|
||||
terminate_end:
|
||||
description: Invoked after terminate
|
||||
change_external_connectivity_start:
|
||||
description: Invoked before change_external_connectivity
|
||||
modify_information_start:
|
||||
description: Invoked before modify_information
|
||||
instantiate_rollback_start:
|
||||
description: Invoked before instantiate_rollback
|
||||
scale_rollback_start:
|
||||
description: Invoked before scale_rollback
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
@@ -52,4 +80,4 @@ node_types:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
||||
type: sample.test.Vnflcm
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import functools
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
@@ -32,20 +34,88 @@ CONF = cfg.CONF
|
||||
|
||||
class ServerNotificationMgmtDriver(object):
|
||||
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir):
|
||||
def __init__(self, req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data):
|
||||
self.req = req
|
||||
self.inst = inst
|
||||
self.grant_req = grant_req
|
||||
self.grant = grant
|
||||
self.csar_dir = csar_dir
|
||||
self.user_script_err_handling_data = user_script_err_handling_data
|
||||
auth_handle = http_client.NoAuthHandle()
|
||||
self.client = http_client.HttpClient(auth_handle)
|
||||
CONF(project='tacker')
|
||||
rpc.init(CONF)
|
||||
self.rpc = conductor_rpc_v2.VnfLcmRpcApiV2()
|
||||
|
||||
def _fail(self, method):
|
||||
if os.path.exists(f'/tmp/{method}'):
|
||||
raise Exception(f'test {method} error')
|
||||
|
||||
def __getattr__(self, name):
|
||||
return functools.partial(self._fail, name)
|
||||
|
||||
def _merge_recent_register(self):
|
||||
"""Merge alarms information
|
||||
|
||||
This method will compare the information in vnf_instance
|
||||
and user_script_err_handling_data, merging the state of
|
||||
user_script_err_handling_data with the state of vnf_instance
|
||||
to make them consistent.
|
||||
"""
|
||||
|
||||
if self.user_script_err_handling_data.get('serverNotification'):
|
||||
tmp_data = copy.deepcopy(self.user_script_err_handling_data)
|
||||
rm_server_id = []
|
||||
for server_notify in tmp_data.get('serverNotification'):
|
||||
server_id = server_notify.get('serverId')
|
||||
res = [res for res in self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'] if res['computeResource'][
|
||||
'resourceId'] == server_id]
|
||||
if res:
|
||||
alarm_id = server_notify.get('alarmId')
|
||||
if alarm_id:
|
||||
# Since there is registered alarm on the
|
||||
# monitoring server side, add it to vnfcResourceInfo.
|
||||
if 'metadata' not in res[0]:
|
||||
res[0]['metadata'] = {}
|
||||
res[0]['metadata']['server_notification'] = {
|
||||
"alarmId": alarm_id}
|
||||
else:
|
||||
# Since there is no registered alarm on the
|
||||
# monitoring server side, delete it from
|
||||
# vnfcResourceInfo.
|
||||
if res[0]['metadata'].get('server_notification'):
|
||||
del res[0]['metadata']['server_notification']
|
||||
else:
|
||||
# Since the alarm registration remains only on the
|
||||
# monitoring server side, unregister it.
|
||||
server_notifier_uri, _, tenant = self.get_params()
|
||||
if server_notify.get('alarmId'):
|
||||
self._request_unregister(
|
||||
server_notifier_uri, tenant, server_id,
|
||||
server_notify.get('alarmId'))
|
||||
rm_server_id.append(server_id)
|
||||
self.user_script_err_handling_data['serverNotification'] = [
|
||||
data for data in self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data['serverId'] not in rm_server_id
|
||||
]
|
||||
|
||||
def _instantiate_scale_rollback(self):
|
||||
if self.user_script_err_handling_data.get('serverNotification'):
|
||||
tmp_data = copy.deepcopy(self.user_script_err_handling_data)
|
||||
server_notifier_uri, _, tenant = self.get_params()
|
||||
for server_notify in tmp_data.get('serverNotification'):
|
||||
server_id = server_notify.get('serverId')
|
||||
alarm_id = server_notify.get('alarmId')
|
||||
if server_id and alarm_id:
|
||||
self._request_unregister(
|
||||
server_notifier_uri, tenant, server_id, alarm_id)
|
||||
|
||||
def make_output_dict(self):
|
||||
return {'vnf_instance': self.inst}
|
||||
return {'vnf_instance': self.inst, 'user_script_err_handling_data':
|
||||
self.user_script_err_handling_data}
|
||||
|
||||
def request_remove_timer(self, vnf_instance_id):
|
||||
self.rpc.server_notification_remove_timer(
|
||||
@@ -66,6 +136,22 @@ class ServerNotificationMgmtDriver(object):
|
||||
LOG.debug(
|
||||
"server_notification unregistration is processed: %d.",
|
||||
resp.status_code)
|
||||
|
||||
if ('serverNotification' not in
|
||||
self.user_script_err_handling_data):
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification'] = []
|
||||
rm_target = [data for data in
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data.get('serverId')
|
||||
and data['serverId'] == server_id]
|
||||
if rm_target and rm_target[0].get('alarmId'):
|
||||
del rm_target[0]['alarmId']
|
||||
else:
|
||||
self.user_script_err_handling_data[
|
||||
'serverNotification'].append({"serverId": server_id})
|
||||
|
||||
except sol_ex.SolException as e:
|
||||
# Even if unregistration is failed for a single alarm_id,
|
||||
# Unregistration should be done for remaining alarm_ids.
|
||||
@@ -125,6 +211,20 @@ class ServerNotificationMgmtDriver(object):
|
||||
LOG.debug(
|
||||
"server_notification registration is processed: %d. "
|
||||
"alarm_id: %s", resp.status_code, res_body['alarm_id'])
|
||||
|
||||
if 'serverNotification' not in self.user_script_err_handling_data:
|
||||
self.user_script_err_handling_data['serverNotification'] = []
|
||||
add_target = [
|
||||
data for data in self.user_script_err_handling_data[
|
||||
'serverNotification']
|
||||
if data.get('serverId') and data['serverId'] == server_id
|
||||
]
|
||||
if add_target:
|
||||
add_target[0]['alarmId'] = res_body['alarm_id']
|
||||
else:
|
||||
self.user_script_err_handling_data['serverNotification'].append(
|
||||
{'serverId': server_id, 'alarmId': res_body['alarm_id']})
|
||||
|
||||
return {'alarmId': res_body['alarm_id'], 'serverId': server_id}
|
||||
|
||||
def request_register(self):
|
||||
@@ -147,9 +247,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
fault_id = None
|
||||
tenant = None
|
||||
additional_params = self.req.get('additionalParams', None)
|
||||
if 'instantiatedVnfInfo' not in self.inst:
|
||||
return (None, None, None)
|
||||
vnf_info = self.inst['instantiatedVnfInfo']
|
||||
vnf_info = self.inst.get('instantiatedVnfInfo')
|
||||
|
||||
if (additional_params and
|
||||
'ServerNotifierUri' in additional_params and
|
||||
@@ -172,6 +270,7 @@ class ServerNotificationMgmtDriver(object):
|
||||
return (None, None, None)
|
||||
|
||||
def terminate_start(self):
|
||||
self._merge_recent_register()
|
||||
at_least_one_id_unregistered = self.request_unregister()
|
||||
if at_least_one_id_unregistered:
|
||||
self.request_remove_timer(self.inst['id'])
|
||||
@@ -179,19 +278,25 @@ class ServerNotificationMgmtDriver(object):
|
||||
if ('metadata' in self.inst['instantiatedVnfInfo'] and
|
||||
key in self.inst['instantiatedVnfInfo']['metadata']):
|
||||
del self.inst['instantiatedVnfInfo']['metadata'][key]
|
||||
if os.path.exists('/tmp/terminate_start'):
|
||||
raise Exception('test terminate_start error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def scale_start(self):
|
||||
if self.req['type'] != 'SCALE_IN':
|
||||
return
|
||||
self._merge_recent_register()
|
||||
vnfc_res_ids = [res_def['resource']['resourceId']
|
||||
for res_def in self.grant_req['removeResources']
|
||||
if res_def.get('type', None) == 'COMPUTE']
|
||||
self.request_unregister(
|
||||
isall=False, vnfc_res_ids=vnfc_res_ids)
|
||||
if os.path.exists('/tmp/scale_start'):
|
||||
raise Exception('test scale_start error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def heal_start(self):
|
||||
self._merge_recent_register()
|
||||
isall = ('additionalParams' in self.req and
|
||||
self.req['additionalParams'].get('all', False) and
|
||||
'vnfcInstanceId' not in self.req)
|
||||
@@ -200,9 +305,12 @@ class ServerNotificationMgmtDriver(object):
|
||||
if res_def.get('type', None) == 'COMPUTE']
|
||||
self.request_unregister(
|
||||
isall=isall, vnfc_res_ids=vnfc_res_ids)
|
||||
if os.path.exists('/tmp/heal_start'):
|
||||
raise Exception('test heal_start error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def instantiate_end(self):
|
||||
self._merge_recent_register()
|
||||
self.request_register()
|
||||
server_notifier_uri, fault_id, _ = self.get_params()
|
||||
vnf_info = self.inst['instantiatedVnfInfo']
|
||||
@@ -210,16 +318,23 @@ class ServerNotificationMgmtDriver(object):
|
||||
vnf_info['metadata'] = {}
|
||||
vnf_info['metadata']['ServerNotifierUri'] = server_notifier_uri
|
||||
vnf_info['metadata']['ServerNotifierFaultID'] = fault_id
|
||||
if os.path.exists('/tmp/instantiate_end'):
|
||||
raise Exception('test instantiate_end error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def scale_end(self):
|
||||
if self.req['type'] != 'SCALE_OUT':
|
||||
return
|
||||
self._merge_recent_register()
|
||||
self.request_register()
|
||||
if os.path.exists('/tmp/scale_end'):
|
||||
raise Exception('test scale_end error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def heal_end(self):
|
||||
self.request_register()
|
||||
if os.path.exists('/tmp/heal_end'):
|
||||
raise Exception('test heal_end error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def instantiate_start(self):
|
||||
@@ -228,6 +343,18 @@ class ServerNotificationMgmtDriver(object):
|
||||
def terminate_end(self):
|
||||
pass
|
||||
|
||||
def instantiate_rollback_start(self):
|
||||
self._instantiate_scale_rollback()
|
||||
if os.path.exists('/tmp/instantiate_rollback_start'):
|
||||
raise Exception('test instantiate_rollback_start error')
|
||||
return self.make_output_dict()
|
||||
|
||||
def scale_rollback_start(self):
|
||||
self._instantiate_scale_rollback()
|
||||
if os.path.exists('/tmp/scale_rollback_start'):
|
||||
raise Exception('test scale_rollback_start error')
|
||||
return self.make_output_dict()
|
||||
|
||||
|
||||
def main():
|
||||
script_dict = pickle.load(sys.stdin.buffer)
|
||||
@@ -238,12 +365,23 @@ def main():
|
||||
grant_req = script_dict['grant_request']
|
||||
grant = script_dict['grant_response']
|
||||
csar_dir = script_dict['tmp_csar_dir']
|
||||
user_script_err_handling_data = script_dict[
|
||||
'user_script_err_handling_data']
|
||||
|
||||
script = ServerNotificationMgmtDriver(
|
||||
req, inst, grant_req, grant, csar_dir)
|
||||
output_dict = getattr(script, operation)()
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
script = ServerNotificationMgmtDriver(
|
||||
req, inst, grant_req, grant, csar_dir,
|
||||
user_script_err_handling_data)
|
||||
output_dict = getattr(script, operation)()
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
except Exception as mgmt_ex:
|
||||
output_dict = {
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
sys.stdout.buffer.write(pickle.dumps(output_dict))
|
||||
sys.stdout.flush()
|
||||
raise common_ex.MgmtDriverOtherError(error_message=str(mgmt_ex))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -312,6 +312,8 @@ class TestConductorV2(db_base.SqlTestCase):
|
||||
# prepare
|
||||
lcmocc = self._create_inst_and_lcmocc(
|
||||
op_state=fields.LcmOperationStateType.FAILED_TEMP)
|
||||
problem_details = {'userScriptErrHandlingData': {"key": "value"}}
|
||||
lcmocc.error = objects.ProblemDetails.from_dict(problem_details)
|
||||
self._create_grant_req_and_grant(lcmocc)
|
||||
mocked_get_vnfd.return_value = mock.Mock()
|
||||
ex = sol_ex.StackOperationFailed(sol_detail="unit test",
|
||||
@@ -337,6 +339,7 @@ class TestConductorV2(db_base.SqlTestCase):
|
||||
# get lcmocc from DB to be sure lcmocc saved to DB
|
||||
lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id)
|
||||
expected = ex.make_problem_details()
|
||||
expected['userScriptErrHandlingData'] = {"key": "value"}
|
||||
self.assertEqual(expected, lcmocc.error.to_dict())
|
||||
|
||||
# check grant_req and grant remain
|
||||
@@ -353,6 +356,8 @@ class TestConductorV2(db_base.SqlTestCase):
|
||||
# prepare
|
||||
lcmocc = self._create_inst_and_lcmocc(
|
||||
op_state=fields.LcmOperationStateType.FAILED_TEMP)
|
||||
problem_details = {'userScriptErrHandlingData': {"key": "value"}}
|
||||
lcmocc.error = objects.ProblemDetails.from_dict(problem_details)
|
||||
self._create_grant_req_and_grant(lcmocc)
|
||||
mocked_get_vnfd.return_value = mock.Mock()
|
||||
|
||||
@@ -386,6 +391,8 @@ class TestConductorV2(db_base.SqlTestCase):
|
||||
# prepare
|
||||
lcmocc = self._create_inst_and_lcmocc(
|
||||
op_state=fields.LcmOperationStateType.FAILED_TEMP)
|
||||
problem_details = {'userScriptErrHandlingData': {"key": "value"}}
|
||||
lcmocc.error = objects.ProblemDetails.from_dict(problem_details)
|
||||
self._create_grant_req_and_grant(lcmocc)
|
||||
mocked_get_vnfd.return_value = mock.Mock()
|
||||
ex = sol_ex.StackOperationFailed(sol_detail="unit test",
|
||||
@@ -412,6 +419,7 @@ class TestConductorV2(db_base.SqlTestCase):
|
||||
# get lcmocc from DB to be sure lcmocc saved to DB
|
||||
lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id)
|
||||
expected = ex.make_problem_details()
|
||||
expected['userScriptErrHandlingData'] = {"key": "value"}
|
||||
self.assertEqual(expected, lcmocc.error.to_dict())
|
||||
|
||||
# check grant_req and grant remain
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
import copy
|
||||
from datetime import datetime
|
||||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
from unittest import mock
|
||||
|
||||
from kubernetes import client
|
||||
@@ -2264,7 +2266,7 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
isCancelPending=False,
|
||||
operationParams=req_inst)
|
||||
self.driver.process(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
# other operation
|
||||
inst = objects.VnfInstanceV2(
|
||||
@@ -2293,7 +2295,7 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
operationParams=objects.TerminateVnfRequest(
|
||||
terminationType='FORCEFUL'))
|
||||
self.driver.process(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
# no flavour_id
|
||||
inst = objects.VnfInstanceV2(
|
||||
@@ -2319,10 +2321,11 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
operationParams=objects.VnfInfoModificationRequest()
|
||||
)
|
||||
self.driver.process(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'scale_rollback')
|
||||
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'instantiate_rollback')
|
||||
def test_rollback(self, mock_inst_rollback):
|
||||
def test_rollback(self, mock_inst_rollback, mock_scale_rollback):
|
||||
# instantiate
|
||||
req_inst = objects.InstantiateVnfRequest.from_dict(
|
||||
_inst_req_example)
|
||||
@@ -2353,7 +2356,7 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
isCancelPending=False,
|
||||
operationParams=req_inst)
|
||||
self.driver.rollback(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
# no rollback
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
@@ -2370,7 +2373,74 @@ class TestVnfLcmDriverV2(base.BaseTestCase):
|
||||
terminationType='FORCEFUL'))
|
||||
self.assertRaises(
|
||||
sol_ex.RollbackNotSupported, self.driver.rollback,
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1)
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
# mgmt rollback
|
||||
req_scale = objects.ScaleVnfRequest(
|
||||
type='SCALE_OUT', aspectId='aspect_id', numberOfSteps=1)
|
||||
lcmocc = objects.VnfLcmOpOccV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
operationState=fields.LcmOperationStateType.STARTING,
|
||||
stateEnteredTime=datetime.utcnow(),
|
||||
startTime=datetime.utcnow(),
|
||||
vnfInstanceId=inst.id,
|
||||
operation=fields.LcmOperationType.SCALE,
|
||||
isAutomaticInvocation=False,
|
||||
isCancelPending=False,
|
||||
operationParams=req_scale)
|
||||
self.driver.rollback(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
inst.instantiatedVnfInfo = objects.VnfInstanceV2_InstantiatedVnfInfo(
|
||||
flavourId='flavour_id')
|
||||
self.driver.rollback(
|
||||
self.context, lcmocc, inst, grant_req, grant, self.vnfd_1, {})
|
||||
|
||||
@mock.patch.object(pickle, 'loads')
|
||||
@mock.patch.object(vnfd_utils.Vnfd, 'remove_tmp_csar_dir')
|
||||
@mock.patch.object(subprocess, 'run')
|
||||
@mock.patch.object(os.path, 'join')
|
||||
@mock.patch.object(vnfd_utils.Vnfd, 'make_tmp_csar_dir')
|
||||
@mock.patch.object(vnfd_utils.Vnfd, 'get_interface_script')
|
||||
def test_exec_mgmt_driver_script_eof_error(
|
||||
self, mock_script, mock_dir, mock_script_path, mock_out,
|
||||
mock_remove, mock_output):
|
||||
# instantiate
|
||||
req_inst = objects.InstantiateVnfRequest.from_dict(
|
||||
_inst_req_example)
|
||||
inst = objects.VnfInstanceV2(
|
||||
# required fields
|
||||
id=uuidutils.generate_uuid(),
|
||||
vnfdId=SAMPLE_VNFD_ID,
|
||||
vnfProvider='provider',
|
||||
vnfProductName='product name',
|
||||
vnfSoftwareVersion='software version',
|
||||
vnfdVersion='vnfd version',
|
||||
instantiationState='NOT_INSTANTIATED',
|
||||
vimConnectionInfo=req_inst.vimConnectionInfo
|
||||
)
|
||||
grant_req = objects.GrantRequestV1(
|
||||
operation=fields.LcmOperationType.INSTANTIATE
|
||||
)
|
||||
grant = objects.GrantV1()
|
||||
|
||||
mock_script.return_value = 'sample_script.py'
|
||||
mock_dir.return_value = 'path'
|
||||
mock_script_path.return_value = '../mgmt_drivers/sample_script.py'
|
||||
mock_output.return_value = {
|
||||
'vnf_instance': {
|
||||
'id': 'id', 'vnfdId': 'vnfdId', 'vnfProvider': 'vnfProvider',
|
||||
'vnfProductName': 'vnfProductName',
|
||||
'vnfSoftwareVersion': 'vnfSoftwareVersion',
|
||||
'vnfdVersion': 'vnfdVersion',
|
||||
'instantiationState': 'INSTANTIATED'},
|
||||
'user_script_err_handling_data': {'key': 'value'}}
|
||||
user_script_err_handling_data = {}
|
||||
self.assertRaises(
|
||||
sol_ex.MgmtDriverExecutionFailed,
|
||||
self.driver._exec_mgmt_driver_script, 'INSTANTIATE', 'flavour_id',
|
||||
req_inst, inst, grant_req, grant, self.vnfd_1,
|
||||
user_script_err_handling_data)
|
||||
|
||||
def test_instantiate_grant_error(self):
|
||||
# instantiate
|
||||
|
||||
@@ -216,3 +216,103 @@ def fake_replica_set(
|
||||
name, image, conf_key, conf_name, sec_key, sec_name)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
fault_notif_inst_example = {
|
||||
"id": "c80f7afa-65f3-4be6-94ae-fdf438ac2d61",
|
||||
"vnfInstanceName": "FaultNotification",
|
||||
"vnfdId": "16ca1a07-2453-47f1-9f00-7ca2dce0a5ea",
|
||||
"vnfProvider": "Company",
|
||||
"vnfProductName": "Sample VNF",
|
||||
"vnfSoftwareVersion": "1.0",
|
||||
"vnfdVersion": "1.0",
|
||||
"vimConnectionInfo": {
|
||||
"vim1": {
|
||||
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
|
||||
"vimId": 'vim id',
|
||||
"interfaceInfo": {"endpoint": "http://localhost/identity/v3"},
|
||||
"accessInfo": {
|
||||
"username": "nfv_user",
|
||||
"region": "RegionOne",
|
||||
"password": "devstack",
|
||||
"project": "nfv",
|
||||
"projectDomain": "Default",
|
||||
"userDomain": "Default"
|
||||
}
|
||||
}
|
||||
},
|
||||
"instantiationState": "INSTANTIATED",
|
||||
"instantiatedVnfInfo": {
|
||||
"flavourId": "simple",
|
||||
"vnfState": "STARTED",
|
||||
"vnfcResourceInfo": [{
|
||||
"id": "daemonset-vdu5-wh824",
|
||||
"vduId": "VDU5",
|
||||
"computeResource": {
|
||||
"resourceId": "s0",
|
||||
"vimLevelResourceType": "DaemonSet"
|
||||
},
|
||||
"metadata": {
|
||||
"server_notification": {
|
||||
"alarmId": "xxx"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"id": "deployment2-vdu6-6f8c5c5ddb-9ptn9",
|
||||
"vduId": "VDU6",
|
||||
"computeResource": {
|
||||
"resourceId": "s1",
|
||||
"vimLevelResourceType": "Deployment"
|
||||
}
|
||||
}, {
|
||||
"id": "env-test",
|
||||
"vduId": "VDU3",
|
||||
"computeResource": {
|
||||
"resourceId": "s2",
|
||||
"vimLevelResourceType": "Pod"
|
||||
},
|
||||
"metadata": {
|
||||
"server_notification": {
|
||||
"alarmId": "a2"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"id": "env-test2",
|
||||
"vduId": "VDU7",
|
||||
"computeResource": {
|
||||
"resourceId": "env-test2",
|
||||
"vimLevelResourceType": "Pod"
|
||||
},
|
||||
"metadata": {}
|
||||
}],
|
||||
"metadata": {
|
||||
"ServerNotifierUri": "http://192.168.10.147:55555",
|
||||
"ServerNotifierFaultID": ["1234"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fault_notif_inst_req_example = {
|
||||
"flavourId": "simple",
|
||||
"instantiationLevelId": "instantiation_level_2",
|
||||
"additionalParams": {
|
||||
"ServerNotifierUri": "http://server_notifier:1234",
|
||||
"ServerNotifierFaultID": ["1234"]
|
||||
},
|
||||
"vimConnectionInfo": {
|
||||
"vim1": {
|
||||
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
|
||||
"vimId": 'vim id',
|
||||
"interfaceInfo": {"endpoint": "http://localhost/identity/v3"},
|
||||
"accessInfo": {
|
||||
"username": "nfv_user",
|
||||
"region": "RegionOne",
|
||||
"password": "devstack",
|
||||
"project": "nfv",
|
||||
"projectDomain": "Default",
|
||||
"userDomain": "Default"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
# Copyright (C) 2023 Fujitsu
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import copy
|
||||
import pickle
|
||||
import sys
|
||||
import webob
|
||||
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import rpc
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
from tacker.sol_refactored.conductor import conductor_rpc_v2
|
||||
from tacker.sol_refactored.mgmt_drivers import (
|
||||
server_notification as mgmt_driver)
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests.unit import base
|
||||
from tacker.tests.unit.sol_refactored.mgmt_drivers import fakes
|
||||
from unittest import mock
|
||||
|
||||
|
||||
SAMPLE_VNFD_ID = "16ca1a07-2453-47f1-9f00-7ca2dce0a5ea"
|
||||
|
||||
|
||||
class TestServerNotification(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestServerNotification, self).setUp()
|
||||
objects.register_all()
|
||||
self.vnfd = vnfd_utils.Vnfd(SAMPLE_VNFD_ID)
|
||||
self.inst = copy.deepcopy(fakes.fault_notif_inst_example)
|
||||
self.csar_dir = None
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_instantiate_rollback_main(
|
||||
self, mock_script, mock_write, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
mock_script.return_value = {
|
||||
"operation": "instantiate_rollback_start",
|
||||
"request": req,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": None,
|
||||
"grant_response": None,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {}
|
||||
mgmt_driver.main()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [{'serverId': 's0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_instantiate_end(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.instantiate_end()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'},
|
||||
{'serverId': 'env-test2', 'alarmId': 'alarm_id'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'a0'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata']['server_notification'])
|
||||
self.assertEqual(
|
||||
{'alarmId': 'alarm_id'},
|
||||
self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1][
|
||||
'metadata']['server_notification'])
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_merge_recent_register(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'},
|
||||
{'serverId': 's1', 'alarmId': 'a1'},
|
||||
{'serverId': 's2'}, {'serverId': 's3', 'alarmId': 'a3'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification._merge_recent_register()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'},
|
||||
{'serverId': 's1', 'alarmId': 'a1'},
|
||||
{'serverId': 's2'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'a0'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata']['server_notification'])
|
||||
self.assertEqual(
|
||||
{'alarmId': 'a1'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][1]['metadata']['server_notification'])
|
||||
self.assertEqual(
|
||||
None, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][2]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_heal_start(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.HealVnfRequest(additionalParams={"all": True})
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s0"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.heal_start()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
None, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_heal_end(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's1', 'alarmId': 'a1'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][0]
|
||||
req = objects.HealVnfRequest(additionalParams={"all": True})
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s1"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.heal_end()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's1', 'alarmId': 'alarm_id'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'alarm_id'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_scale_start(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_IN', aspectId='aspect_id', numberOfSteps=1)
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s0"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.scale_start()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
None, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_scale_end(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_OUT', aspectId='aspect_id', numberOfSteps=1)
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s0"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.scale_end()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'a0'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(conductor_rpc_v2.VnfLcmRpcApiV2,
|
||||
'server_notification_remove_timer')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_terminate_start(
|
||||
self, mock_conf, mock_init, mock_resp, mock_timer):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_OUT', aspectId='aspect_id', numberOfSteps=1)
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s0"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.terminate_start()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
None, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_scale_rollback_start(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][3]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_OUT', aspectId='aspect_id', numberOfSteps=1)
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s0"))])
|
||||
grant = objects.GrantV1()
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, grant_req, grant, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.scale_rollback_start()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'xxx'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata'].get('server_notification'))
|
||||
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_heal_end_main_error(
|
||||
self, mock_script, mock_conf, mock_init, mock_resp, mock_write):
|
||||
user_script_err_handling_data = None
|
||||
resp = webob.Response()
|
||||
resp.status_code = 400
|
||||
mock_resp.return_value = resp, None
|
||||
req = objects.HealVnfRequest(additionalParams={"all": True})
|
||||
grant_req = objects.GrantRequestV1(
|
||||
removeResources=[objects.ResourceDefinitionV1(
|
||||
type='COMPUTE', resourceTemplateId='VDU1',
|
||||
resource=objects.ResourceHandle(
|
||||
resourceId="s1"))])
|
||||
grant = objects.GrantV1()
|
||||
mock_script.return_value = {
|
||||
"operation": "heal_end",
|
||||
"request": req,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": grant_req,
|
||||
"grant_response": grant,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
self.assertRaises(
|
||||
exceptions.MgmtDriverOtherError, mgmt_driver.main)
|
||||
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_instantiate_start(
|
||||
self, mock_script, mock_conf, mock_init, mock_write):
|
||||
user_script_err_handling_data = None
|
||||
mock_script.return_value = {
|
||||
"operation": "instantiate_start",
|
||||
"request": None,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": None,
|
||||
"grant_response": None,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
mgmt_driver.main()
|
||||
self.assertEqual(None, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_terminate_end(
|
||||
self, mock_script, mock_conf, mock_init, mock_write):
|
||||
user_script_err_handling_data = None
|
||||
mock_script.return_value = {
|
||||
"operation": "terminate_end",
|
||||
"request": None,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": None,
|
||||
"grant_response": None,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
mgmt_driver.main()
|
||||
self.assertEqual(None, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_scale_end_type(
|
||||
self, mock_script, mock_conf, mock_init, mock_write):
|
||||
user_script_err_handling_data = None
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_IN', aspectId='aspect_id', numberOfSteps=1)
|
||||
mock_script.return_value = {
|
||||
"operation": "scale_end",
|
||||
"request": req,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": None,
|
||||
"grant_response": None,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
mgmt_driver.main()
|
||||
self.assertEqual(None, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(sys.stdout.buffer, 'write')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
@mock.patch.object(pickle, 'load')
|
||||
def test_scale_start_type(
|
||||
self, mock_script, mock_conf, mock_init, mock_write):
|
||||
user_script_err_handling_data = None
|
||||
req = objects.ScaleVnfRequest(
|
||||
type='SCALE_OUT', aspectId='aspect_id', numberOfSteps=1)
|
||||
mock_script.return_value = {
|
||||
"operation": "scale_start",
|
||||
"request": req,
|
||||
"vnf_instance": self.inst,
|
||||
"grant_request": None,
|
||||
"grant_response": None,
|
||||
"tmp_csar_dir": self.csar_dir,
|
||||
"user_script_err_handling_data": user_script_err_handling_data
|
||||
}
|
||||
mgmt_driver.main()
|
||||
self.assertEqual(None, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_instantiate_end_no_metadata(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'}]}
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][2]
|
||||
del self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1]
|
||||
del self.inst['instantiatedVnfInfo']['metadata']
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.instantiate_end()
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'},
|
||||
{'serverId': 'env-test2', 'alarmId': 'alarm_id'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
self.assertEqual(
|
||||
{'alarmId': 'a0'}, self.inst['instantiatedVnfInfo'][
|
||||
'vnfcResourceInfo'][0]['metadata']['server_notification'])
|
||||
self.assertEqual(
|
||||
{'alarmId': 'alarm_id'},
|
||||
self.inst['instantiatedVnfInfo']['vnfcResourceInfo'][1][
|
||||
'metadata']['server_notification'])
|
||||
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_get_params_none(self, mock_conf, mock_init):
|
||||
user_script_err_handling_data = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'a0'},
|
||||
{'serverId': 's1', 'alarmId': 'a1'},
|
||||
{'serverId': 's2'}, {'serverId': 's3', 'alarmId': 'a3'}]}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
|
||||
del self.inst['vimConnectionInfo']
|
||||
result1 = server_notification.get_params()
|
||||
self.assertEqual((None, None, None), result1)
|
||||
|
||||
del self.inst['instantiatedVnfInfo']
|
||||
result2 = server_notification.get_params()
|
||||
self.assertEqual((None, None, None), result2)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_request_register_no_server_notification(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
vnfc_resource = {'computeResource': {'resourceId': 's0'}}
|
||||
user_script_err_handling_data = {}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification._request_register(vnfc_resource)
|
||||
err_handling_data_expected = {
|
||||
'serverNotification': [
|
||||
{'serverId': 's0', 'alarmId': 'alarm_id'}]}
|
||||
self.assertEqual(
|
||||
err_handling_data_expected, user_script_err_handling_data)
|
||||
|
||||
@mock.patch.object(mgmt_driver.ServerNotificationMgmtDriver,
|
||||
'_request_unregister')
|
||||
@mock.patch.object(mgmt_driver.ServerNotificationMgmtDriver, 'get_params')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_request_register_cancel(
|
||||
self, mock_conf, mock_init, mock_params, mock_unregister):
|
||||
rsc_list = [{
|
||||
'computeResource': {'resourceId': 's0'},
|
||||
'metadata': {'server_notification': {'alarmId': 'a0'}}}]
|
||||
mock_params.return_value = (None, None, None)
|
||||
user_script_err_handling_data = {}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification.request_register_cancel(rsc_list)
|
||||
self.assertEqual(1, mock_unregister.call_count)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_request_unregister_400(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 400
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification._request_unregister('', '', 's0', '')
|
||||
self.assertEqual(1, mock_resp.call_count)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(rpc, 'init')
|
||||
@mock.patch.object(mgmt_driver, 'CONF')
|
||||
def test_request_unregister_no_server_notification(
|
||||
self, mock_conf, mock_init, mock_resp):
|
||||
user_script_err_handling_data = {}
|
||||
req = objects.InstantiateVnfRequest.from_dict(
|
||||
fakes.fault_notif_inst_req_example)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 200
|
||||
mock_resp.return_value = resp, {'alarm_id': 'alarm_id'}
|
||||
server_notification = mgmt_driver.ServerNotificationMgmtDriver(
|
||||
req, self.inst, None, None, self.csar_dir,
|
||||
user_script_err_handling_data)
|
||||
server_notification._request_unregister('', '', 's0', '')
|
||||
self.assertEqual(
|
||||
{'serverNotification': [{'serverId': 's0'}]},
|
||||
user_script_err_handling_data)
|
||||
Reference in New Issue
Block a user