tacker/tacker/vnflcm/vnflcm_driver.py
Hiromu Asahina 2d9521b2bd Fix scaling during the heat cooldown
When scaling occurs during the heat cooldown, VNF is not actually
scaled but the scale_level value in the VNF information will change.
This bug happens because Heat does not return any error responses when
it cannot perform scaling action due to the cooldown, and thus Tacker
cannot detect a scaling failure.

To fix this bug, this patch changes the ``scale`` method of OpenStack
infra-driver to wait for scaling until cooldown ends.

Closes-Bug: #1925119
Change-Id: Ieca345d7e46e03756d34f7d00b37ebc9c25d8d8b
Signed-off-by: Hiromu Asahina <hiromu.asahina.az@hco.ntt.co.jp>
2021-09-26 14:17:34 +00:00

1855 lines
86 KiB
Python

# Copyright (C) 2020 NTT DATA
# 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
from datetime import datetime
import functools
import hashlib
import inspect
import os
import re
import traceback
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import excutils
from toscaparser import tosca_template
from tacker.common import driver_manager
from tacker.common import exceptions
from tacker.common import log
from tacker.common import safe_utils
from tacker.common import utils
from tacker.conductor.conductorrpc import vnf_lcm_rpc
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.objects.fields import ErrorPoint as EP
from tacker.vnflcm import abstract_driver
from tacker.vnflcm import utils as vnflcm_utils
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
DEFAULT_VNFLCM_MGMT_DRIVER = "vnflcm_noop"
@utils.expects_func_args('vnf_info', 'vnf_instance', 'scale_vnf_request')
def revert_to_error_scale(function):
"""Decorator to revert task_state to error on failure."""
@functools.wraps(function)
def decorated_function(self, context, *args, **kwargs):
try:
return function(self, context, *args, **kwargs)
except Exception as ex:
with excutils.save_and_reraise_exception():
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = inspect.getcallargs(wrapped_func, self, context,
*args, **kwargs)
try:
vnf_info = keyed_args['vnf_info']
vnf_instance = keyed_args['vnf_instance']
scale_vnf_request = keyed_args['scale_vnf_request']
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = \
objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
if vnf_info.get('resource_changes'):
resource_changes = vnf_info.get('resource_changes')
else:
resource_changes = self._scale_resource_update(context,
vnf_info,
vnf_instance,
scale_vnf_request,
vim_connection_info,
error=True)
except Exception as e:
LOG.warning(traceback.format_exc())
LOG.warning("Failed to scale resource update "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
self._vnfm_plugin._update_vnf_scaling_status_err(context,
vnf_info)
except Exception as e:
LOG.warning("Failed to revert scale info for event "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
vnf_instance.task_state = None
self._vnf_instance_update(context, vnf_instance)
except Exception as e:
LOG.warning("Failed to revert instantiation info for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
problem = objects.ProblemDetails(status=500,
detail=str(ex))
try:
timestamp = datetime.utcnow()
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ.operation_state = 'FAILED_TEMP'
vnf_lcm_op_occ.state_entered_time = timestamp
vnf_lcm_op_occ.resource_changes = resource_changes
vnf_lcm_op_occ.error = problem
vnf_lcm_op_occ.error_point = \
vnf_info['current_error_point']
vnf_lcm_op_occ.save()
except Exception as e:
LOG.warning("Failed to update vnf_lcm_op_occ for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
notification = vnf_info['notification']
notification['notificationStatus'] = 'RESULT'
notification['operationState'] = 'FAILED_TEMP'
notification['error'] = problem.to_dict()
resource_dict = resource_changes.to_dict()
if resource_dict.get('affected_vnfcs'):
notification['affectedVnfcs'] =\
jsonutils.dump_as_bytes(
resource_dict.get('affected_vnfcs'))
if resource_dict.get('affected_virtual_links'):
notification['affectedVirtualLinks'] =\
jsonutils.dump_as_bytes(
resource_dict.get('affected_virtual_links'))
if resource_dict.get('affected_virtual_storages'):
notification['affectedVirtualStorages'] =\
jsonutils.dump_as_bytes(
resource_dict.get('affected_virtual_storages'))
self.rpc_api.send_notification(context, notification)
except Exception as e:
LOG.warning("Failed to revert scale info for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
return decorated_function
@utils.expects_func_args('vnf_instance')
def revert_to_error_task_state(function):
"""Decorator to revert task_state to error on failure."""
@functools.wraps(function)
def decorated_function(self, context, *args, **kwargs):
try:
return function(self, context, *args, **kwargs)
except Exception:
with excutils.save_and_reraise_exception():
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = inspect.getcallargs(wrapped_func, self, context,
*args, **kwargs)
vnf_instance = keyed_args['vnf_instance']
previous_task_state = vnf_instance.task_state
try:
self._vnf_instance_update(context, vnf_instance,
task_state=fields.VnfInstanceTaskState.ERROR)
LOG.info("Successfully reverted task state from "
"%(state)s to %(error)s on failure for vnf "
"instance %(id)s.",
{"state": previous_task_state,
"id": vnf_instance.id,
"error": fields.VnfInstanceTaskState.ERROR})
except Exception as e:
LOG.warning("Failed to revert task state for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
return decorated_function
@utils.expects_func_args('vnf_info', 'vnf_instance', 'operation_params')
def revert_to_error_rollback(function):
"""Decorator to revert task_state to error on failure."""
@functools.wraps(function)
def decorated_function(self, context, *args, **kwargs):
try:
return function(self, context, *args, **kwargs)
except Exception as ex:
with excutils.save_and_reraise_exception():
wrapped_func = safe_utils.get_wrapped_function(function)
keyed_args = inspect.getcallargs(wrapped_func, self, context,
*args, **kwargs)
resource_changes = None
try:
vnf_info = keyed_args['vnf_info']
vnf_instance = keyed_args['vnf_instance']
operation_params = keyed_args['operation_params']
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info =\
objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
if vnf_info.get('resource_changes'):
resource_changes = vnf_info.get('resource_changes')
else:
if vnf_lcm_op_occs.operation == 'SCALE':
scale_vnf_request =\
objects.ScaleVnfRequest.obj_from_primitive(
operation_params, context=context)
scale_vnf_request_copy = \
copy.deepcopy(scale_vnf_request)
scale_vnf_request_copy.type = 'SCALE_IN'
resource_changes = self._scale_resource_update(
context,
vnf_info,
vnf_instance,
scale_vnf_request_copy,
vim_connection_info,
error=True)
else:
resource_changes = self._term_resource_update(
context,
vnf_info,
vnf_instance)
except Exception as e:
LOG.warning(traceback.format_exc())
LOG.warning("Failed to scale resource update "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
self._update_vnf_rollback_status_err(context, vnf_info)
except Exception as e:
LOG.warning("Failed to revert scale info for event "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
self._vnf_instance_update(context, vnf_instance)
except Exception as e:
LOG.warning("Failed to revert instantiation info for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
problem = objects.ProblemDetails(status=500,
detail=str(ex))
try:
timestamp = datetime.utcnow()
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ.operation_state = 'FAILED_TEMP'
vnf_lcm_op_occ.state_entered_time = timestamp
if resource_changes:
vnf_lcm_op_occ.resource_changes = resource_changes
vnf_lcm_op_occ.error = problem
vnf_lcm_op_occ.save()
except Exception as e:
LOG.warning("Failed to update vnf_lcm_op_occ for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
try:
notification = vnf_info['notification']
notification['notificationStatus'] = 'RESULT'
notification['operationState'] = 'FAILED_TEMP'
notification['error'] = problem.to_dict()
if resource_changes:
resource_dict = resource_changes.to_dict()
if resource_dict.get('affected_vnfcs'):
notification['affectedVnfcs'] = \
jsonutils.dump_as_bytes(
resource_dict.get('affected_vnfcs'))
if resource_dict.get('affected_virtual_links'):
notification['affectedVirtualLinks'] = \
jsonutils.dump_as_bytes(
resource_dict.get(
'affected_virtual_links'))
if resource_dict.get('affected_virtual_storages'):
notification['affectedVirtualStorages'] = \
jsonutils.dump_as_bytes(
resource_dict.get(
'affected_virtual_storages'))
self.rpc_api.send_notification(context, notification)
except Exception as e:
LOG.warning("Failed to revert scale info for vnf "
"instance %(id)s. Error: %(error)s",
{"id": vnf_instance.id, "error": e})
return decorated_function
def config_opts():
return [('tacker', VnfLcmDriver.OPTS)]
class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
OPTS = [
cfg.ListOpt(
'vnflcm_infra_driver', default=['openstack', 'kubernetes'],
help=_('Hosting vnf drivers tacker plugin will use')
),
cfg.ListOpt(
'vnflcm_mgmt_driver', default=[DEFAULT_VNFLCM_MGMT_DRIVER],
help=_('MGMT driver to communicate with '
'Hosting VNF/logical service '
'instance tacker plugin will use')
)
]
cfg.CONF.register_opts(OPTS, 'tacker')
def __init__(self):
super(VnfLcmDriver, self).__init__()
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
self._vnf_manager = driver_manager.DriverManager(
'tacker.tacker.vnfm.drivers',
cfg.CONF.tacker.vnflcm_infra_driver)
self._mgmt_manager = driver_manager.DriverManager(
'tacker.tacker.mgmt.drivers', cfg.CONF.tacker.vnflcm_mgmt_driver)
self._mgmt_driver_hash = self._init_mgmt_driver_hash()
def _init_mgmt_driver_hash(self):
driver_hash = {}
for mgmt_driver in cfg.CONF.tacker.vnflcm_mgmt_driver:
path = inspect.getfile(self._mgmt_manager[mgmt_driver].__class__)
driver_hash[mgmt_driver] = self._get_file_hash(path)
return driver_hash
def _vnf_instance_update(self, context, vnf_instance, **kwargs):
"""Update vnf instance in the database using kwargs as value."""
for k, v in kwargs.items():
setattr(vnf_instance, k, v)
vnf_instance.save()
def _instantiate_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_req):
vnfd_dict = vnflcm_utils._get_vnfd_dict(context, vnf_instance.vnfd_id,
instantiate_vnf_req.flavour_id)
vnf_package_path = vnflcm_utils._get_vnf_package_path(
context, vnf_instance.vnfd_id)
param_for_subs_map = vnflcm_utils._get_param_data(vnfd_dict,
instantiate_vnf_req)
package_uuid = vnflcm_utils._get_vnf_package_id(context,
vnf_instance.vnfd_id)
vnf_software_images = vnflcm_utils._create_grant_request(vnfd_dict,
package_uuid)
vnf_resources = self._vnf_manager.invoke(
vim_connection_info.vim_type, 'pre_instantiation_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
vnf_software_images=vnf_software_images,
instantiate_vnf_req=instantiate_vnf_req,
vnf_package_path=vnf_package_path)
# save the vnf resources in the db
for _, resources in vnf_resources.items():
for vnf_resource in resources:
vnf_resource.create()
vnfd_dict_to_create_final_dict = copy.deepcopy(vnfd_dict)
final_vnf_dict = vnflcm_utils._make_final_vnf_dict(
vnfd_dict_to_create_final_dict, vnf_instance.id,
vnf_instance.vnf_instance_name, param_for_subs_map, vnf_dict)
final_vnf_dict['before_error_point'] = \
vnf_dict['before_error_point']
try:
instance_id = self._vnf_manager.invoke(
vim_connection_info.vim_type, 'instantiate_vnf',
context=context, plugin=self._vnfm_plugin,
vnf_instance=vnf_instance,
vnfd_dict=final_vnf_dict, grant_response=vnf_resources,
vim_connection_info=vim_connection_info,
vnf_package_path=vnf_package_path,
instantiate_vnf_req=instantiate_vnf_req)
except Exception as exp:
with excutils.save_and_reraise_exception():
exp.reraise = False
LOG.error("Unable to instantiate vnf instance "
"%(id)s due to error : %(error)s",
{"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfInstantiationFailed(
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
if vnf_instance.instantiated_vnf_info and\
vnf_instance.instantiated_vnf_info.instance_id != instance_id:
vnf_instance.instantiated_vnf_info.instance_id = instance_id
if vnf_dict['attributes'].get('scaling_group_names'):
vnf_instance.instantiated_vnf_info.scale_status = \
vnf_dict['scale_status']
elif vnf_instance.instantiated_vnf_info:
default_scale_status = vnflcm_utils.\
get_default_scale_status(
context=context,
vnf_instance=vnf_instance,
vnfd_dict=vnfd_dict)
if default_scale_status is not None:
vnf_instance.instantiated_vnf_info.scale_status = \
default_scale_status
if vnf_dict['before_error_point'] <= EP.PRE_VIM_CONTROL:
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'create_wait',
plugin=self._vnfm_plugin, context=context,
vnf_dict=final_vnf_dict,
vnf_id=final_vnf_dict['instance_id'],
auth_attr=vim_connection_info.access_info)
except Exception as exp:
with excutils.save_and_reraise_exception():
exp.reraise = False
LOG.error("Vnf creation wait failed for vnf instance "
"%(id)s due to error : %(error)s",
{"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfInstantiationWaitFailed(
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
elif vnf_dict['before_error_point'] == EP.POST_VIM_CONTROL:
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'update_stack_wait',
plugin=self._vnfm_plugin, context=context,
vnf_dict=final_vnf_dict,
stack_id=instance_id,
auth_attr=vim_connection_info.access_info)
except Exception as exp:
with excutils.save_and_reraise_exception():
exp.reraise = False
LOG.error("Vnf update wait failed for vnf instance "
"%(id)s due to error : %(error)s",
{"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfInstantiationWaitFailed(
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
if vnf_instance.instantiated_vnf_info.instance_id:
self._vnf_manager.invoke(vim_connection_info.vim_type,
'post_vnf_instantiation', context=context,
vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
instantiate_vnf_req=instantiate_vnf_req)
def _get_file_hash(self, path):
hash_obj = hashlib.sha256()
with open(path) as f:
hash_obj.update(f.read().encode('utf-8'))
return hash_obj.hexdigest()
def _check_mgmt_driver(self, artifact_mgmt_driver, artifacts_value,
vnf_package_path):
# check implementation and artifacts exist in cfg.CONF.tacker
if artifact_mgmt_driver not in self._mgmt_driver_hash:
LOG.error('The {} specified in the VNFD '
'is inconsistent with the MgmtDriver in '
'the configuration file.'.format(artifact_mgmt_driver))
raise exceptions.MgmtDriverInconsistent(
MgmtDriver=artifact_mgmt_driver)
# check file content
pkg_mgmt_driver_path = os.path.join(vnf_package_path,
artifacts_value[artifact_mgmt_driver]['file'])
pkg_mgmt_driver_hash = self._get_file_hash(pkg_mgmt_driver_path)
if pkg_mgmt_driver_hash == \
self._mgmt_driver_hash[artifact_mgmt_driver]:
return artifact_mgmt_driver
else:
LOG.error('The hash verification of VNF Package MgmtDriver '
'and Tacker MgmtDriver does not match.')
raise exceptions.MgmtDriverHashMatchFailure()
def _load_vnf_interface(self, context, method_name,
vnf_instance, vnfd_dict):
VNF_value = vnfd_dict['topology_template']['node_templates']['VNF']
tacker_mgmt_driver = DEFAULT_VNFLCM_MGMT_DRIVER
interfaces_vnflcm_value = \
VNF_value.get('interfaces', {}).get('Vnflcm', {})
if not interfaces_vnflcm_value:
return tacker_mgmt_driver
artifacts_value = VNF_value.get('artifacts')
if not artifacts_value:
return tacker_mgmt_driver
vnf_package_path = vnflcm_utils._get_vnf_package_path(
context, vnf_instance.vnfd_id)
if interfaces_vnflcm_value.get(method_name):
artifact_mgmt_driver = interfaces_vnflcm_value.get(
method_name).get('implementation')
if artifact_mgmt_driver:
tacker_mgmt_driver = self._check_mgmt_driver(
artifact_mgmt_driver, artifacts_value, vnf_package_path)
return tacker_mgmt_driver
@log.log
def instantiate_vnf(self, context, vnf_instance, vnf_dict,
instantiate_vnf_req):
vnf_dict['current_error_point'] = EP.VNF_CONFIG_START
vim_connection_info_list = vnflcm_utils.\
_get_vim_connection_info_from_vnf_req(vnf_instance,
instantiate_vnf_req)
self._vnf_instance_update(context, vnf_instance,
vim_connection_info=vim_connection_info_list)
vim_info = vnflcm_utils._get_vim(context,
instantiate_vnf_req.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id, instantiate_vnf_req.flavour_id)
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request is not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'instantiate_start', vnf_instance, vnfd_dict),
'instantiate_start', context=context,
vnf_instance=vnf_instance,
instantiate_vnf_request=instantiate_vnf_req,
grant=vnf_dict.get('grant'), grant_request=None)
vnf_dict['current_error_point'] = EP.PRE_VIM_CONTROL
if vnf_dict['before_error_point'] <= EP.POST_VIM_CONTROL:
self._instantiate_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_req)
vnf_dict['current_error_point'] = EP.INTERNAL_PROCESSING
vnf_dict['current_error_point'] = EP.VNF_CONFIG_END
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request is not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'instantiate_end', vnf_instance, vnfd_dict),
'instantiate_end', context=context,
vnf_instance=vnf_instance,
instantiate_vnf_request=instantiate_vnf_req,
grant=vnf_dict.get('grant'), grant_request=None)
@log.log
@revert_to_error_task_state
def terminate_vnf(self, context, vnf_instance, terminate_vnf_req,
vnf_dict):
vnf_dict['current_error_point'] = EP.VNF_CONFIG_START
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(LiangLu): grant_request and grant here is planned to
# pass as a parameter, however due to they are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'terminate_start', vnf_instance, vnfd_dict),
'terminate_start', context=context,
vnf_instance=vnf_instance,
terminate_vnf_request=terminate_vnf_req,
grant=None, grant_request=None)
vnf_dict['current_error_point'] = EP.PRE_VIM_CONTROL
LOG.info("Terminating vnf %s", vnf_instance.id)
try:
if vnf_dict['before_error_point'] <= EP.POST_VIM_CONTROL:
self._delete_vnf_instance_resources(context, vnf_instance,
vim_connection_info,
terminate_vnf_req=terminate_vnf_req)
vnf_dict['current_error_point'] = EP.INTERNAL_PROCESSING
vnf_instance.instantiated_vnf_info.reinitialize()
self._vnf_instance_update(context, vnf_instance,
vim_connection_info=[], task_state=None)
LOG.info("Vnf terminated %s successfully", vnf_instance.id)
except Exception as exp:
with excutils.save_and_reraise_exception():
if vnf_dict['current_error_point'] == EP.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'] = \
EP.POST_VIM_CONTROL
LOG.error("Unable to terminate vnf '%s' instance. "
"Error: %s", vnf_instance.id,
encodeutils.exception_to_unicode(exp))
vnf_dict['current_error_point'] = EP.VNF_CONFIG_END
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(LiangLu): grant_request and grant here is planned to
# pass as a parameter, however due to they are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'terminate_end', vnf_instance, vnfd_dict),
'terminate_end', context=context,
vnf_instance=vnf_instance,
terminate_vnf_request=terminate_vnf_req,
grant=None, grant_request=None)
def _delete_vnf_instance_resources(self, context, vnf_instance,
vim_connection_info, terminate_vnf_req=None,
update_instantiated_state=True):
if (vnf_instance.instantiated_vnf_info and
vnf_instance.instantiated_vnf_info.instance_id) or \
vim_connection_info.vim_type == 'kubernetes':
instance_id = vnf_instance.instantiated_vnf_info.instance_id \
if vnf_instance.instantiated_vnf_info else None
access_info = vim_connection_info.access_info
LOG.info("Deleting stack %(instance)s for vnf %(id)s ",
{"instance": instance_id, "id": vnf_instance.id})
self._vnf_manager.invoke(vim_connection_info.vim_type,
'delete', plugin=self, context=context,
vnf_id=instance_id, auth_attr=access_info,
vnf_instance=vnf_instance, terminate_vnf_req=terminate_vnf_req)
if update_instantiated_state:
vnf_instance.instantiation_state = \
fields.VnfInstanceState.NOT_INSTANTIATED
vnf_instance.save()
self._vnf_manager.invoke(vim_connection_info.vim_type,
'delete_wait', plugin=self, context=context,
vnf_id=instance_id, auth_attr=access_info,
vnf_instance=vnf_instance)
vnf_resources = objects.VnfResourceList.get_by_vnf_instance_id(
context, vnf_instance.id)
for vnf_resource in vnf_resources:
self._vnf_manager.invoke(vim_connection_info.vim_type,
'delete_vnf_instance_resource',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
vnf_resource=vnf_resource)
vnf_resource.destroy(context)
def _heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request, vnf):
inst_vnf_info = vnf_instance.instantiated_vnf_info
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'heal_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
heal_vnf_request=heal_vnf_request)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to heal vnf %(id)s in infra driver. "
"Error: %(error)s", {"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnf['current_error_point'] = EP.POST_VIM_CONTROL
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'heal_vnf_wait',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
heal_vnf_request=heal_vnf_request)
except Exception as exp:
LOG.error("Failed to update vnf %(id)s resources for instance "
"%(instance)s. Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'post_heal_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
heal_vnf_request=heal_vnf_request)
self._vnf_instance_update(context, vnf_instance, task_state=None)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to store updated resources information for "
"instance %(instance)s for vnf %(id)s. "
"Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
def _respawn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, heal_vnf_request):
if vnf_dict['before_error_point'] != EP.POST_VIM_CONTROL:
try:
self._delete_vnf_instance_resources(context, vnf_instance,
vim_connection_info, update_instantiated_state=False)
except Exception as exc:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
err_msg = ("Failed to delete vnf resources for "
"vnf instance %(id)s before respawning. "
"The vnf is in inconsistent state. "
"Error: %(error)s")
LOG.error(err_msg % {"id": vnf_instance.id,
"error": str(exc)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exc))
# InstantiateVnfRequest is not stored in the db as it's mapped
# to InstantiatedVnfInfo version object. Convert InstantiatedVnfInfo
# version object to InstantiateVnfRequest so that vnf can be
# instantiated.
instantiate_vnf_request = objects.InstantiateVnfRequest.\
from_vnf_instance(vnf_instance)
vnf_instance.instantiated_vnf_info.reinitialize()
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id, instantiate_vnf_request.flavour_id)
if vnf_dict.get('vnf_instance_after'):
vnf_instance.instantiated_vnf_info = \
vnf_dict.get('vnf_instance_after').instantiated_vnf_info
else:
vnflcm_utils._build_instantiated_vnf_info(
vnfd_dict, instantiate_vnf_request, vnf_instance,
vim_connection_info.vim_id)
try:
self._instantiate_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_request)
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'post_vnf_instantiation',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info,
instantiate_vnf_req=instantiate_vnf_request)
except Exception as exc:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
err_msg = ("Failed to instantiate vnf instance "
"%(id)s after termination. The vnf is in "
"inconsistent state. Error: %(error)s")
LOG.error(err_msg % {"id": vnf_instance.id,
"error": str(exc)})
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exc))
self._vnf_instance_update(context, vnf_instance,
instantiation_state=fields.VnfInstanceState.INSTANTIATED,
task_state=None)
@log.log
@revert_to_error_task_state
def heal_vnf(self, context, vnf_instance, vnf_dict, heal_vnf_request):
vnf_dict['current_error_point'] = EP.VNF_CONFIG_START
LOG.info("Request received for healing vnf '%s'", vnf_instance.id)
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'heal_start', vnf_instance, vnfd_dict),
'heal_start', context=context,
vnf_instance=vnf_instance,
heal_vnf_request=heal_vnf_request,
grant=vnf_dict.get('grant'), grant_request=None)
vnf_dict['current_error_point'] = EP.PRE_VIM_CONTROL
try:
heal_flag = False
if vnf_dict['before_error_point'] <= EP.POST_VIM_CONTROL:
if not heal_vnf_request.vnfc_instance_id:
self._respawn_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, heal_vnf_request)
else:
heal_flag = True
self._heal_vnf(context, vnf_instance, vim_connection_info,
heal_vnf_request, vnf_dict)
LOG.info("Request received for healing vnf '%s' is completed "
"successfully", vnf_instance.id)
except Exception as exp:
with excutils.save_and_reraise_exception():
if vnf_dict['current_error_point'] == EP.PRE_VIM_CONTROL:
if not heal_flag:
if hasattr(vnf_instance.instantiated_vnf_info,
'instance_id'):
if vnf_instance.instantiated_vnf_info.instance_id:
vnf_dict['current_error_point'] = \
EP.POST_VIM_CONTROL
LOG.error("Unable to heal vnf '%s' instance. "
"Error: %s", heal_vnf_request.vnfc_instance_id,
encodeutils.exception_to_unicode(exp))
raise exceptions.VnfHealFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnf_dict['current_error_point'] = EP.VNF_CONFIG_END
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'heal_end', vnf_instance, vnfd_dict),
'heal_end', context=context,
vnf_instance=vnf_instance,
heal_vnf_request=heal_vnf_request,
grant=vnf_dict.get('grant'), grant_request=None)
def _scale_vnf_pre(self, context, vnf_info, vnf_instance,
scale_vnf_request, vim_connection_info):
if vnf_info['before_error_point'] <= EP.NOTIFY_PROCESSING:
self._vnfm_plugin._update_vnf_scaling(
context, vnf_info, 'ACTIVE', 'PENDING_'
+ scale_vnf_request.type)
vnf_info['current_error_point'] = EP.VNF_CONFIG_START
scale_id_list = []
scale_name_list = []
grp_id = None
vnf_info['policy_name'] = scale_vnf_request.aspect_id
if scale_vnf_request.type == 'SCALE_IN':
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
vnf_info['action'] = 'in'
scale_id_list, scale_name_list, grp_id, res_num = \
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'get_scale_in_ids',
plugin=self,
context=context,
vnf_dict=vnf_info,
is_reverse=scale_vnf_request.additional_params.get('\
is_reverse'),
auth_attr=vim_connection_info.access_info,
region_name=vim_connection_info.access_info.get('\
region_name'),
number_of_steps=scale_vnf_request.number_of_steps
)
vnf_info['res_num'] = res_num
if vnf_info['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
if len(scale_id_list) != 0 or \
vim_connection_info.vim_type == 'kubernetes':
kwargs = {'scale_name_list': scale_name_list}
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'scale_start', vnf_instance, vnfd_dict),
'scale_start', context=context,
vnf_instance=vnf_instance,
scale_vnf_request=scale_vnf_request,
grant=vnf_info.get('grant'), grant_request=None,
**kwargs)
else:
vnf_info['action'] = 'out'
scale_id_list = self._vnf_manager.invoke(
vim_connection_info.vim_type,
'get_scale_ids',
plugin=self,
context=context,
vnf_dict=vnf_info,
auth_attr=vim_connection_info.access_info,
region_name=vim_connection_info.access_info.get('region_name')
)
vnf_info['current_error_point'] = EP.PRE_VIM_CONTROL
return scale_id_list, scale_name_list, grp_id
def _get_node_template_for_vnf(self, vnfd_dict):
node_tmp = vnfd_dict['topology_template']['node_templates']
for node_template in node_tmp.values():
LOG.debug("node_template %s", node_template)
if not re.match('^tosca', node_template['type']):
LOG.debug("VNF node_template %s", node_template)
return node_template
return {}
def _scale_vnf_post(self, context, vnf_info, vnf_instance,
scale_vnf_request, vim_connection_info,
scale_id_list,
resource_changes):
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_info['current_error_point'] = EP.VNF_CONFIG_END
if scale_vnf_request.type == 'SCALE_OUT':
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
scale_id_after = self._vnf_manager.invoke(
vim_connection_info.vim_type,
'get_scale_ids',
plugin=self,
context=context,
vnf_dict=vnf_info,
auth_attr=vim_connection_info.access_info,
region_name=vim_connection_info.access_info.get('region_name')
)
id_list = []
id_list = list(set(scale_id_after) - set(scale_id_list))
vnf_info['res_num'] = len(scale_id_after)
if vnf_info['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
if len(id_list) != 0 or \
vim_connection_info.vim_type == 'kubernetes':
kwargs = {'scale_out_id_list': id_list}
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'scale_end', vnf_instance, vnfd_dict),
'scale_end', context=context,
vnf_instance=vnf_instance,
scale_vnf_request=scale_vnf_request,
grant=vnf_info.get('grant'), grant_request=None,
**kwargs)
vnf_instance.instantiated_vnf_info.scale_level =\
vnf_info['after_scale_level']
if vim_connection_info.vim_type != 'kubernetes':
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely hard
# coded with `vnf_info`. This dependency is to be refactored in
# future.
scaleGroupDict = \
jsonutils.loads(vnf_info['attributes']['scale_group'])
(scaleGroupDict
['scaleGroupDict'][scale_vnf_request.aspect_id]['default']) =\
vnf_info['res_num']
vnf_info['attributes']['scale_group'] =\
jsonutils.dump_as_bytes(scaleGroupDict)
if vnf_info['before_error_point'] < EP.NOTIFY_COMPLETED:
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ.operation_state = 'COMPLETED'
vnf_lcm_op_occ.resource_changes = resource_changes
vnf_instance.task_state = None
self._vnfm_plugin._update_vnf_scaling(context, vnf_info,
'PENDING_' + scale_vnf_request.type,
'ACTIVE', vnf_instance=vnf_instance,
vnf_lcm_op_occ=vnf_lcm_op_occ)
vnf_info['current_error_point'] = EP.NOTIFY_COMPLETED
notification = vnf_info['notification']
notification['notificationStatus'] = 'RESULT'
notification['operationState'] = 'COMPLETED'
resource_dict = resource_changes.to_dict()
if resource_dict.get('affected_vnfcs'):
notification['affectedVnfcs'] = resource_dict.get('affected_vnfcs')
if resource_dict.get('affected_virtual_links'):
notification['affectedVirtualLinks'] =\
resource_dict.get('affected_virtual_links')
if resource_dict.get('affected_virtual_storages'):
notification['affectedVirtualStorages'] =\
resource_dict.get('affected_virtual_storages')
self.rpc_api.send_notification(context, notification)
def _scale_resource_update(self, context, vnf_info, vnf_instance,
scale_vnf_request,
vim_connection_info,
error=False):
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
instantiated_vnf_before = \
copy.deepcopy(vnf_instance.instantiated_vnf_info)
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_resource_update',
context=context,
vnf_instance=vnf_instance,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
vim_connection_info=vim_connection_info
)
for scale in vnf_instance.instantiated_vnf_info.scale_status:
if scale_vnf_request.aspect_id == scale.aspect_id:
if not error:
scale.scale_level = vnf_info['after_scale_level']
break
else:
scale.scale_level = vnf_info['scale_level']
break
LOG.debug("vnf_instance.instantiated_vnf_info %s",
vnf_instance.instantiated_vnf_info)
affected_vnfcs = []
affected_virtual_storages = []
affected_virtual_links = []
if scale_vnf_request.type == 'SCALE_IN':
for vnfc in instantiated_vnf_before.vnfc_resource_info:
vnfc_delete = True
for rsc in vnf_instance.instantiated_vnf_info.\
vnfc_resource_info:
if vnfc.compute_resource.resource_id == \
rsc.compute_resource.resource_id:
vnfc_delete = False
break
if vnfc_delete:
affected_vnfc = objects.AffectedVnfc(id=vnfc.id,
vdu_id=vnfc.vdu_id,
change_type='REMOVED',
compute_resource=vnfc.compute_resource)
affected_vnfcs.append(affected_vnfc)
for st in instantiated_vnf_before.virtual_storage_resource_info:
st_delete = True
for rsc in vnf_instance.instantiated_vnf_info.\
virtual_storage_resource_info:
if st.storage_resource.resource_id == \
rsc.storage_resource.resource_id:
st_delete = False
break
if st_delete:
affected_st = objects.AffectedVirtualStorage(
id=st.id,
virtual_storage_desc_id=st.virtual_storage_desc_id,
change_type='REMOVED',
storage_resource=st.storage_resource)
affected_virtual_storages.append(affected_st)
for vl in instantiated_vnf_before.vnf_virtual_link_resource_info:
port_delete = False
for rsc in vnf_instance.\
instantiated_vnf_info.vnf_virtual_link_resource_info:
if vl.network_resource.resource_id == \
rsc.network_resource.resource_id:
if len(vl.vnf_link_ports) != len(rsc.vnf_link_ports):
port_delete = True
break
if port_delete:
affected_vl = objects.AffectedVirtualLink(
id=vl.id,
vnf_virtual_link_desc_id=vl.vnf_virtual_link_desc_id,
change_type='LINK_PORT_REMOVED',
network_resource=vl.network_resource)
affected_virtual_links.append(affected_vl)
else:
for rsc in vnf_instance.instantiated_vnf_info.vnfc_resource_info:
vnfc_add = True
for vnfc in instantiated_vnf_before.vnfc_resource_info:
if vnfc.compute_resource.resource_id == \
rsc.compute_resource.resource_id:
vnfc_add = False
break
if vnfc_add:
affected_vnfc = objects.AffectedVnfc(
id=rsc.id,
vdu_id=rsc.vdu_id,
change_type='ADDED',
compute_resource=rsc.compute_resource)
affected_vnfcs.append(affected_vnfc)
for rsc in vnf_instance.instantiated_vnf_info.\
virtual_storage_resource_info:
st_add = True
for st in instantiated_vnf_before.\
virtual_storage_resource_info:
if st.storage_resource.resource_id == \
rsc.storage_resource.resource_id:
st_add = False
break
if st_add:
affected_st = objects.AffectedVirtualStorage(
id=rsc.id,
virtual_storage_desc_id=rsc.virtual_storage_desc_id,
change_type='ADDED',
storage_resource=rsc.storage_resource)
affected_virtual_storages.append(affected_st)
for vl in instantiated_vnf_before.vnf_virtual_link_resource_info:
port_add = False
for rsc in vnf_instance.instantiated_vnf_info.\
vnf_virtual_link_resource_info:
if vl.network_resource.resource_id == \
rsc.network_resource.resource_id:
if len(vl.vnf_link_ports) != len(rsc.vnf_link_ports):
port_add = True
break
if port_add:
affected_vl = objects.AffectedVirtualLink(
id=vl.id,
vnf_virtual_link_desc_id=vl.vnf_virtual_link_desc_id,
change_type='LINK_PORT_ADDED',
network_resource=vl.network_resource)
affected_virtual_links.append(affected_vl)
resource_changes = objects.ResourceChanges()
resource_changes.affected_vnfcs = []
resource_changes.affected_virtual_links = []
resource_changes.affected_virtual_storages = []
if 'resource_changes' in \
vnf_lcm_op_occs and vnf_lcm_op_occs.resource_changes:
res_chg = vnf_lcm_op_occs.resource_changes
if 'affected_vnfcs' in res_chg:
if res_chg.affected_vnfcs and \
len(res_chg.affected_vnfcs) > 0:
resource_changes.affected_vnfcs.\
extend(res_chg.affected_vnfcs)
if 'affected_virtual_storages' in res_chg:
if res_chg.affected_virtual_storages and \
len(res_chg.affected_virtual_storages) > 0:
resource_changes.affected_virtual_storages.extend(
res_chg.affected_virtual_storages)
if 'affected_virtual_links' in res_chg:
if res_chg.affected_virtual_links and \
len(res_chg.affected_virtual_links) > 0:
resource_changes.affected_virtual_links.\
extend(res_chg.affected_virtual_links)
resource_changes.affected_vnfcs.extend(affected_vnfcs)
resource_changes.affected_virtual_storages.extend(
affected_virtual_storages)
resource_changes.affected_virtual_links = []
resource_changes.affected_virtual_links.extend(affected_virtual_links)
vnf_info['resource_changes'] = resource_changes
return resource_changes
def _scale_vnf(self, context, vnf_info, vnf_instance,
scale_vnf_request, vim_connection_info,
scale_name_list, grp_id):
# action_driver
LOG.debug("vnf_info['vnfd']['attributes'] %s",
vnf_info['vnfd']['attributes'])
self.scale(context, vnf_info, scale_vnf_request,
vim_connection_info, scale_name_list, grp_id)
@log.log
@revert_to_error_scale
def scale_vnf(self, context, vnf_info, vnf_instance, scale_vnf_request):
LOG.info("Request received for scale vnf '%s'", vnf_instance.id)
timestamp = datetime.utcnow()
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ.operation_state = 'PROCESSING'
vnf_lcm_op_occ.state_entered_time = timestamp
LOG.debug("vnf_lcm_op_occ %s", vnf_lcm_op_occ)
vnf_lcm_op_occ.save()
notification = vnf_info['notification']
notification['operationState'] = 'PROCESSING'
self.rpc_api.send_notification(context, notification)
vnf_info['current_error_point'] = EP.NOTIFY_PROCESSING
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
scale_id_list, scale_name_list, grp_id = self._scale_vnf_pre(
context, vnf_info,
vnf_instance,
scale_vnf_request,
vim_connection_info)
if vnf_info['before_error_point'] <= EP.POST_VIM_CONTROL:
self._scale_vnf(context, vnf_info,
vnf_instance,
scale_vnf_request,
vim_connection_info,
scale_name_list, grp_id)
resource_changes = self._scale_resource_update(context, vnf_info,
vnf_instance,
scale_vnf_request,
vim_connection_info)
vnf_info['current_error_point'] = EP.INTERNAL_PROCESSING
self._scale_vnf_post(context, vnf_info,
vnf_instance,
scale_vnf_request,
vim_connection_info,
scale_id_list,
resource_changes)
LOG.info("Request received for scale vnf '%s' is completed "
"successfully", vnf_instance.id)
def scale(
self,
context,
vnf_info,
scale_vnf_request,
vim_connection_info,
scale_name_list,
grp_id):
self._vnf_manager = driver_manager.DriverManager(
'tacker.tacker.vnfm.drivers',
cfg.CONF.tacker.infra_driver)
if scale_vnf_request.type == 'SCALE_IN':
action = 'in'
elif scale_vnf_request.type == 'SCALE_OUT':
action = 'out'
else:
msg = 'Unknown scale type: %s' % scale_vnf_request.type
raise exceptions.VnfScaleFailed(id=vnf_info['instance_id'],
error=msg)
policy = {'instance_id': vnf_info['instance_id'],
'name': scale_vnf_request.aspect_id,
'vnf': vnf_info,
'action': action}
LOG.debug(
"is_reverse: %s",
scale_vnf_request.additional_params.get('is_reverse'))
default = None
if vim_connection_info.vim_type == 'kubernetes':
policy['vnf_instance_id'] = \
vnf_info['vnf_lcm_op_occ'].get('vnf_instance_id')
vnf_instance = objects.VnfInstance.get_by_id(context,
policy['vnf_instance_id'])
vnfd_dict = vnflcm_utils._get_vnfd_dict(context,
vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
tosca = tosca_template.ToscaTemplate(
parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict)
extract_policy_infos = vnflcm_utils.get_extract_policy_infos(tosca)
policy['vdu_defs'] = vnflcm_utils.get_target_vdu_def_dict(
extract_policy_infos=extract_policy_infos,
aspect_id=scale_vnf_request.aspect_id,
tosca=tosca)
policy['delta_num'] = vnflcm_utils.get_scale_delta_num(
extract_policy_infos=extract_policy_infos,
aspect_id=scale_vnf_request.aspect_id)
elif vim_connection_info.vim_type == 'openstack':
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely hard
# coded with `vnf_info`. This dependency is to be refactored in
# future.
scale_json = vnf_info['attributes']['scale_group']
scale_group_dict = jsonutils.loads(scale_json)
key_aspect = scale_vnf_request.aspect_id
default = scale_group_dict['scaleGroupDict'][key_aspect]['default']
else:
msg = 'Unknown vim type: %s' % vim_connection_info.vim_type
raise exceptions.VnfScaleFailed(id=vnf_info['instance_id'],
error=msg)
if (scale_vnf_request.type == 'SCALE_IN' and
scale_vnf_request.additional_params['is_reverse'] == 'True'):
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_in_reverse',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
region_name=vim_connection_info.access_info.get('region_name'),
scale_name_list=scale_name_list,
grp_id=grp_id
)
vnf_info['current_error_point'] = EP.POST_VIM_CONTROL
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_update_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
region_name=vim_connection_info.access_info.get('region_name')
)
elif scale_vnf_request.type == 'SCALE_OUT' and default == 0:
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_out_initial',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
region_name=vim_connection_info.access_info.get('region_name')
)
vnf_info['current_error_point'] = EP.POST_VIM_CONTROL
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_update_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
region_name=vim_connection_info.access_info.get('region_name')
)
else:
for _ in range(scale_vnf_request.number_of_steps):
last_event_id = self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
policy=policy,
region_name=vim_connection_info.access_info.get('\
region_name')
)
vnf_info['current_error_point'] = EP.POST_VIM_CONTROL
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
policy=policy,
region_name=vim_connection_info.access_info.get('\
region_name'),
last_event_id=last_event_id)
def _term_resource_update(self, context, vnf_info, vnf_instance,
error=False):
if not vnf_instance.instantiated_vnf_info:
resource_changes = objects.ResourceChanges()
resource_changes.affected_vnfcs = []
resource_changes.affected_virtual_links = []
resource_changes.affected_virtual_storages = []
vnf_info['resource_changes'] = resource_changes
return resource_changes
instantiated_vnf_before = copy.deepcopy(
vnf_instance.instantiated_vnf_info)
vnf_instance.instantiated_vnf_info.reinitialize()
if not error:
vnf_instance.vim_connection_info = []
vnf_instance.task_state = None
LOG.debug(
"vnf_instance.instantiated_vnf_info %s",
vnf_instance.instantiated_vnf_info)
affected_vnfcs = []
affected_virtual_storages = []
affected_virtual_links = []
for vnfc in instantiated_vnf_before.vnfc_resource_info:
vnfc_delete = True
for rsc in vnf_instance.instantiated_vnf_info.vnfc_resource_info:
if vnfc.compute_resource.resource_id == \
rsc.compute_resource.resource_id:
vnfc_delete = False
break
if vnfc_delete:
affected_vnfc = objects.AffectedVnfc(
id=vnfc.id,
vdu_id=vnfc.vdu_id,
change_type='REMOVED',
compute_resource=vnfc.compute_resource)
affected_vnfcs.append(affected_vnfc)
for st in instantiated_vnf_before.virtual_storage_resource_info:
st_delete = True
for rsc in \
vnf_instance.instantiated_vnf_info.\
virtual_storage_resource_info:
if st.storage_resource.resource_id == \
rsc.storage_resource.resource_id:
st_delete = False
break
if st_delete:
affected_st = objects.AffectedVirtualStorage(
id=st.id,
virtual_storage_desc_id=st.virtual_storage_desc_id,
change_type='REMOVED',
storage_resource=st.storage_resource)
affected_virtual_storages.append(affected_st)
for vl in instantiated_vnf_before.vnf_virtual_link_resource_info:
vm_delete = False
for rsc in \
vnf_instance.instantiated_vnf_info.\
vnf_virtual_link_resource_info:
if st.network_resource.resource_id == \
rsc.network_resource.resource_id:
vm_delete = False
break
if vm_delete:
affected_vl = objects.AffectedVirtualLink(
id=vl.id,
vnf_virtual_link_desc_id=vl.vnf_virtual_link_desc_id,
change_type='REMOVED',
network_resource=vl.network_resource)
affected_virtual_links.append(affected_vl)
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
resource_changes = objects.ResourceChanges()
resource_changes.affected_vnfcs = []
resource_changes.affected_virtual_links = []
resource_changes.affected_virtual_storages = []
if 'resource_changes' in vnf_lcm_op_occs and \
vnf_lcm_op_occs.resource_changes:
res_chg = vnf_lcm_op_occs.resource_changes
if 'affected_vnfcs' in res_chg:
if res_chg.affected_vnfcs and \
len(res_chg.affected_vnfcs) > 0:
resource_changes.affected_vnfcs.extend(
res_chg.affected_vnfcs)
if 'affected_virtual_storages' in res_chg:
if res_chg.affected_virtual_storages and \
len(res_chg.affected_virtual_storages) > 0:
resource_changes.affected_virtual_storages.extend(
res_chg.affected_virtual_storages)
if 'affected_virtual_links' in res_chg:
if res_chg.affected_virtual_links and \
len(res_chg.affected_virtual_links) > 0:
resource_changes.affected_virtual_links.extend(
res_chg.affected_virtual_links)
resource_changes.affected_vnfcs.extend(affected_vnfcs)
resource_changes.affected_virtual_storages.extend(
affected_virtual_storages)
resource_changes.affected_virtual_links.extend(affected_virtual_links)
vnf_info['resource_changes'] = resource_changes
return resource_changes
def _rollback_vnf_pre(
self,
context,
vnf_info,
vnf_instance,
operation_params,
vim_connection_info):
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
scale_id_list = []
scale_name_list = []
grp_id = None
self._update_vnf_rollback_pre(context, vnf_info)
if vnf_lcm_op_occs.operation == 'SCALE':
if vim_connection_info.vim_type != 'kubernetes':
# NOTE(ueha): The logic of Scale for OpenStack VIM is widely
# hard coded with `vnf_info`. This dependency is to be
# refactored in future.
scaleGroupDict = jsonutils.loads(
vnf_info['attributes']['scale_group'])
cap_size = scaleGroupDict['scaleGroupDict'][operation_params
['aspect_id']]['default']
vnf_info['res_num'] = cap_size
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
operation_params, context=context)
for scale in vnf_instance.instantiated_vnf_info.scale_status:
if scale_vnf_request.aspect_id == scale.aspect_id:
vnf_info['after_scale_level'] = scale.scale_level
break
if vnf_lcm_op_occs.operation == 'SCALE' \
and vnf_lcm_op_occs.error_point >= EP.POST_VIM_CONTROL:
scale_id_list, scale_name_list, grp_id = self._vnf_manager.invoke(
vim_connection_info.vim_type,
'get_rollback_ids',
plugin=self,
context=context,
vnf_dict=vnf_info,
aspect_id=operation_params['aspect_id'],
auth_attr=vim_connection_info.access_info,
region_name=vim_connection_info.access_info.get('region_name')
)
if vnf_lcm_op_occs.error_point == EP.NOTIFY_COMPLETED:
if vnf_lcm_op_occs.operation == 'SCALE':
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
vnf_info['action'] = 'in'
if len(scale_id_list) != 0:
kwargs = {'scale_name_list': scale_name_list}
# TODO(LiangLu): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(context, 'scale_start',
vnf_instance, vnfd_dict),
'scale_start', context=context,
vnf_instance=vnf_instance,
scale_vnf_request=scale_vnf_request,
grant=vnf_info.get('grant'), grant_request=None,
**kwargs)
else:
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
# TODO(LiangLu): grant_request and grant here is planned to
# pass as a parameter, however due to they are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
if len(scale_id_list) != 0:
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'terminate_start',
vnf_instance, vnfd_dict),
'terminate_start', context=context,
vnf_instance=vnf_instance,
terminate_vnf_request=None,
grant=None, grant_request=None)
vnf_lcm_op_occs.error_point = EP.VNF_CONFIG_END
return scale_name_list, grp_id
def _rollback_vnf(
self,
context,
vnf_info,
vnf_instance,
operation_params,
vim_connection_info,
scale_name_list,
grp_id):
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
if vnf_lcm_op_occs.error_point >= EP.POST_VIM_CONTROL:
if vnf_lcm_op_occs.operation == 'SCALE':
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
operation_params, context=context)
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_in_reverse',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
scale_vnf_request=scale_vnf_request,
region_name=vim_connection_info.access_info.get(
'region_name'),
scale_name_list=scale_name_list,
grp_id=grp_id)
self._vnf_manager.invoke(
vim_connection_info.vim_type,
'scale_update_wait',
plugin=self,
context=context,
auth_attr=vim_connection_info.access_info,
vnf_info=vnf_info,
region_name=vim_connection_info.access_info.get(
'region_name'))
else:
instance_id = vnf_instance.instantiated_vnf_info.instance_id
access_info = vim_connection_info.access_info
self._vnf_manager.invoke(vim_connection_info.vim_type,
'delete', plugin=self, context=context,
vnf_id=instance_id, auth_attr=access_info,
vnf_instance=vnf_instance)
self._vnf_manager.invoke(vim_connection_info.vim_type,
'delete_wait', plugin=self, context=context,
vnf_id=instance_id, auth_attr=access_info,
vnf_instance=vnf_instance)
vnf_lcm_op_occs.error_point = EP.PRE_VIM_CONTROL
def _update_vnf_rollback_pre(self, context, vnf_info):
self._vnfm_plugin._update_vnf_rollback_pre(context, vnf_info)
def _update_vnf_rollback(self, context, vnf_info,
vnf_instance, vnf_lcm_op_occs):
if vnf_lcm_op_occs.operation == 'SCALE':
status = 'ACTIVE'
else:
status = 'INACTIVE'
vnf_instance.task_state = None
self._vnfm_plugin._update_vnf_rollback(context, vnf_info,
'ERROR',
status,
vnf_instance=vnf_instance,
vnf_lcm_op_occ=vnf_lcm_op_occs)
def _update_vnf_rollback_status_err(self, context, vnf_info):
self._vnfm_plugin.update_vnf_rollback_status_err(context, vnf_info)
def _rollback_vnf_post(
self,
context,
vnf_info,
vnf_instance,
operation_params,
vim_connection_info):
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
if vnf_lcm_op_occs.operation == 'SCALE':
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
operation_params, context=context)
scale_vnf_request_copy = copy.deepcopy(scale_vnf_request)
scale_vnf_request_copy.type = 'SCALE_IN'
resource_changes = self._scale_resource_update(context, vnf_info,
vnf_instance,
scale_vnf_request_copy,
vim_connection_info)
else:
vnfd_dict = vnflcm_utils.get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
# TODO(LiangLu): grant_request and grant here is planned to
# pass as a parameter, however due to they are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'terminate_end',
vnf_instance, vnfd_dict),
'terminate_end', context=context,
vnf_instance=vnf_instance,
terminate_vnf_request=None,
grant=None, grant_request=None)
resource_changes = self._term_resource_update(
context, vnf_info, vnf_instance)
vnf_lcm_op_occs.error_point = EP.VNF_CONFIG_START
timestamp = datetime.utcnow()
vnf_lcm_op_occs.operation_state = 'ROLLED_BACK'
vnf_lcm_op_occs.state_entered_time = timestamp
vnf_lcm_op_occs.resource_changes = resource_changes
self._update_vnf_rollback(context, vnf_info,
vnf_instance,
vnf_lcm_op_occs)
notification = vnf_info['notification']
notification['notificationStatus'] = 'RESULT'
notification['operationState'] = 'ROLLED_BACK'
resource_dict = resource_changes.to_dict()
if resource_dict.get('affected_vnfcs'):
notification['affectedVnfcs'] = resource_dict.get('affected_vnfcs')
if resource_dict.get('affected_virtual_links'):
notification['affectedVirtualLinks'] = \
resource_dict.get('affected_virtual_links')
if resource_dict.get('affected_virtual_storages'):
notification['affectedVirtualStorages'] = \
resource_dict.get('affected_virtual_storages')
self.rpc_api.send_notification(context, notification)
@log.log
@revert_to_error_rollback
def rollback_vnf(self, context, vnf_info, vnf_instance, operation_params):
LOG.info("Request received for rollback vnf '%s'", vnf_instance.id)
vnf_lcm_op_occs = vnf_info['vnf_lcm_op_occ']
if vnf_lcm_op_occs.operation == 'SCALE':
scale_vnf_request = objects.ScaleVnfRequest.obj_from_primitive(
operation_params, context=context)
for scale in vnf_instance.instantiated_vnf_info.scale_status:
if scale_vnf_request.aspect_id == scale.aspect_id:
vnf_info['after_scale_level'] = scale.scale_level
break
timestamp = datetime.utcnow()
vnf_lcm_op_occs.operation_state = 'ROLLING_BACK'
vnf_lcm_op_occs.state_entered_time = timestamp
LOG.debug("vnf_lcm_op_occs %s", vnf_lcm_op_occs)
insta_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_instances/" + \
vnf_instance.id
vnflcm_url = CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/vnf_lcm_op_occs/" + \
vnf_lcm_op_occs.id
notification = {}
notification['notificationType'] = \
'VnfLcmOperationOccurrenceNotification'
notification['vnfInstanceId'] = vnf_instance.id
notification['notificationStatus'] = 'START'
notification['operation'] = vnf_lcm_op_occs.operation
notification['operationState'] = 'ROLLING_BACK'
if vnf_lcm_op_occs.operation == 'SCALE':
notification['isAutomaticInvocation'] = \
vnf_lcm_op_occs.is_automatic_invocation
else:
notification['isAutomaticInvocation'] = False
notification['vnfLcmOpOccId'] = vnf_lcm_op_occs.id
notification['_links'] = {}
notification['_links']['vnfInstance'] = {}
notification['_links']['vnfInstance']['href'] = insta_url
notification['_links']['vnfLcmOpOcc'] = {}
notification['_links']['vnfLcmOpOcc']['href'] = vnflcm_url
vnf_info['notification'] = notification
vnf_lcm_op_occs.save()
self.rpc_api.send_notification(context, notification)
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
scale_name_list, grp_id = self._rollback_vnf_pre(
context, vnf_info, vnf_instance,
operation_params, vim_connection_info)
self._rollback_vnf(
context,
vnf_info,
vnf_instance,
operation_params,
vim_connection_info,
scale_name_list,
grp_id)
self._rollback_vnf_post(
context,
vnf_info,
vnf_instance,
operation_params,
vim_connection_info)
def _change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
inst_vnf_info = vnf_instance.instantiated_vnf_info
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'change_ext_conn_vnf',
context=context, vnf_instance=vnf_instance, vnf_dict=vnf_dict,
vim_connection_info=vim_connection_info,
change_ext_conn_req=change_ext_conn_req)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to change external connectivity "
"vnf %(id)s in infra driver. "
"Error: %(error)s", {"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfChangeExtConnFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnf_dict['current_error_point'] = fields.ErrorPoint.POST_VIM_CONTROL
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'change_ext_conn_vnf_wait',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info)
except Exception as exp:
LOG.error("Failed to update vnf %(id)s resources for instance "
"%(instance)s. Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfChangeExtConnWaitFailed(
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
@log.log
@revert_to_error_task_state
def change_ext_conn_vnf(
self,
context,
vnf_instance,
vnf_dict,
change_ext_conn_req):
LOG.info("Request received for changing external connectivity "
"vnf '%s'", vnf_instance.id)
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
vnf_dict['current_error_point'] = EP.VNF_CONFIG_START
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(esto-aln): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'change_external_connectivity_start',
vnf_instance, vnfd_dict),
'change_external_connectivity_start', context=context,
vnf_instance=vnf_instance,
change_ext_conn_request=change_ext_conn_req,
grant=vnf_dict.get('grant'), grant_request=None)
vnf_dict['current_error_point'] = EP.PRE_VIM_CONTROL
if vnf_dict['before_error_point'] <= EP.POST_VIM_CONTROL:
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = \
objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
self._change_ext_conn_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req)
# Since there is no processing corresponding to
# EP.INTERNAL_PROCESSING, it transitions to EP.VNF_CONFIG_END.
vnf_dict['current_error_point'] = EP.VNF_CONFIG_END
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(esto-aln): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'change_external_connectivity_end',
vnf_instance, vnfd_dict),
'change_external_connectivity_end', context=context,
vnf_instance=vnf_instance,
change_ext_conn_request=change_ext_conn_req,
grant=vnf_dict.get('grant'), grant_request=None)
LOG.info("Request received for changing external connectivity "
"vnf '%s' is completed successfully", vnf_instance.id)