Support for Retry Operation

This feature will enable the client to use
this API to initiate retrying a VNF lifecycle
operation.

It is based on the ETSI NFV specification that
retry can be used when a VNF lifecycle operation
is in a FAILED_TEMP state.

Implements: blueprint support-error-handling
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-error-handling-based-on-ETSI-NFV.html
Change-Id: I96546338fba9946d8df3ffc7ab0003c5b44be85b
changes/48/778948/13
Aldinson Esto 2 years ago
parent 476a52df13
commit 02ddbd6310
  1. 37
      api-ref/source/v1/vnflcm.inc
  2. 4
      releasenotes/notes/errorhandling_fail_retry-31a42c3c7877e0e8.yaml
  3. 249
      tacker/api/vnflcm/v1/controller.py
  4. 6
      tacker/api/vnflcm/v1/router.py
  5. 7
      tacker/common/exceptions.py
  6. 176
      tacker/conductor/conductor_server.py
  7. 14
      tacker/objects/fields.py
  8. 24
      tacker/objects/vnf_lcm_op_occs.py
  9. 11
      tacker/policies/vnf_lcm.py
  10. 30
      tacker/tests/functional/sol/vnflcm/base.py
  11. 222
      tacker/tests/functional/sol/vnflcm/test_vnf_instance_with_user_data.py
  12. 343
      tacker/tests/unit/conductor/test_conductor_server.py
  13. 24
      tacker/tests/unit/vnflcm/fakes.py
  14. 288
      tacker/tests/unit/vnflcm/test_controller.py
  15. 13
      tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py
  16. 836
      tacker/tests/unit/vnflcm/test_vnflcm_driver.py
  17. 16
      tacker/tests/unit/vnfm/infra_drivers/openstack/fixture_data/fixture_data_utils.py
  18. 3
      tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack.py
  19. 163
      tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py
  20. 459
      tacker/vnflcm/vnflcm_driver.py
  21. 6
      tacker/vnfm/infra_drivers/openstack/heat_client.py
  22. 110
      tacker/vnfm/infra_drivers/openstack/openstack.py

@ -1119,3 +1119,40 @@ Response Example
.. literalinclude:: samples/vnflcm/list-subscription-response.json
:language: javascript
Retry
=================
.. rest_method:: POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry
The POST method initiates retrying a VNF lifecycle operation if that
operation has experienced a temporary failure, i.e. the related
"Individual VNF LCM operation occurrence" resource
is in "FAILED_TEMP" state.
In case of success of processing the asynchronous request,
the "operationState" attribute in the representation of the
parent resource shall be changed to "PROCESSING" and
the applicable "start" notification shall be emitted to indicate
that the underlying VNF LCM operation occurrence proceeds.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 202
.. rest_status_code:: error status.yaml
- 401
- 403
- 404
- 409
Request Parameters
------------------
.. rest_parameters:: parameters_vnflcm.yaml
- vnfLcmOpOccId: vnf_lcm_op_occ_id

@ -0,0 +1,4 @@
---
features:
- Add new RESTful APIs for Fail VNF, Retry VNF as part of error handling
operation based on ETSI NFV specifications.

