845 lines
35 KiB
Python
845 lines
35 KiB
Python
# Copyright 2013, 2014 Intel Corporation.
|
|
# 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 inspect
|
|
import six
|
|
import yaml
|
|
|
|
import eventlet
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_log import versionutils
|
|
from oslo_utils import excutils
|
|
from toscaparser.tosca_template import ToscaTemplate
|
|
|
|
from tacker._i18n import _LE
|
|
from tacker.api.v1 import attributes
|
|
from tacker.common import driver_manager
|
|
from tacker.common import exceptions
|
|
from tacker.common import utils
|
|
from tacker.db.vnfm import vnfm_db
|
|
from tacker.extensions import vnfm
|
|
from tacker.plugins.common import constants
|
|
from tacker.vnfm.mgmt_drivers import constants as mgmt_constants
|
|
from tacker.vnfm import monitor
|
|
from tacker.vnfm.tosca import utils as toscautils
|
|
from tacker.vnfm import vim_client
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
|
|
|
|
def config_opts():
|
|
return [('tacker', VNFMMgmtMixin.OPTS),
|
|
('tacker', VNFMPlugin.OPTS)]
|
|
|
|
|
|
class VNFMMgmtMixin(object):
|
|
OPTS = [
|
|
cfg.ListOpt(
|
|
'mgmt_driver', default=['noop', 'openwrt'],
|
|
help=_('MGMT driver to communicate with '
|
|
'Hosting VNF/logical service '
|
|
'instance tacker plugin will use')),
|
|
cfg.IntOpt('boot_wait', default=30,
|
|
help=_('Time interval to wait for VM to boot'))
|
|
]
|
|
cfg.CONF.register_opts(OPTS, 'tacker')
|
|
|
|
def __init__(self):
|
|
super(VNFMMgmtMixin, self).__init__()
|
|
self._mgmt_manager = driver_manager.DriverManager(
|
|
'tacker.tacker.mgmt.drivers', cfg.CONF.tacker.mgmt_driver)
|
|
|
|
def _invoke(self, vnf_dict, **kwargs):
|
|
method = inspect.stack()[1][3]
|
|
return self._mgmt_manager.invoke(
|
|
self._mgmt_driver_name(vnf_dict), method, **kwargs)
|
|
|
|
def mgmt_create_pre(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_create_post(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_update_pre(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_update_post(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_delete_pre(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_delete_post(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_get_config(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_url(self, context, vnf_dict):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict)
|
|
|
|
def mgmt_call(self, context, vnf_dict, kwargs):
|
|
return self._invoke(
|
|
vnf_dict, plugin=self, context=context, vnf=vnf_dict,
|
|
kwargs=kwargs)
|
|
|
|
|
|
class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|
"""VNFMPlugin which supports VNFM framework.
|
|
|
|
Plugin which supports Tacker framework
|
|
"""
|
|
OPTS = [
|
|
cfg.ListOpt(
|
|
'infra_driver', default=['heat', 'noop', 'openstack'],
|
|
help=_('Hosting vnf drivers tacker plugin will use')),
|
|
]
|
|
cfg.CONF.register_opts(OPTS, 'tacker')
|
|
supported_extension_aliases = ['vnfm']
|
|
|
|
def __init__(self):
|
|
super(VNFMPlugin, self).__init__()
|
|
self._pool = eventlet.GreenPool()
|
|
self.boot_wait = cfg.CONF.tacker.boot_wait
|
|
self.vim_client = vim_client.VimClient()
|
|
self._vnf_manager = driver_manager.DriverManager(
|
|
'tacker.tacker.vnfm.drivers',
|
|
cfg.CONF.tacker.infra_driver)
|
|
self._vnf_monitor = monitor.VNFMonitor(self.boot_wait)
|
|
self._vnf_alarm_monitor = monitor.VNFAlarmMonitor()
|
|
|
|
def spawn_n(self, function, *args, **kwargs):
|
|
self._pool.spawn_n(function, *args, **kwargs)
|
|
|
|
def create_vnfd(self, context, vnfd):
|
|
vnfd_data = vnfd['vnfd']
|
|
template = vnfd_data['attributes'].get('vnfd')
|
|
if isinstance(template, dict):
|
|
# TODO(sripriya) remove this yaml dump once db supports storing
|
|
# json format of yaml files in a separate column instead of
|
|
# key value string pairs in vnf attributes table
|
|
vnfd_data['attributes']['vnfd'] = yaml.safe_dump(
|
|
template)
|
|
elif isinstance(template, str):
|
|
self._report_deprecated_yaml_str()
|
|
if "tosca_definitions_version" not in template:
|
|
versionutils.report_deprecated_feature(LOG, 'VNFD legacy vnfds'
|
|
' are deprecated since Mitaka release and will be removed in'
|
|
' Ocata release. Please use NFV TOSCA vnfds.')
|
|
|
|
LOG.debug(_('vnfd %s'), vnfd_data)
|
|
|
|
if 'infra_driver' in vnfd_data or 'mgmt_driver' in vnfd_data:
|
|
versionutils.report_deprecated_feature(LOG, "Deriving "
|
|
"infra_driver and mgmt_driver from VNFD API is deprecated and"
|
|
" will be removed in Ocata. infra_driver will be automatically"
|
|
" derived from target vim type. mgmt_driver will be derived "
|
|
"from TOSCA template values.")
|
|
name = vnfd_data['name']
|
|
if self._get_by_name(context, vnfm_db.VNFD, name):
|
|
raise exceptions.DuplicateResourceName(resource='VNFD', name=name)
|
|
|
|
service_types = vnfd_data.get('service_types')
|
|
if not attributes.is_attr_set(service_types):
|
|
LOG.debug(_('service type must be specified'))
|
|
raise vnfm.ServiceTypesNotSpecified()
|
|
for service_type in service_types:
|
|
# TODO(yamahata):
|
|
# framework doesn't know what services are valid for now.
|
|
# so doesn't check it here yet.
|
|
pass
|
|
|
|
self._parse_template_input(vnfd)
|
|
return super(VNFMPlugin, self).create_vnfd(
|
|
context, vnfd)
|
|
|
|
def _parse_template_input(self, vnfd):
|
|
vnfd_dict = vnfd['vnfd']
|
|
vnfd_yaml = vnfd_dict['attributes'].get('vnfd')
|
|
if vnfd_yaml is None:
|
|
return
|
|
|
|
inner_vnfd_dict = yaml.load(vnfd_yaml)
|
|
LOG.debug(_('vnfd_dict: %s'), inner_vnfd_dict)
|
|
|
|
if 'tosca_definitions_version' in inner_vnfd_dict:
|
|
# Prepend the tacker_defs.yaml import file with the full
|
|
# path to the file
|
|
toscautils.updateimports(inner_vnfd_dict)
|
|
|
|
try:
|
|
tosca = ToscaTemplate(a_file=False,
|
|
yaml_dict_tpl=inner_vnfd_dict)
|
|
except Exception as e:
|
|
LOG.exception(_("tosca-parser error: %s"), str(e))
|
|
raise vnfm.ToscaParserFailed(error_msg_details=str(e))
|
|
|
|
if ('description' not in vnfd_dict or
|
|
vnfd_dict['description'] == ''):
|
|
vnfd_dict['description'] = inner_vnfd_dict.get(
|
|
'description', '')
|
|
if (('name' not in vnfd_dict or
|
|
not len(vnfd_dict['name'])) and
|
|
'metadata' in inner_vnfd_dict):
|
|
vnfd_dict['name'] = inner_vnfd_dict['metadata'].get(
|
|
'template_name', '')
|
|
|
|
vnfd_dict['mgmt_driver'] = toscautils.get_mgmt_driver(
|
|
tosca)
|
|
else:
|
|
KEY_LIST = (('name', 'template_name'),
|
|
('description', 'description'))
|
|
|
|
vnfd_dict.update(
|
|
dict((key, inner_vnfd_dict[vnfd_key]) for (key, vnfd_key)
|
|
in KEY_LIST
|
|
if ((key not in vnfd_dict or
|
|
vnfd_dict[key] == '') and
|
|
vnfd_key in inner_vnfd_dict and
|
|
inner_vnfd_dict[vnfd_key] != '')))
|
|
|
|
service_types = inner_vnfd_dict.get(
|
|
'service_properties', {}).get('type', [])
|
|
if service_types:
|
|
vnfd_dict.setdefault('service_types', []).extend(
|
|
[{'service_type': service_type}
|
|
for service_type in service_types])
|
|
# TODO(anyone) - this code assumes one mgmt_driver per VNFD???
|
|
for vdu in inner_vnfd_dict.get('vdus', {}).values():
|
|
mgmt_driver = vdu.get('mgmt_driver')
|
|
if mgmt_driver:
|
|
vnfd_dict['mgmt_driver'] = mgmt_driver
|
|
LOG.debug(_('vnfd %s'), vnfd)
|
|
|
|
def add_vnf_to_monitor(self, vnf_dict, infra_driver):
|
|
dev_attrs = vnf_dict['attributes']
|
|
mgmt_url = vnf_dict['mgmt_url']
|
|
if 'monitoring_policy' in dev_attrs and mgmt_url:
|
|
def action_cb(action):
|
|
action_cls = monitor.ActionPolicy.get_policy(action,
|
|
infra_driver)
|
|
if action_cls:
|
|
action_cls.execute_action(self, hosting_vnf['vnf'])
|
|
|
|
hosting_vnf = self._vnf_monitor.to_hosting_vnf(
|
|
vnf_dict, action_cb)
|
|
LOG.debug('hosting_vnf: %s', hosting_vnf)
|
|
self._vnf_monitor.add_hosting_vnf(hosting_vnf)
|
|
|
|
def add_alarm_url_to_vnf(self, context, vnf_dict):
|
|
vnfd_yaml = vnf_dict['vnfd']['attributes'].get('vnfd', '')
|
|
vnfd_dict = yaml.load(vnfd_yaml)
|
|
if vnfd_dict and vnfd_dict.get('tosca_definitions_version'):
|
|
polices = vnfd_dict['topology_template'].get('policies', [])
|
|
for policy_dict in polices:
|
|
name, policy = list(policy_dict.items())[0]
|
|
if policy['type'] in constants.POLICY_ALARMING:
|
|
alarm_url =\
|
|
self._vnf_alarm_monitor.update_vnf_with_alarm(
|
|
self, context, vnf_dict, policy)
|
|
vnf_dict['attributes']['alarming_policy'] = vnf_dict['id']
|
|
vnf_dict['attributes'].update(alarm_url)
|
|
break
|
|
|
|
def config_vnf(self, context, vnf_dict):
|
|
config = vnf_dict['attributes'].get('config')
|
|
if not config:
|
|
return
|
|
eventlet.sleep(self.boot_wait) # wait for vm to be ready
|
|
vnf_id = vnf_dict['id']
|
|
update = {
|
|
'vnf': {
|
|
'id': vnf_id,
|
|
'attributes': {'config': config},
|
|
}
|
|
}
|
|
self.update_vnf(context, vnf_id, update)
|
|
|
|
def _get_infra_driver(self, context, vnf_info):
|
|
vim_res = self.get_vim(context, vnf_info)
|
|
return vim_res['vim_type'], vim_res['vim_auth']
|
|
|
|
def _create_vnf_wait(self, context, vnf_dict, auth_attr, driver_name):
|
|
vnf_id = vnf_dict['id']
|
|
instance_id = self._instance_id(vnf_dict)
|
|
create_failed = False
|
|
|
|
try:
|
|
self._vnf_manager.invoke(
|
|
driver_name, 'create_wait', plugin=self, context=context,
|
|
vnf_dict=vnf_dict, vnf_id=instance_id,
|
|
auth_attr=auth_attr)
|
|
except vnfm.VNFCreateWaitFailed as e:
|
|
LOG.error(_LE("VNF Create failed for vnf_id %s"), vnf_id)
|
|
create_failed = True
|
|
vnf_dict['status'] = constants.ERROR
|
|
self.set_vnf_error_status_reason(context, vnf_id,
|
|
six.text_type(e))
|
|
|
|
if instance_id is None or create_failed:
|
|
mgmt_url = None
|
|
else:
|
|
# mgmt_url = self.mgmt_url(context, vnf_dict)
|
|
# FIXME(yamahata):
|
|
mgmt_url = vnf_dict['mgmt_url']
|
|
|
|
self._create_vnf_post(
|
|
context, vnf_id, instance_id, mgmt_url, vnf_dict)
|
|
self.mgmt_create_post(context, vnf_dict)
|
|
|
|
if instance_id is None or create_failed:
|
|
return
|
|
|
|
vnf_dict['mgmt_url'] = mgmt_url
|
|
|
|
kwargs = {
|
|
mgmt_constants.KEY_ACTION: mgmt_constants.ACTION_CREATE_VNF,
|
|
mgmt_constants.KEY_KWARGS: {'vnf': vnf_dict},
|
|
}
|
|
new_status = constants.ACTIVE
|
|
try:
|
|
self.mgmt_call(context, vnf_dict, kwargs)
|
|
except exceptions.MgmtDriverException:
|
|
LOG.error(_('VNF configuration failed'))
|
|
new_status = constants.ERROR
|
|
self.set_vnf_error_status_reason(context, vnf_id,
|
|
'Unable to configure VDU')
|
|
vnf_dict['status'] = new_status
|
|
self._create_vnf_status(context, vnf_id, new_status)
|
|
|
|
def get_vim(self, context, vnf):
|
|
region_name = vnf.setdefault('placement_attr', {}).get(
|
|
'region_name', None)
|
|
vim_res = self.vim_client.get_vim(context, vnf['vim_id'],
|
|
region_name)
|
|
vnf['placement_attr']['vim_name'] = vim_res['vim_name']
|
|
vnf['vim_id'] = vim_res['vim_id']
|
|
return vim_res
|
|
|
|
def _create_vnf(self, context, vnf, vim_auth, driver_name):
|
|
vnf_dict = self._create_vnf_pre(
|
|
context, vnf) if not vnf.get('id') else vnf
|
|
vnf_id = vnf_dict['id']
|
|
LOG.debug(_('vnf_dict %s'), vnf_dict)
|
|
self.mgmt_create_pre(context, vnf_dict)
|
|
self.add_alarm_url_to_vnf(context, vnf_dict)
|
|
try:
|
|
instance_id = self._vnf_manager.invoke(
|
|
driver_name, 'create', plugin=self,
|
|
context=context, vnf=vnf_dict, auth_attr=vim_auth)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
self.delete_vnf(context, vnf_id)
|
|
|
|
if instance_id is None:
|
|
self._create_vnf_post(context, vnf_id, None, None,
|
|
vnf_dict)
|
|
return
|
|
vnf_dict['instance_id'] = instance_id
|
|
return vnf_dict
|
|
|
|
def create_vnf(self, context, vnf):
|
|
vnf_info = vnf['vnf']
|
|
name = vnf_info['name']
|
|
if self._get_by_name(context, vnfm_db.VNF, name):
|
|
raise exceptions.DuplicateResourceName(resource='VNF', name=name)
|
|
vnf_attributes = vnf_info['attributes']
|
|
if vnf_attributes.get('param_values'):
|
|
param = vnf_attributes['param_values']
|
|
if isinstance(param, dict):
|
|
# TODO(sripriya) remove this yaml dump once db supports storing
|
|
# json format of yaml files in a separate column instead of
|
|
# key value string pairs in vnf attributes table
|
|
vnf_attributes['param_values'] = yaml.safe_dump(param)
|
|
else:
|
|
self._report_deprecated_yaml_str()
|
|
if vnf_attributes.get('config'):
|
|
config = vnf_attributes['config']
|
|
if isinstance(config, dict):
|
|
# TODO(sripriya) remove this yaml dump once db supports storing
|
|
# json format of yaml files in a separate column instead of
|
|
# key value string pairs in vnf attributes table
|
|
vnf_attributes['config'] = yaml.safe_dump(config)
|
|
else:
|
|
self._report_deprecated_yaml_str()
|
|
infra_driver, vim_auth = self._get_infra_driver(context, vnf_info)
|
|
if infra_driver not in self._vnf_manager:
|
|
LOG.debug(_('unknown vim driver '
|
|
'%(infra_driver)s in %(drivers)s'),
|
|
{'infra_driver': infra_driver,
|
|
'drivers': cfg.CONF.tacker.infra_driver})
|
|
raise vnfm.InvalidInfraDriver(vim_name=infra_driver)
|
|
|
|
vnf_dict = self._create_vnf(context, vnf_info, vim_auth, infra_driver)
|
|
|
|
def create_vnf_wait():
|
|
self._create_vnf_wait(context, vnf_dict, vim_auth, infra_driver)
|
|
if vnf_dict['status'] is not constants.ERROR:
|
|
self.add_vnf_to_monitor(vnf_dict, infra_driver)
|
|
self.config_vnf(context, vnf_dict)
|
|
self.spawn_n(create_vnf_wait)
|
|
return vnf_dict
|
|
|
|
# not for wsgi, but for service to create hosting vnf
|
|
# the vnf is NOT added to monitor.
|
|
def create_vnf_sync(self, context, vnf):
|
|
infra_driver, vim_auth = self._get_infra_driver(context, vnf)
|
|
vnf_dict = self._create_vnf(context, vnf, vim_auth, infra_driver)
|
|
self._create_vnf_wait(context, vnf_dict, vim_auth, infra_driver)
|
|
return vnf_dict
|
|
|
|
def _update_vnf_wait(self, context, vnf_dict, vim_auth, driver_name):
|
|
instance_id = self._instance_id(vnf_dict)
|
|
kwargs = {
|
|
mgmt_constants.KEY_ACTION: mgmt_constants.ACTION_UPDATE_VNF,
|
|
mgmt_constants.KEY_KWARGS: {'vnf': vnf_dict},
|
|
}
|
|
new_status = constants.ACTIVE
|
|
placement_attr = vnf_dict['placement_attr']
|
|
region_name = placement_attr.get('region_name')
|
|
|
|
try:
|
|
self._vnf_manager.invoke(
|
|
driver_name, 'update_wait', plugin=self,
|
|
context=context, vnf_id=instance_id, auth_attr=vim_auth,
|
|
region_name=region_name)
|
|
self.mgmt_call(context, vnf_dict, kwargs)
|
|
except exceptions.MgmtDriverException as e:
|
|
LOG.error(_('VNF configuration failed'))
|
|
new_status = constants.ERROR
|
|
self._vnf_monitor.delete_hosting_vnf(vnf_dict['id'])
|
|
self.set_vnf_error_status_reason(context, vnf_dict['id'],
|
|
six.text_type(e))
|
|
vnf_dict['status'] = new_status
|
|
self.mgmt_update_post(context, vnf_dict)
|
|
|
|
self._update_vnf_post(context, vnf_dict['id'],
|
|
new_status, vnf_dict)
|
|
|
|
def update_vnf(self, context, vnf_id, vnf):
|
|
vnf_attributes = vnf['vnf']['attributes']
|
|
if vnf_attributes.get('config'):
|
|
config = vnf_attributes['config']
|
|
if isinstance(config, dict):
|
|
# TODO(sripriya) remove this yaml dump once db supports storing
|
|
# json format of yaml files in a separate column instead of
|
|
# key value string pairs in vnf attributes table
|
|
vnf_attributes['config'] = yaml.safe_dump(config)
|
|
else:
|
|
self._report_deprecated_yaml_str()
|
|
vnf_dict = self._update_vnf_pre(context, vnf_id)
|
|
driver_name, vim_auth = self._get_infra_driver(context, vnf_dict)
|
|
instance_id = self._instance_id(vnf_dict)
|
|
|
|
try:
|
|
self.mgmt_update_pre(context, vnf_dict)
|
|
self._vnf_manager.invoke(
|
|
driver_name, 'update', plugin=self, context=context,
|
|
vnf_id=instance_id, vnf_dict=vnf_dict,
|
|
vnf=vnf, auth_attr=vim_auth)
|
|
except Exception as e:
|
|
with excutils.save_and_reraise_exception():
|
|
vnf_dict['status'] = constants.ERROR
|
|
self._vnf_monitor.delete_hosting_vnf(vnf_id)
|
|
self.set_vnf_error_status_reason(context,
|
|
vnf_dict['id'],
|
|
six.text_type(e))
|
|
self.mgmt_update_post(context, vnf_dict)
|
|
self._update_vnf_post(context, vnf_id, constants.ERROR)
|
|
|
|
self.spawn_n(self._update_vnf_wait, context, vnf_dict, vim_auth,
|
|
driver_name)
|
|
return vnf_dict
|
|
|
|
def _delete_vnf_wait(self, context, vnf_dict, auth_attr, driver_name):
|
|
instance_id = self._instance_id(vnf_dict)
|
|
e = None
|
|
if instance_id:
|
|
placement_attr = vnf_dict['placement_attr']
|
|
region_name = placement_attr.get('region_name')
|
|
try:
|
|
self._vnf_manager.invoke(
|
|
driver_name,
|
|
'delete_wait',
|
|
plugin=self,
|
|
context=context,
|
|
vnf_id=instance_id,
|
|
auth_attr=auth_attr,
|
|
region_name=region_name)
|
|
except Exception as e_:
|
|
e = e_
|
|
vnf_dict['status'] = constants.ERROR
|
|
vnf_dict['error_reason'] = six.text_type(e)
|
|
LOG.exception(_('_delete_vnf_wait'))
|
|
|
|
self.mgmt_delete_post(context, vnf_dict)
|
|
vnf_id = vnf_dict['id']
|
|
self._delete_vnf_post(context, vnf_id, e)
|
|
|
|
def delete_vnf(self, context, vnf_id):
|
|
vnf_dict = self._delete_vnf_pre(context, vnf_id)
|
|
driver_name, vim_auth = self._get_infra_driver(context, vnf_dict)
|
|
self._vnf_monitor.delete_hosting_vnf(vnf_id)
|
|
instance_id = self._instance_id(vnf_dict)
|
|
placement_attr = vnf_dict['placement_attr']
|
|
region_name = placement_attr.get('region_name')
|
|
kwargs = {
|
|
mgmt_constants.KEY_ACTION: mgmt_constants.ACTION_DELETE_VNF,
|
|
mgmt_constants.KEY_KWARGS: {'vnf': vnf_dict},
|
|
}
|
|
try:
|
|
self.mgmt_delete_pre(context, vnf_dict)
|
|
self.mgmt_call(context, vnf_dict, kwargs)
|
|
if instance_id:
|
|
self._vnf_manager.invoke(driver_name,
|
|
'delete',
|
|
plugin=self,
|
|
context=context,
|
|
vnf_id=instance_id,
|
|
auth_attr=vim_auth,
|
|
region_name=region_name)
|
|
except Exception as e:
|
|
# TODO(yamahata): when the devaice is already deleted. mask
|
|
# the error, and delete row in db
|
|
# Other case mark error
|
|
with excutils.save_and_reraise_exception():
|
|
vnf_dict['status'] = constants.ERROR
|
|
vnf_dict['error_reason'] = six.text_type(e)
|
|
self.mgmt_delete_post(context, vnf_dict)
|
|
self._delete_vnf_post(context, vnf_id, e)
|
|
|
|
self.spawn_n(self._delete_vnf_wait, context, vnf_dict, vim_auth,
|
|
driver_name)
|
|
|
|
def _handle_vnf_scaling(self, context, policy):
|
|
# validate
|
|
def _validate_scaling_policy():
|
|
type = policy['type']
|
|
|
|
if type not in constants.POLICY_ACTIONS.keys():
|
|
raise exceptions.VnfPolicyTypeInvalid(
|
|
type=type,
|
|
valid_types=constants.POLICY_ACTIONS.keys(),
|
|
policy=policy['id']
|
|
)
|
|
action = policy['action']
|
|
|
|
if action not in constants.POLICY_ACTIONS[type]:
|
|
raise exceptions.VnfPolicyActionInvalid(
|
|
action=action,
|
|
valid_actions=constants.POLICY_ACTIONS[type],
|
|
policy=policy['id']
|
|
)
|
|
|
|
LOG.debug(_("Policy %s is validated successfully"), policy['id'])
|
|
|
|
def _get_status():
|
|
if policy['action'] == constants.ACTION_SCALE_IN:
|
|
status = constants.PENDING_SCALE_IN
|
|
else:
|
|
status = constants.PENDING_SCALE_OUT
|
|
|
|
return status
|
|
|
|
# pre
|
|
def _handle_vnf_scaling_pre():
|
|
status = _get_status()
|
|
result = self._update_vnf_scaling_status(context,
|
|
policy,
|
|
[constants.ACTIVE],
|
|
status)
|
|
LOG.debug(_("Policy %(policy)s vnf is at %(status)s"),
|
|
{'policy': policy['id'],
|
|
'status': status})
|
|
return result
|
|
|
|
# post
|
|
def _handle_vnf_scaling_post(new_status, mgmt_url=None):
|
|
status = _get_status()
|
|
result = self._update_vnf_scaling_status(context,
|
|
policy,
|
|
[status],
|
|
new_status,
|
|
mgmt_url)
|
|
LOG.debug(_("Policy %(policy)s vnf is at %(status)s"),
|
|
{'policy': policy['id'],
|
|
'status': new_status})
|
|
return result
|
|
|
|
# action
|
|
def _vnf_policy_action():
|
|
try:
|
|
last_event_id = self._vnf_manager.invoke(
|
|
infra_driver,
|
|
'scale',
|
|
plugin=self,
|
|
context=context,
|
|
auth_attr=vim_auth,
|
|
policy=policy,
|
|
region_name=region_name
|
|
)
|
|
LOG.debug(_("Policy %s action is started successfully"),
|
|
policy['id'])
|
|
return last_event_id
|
|
except Exception as e:
|
|
LOG.error(_("Policy %s action is failed to start"),
|
|
policy)
|
|
with excutils.save_and_reraise_exception():
|
|
vnf['status'] = constants.ERROR
|
|
self.set_vnf_error_status_reason(
|
|
context,
|
|
policy['vnf']['id'],
|
|
six.text_type(e))
|
|
_handle_vnf_scaling_post(constants.ERROR)
|
|
|
|
# wait
|
|
def _vnf_policy_action_wait():
|
|
try:
|
|
LOG.debug(_("Policy %s action is in progress"),
|
|
policy['id'])
|
|
mgmt_url = self._vnf_manager.invoke(
|
|
infra_driver,
|
|
'scale_wait',
|
|
plugin=self,
|
|
context=context,
|
|
auth_attr=vim_auth,
|
|
policy=policy,
|
|
region_name=region_name,
|
|
last_event_id=last_event_id
|
|
)
|
|
LOG.debug(_("Policy %s action is completed successfully"),
|
|
policy['id'])
|
|
_handle_vnf_scaling_post(constants.ACTIVE, mgmt_url)
|
|
# TODO(kanagaraj-manickam): Add support for config and mgmt
|
|
except Exception as e:
|
|
LOG.error(_("Policy %s action is failed to complete") %
|
|
policy['id'])
|
|
with excutils.save_and_reraise_exception():
|
|
self.set_vnf_error_status_reason(
|
|
context,
|
|
policy['vnf']['id'],
|
|
six.text_type(e))
|
|
_handle_vnf_scaling_post(constants.ERROR)
|
|
|
|
_validate_scaling_policy()
|
|
|
|
vnf = _handle_vnf_scaling_pre()
|
|
policy['instance_id'] = vnf['instance_id']
|
|
|
|
infra_driver, vim_auth = self._get_infra_driver(context, vnf)
|
|
region_name = vnf.get('placement_attr', {}).get('region_name', None)
|
|
last_event_id = _vnf_policy_action()
|
|
self.spawn_n(_vnf_policy_action_wait)
|
|
|
|
return policy
|
|
|
|
def _report_deprecated_yaml_str(self):
|
|
utils.deprecate_warning(what='yaml as string',
|
|
as_of='N', in_favor_of='yaml as dictionary')
|
|
|
|
def _make_policy_dict(self, vnf, name, policy):
|
|
p = {}
|
|
p['type'] = policy.get('type')
|
|
p['properties'] = policy.get('properties') or policy.get('triggers')
|
|
p['vnf'] = vnf
|
|
p['name'] = name
|
|
p['id'] = p['name']
|
|
return p
|
|
|
|
def get_vnf_policies(
|
|
self, context, vnf_id, filters=None, fields=None):
|
|
vnf = self.get_vnf(context, vnf_id)
|
|
vnfd_tmpl = yaml.load(vnf['vnfd']['attributes']['vnfd'])
|
|
policy_list = []
|
|
|
|
if vnfd_tmpl.get('tosca_definitions_version'):
|
|
polices = vnfd_tmpl['topology_template'].get('policies', [])
|
|
for policy_dict in polices:
|
|
for name, policy in policy_dict.items():
|
|
def _add(policy):
|
|
p = self._make_policy_dict(vnf, name, policy)
|
|
p['name'] = name
|
|
policy_list.append(p)
|
|
|
|
# Check for filters
|
|
if filters.get('name') or filters.get('type'):
|
|
if name == filters.get('name'):
|
|
_add(policy)
|
|
if policy['type'] == filters.get('type'):
|
|
_add(policy)
|
|
break
|
|
else:
|
|
continue
|
|
|
|
_add(policy)
|
|
|
|
return policy_list
|
|
|
|
def get_vnf_policy(
|
|
self, context, policy_id, vnf_id, fields=None):
|
|
policies = self.get_vnf_policies(context,
|
|
vnf_id,
|
|
filters={'name': policy_id})
|
|
if policies:
|
|
return policies[0]
|
|
|
|
raise exceptions.VnfPolicyNotFound(policy=policy_id,
|
|
vnf_id=vnf_id)
|
|
|
|
def create_vnf_scale(self, context, vnf_id, scale):
|
|
policy_ = self.get_vnf_policy(context,
|
|
scale['scale']['policy'],
|
|
vnf_id)
|
|
policy_.update({'action': scale['scale']['type']})
|
|
self._handle_vnf_scaling(context, policy_)
|
|
|
|
return scale['scale']
|
|
|
|
def get_vnf_policy_by_type(self, context, vnf_id, policy_type=None, fields=None): # noqa
|
|
policies = self.get_vnf_policies(context,
|
|
vnf_id,
|
|
filters={'type': policy_type})
|
|
if policies:
|
|
return policies[0]
|
|
|
|
raise exceptions.VnfPolicyTypeInvalid(type=constants.POLICY_ALARMING,
|
|
vnf_id=vnf_id)
|
|
|
|
def _validate_alarming_policy(self, context, vnf_id, trigger):
|
|
# validate alarm status
|
|
if not self._vnf_alarm_monitor.process_alarm_for_vnf(vnf_id, trigger):
|
|
raise exceptions.AlarmUrlInvalid(vnf_id=vnf_id)
|
|
|
|
policy_ = None
|
|
action_ = None
|
|
# validate policy action. if action is composite, split it.
|
|
# ex: SP1-in, SP1-out
|
|
action = trigger['action_name']
|
|
sp_action = action.split('-')
|
|
if len(sp_action) == 2:
|
|
bk_policy_name = sp_action[0]
|
|
bk_policy_action = sp_action[1]
|
|
policies_ = self.get_vnf_policies(context, vnf_id,
|
|
filters={'name': bk_policy_name})
|
|
if policies_:
|
|
policy_ = policies_[0]
|
|
action_ = bk_policy_action
|
|
|
|
if not policy_:
|
|
if action not in constants.DEFAULT_ALARM_ACTIONS:
|
|
policy_ = self.get_vnf_policy(context, action, vnf_id)
|
|
LOG.debug(_("Trigger %s is validated successfully") % trigger)
|
|
return policy_, action_
|
|
# validate url
|
|
|
|
def _get_vnf_triggers(self, context, vnf_id, filters=None, fields=None):
|
|
policy = self.get_vnf_policy_by_type(
|
|
context, vnf_id, policy_type=constants.POLICY_ALARMING)
|
|
triggers = policy['properties']
|
|
vnf_trigger = dict()
|
|
for trigger_name, trigger_dict in triggers.items():
|
|
if trigger_name == filters.get('name'):
|
|
vnf_trigger['trigger'] = {trigger_name: trigger_dict}
|
|
vnf_trigger['vnf'] = policy['vnf']
|
|
break
|
|
|
|
return vnf_trigger
|
|
|
|
def get_vnf_trigger(self, context, vnf_id, trigger_name):
|
|
trigger = self._get_vnf_triggers(
|
|
context, vnf_id, filters={'name': trigger_name})
|
|
if not trigger:
|
|
raise exceptions.TriggerNotFound(
|
|
trigger_name=trigger_name,
|
|
vnf_id=vnf_id
|
|
)
|
|
return trigger
|
|
|
|
def _handle_vnf_monitoring(self, context, trigger):
|
|
vnf_dict = trigger['vnf']
|
|
if trigger['action_name'] in constants.DEFAULT_ALARM_ACTIONS:
|
|
action = trigger['action_name']
|
|
LOG.debug(_('vnf for monitoring: %s'), vnf_dict)
|
|
infra_driver, vim_auth = self._get_infra_driver(context, vnf_dict)
|
|
action_cls = monitor.ActionPolicy.get_policy(action,
|
|
infra_driver)
|
|
if action_cls:
|
|
action_cls.execute_action(self, vnf_dict)
|
|
|
|
if trigger.get('bckend_policy'):
|
|
bckend_policy = trigger['bckend_policy']
|
|
bckend_policy_type = bckend_policy['type']
|
|
if bckend_policy_type == constants.POLICY_SCALING:
|
|
if vnf_dict['status'] != constants.ACTIVE:
|
|
LOG.info(context, vnf_dict,
|
|
"Scaling Policy action skipped")
|
|
return
|
|
action = 'scaling'
|
|
scale = {}
|
|
scale.setdefault('scale', {})
|
|
scale['scale']['type'] = trigger['bckend_action']
|
|
scale['scale']['policy'] = bckend_policy['name']
|
|
infra_driver, vim_auth = self._get_infra_driver(context,
|
|
vnf_dict)
|
|
action_cls = monitor.ActionPolicy.get_policy(action,
|
|
infra_driver)
|
|
if action_cls:
|
|
action_cls.execute_action(self, vnf_dict, scale)
|
|
|
|
def create_vnf_trigger(
|
|
self, context, vnf_id, trigger):
|
|
trigger_ = self.get_vnf_trigger(
|
|
context, vnf_id, trigger['trigger']['policy_name'])
|
|
trigger_.update({'action_name': trigger['trigger']['action_name']})
|
|
trigger_.update({'params': trigger['trigger']['params']})
|
|
bk_policy, bk_action = self._validate_alarming_policy(
|
|
context, vnf_id, trigger_)
|
|
if bk_policy:
|
|
trigger_.update({'bckend_policy': bk_policy,
|
|
'bckend_action': bk_action})
|
|
self._handle_vnf_monitoring(context, trigger_)
|
|
return trigger['trigger']
|
|
|
|
def get_vnf_resources(self, context, vnf_id, fields=None, filters=None):
|
|
vnf_info = self.get_vnf(context, vnf_id)
|
|
infra_driver, vim_auth = self._get_infra_driver(context, vnf_info)
|
|
if vnf_info['status'] == constants.ACTIVE:
|
|
vnf_details = self._vnf_manager.invoke(infra_driver,
|
|
'get_resource_info',
|
|
plugin=self,
|
|
context=context,
|
|
vnf_info=vnf_info,
|
|
auth_attr=vim_auth)
|
|
resources = [{'name': name,
|
|
'type': info.get('type'),
|
|
'id': info.get('id')}
|
|
for name, info in vnf_details.items()]
|
|
return resources
|
|
# Raise exception when VNF.status != ACTIVE
|
|
else:
|
|
raise vnfm.VNFInactive(vnf_id=vnf_id,
|
|
message=_(' Cannot fetch details'))
|