1532 lines
69 KiB
Python
1532 lines
69 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 time
|
|
import traceback
|
|
import yaml
|
|
|
|
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 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.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:
|
|
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.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)
|
|
|
|
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']
|
|
|
|
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))
|
|
|
|
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):
|
|
|
|
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)
|
|
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'instantiate_start', vnf_instance, vnfd_dict),
|
|
'instantiate_start', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=instantiate_vnf_req.additional_params)
|
|
|
|
self._instantiate_vnf(context, vnf_instance, vnf_dict,
|
|
vim_connection_info, instantiate_vnf_req)
|
|
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'instantiate_end', vnf_instance, vnfd_dict),
|
|
'instantiate_end', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=instantiate_vnf_req.additional_params)
|
|
|
|
@log.log
|
|
@revert_to_error_task_state
|
|
def terminate_vnf(self, context, vnf_instance, terminate_vnf_req):
|
|
|
|
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)
|
|
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'terminate_start', vnf_instance, vnfd_dict),
|
|
'terminate_start', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=terminate_vnf_req.additional_params)
|
|
LOG.info("Terminating vnf %s", vnf_instance.id)
|
|
try:
|
|
self._delete_vnf_instance_resources(context, vnf_instance,
|
|
vim_connection_info, terminate_vnf_req=terminate_vnf_req)
|
|
|
|
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():
|
|
LOG.error("Unable to terminate vnf '%s' instance. "
|
|
"Error: %s", vnf_instance.id,
|
|
encodeutils.exception_to_unicode(exp))
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'terminate_end', vnf_instance, vnfd_dict),
|
|
'terminate_end', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=terminate_vnf_req.additional_params)
|
|
|
|
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):
|
|
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))
|
|
|
|
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)
|
|
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):
|
|
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)
|
|
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)
|
|
|
|
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):
|
|
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)
|
|
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'heal_start', vnf_instance, vnfd_dict),
|
|
'heal_start', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=heal_vnf_request.additional_params)
|
|
|
|
if not heal_vnf_request.vnfc_instance_id:
|
|
self._respawn_vnf(context, vnf_instance, vnf_dict,
|
|
vim_connection_info, heal_vnf_request)
|
|
else:
|
|
self._heal_vnf(context, vnf_instance, vim_connection_info,
|
|
heal_vnf_request)
|
|
|
|
LOG.info("Request received for healing vnf '%s' is completed "
|
|
"successfully", vnf_instance.id)
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'heal_end', vnf_instance, vnfd_dict),
|
|
'heal_end', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=heal_vnf_request.additional_params)
|
|
|
|
def _scale_vnf_pre(self, context, vnf_info, vnf_instance,
|
|
scale_vnf_request, vim_connection_info):
|
|
self._vnfm_plugin._update_vnf_scaling(
|
|
context, vnf_info, 'ACTIVE', 'PENDING_' + scale_vnf_request.type)
|
|
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
|
|
vnf_lcm_op_occ.error_point = 2
|
|
|
|
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
|
|
|
|
# mgmt_driver pre
|
|
if len(scale_id_list) != 0:
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'scale_start', vnf_instance, vnfd_dict),
|
|
'scale_start', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=scale_vnf_request.additional_params)
|
|
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_lcm_op_occ.error_point = 3
|
|
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_lcm_op_occ.error_point = 6
|
|
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 len(id_list) != 0:
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(
|
|
context, 'scale_end', vnf_instance, vnfd_dict),
|
|
'scale_end', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=scale_vnf_request.additional_params)
|
|
vnf_lcm_op_occ.error_point = 7
|
|
vnf_instance.instantiated_vnf_info.scale_level =\
|
|
vnf_info['after_scale_level']
|
|
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)
|
|
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
|
|
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)
|
|
|
|
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,
|
|
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'])
|
|
vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ']
|
|
vnf_lcm_op_occ.error_point = 4
|
|
self.scale(context, vnf_info, scale_vnf_request,
|
|
vim_connection_info, scale_name_list, grp_id)
|
|
vnf_lcm_op_occ.error_point = 5
|
|
|
|
@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)
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
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)
|
|
policy = {}
|
|
policy['instance_id'] = vnf_info['instance_id']
|
|
policy['name'] = scale_vnf_request.aspect_id
|
|
policy['vnf'] = vnf_info
|
|
if scale_vnf_request.type == 'SCALE_IN':
|
|
policy['action'] = 'in'
|
|
else:
|
|
policy['action'] = 'out'
|
|
LOG.debug(
|
|
"is_reverse: %s",
|
|
scale_vnf_request.additional_params.get('is_reverse'))
|
|
scale_json = vnf_info['attributes']['scale_group']
|
|
scaleGroupDict = jsonutils.loads(scale_json)
|
|
key_aspect = scale_vnf_request.aspect_id
|
|
default = scaleGroupDict['scaleGroupDict'][key_aspect]['default']
|
|
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
|
|
)
|
|
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')
|
|
)
|
|
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:
|
|
heat_template = vnf_info['attributes']['heat_template']
|
|
policy_in_name = scale_vnf_request.aspect_id + '_scale_in'
|
|
policy_out_name = scale_vnf_request.aspect_id + '_scale_out'
|
|
|
|
heat_resource = yaml.safe_load(heat_template)
|
|
if scale_vnf_request.type == 'SCALE_IN':
|
|
policy['action'] = 'in'
|
|
policy_temp = heat_resource['resources'][policy_in_name]
|
|
policy_prop = policy_temp['properties']
|
|
cooldown = policy_prop.get('cooldown')
|
|
policy_name = policy_in_name
|
|
else:
|
|
policy['action'] = 'out'
|
|
policy_temp = heat_resource['resources'][policy_out_name]
|
|
policy_prop = policy_temp['properties']
|
|
cooldown = policy_prop.get('cooldown')
|
|
policy_name = policy_out_name
|
|
|
|
policy_temp = heat_resource['resources'][policy_name]
|
|
policy_prop = policy_temp['properties']
|
|
for i 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')
|
|
)
|
|
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)
|
|
if i != scale_vnf_request.number_of_steps - 1:
|
|
if cooldown:
|
|
time.sleep(cooldown)
|
|
|
|
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:
|
|
if 'affected_vnfcs' in vnf_lcm_op_occs.resource_changes:
|
|
if len(vnf_lcm_op_occs.resource_changes.affected_vnfcs) > 0:
|
|
resource_changes.affected_vnfcs.extend(
|
|
vnf_lcm_op_occs.resource_changes.affected_vnfcs)
|
|
if 'affected_virtual_storages' in vnf_lcm_op_occs.resource_changes:
|
|
if len(vnf_lcm_op_occs.resource_changes.
|
|
affected_virtual_storages) > 0:
|
|
resource_changes.affected_virtual_storages.extend(
|
|
vnf_lcm_op_occs.resource_changes.
|
|
affected_virtual_storages)
|
|
if 'affected_virtual_links' in vnf_lcm_op_occs.resource_changes:
|
|
if len(vnf_lcm_op_occs.resource_changes.
|
|
affected_virtual_links) > 0:
|
|
resource_changes.affected_virtual_links.extend(
|
|
vnf_lcm_op_occs.resource_changes.
|
|
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':
|
|
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 >= 4:
|
|
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 == 7:
|
|
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:
|
|
self._mgmt_manager.invoke(
|
|
self._load_vnf_interface(context, 'scale_start',
|
|
vnf_instance, vnfd_dict),
|
|
'scale_start', context=context,
|
|
vnf_instance=vnf_instance,
|
|
additional_params=scale_vnf_request.additional_params)
|
|
|
|
else:
|
|
vnfd_dict = vnflcm_utils._get_vnfd_dict(
|
|
context, vnf_instance.vnfd_id,
|
|
vnf_instance.instantiated_vnf_info.flavour_id)
|
|
|
|
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,
|
|
additional_params=scale_vnf_request.additional_params)
|
|
vnf_lcm_op_occs.error_point = 6
|
|
|
|
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 >= 4:
|
|
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)
|
|
|
|
self._vnf_manager.invoke(vim_connection_info.vim_type,
|
|
'delete_wait', plugin=self, context=context,
|
|
vnf_id=instance_id, auth_attr=access_info)
|
|
|
|
vnf_lcm_op_occs.error_point = 3
|
|
|
|
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'
|
|
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:
|
|
resource_changes = self._term_resource_update(
|
|
context, vnf_info, vnf_instance)
|
|
|
|
vnf_lcm_op_occs.error_point = 2
|
|
|
|
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)
|