@ -127,6 +127,36 @@ def check_vnf_status(action, status=None):
return outer
def check_vnf_status_and_error_point(action, status=None):
"""Decorator to check vnf status are valid for particular action.
If the vnf has the wrong status with the wrong error point,
it will raise conflict exception.
"""
if status is not None and not isinstance(status, set):
status = set(status)
def outer(f):
@functools.wraps(f)
def inner(self, context, vnf_instance, vnf, *args, **kw):
vnf['current_error_point'] = fields.ErrorPoint.INITIAL
if 'before_error_point' not in vnf:
vnf['before_error_point'] = fields.ErrorPoint.INITIAL
if status is not None and vnf['status'] not in status and \
vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
raise exceptions.VnfConflictStateWithErrorPoint(
uuid=vnf['id'],
state=vnf['status'],
action=action,
error_point=vnf['before_error_point'])
return f(self, context, vnf_instance, vnf, *args, **kw)
return inner
return outer
class VnfLcmController(wsgi.Controller):
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
@ -573,11 +603,13 @@ class VnfLcmController(wsgi.Controller):
@check_vnf_state(action="instantiate",
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
task_state=[None])
@check_vnf_status(action="instantiate",
@check_vnf_status_and_error_point(action="instantiate",
status=[constants.INACTIVE])
def _instantiate(self, context, vnf_instance, vnf, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
vnf_lcm_op_occs_id = vnf.get('vnf_lcm_op_occs_id')
try:
self._validate_flavour_and_inst_level(context, req_body,
vnf_instance)
@ -595,12 +627,16 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.save()
# lcm op process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.INSTANTIATE,
instantiate_vnf_request, request_body)
self.rpc_api.instantiate(context, vnf_instance, vnf,
instantiate_vnf_request, vnf_lcm_op_occs_id)
if vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.INSTANTIATE,
instantiate_vnf_request, request_body)
if vnf_lcm_op_occs_id:
self.rpc_api.instantiate(context, vnf_instance, vnf,
instantiate_vnf_request,
vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
@ -617,9 +653,12 @@ class VnfLcmController(wsgi.Controller):
@check_vnf_state(action="terminate",
instantiation_state=[
fields.VnfInstanceState.INSTANTIATED],
fields.VnfInstanceState.INSTANTIATED,
fields.VnfInstanceState.NOT_INSTANTIATED],
task_state=[None])
def _terminate(self, context, vnf_instance, request_body, vnf):
@check_vnf_status_and_error_point(action="terminate",
status=[constants.ACTIVE])
def _terminate(self, context, vnf_instance, vnf, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
terminate_vnf_req = \
objects.TerminateVnfRequest.obj_from_primitive(
@ -628,14 +667,18 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.task_state = fields.VnfInstanceTaskState.TERMINATING
vnf_instance.save()
vnf_lcm_op_occs_id = vnf.get('vnf_lcm_op_occs_id')
# lcm op process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.TERMINATE,
terminate_vnf_req, request_body)
if vnf['before_error_point'] == fields.ErrorPoint.INITIAL:
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.TERMINATE,
terminate_vnf_req, request_body)
self.rpc_api.terminate(context, vnf_instance, vnf,
terminate_vnf_req, vnf_lcm_op_occs_id)
if vnf_lcm_op_occs_id:
self.rpc_api.terminate(context, vnf_instance, vnf,
terminate_vnf_req, vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
@ -647,12 +690,9 @@ class VnfLcmController(wsgi.Controller):
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._terminate(context, vnf_instance, body, vnf)
self._terminate(context, vnf_instance, vnf, body)
@check_vnf_state(action="heal",
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
task_state=[None])
@check_vnf_status(action="heal",
@check_vnf_status_and_error_point(action="heal",
status=[constants.ACTIVE])
def _heal(self, context, vnf_instance, vnf_dict, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
@ -662,6 +702,8 @@ class VnfLcmController(wsgi.Controller):
vnfc_resource_info.id for vnfc_resource_info in
inst_vnf_info.vnfc_resource_info]
vnf_lcm_op_occs_id = vnf_dict.get('vnf_lcm_op_occs_id')
for vnfc_id in heal_vnf_request.vnfc_instance_id:
# check if vnfc_id exists in vnfc_resource_info
if vnfc_id not in vnfc_resource_info_ids:
@ -675,13 +717,15 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.save()
# call notification process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.HEAL,
heal_vnf_request, request_body)
if vnf_dict['before_error_point'] == fields.ErrorPoint.INITIAL:
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.HEAL,
heal_vnf_request, request_body)
self.rpc_api.heal(context, vnf_instance, vnf_dict, heal_vnf_request,
vnf_lcm_op_occs_id)
if vnf_lcm_op_occs_id:
self.rpc_api.heal(context, vnf_instance, vnf_dict,
heal_vnf_request, vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
@ -693,6 +737,21 @@ class VnfLcmController(wsgi.Controller):
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if vnf_instance.instantiation_state not in \
[fields.VnfInstanceState.INSTANTIATED]:
raise exceptions.VnfInstanceConflictState(
attr='instantiation_state',
uuid=vnf_instance.id,
state=vnf_instance.instantiation_state,
action='heal')
if vnf_instance.task_state not in [None]:
raise exceptions.VnfInstanceConflictState(
attr='task_state',
uuid=vnf_instance.id,
state=vnf_instance.task_state,
action='heal')
self._heal(context, vnf_instance, vnf, body)
@wsgi.response(http_client.OK)
@ -1017,12 +1076,17 @@ class VnfLcmController(wsgi.Controller):
@check_vnf_state(action="scale",
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
task_state=[None])
@check_vnf_status_and_error_point(action="scale",
status=[constants.ACTIVE])
def _scale(self, context, vnf_instance, vnf_info, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
req_body, context=context)
inst_vnf_info = vnf_instance.instantiated_vnf_info
if 'vnf_lcm_op_occs_id' in vnf_info:
vnf_lcm_op_occs_id = vnf_info['vnf_lcm_op_occs_id']
aspect = False
current_level = 0
for scale in inst_vnf_info.scale_status:
@ -1077,27 +1141,47 @@ class VnfLcmController(wsgi.Controller):
if max_level < scale_level:
return self._make_problem_detail(
'can not scale_out', 400, title='can not scale_out')
vnf_lcm_op_occs_id = uuidutils.generate_uuid()
timestamp = datetime.datetime.utcnow()
operation_params = {
'type': scale_vnf_request.type,
'aspect_id': scale_vnf_request.aspect_id,
'number_of_steps': scale_vnf_request.number_of_steps,
'additional_params': scale_vnf_request.additional_params}
vnf_lcm_op_occ = objects.VnfLcmOpOcc(
context=context,
id=vnf_lcm_op_occs_id,
operation_state='STARTING',
state_entered_time=timestamp,
start_time=timestamp,
vnf_instance_id=inst_vnf_info.vnf_instance_id,
operation='SCALE',
is_automatic_invocation=scale_vnf_request.additional_params.get('\
is_auto'),
operation_params=json.dumps(operation_params),
error_point=1)
vnf_lcm_op_occ.create()
if 'vnf_lcm_op_occs_id' in vnf_info:
num = (scaleGroupDict['scaleGroupDict']
[scale_vnf_request.aspect_id]['num'])
default = (scaleGroupDict['scaleGroupDict']
[scale_vnf_request.aspect_id]['default'])
vnf_info['res_num'] = (num *
scale_vnf_request.number_of_steps + default)
if vnf_info['before_error_point'] == fields.ErrorPoint.INITIAL:
vnf_lcm_op_occs_id = uuidutils.generate_uuid()
timestamp = datetime.datetime.utcnow()
operation_params = {
'type': scale_vnf_request.type,
'aspect_id': scale_vnf_request.aspect_id,
'number_of_steps': scale_vnf_request.number_of_steps,
'additional_params': scale_vnf_request.additional_params}
vnf_lcm_op_occ = objects.VnfLcmOpOcc(
context=context,
id=vnf_lcm_op_occs_id,
operation_state='STARTING',
state_entered_time=timestamp,
start_time=timestamp,
vnf_instance_id=inst_vnf_info.vnf_instance_id,
operation='SCALE',
is_automatic_invocation=scale_vnf_request.additional_params.get('\
is_auto'),
operation_params=json.dumps(operation_params),
error_point=1)
vnf_lcm_op_occ.create()
else:
try:
vnf_lcm_op_occ = objects.VnfLcmOpOcc.get_by_id(
context, vnf_lcm_op_occs_id)
except exceptions.NotFound as lcm_e:
return self._make_problem_detail(str(lcm_e),
404, title='Not Found')
except (sqlexc.SQLAlchemyError, Exception) as exc:
LOG.exception(exc)
return self._make_problem_detail(str(exc),
500, title='Internal Server Error')
vnf_instance.task_state = fields.VnfInstanceTaskState.SCALING
vnf_instance.save()
@ -1129,7 +1213,9 @@ class VnfLcmController(wsgi.Controller):
notification['_links']['vnfLcmOpOcc'] = {}
notification['_links']['vnfLcmOpOcc']['href'] = vnflcm_url
vnf_info['notification'] = notification
self.rpc_api.send_notification(context, notification)
if vnf_info['before_error_point'] == fields.ErrorPoint.INITIAL:
self.rpc_api.send_notification(context, notification)
self.rpc_api.scale(context, vnf_info, vnf_instance, scale_vnf_request)
res = webob.Response()
@ -1350,6 +1436,73 @@ class VnfLcmController(wsgi.Controller):
return self._view_builder.show_lcm_op_occs(vnf_lcm_op_occs)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
http_client.NOT_FOUND, http_client.CONFLICT))
def retry(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'retry')
try:
vnf_lcm_op_occs = objects.VnfLcmOpOcc.get_by_id(context, id)
except exceptions.NotFound as lcm_e:
return self._make_problem_detail(str(lcm_e),
404, title='Not Found')
except (sqlexc.SQLAlchemyError, Exception) as exc:
LOG.exception(exc)
return self._make_problem_detail(str(exc),
500, title='Internal Server Error')
# operation state checking
if vnf_lcm_op_occs.operation_state != \
fields.LcmOccsOperationState.FAILED_TEMP:
error_msg = ('Cannot proceed with operation_state %s'
% vnf_lcm_op_occs.operation_state)
return self._make_problem_detail(error_msg,
409, title='Conflict')
# get vnf
try:
vnf = self._get_vnf(context, vnf_lcm_op_occs.vnf_instance_id)
except webob.exc.HTTPNotFound as lcm_e:
return self._make_problem_detail(str(lcm_e),
404, title='Not Found')
except Exception as exc:
LOG.exception(exc)
return self._make_problem_detail(str(exc),
500, title='Internal Server Error')
# get vnf instance
try:
vnf_instance = objects.VnfInstance.get_by_id(
context, vnf_lcm_op_occs.vnf_instance_id)
except exceptions.VnfInstanceNotFound:
msg = (_("Can not find requested vnf instance: %s")
% vnf_lcm_op_occs.vnf_instance_id)
return self._make_problem_detail(msg,
404, title='Not Found')
except Exception as exc:
LOG.exception(exc)
return self._make_problem_detail(str(exc),
500, title='Internal Server Error')
operation = vnf_lcm_op_occs.operation
body = jsonutils.loads(vnf_lcm_op_occs.operation_params)
vnf['before_error_point'] = vnf_lcm_op_occs.error_point
vnf['vnf_lcm_op_occs_id'] = id
if operation == fields.LcmOccsOperationType.INSTANTIATE:
self._instantiate(context, vnf_instance, vnf, body)
elif operation == fields.LcmOccsOperationType.TERMINATE:
self._terminate(context, vnf_instance, vnf, body)
elif operation == fields.LcmOccsOperationType.HEAL:
self._heal(context, vnf_instance, vnf, body)
elif operation == fields.LcmOccsOperationType.SCALE:
self._scale(context, vnf_instance, vnf, body)
else:
error_msg = 'Operation type %s is inavalid' % operation
return self._make_problem_detail(error_msg,
500, title='Internal Server Error')
def _make_problem_detail(
self,
detail,

@ -125,6 +125,12 @@ class VnflcmAPIRouter(wsgi.Router):
"/vnf_lcm_op_occs/{id}/fail",
methods, controller, default_resource)
# {apiRoot}/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry resource
methods = {"POST": "retry"}
self._setup_route(mapper,
"/vnf_lcm_op_occs/{id}/retry",
methods, controller, default_resource)
methods = {"GET": "subscription_list", "POST": "register_subscription"}
self._setup_route(mapper, "/subscriptions",
methods, controller, default_resource)

@ -406,3 +406,10 @@ class MgmtDriverRemoteCommandTimeOut(TackerException):
class MgmtDriverOtherError(TackerException):
message = _('An error occurred in MgmtDriver: %(error_message)s.')
class VnfConflictStateWithErrorPoint(Conflict):
message = _("Vnf %(uuid)s in status %(state)s. "
"Error point %(error_point)s. "
"Cannot %(action)s while the vnf is in this state "
"with this error point.")

@ -103,12 +103,15 @@ cfg.CONF.register_opts(OPTS, 'keystone_authtoken')
LOG = logging.getLogger(__name__)
_INACTIVE_STATUS = ('INACTIVE')
_ACTIVE_STATUS = ('ACTIVE')
_INACTIVE_STATUS = ('INACTIVE',)
_ACTIVE_STATUS = ('ACTIVE',)
_PENDING_STATUS = ('PENDING_CREATE',
'PENDING_TERMINATE',
'PENDING_DELETE',
'PENDING_HEAL')
_ERROR_STATUS = ('ERROR',)
_ALL_STATUSES = _ACTIVE_STATUS + _INACTIVE_STATUS + _PENDING_STATUS + \
_ERROR_STATUS
def _delete_csar(context, vnf_package):
@ -1559,13 +1562,12 @@ class Conductor(manager.Manager):
instantiate_vnf,
vnf_lcm_op_occs_id):
vnf_dict['error_point'] = 1
self._instantiate_grant(context,
vnf_instance,
vnf_dict,
instantiate_vnf,
vnf_lcm_op_occs_id)
if vnf_dict['before_error_point'] == fields.ErrorPoint.INITIAL:
self._instantiate_grant(context,
vnf_instance,
vnf_dict,
instantiate_vnf,
vnf_lcm_op_occs_id)
try:
# Update vnf_lcm_op_occs table and send notification "PROCESSING"
@ -1576,24 +1578,32 @@ class Conductor(manager.Manager):
vnf_instance=vnf_instance,
request_obj=instantiate_vnf
)
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_PROCESSING
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.NOTIFY_PROCESSING:
# change vnf_status
if vnf_dict['status'] == 'INACTIVE':
vnf_dict['status'] = 'PENDING_CREATE'
self._change_vnf_status(context, vnf_instance.id,
_INACTIVE_STATUS, 'PENDING_CREATE')
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.VNF_CONFIG_END:
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
vnf_dict, instantiate_vnf)
self._build_instantiated_vnf_info(context,
vnf_instance,
instantiate_vnf_req=instantiate_vnf)
self._update_vnf_attributes(context, vnf_instance, vnf_dict,
_PENDING_STATUS, _ACTIVE_STATUS)
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_COMPLETED
# change vnf_status
if vnf_dict['status'] == 'INACTIVE':
vnf_dict['status'] = 'PENDING_CREATE'
self._change_vnf_status(context, vnf_instance.id,
_INACTIVE_STATUS, 'PENDING_CREATE')
vnf_dict['error_point'] = 3
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
vnf_dict, instantiate_vnf)
vnf_dict['error_point'] = 5
self._build_instantiated_vnf_info(context,
vnf_instance,
instantiate_vnf_req=instantiate_vnf)
vnf_dict['error_point'] = 7
self._update_vnf_attributes(context, vnf_instance, vnf_dict,
_PENDING_STATUS, _ACTIVE_STATUS)
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
instantiation_state=fields.VnfInstanceState.
INSTANTIATED, task_state=None)
@ -1611,8 +1621,14 @@ class Conductor(manager.Manager):
)
except Exception as ex:
if vnf_dict['current_error_point'] == \
fields.ErrorPoint.PRE_VIM_CONTROL:
if hasattr(vnf_instance.instantiated_vnf_info, 'instance_id'):
if vnf_instance.instantiated_vnf_info.instance_id:
vnf_dict['current_error_point'] = \
fields.ErrorPoint.POST_VIM_CONTROL
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'ERROR')
_ALL_STATUSES, 'ERROR')
self._build_instantiated_vnf_info(context, vnf_instance,
instantiate_vnf)
@ -1628,16 +1644,17 @@ class Conductor(manager.Manager):
request_obj=instantiate_vnf,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(ex),
error_point=vnf_dict['error_point']
error_point=vnf_dict['current_error_point']
)
@coordination.synchronized('{vnf_instance[id]}')
def terminate(self, context, vnf_lcm_op_occs_id,
vnf_instance, terminate_vnf_req, vnf_dict):
self._terminate_grant(context,
vnf_instance,
vnf_lcm_op_occs_id)
if vnf_dict['before_error_point'] == fields.ErrorPoint.INITIAL:
self._terminate_grant(context,
vnf_instance,
vnf_lcm_op_occs_id)
try:
old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1652,16 +1669,26 @@ class Conductor(manager.Manager):
operation=fields.LcmOccsOperationType.TERMINATE
)
self._change_vnf_status(context, vnf_instance.id,
_ACTIVE_STATUS, 'PENDING_TERMINATE')
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_PROCESSING
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req)
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.NOTIFY_PROCESSING:
self._change_vnf_status(context, vnf_instance.id,
_ACTIVE_STATUS, 'PENDING_TERMINATE')
self._delete_placement(context, vnf_instance.id)
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.VNF_CONFIG_END:
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req, vnf_dict)
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'INACTIVE')
self._delete_placement(context, vnf_instance.id)
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'INACTIVE')
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_COMPLETED
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
vim_connection_info=[], task_state=None,
@ -1682,7 +1709,7 @@ class Conductor(manager.Manager):
except Exception as exc:
# set vnf_status to error
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, 'ERROR')
_ALL_STATUSES, 'ERROR')
# Update vnf_lcm_op_occs table and send notification "FAILED_TEMP"
self._send_lcm_op_occ_notification(
@ -1693,7 +1720,8 @@ class Conductor(manager.Manager):
request_obj=terminate_vnf_req,
operation=fields.LcmOccsOperationType.TERMINATE,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(exc)
error=str(exc),
error_point=vnf_dict['current_error_point']
)
@coordination.synchronized('{vnf_instance[id]}')
@ -1704,11 +1732,12 @@ class Conductor(manager.Manager):
heal_vnf_request,
vnf_lcm_op_occs_id):
self._heal_grant(context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id)
if vnf_dict['before_error_point'] == fields.ErrorPoint.INITIAL:
self._heal_grant(context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id)
try:
old_vnf_instance = copy.deepcopy(vnf_instance)
@ -1723,19 +1752,32 @@ class Conductor(manager.Manager):
operation=fields.LcmOccsOperationType.HEAL
)
# update vnf status to PENDING_HEAL
self._change_vnf_status(context, vnf_instance.id,
_ACTIVE_STATUS, constants.PENDING_HEAL)
self.vnflcm_driver.heal_vnf(context, vnf_instance,
vnf_dict, heal_vnf_request)
self._update_instantiated_vnf_info(context, vnf_instance,
heal_vnf_request)
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_PROCESSING
# update instance_in in vnf_table
self._add_additional_vnf_info(context, vnf_instance)
# update vnf status to ACTIVE
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, constants.ACTIVE)
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.NOTIFY_PROCESSING:
# update vnf status to PENDING_HEAL
self._change_vnf_status(context, vnf_instance.id,
_ACTIVE_STATUS, constants.PENDING_HEAL)
if vnf_dict['before_error_point'] <= \
fields.ErrorPoint.VNF_CONFIG_END:
self.vnflcm_driver.heal_vnf(context, vnf_instance,
vnf_dict, heal_vnf_request)
self._update_instantiated_vnf_info(context, vnf_instance,
heal_vnf_request)
# update instance_in in vnf_table
self._add_additional_vnf_info(context, vnf_instance)
# update vnf status to ACTIVE
self._change_vnf_status(context, vnf_instance.id,
_PENDING_STATUS, constants.ACTIVE)
vnf_dict['current_error_point'] = \
fields.ErrorPoint.NOTIFY_COMPLETED
# during .save() ,instantiated_vnf_info is also saved to DB
self.vnflcm_driver._vnf_instance_update(context, vnf_instance,
@ -1755,8 +1797,8 @@ class Conductor(manager.Manager):
)
except Exception as ex:
# update vnf_status to 'ERROR' and create event with 'ERROR' status
self._change_vnf_status(context, vnf_instance,
_PENDING_STATUS, constants.ERROR, str(ex))
self._change_vnf_status(context, vnf_instance.id,
_ALL_STATUSES, constants.ERROR, str(ex))
# call _update_instantiated_vnf_info for notification
self._update_instantiated_vnf_info(context, vnf_instance,
@ -1771,7 +1813,8 @@ class Conductor(manager.Manager):
request_obj=heal_vnf_request,
operation=fields.LcmOccsOperationType.HEAL,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(ex)
error=str(ex),
error_point=vnf_dict['current_error_point']
)
@coordination.synchronized('{vnf_instance[id]}')
@ -1779,12 +1822,13 @@ class Conductor(manager.Manager):
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ_id = vnf_lcm_op_occ.id
self._scale_grant(
context,
vnf_info,
vnf_instance,
scale_vnf_request,
vnf_lcm_op_occ_id)
if vnf_info['before_error_point'] == fields.ErrorPoint.INITIAL:
self._scale_grant(
context,
vnf_info,
vnf_instance,
scale_vnf_request,
vnf_lcm_op_occ_id)
self.vnflcm_driver.scale_vnf(
context, vnf_info, vnf_instance, scale_vnf_request)

@ -233,8 +233,9 @@ class LcmOccsOperationType(BaseTackerEnum):
INSTANTIATE = 'INSTANTIATE'
TERMINATE = 'TERMINATE'
HEAL = 'HEAL'
SCALE = 'SCALE'
ALL = (INSTANTIATE, TERMINATE, HEAL)
ALL = (INSTANTIATE, TERMINATE, HEAL, SCALE)
class LcmOccsNotificationStatus(BaseTackerEnum):
@ -269,3 +270,14 @@ class VnfStatus(BaseTackerEnum):
class InstanceOperation(BaseTackerEnum):
MODIFY_INFO = 'MODIFY_INFO'
class ErrorPoint(BaseTackerEnum):
INITIAL = 0
NOTIFY_PROCESSING = 1
VNF_CONFIG_START = 2
PRE_VIM_CONTROL = 3
POST_VIM_CONTROL = 4
INTERNAL_PROCESSING = 5
VNF_CONFIG_END = 6
NOTIFY_COMPLETED = 7

@ -76,6 +76,21 @@ def _vnf_lcm_op_occs_get_by_id(context, vnf_lcm_op_occ_id):
return result
@db_api.context_manager.reader
def _vnf_lcm_op_occs_get_by_vnf_instance_id(context, vnf_instance_id):
query = api.model_query(context, models.VnfLcmOpOccs,
read_deleted="no", project_only=True). \
filter_by(vnf_instance_id=vnf_instance_id)
result = query.first()
if not result:
raise exceptions.VnfInstanceNotFound(id=vnf_instance_id)
return result
@db_api.context_manager.reader
def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None):
@ -287,6 +302,12 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
db_vnf_lcm_op_occs = _vnf_lcm_op_occs_get_by_id(context, id)
return cls._from_db_object(context, cls(), db_vnf_lcm_op_occs)
@base.remotable_classmethod
def get_by_vnf_instance_id(cls, context, id):
db_vnf_lcm_op_occs = _vnf_lcm_op_occs_get_by_vnf_instance_id(
context, id)
return cls._from_db_object(context, cls(), db_vnf_lcm_op_occs)
@base.TackerObjectRegistry.register
class ResourceChanges(base.TackerObject,
@ -618,6 +639,9 @@ class VnfInfoModifications(base.TackerObject,
VnfInfoModifications, cls).obj_from_primitive(
primitive, context)
else:
if isinstance(primitive, str):
primitive = jsonutils.loads(primitive)
if 'vim_connection_info' in primitive.keys():
obj_data = [objects.VimConnectionInfo._from_dict(
vim_conn) for vim_conn in primitive.get(

@ -154,6 +154,17 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=VNFLCM % 'retry',
check_str=base.RULE_ADMIN_OR_OWNER,
description="Retry a VNF instance.",
operations=[
{
'method': 'POST',
'path': '/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry'
}
]
),
]

@ -469,6 +469,15 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return resp, response_body
def _retry_op_occs(self, vnf_lcm_op_occs_id):
retry_url = os.path.join(
self.base_vnf_lcm_op_occs_url,
vnf_lcm_op_occs_id, 'retry')
resp, response_body = self.http_client.do_request(
retry_url, "POST")
return resp, response_body
def _show_op_occs(self, vnf_lcm_op_occs_id):
show_url = os.path.join(
self.base_vnf_lcm_op_occs_url,
@ -1023,6 +1032,27 @@ class BaseVnfLcmTest(base.BaseTackerTest):
'VnfLcmOperationOccurrenceNotification',
'FAILED')
def assert_retry_vnf(self, resp, vnf_instance_id):
self.assertEqual(202, resp.status_code)
# FT-checkpoint: Notification
callback_url = os.path.join(
MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
notify_mock_responses = self._filter_notify_history(callback_url,
vnf_instance_id)
self.assertEqual(2, len(notify_mock_responses))
self.assert_notification_mock_response(
notify_mock_responses[0],
'VnfLcmOperationOccurrenceNotification',
'PROCESSING')
self.assert_notification_mock_response(
notify_mock_responses[1],
'VnfLcmOperationOccurrenceNotification',
'FAILED_TEMP')
def assert_update_vnf(
self,
resp,

@ -766,6 +766,228 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_retry_instantiate(self):
"""Test retry operation for instantiation.
In this test case, we do following steps.
- Create subscription.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF(Will fail).
- Get vnflcmOpOccId to retry.
- Retry instantiation operation.
- Get opOccs information.
- Delete subscription.
"""
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(self._delete_subscription, subscription_id)
# Pre Setting: Create vnf package.
sample_name = 'functional3'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Post Setting: Reserve deleting vnf package.
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id))
vnf_instance_id = vnf_instance['id']
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_create_vnf(resp, vnf_instance, vnf_package_id)
self.addCleanup(self._delete_vnf_instance, vnf_instance_id)
# Failed instantiate VNF
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks,
self.ext_link_ports, self.ext_subnets)
resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('FAILED_TEMP', vnf_instance_id=vnf_instance_id)
callback_url = os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
notify_mock_responses = vnflcm_base.FAKE_SERVER_MANAGER.get_history(
callback_url)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
callback_url)
# get vnflcm_op_occ_id
vnflcm_op_occ_id = notify_mock_responses[0].request_body.get(
'vnfLcmOpOccId')
self.assertIsNotNone(vnflcm_op_occ_id)
# retry
resp, _ = self._retry_op_occs(vnflcm_op_occ_id)
self._wait_lcm_done('FAILED_TEMP', vnf_instance_id=vnf_instance_id)
self.assert_retry_vnf(resp, vnf_instance_id)
# rollback (Execute because it's needed to delete VNF)
resp, _ = self._rollback_op_occs(vnflcm_op_occ_id)
self._wait_lcm_done('ROLLING_BACK', vnf_instance_id=vnf_instance_id)
self._wait_lcm_done('ROLLED_BACK', vnf_instance_id=vnf_instance_id)
self.assert_rollback_vnf(resp, vnf_instance_id)
# occ-show
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id, vnf_package_id)
# Subscription delete
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
def test_retry_scale_out(self):
"""Test retry operation for Scale-Out operation.
In this test case, we do following steps.
- Create subscription.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF.
- Scale-Out(Will fail).
- Get vnfcmOpOccId to retry.
- Retry Scale-Out operation.
- Get opOccs information.
- Terminate VNF.
- Delete subscription.
"""
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(self._delete_subscription, subscription_id)
# Pre Setting: Create vnf package.
sample_name = 'functional4'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Post Setting: Reserve deleting vnf package.
self.addCleanup(
vnflcm_base._delete_vnf_package,
self.tacker_client,
vnf_package_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id))
vnf_instance_id = vnf_instance['id']
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_create_vnf(resp, vnf_instance, vnf_package_id)
self.addCleanup(self._delete_vnf_instance, vnf_instance_id)
# instantiate VNF
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks,
self.ext_link_ports, self.ext_subnets)
self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName))
# Fail Scale-out vnf instance
request_body = fake_vnflcm.VnfInstances.make_scale_request_body(
'SCALE_OUT')
resp, _ = self._scale_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('FAILED_TEMP', vnf_instance_id=vnf_instance_id)
callback_url = os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
notify_mock_responses = vnflcm_base.FAKE_SERVER_MANAGER.get_history(
callback_url)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
callback_url)
# get vnflcm_op_occ_id
vnflcm_op_occ_id = notify_mock_responses[0].request_body.get(
'vnfLcmOpOccId')
self.assertIsNotNone(vnflcm_op_occ_id)
# retry
resp, _ = self._retry_op_occs(vnflcm_op_occ_id)
self._wait_lcm_done('FAILED_TEMP', vnf_instance_id=vnf_instance_id)
self.assert_retry_vnf(resp, vnf_instance_id)
# rollback (Execute because it's needed to delete VNF)
resp, _ = self._rollback_op_occs(vnflcm_op_occ_id)
self._wait_lcm_done('ROLLING_BACK', vnf_instance_id=vnf_instance_id)
self._wait_lcm_done('ROLLED_BACK', vnf_instance_id=vnf_instance_id)
self.assert_rollback_vnf(resp, vnf_instance_id)
# occ-show
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
resource_name_list = [r.resource_name for r in resources_list]
glance_image_id_list = self._get_glance_image_list_from_stack_resource(
stack.id,
resource_name_list)
terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body()
resp, _ = self._terminate_vnf_instance(vnf_instance_id,
terminate_req_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_terminate_vnf(resp, vnf_instance_id, stack.id,
resource_name_list, glance_image_id_list, vnf_package_id)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id, vnf_package_id)
# Subscription delete
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
def test_rollback_instantiate(self):
"""Test rollback operation for instantiation.

@ -344,6 +344,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
vnfd_yaml = vnf_dict['vnfd']['attributes'].get(vnfd_key, '')
mock_vnfd_dict.return_value = yaml.safe_load(vnfd_yaml)
@ -387,6 +388,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c'
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
@ -471,6 +473,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c'
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
@ -549,6 +552,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
vnf_lcm_op_occs_id = 'a9c36d21-21aa-4692-8922-7999bbcae08c'
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
@ -679,6 +683,7 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
@ -696,6 +701,117 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
vnf_instance.id, mock.ANY, 'ERROR')
mock_update_vnf_attributes.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._build_instantiated_vnf_info')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.utils._convert_desired_capacity')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance_error_point_notify_processing(
self, mock_vnf_by_id, mock_des, mock_vnfd_dict, mock_get_lock,
mock_save, mock_build_info, mock_change_vnf_status,
mock_update_vnf_attributes):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
**lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.NOTIFY_PROCESSING
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
vnfd_yaml = vnf_dict['vnfd']['attributes'].get(vnfd_key, '')
mock_vnfd_dict.return_value = yaml.safe_load(vnfd_yaml)
self.conductor.instantiate(self.context, vnf_instance, vnf_dict,
instantiate_vnf_req, vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_called_once_with(
self.context, mock.ANY, vnf_dict, instantiate_vnf_req)
self.vnflcm_driver._vnf_instance_update.assert_called_once()
mock_change_vnf_status. \
assert_called_once_with(self.context, vnf_instance.id,
mock.ANY, 'PENDING_CREATE')
mock_update_vnf_attributes.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._build_instantiated_vnf_info')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.utils._convert_desired_capacity')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance_error_point_vnf_config_end(
self, mock_vnf_by_id, mock_des, mock_vnfd_dict, mock_get_lock,
mock_save, mock_build_info, mock_update_vnf_attributes):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
**lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.VNF_CONFIG_END
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
vnfd_yaml = vnf_dict['vnfd']['attributes'].get(vnfd_key, '')
mock_vnfd_dict.return_value = yaml.safe_load(vnfd_yaml)
self.conductor.instantiate(self.context, vnf_instance, vnf_dict,
instantiate_vnf_req, vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_called_once_with(
self.context, vnf_instance, vnf_dict, instantiate_vnf_req)
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.utils._convert_desired_capacity')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance_error_point_notify_completed(
self, mock_vnf_by_id, mock_des, mock_vnfd_dict,
mock_get_lock, mock_save):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = \
objects.VnfLcmOpOcc(context=self.context,
**lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf_etsi(instance_id=self.instance_uuid,
flavour=instantiate_vnf_req.flavour_id)
vnf_dict['before_error_point'] = fields.ErrorPoint.NOTIFY_COMPLETED
vnfd_key = 'vnfd_' + instantiate_vnf_req.flavour_id
vnfd_yaml = vnf_dict['vnfd']['attributes'].get(vnfd_key, '')
mock_vnfd_dict.return_value = yaml.safe_load(vnfd_yaml)
self.conductor.instantiate(self.context, vnf_instance, vnf_dict,
instantiate_vnf_req, vnf_lcm_op_occs_id)
self.vnflcm_driver._vnf_instance_update.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
@ -713,15 +829,98 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
additional_params={"key": "value"})
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
vnf_dict['before_error_point'] = fields.ErrorPoint.INITIAL
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance, terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_called_once_with(
self.context, vnf_instance, terminate_vnf_req, vnf_dict)
self.vnflcm_driver._vnf_instance_update.assert_called_once()
self.assertEqual(mock_send_notification.call_count, 2)
self.assertEqual(mock_change_vnf_status.call_count, 2)
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._send_lcm_op_occ_notification')