
- Apmec to collaborate with other orchestration frameworks (e.g, NFV framework) - for oprators to excecute smart placement decisions of MEC applications Change-Id: I28fe23029f11632f1909967238b9fd440aa6f936
944 lines
43 KiB
Python
944 lines
43 KiB
Python
# Copyright 2016 Brocade Communications System, Inc.
|
|
# 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 ast
|
|
import copy
|
|
import eventlet
|
|
import random
|
|
import time
|
|
import yaml
|
|
|
|
from oslo_config import cfg
|
|
from oslo_log import log as logging
|
|
from oslo_utils import excutils
|
|
from oslo_utils import uuidutils
|
|
|
|
|
|
from apmec._i18n import _
|
|
from apmec.common import driver_manager
|
|
from apmec.common import log
|
|
from apmec.common import utils
|
|
from apmec.db.meso import meso_db
|
|
from apmec.extensions import common_services as cs
|
|
from apmec.extensions import meso
|
|
from apmec import manager
|
|
|
|
from apmec.mem import vim_client
|
|
from apmec.plugins.common import constants
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
CONF = cfg.CONF
|
|
NS_RETRIES = 30
|
|
NS_RETRY_WAIT = 6
|
|
MEC_RETRIES = 30
|
|
MEC_RETRY_WAIT = 6
|
|
VNFFG_RETRIES = 30
|
|
VNFFG_RETRY_WAIT = 6
|
|
|
|
|
|
def config_opts():
|
|
return [('meso', MesoPlugin.OPTS)]
|
|
|
|
|
|
NF_CAP_MAX = 3
|
|
VNF_LIST = ['VNF1', 'VNF2', 'VNF2', 'VNF3', 'VNF4', 'VNF5', 'VNF6']
|
|
VM_CAPA = dict()
|
|
for vnf_name in VNF_LIST:
|
|
VM_CAPA[vnf_name] = random.randint(1, NF_CAP_MAX)
|
|
|
|
|
|
class MesoPlugin(meso_db.MESOPluginDb):
|
|
"""MESO reference plugin for MESO extension
|
|
|
|
Implements the MESO extension and defines public facing APIs for VIM
|
|
operations. MESO internally invokes the appropriate VIM driver in
|
|
backend based on configured VIM types. Plugin also interacts with MEM
|
|
extension for providing the specified VIM information
|
|
"""
|
|
supported_extension_aliases = ['meso']
|
|
|
|
OPTS = [
|
|
cfg.ListOpt(
|
|
'nfv_drivers', default=['tacker'],
|
|
help=_('NFV drivers for launching NSs')),
|
|
]
|
|
cfg.CONF.register_opts(OPTS, 'meso')
|
|
|
|
def __init__(self):
|
|
super(MesoPlugin, self).__init__()
|
|
self._pool = eventlet.GreenPool()
|
|
self._nfv_drivers = driver_manager.DriverManager(
|
|
'apmec.meso.drivers',
|
|
cfg.CONF.meso.nfv_drivers)
|
|
self.vim_client = vim_client.VimClient()
|
|
|
|
def get_auth_dict(self, context):
|
|
auth = CONF.keystone_authtoken
|
|
return {
|
|
'auth_url': auth.auth_url + '/v3',
|
|
'token': context.auth_token,
|
|
'project_domain_name': auth.project_domain_name or context.domain,
|
|
'project_name': context.tenant_name
|
|
}
|
|
|
|
def spawn_n(self, function, *args, **kwargs):
|
|
self._pool.spawn_n(function, *args, **kwargs)
|
|
|
|
def _build_vim_auth(self, context, vim_info):
|
|
LOG.debug('VIM id is %s', vim_info['id'])
|
|
vim_auth = vim_info['auth_cred']
|
|
vim_auth['password'] = self._decode_vim_auth(context,
|
|
vim_info['id'],
|
|
vim_auth)
|
|
vim_auth['auth_url'] = vim_info['auth_url']
|
|
|
|
# These attributes are needless for authentication
|
|
# from keystone, so we remove them.
|
|
needless_attrs = ['key_type', 'secret_uuid']
|
|
for attr in needless_attrs:
|
|
if attr in vim_auth:
|
|
vim_auth.pop(attr, None)
|
|
return vim_auth
|
|
|
|
@log.log
|
|
def create_mesd(self, context, mesd):
|
|
mesd_data = mesd['mesd']
|
|
template = mesd_data['attributes'].get('mesd')
|
|
if isinstance(template, dict):
|
|
mesd_data['attributes']['mesd'] = yaml.safe_dump(
|
|
template)
|
|
LOG.debug('mesd %s', mesd_data)
|
|
|
|
if 'template_source' in mesd_data:
|
|
template_source = mesd_data.get('template_source')
|
|
else:
|
|
template_source = "onboarded"
|
|
mesd['mesd']['template_source'] = template_source
|
|
|
|
self._parse_template_input(context, mesd)
|
|
return super(MesoPlugin, self).create_mesd(
|
|
context, mesd)
|
|
|
|
def _parse_template_input(self, context, mesd):
|
|
mesd_dict = mesd['mesd']
|
|
mesd_yaml = mesd_dict['attributes'].get('mesd')
|
|
inner_mesd_dict = yaml.safe_load(mesd_yaml)
|
|
mesd_dict['mesd_mapping'] = dict()
|
|
LOG.debug('mesd_dict: %s', inner_mesd_dict)
|
|
# From import we can deploy both NS and MEC Application
|
|
nsd_imports = inner_mesd_dict['imports'].get('nsds')
|
|
vnffg_imports = inner_mesd_dict['imports'].get('vnffgds')
|
|
if nsd_imports:
|
|
nsd_tpls = nsd_imports.get('nsd_templates')
|
|
nfv_driver = nsd_imports.get('nfv_driver')
|
|
if not nsd_tpls:
|
|
raise meso.NSDNotFound(mesd_name=mesd_dict['name'])
|
|
if nfv_driver.lower() not in\
|
|
[driver.lower() for driver in constants.NFV_DRIVER]:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
if isinstance(nsd_tpls, list):
|
|
mesd_dict['attributes']['nsds'] = '-'.join(nsd_tpls)
|
|
mesd_dict['mesd_mapping']['NSD'] = nsd_tpls
|
|
if vnffg_imports:
|
|
vnffgd_tpls = vnffg_imports.get('vnffgd_templates')
|
|
nfv_driver = vnffg_imports.get('nfv_driver')
|
|
if not vnffgd_tpls:
|
|
raise meso.VNFFGDNotFound(mesd_name=mesd_dict['name'])
|
|
if nfv_driver.lower() not in \
|
|
[driver.lower() for driver in constants.NFV_DRIVER]:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
if isinstance(vnffgd_tpls, list):
|
|
mesd_dict['mesd_mapping']['VNFFGD'] = vnffgd_tpls
|
|
mesd_dict['attributes']['vnffgds'] = '-'.join(vnffgd_tpls)
|
|
|
|
if ('description' not in mesd_dict or
|
|
mesd_dict['description'] == ''):
|
|
mesd_dict['description'] = inner_mesd_dict.get(
|
|
'description', '')
|
|
if (('name' not in mesd_dict or
|
|
not len(mesd_dict['name'])) and
|
|
'metadata' in inner_mesd_dict):
|
|
mesd_dict['name'] = inner_mesd_dict['metadata'].get(
|
|
'template_name', '')
|
|
|
|
LOG.debug('mesd %s', mesd)
|
|
|
|
def _get_mead_id(self, mead_name, onboarded_meads):
|
|
for mead in onboarded_meads:
|
|
if mead_name == mead['name']:
|
|
return mead['id']
|
|
|
|
@log.log
|
|
def create_mes(self, context, mes):
|
|
"""Create MES and corresponding MEAs.
|
|
|
|
:param mes: mes dict which contains mesd_id and attributes
|
|
This method has 2 steps:
|
|
step-1: Call MEO API to create MEAs
|
|
step-2: Call Tacker drivers to create NSs
|
|
"""
|
|
mes_info = mes['mes']
|
|
name = mes_info['name']
|
|
mes_info['mes_mapping'] = dict()
|
|
|
|
if mes_info.get('mesd_template'):
|
|
mesd_name = utils.generate_resource_name(name, 'inline')
|
|
mesd = {'mesd': {
|
|
'attributes': {'mesd': mes_info['mesd_template']},
|
|
'description': mes_info['description'],
|
|
'name': mesd_name,
|
|
'template_source': 'inline',
|
|
'tenant_id': mes_info['tenant_id']}}
|
|
mes_info['mesd_id'] = self.create_mesd(context, mesd).get('id')
|
|
|
|
mesd = self.get_mesd(context, mes['mes']['mesd_id'])
|
|
mesd_dict = yaml.safe_load(mesd['attributes']['mesd'])
|
|
meo_plugin = manager.ApmecManager.get_service_plugins()['MEO']
|
|
|
|
region_name = mes.setdefault('placement_attr', {}).get(
|
|
'region_name', None)
|
|
vim_res = self.vim_client.get_vim(context, mes['mes']['vim_id'],
|
|
region_name)
|
|
# driver_type = vim_res['vim_type']
|
|
if not mes['mes']['vim_id']:
|
|
mes['mes']['vim_id'] = vim_res['vim_id']
|
|
|
|
##########################################
|
|
# Detect MANO driver here:
|
|
# Defined in the Tosca template
|
|
nfv_driver = None
|
|
if mesd_dict['imports'].get('nsds'):
|
|
nfv_driver = mesd_dict['imports']['nsds']['nfv_driver']
|
|
nfv_driver = nfv_driver.lower()
|
|
if mesd_dict['imports'].get('vnffgds'):
|
|
nfv_driver = mesd_dict['imports']['vnffgds']['nfv_driver']
|
|
nfv_driver = nfv_driver.lower()
|
|
|
|
##########################################
|
|
def _find_vnf_ins(cd_mes):
|
|
al_ns_id_list = cd_mes['mes_mapping'].get('NS')
|
|
if not al_ns_id_list:
|
|
return None, None
|
|
al_ns_id = al_ns_id_list[0]
|
|
try:
|
|
ns_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_get',
|
|
ns_id=al_ns_id,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
except Exception:
|
|
return None, None
|
|
if ns_instance['status'] != 'ACTIVE':
|
|
return None, None
|
|
al_vnf = ns_instance['vnf_ids']
|
|
al_vnf_dict = ast.literal_eval(al_vnf)
|
|
return ns_instance['id'], al_vnf_dict
|
|
|
|
def _run_meso_algorithm(req_vnf_list):
|
|
is_accepted = False
|
|
al_mes_list = self.get_mess(context)
|
|
ns_candidate = dict()
|
|
for al_mes in al_mes_list:
|
|
ns_candidate[al_mes['id']] = dict()
|
|
if al_mes['status'] != "ACTIVE":
|
|
continue
|
|
al_ns_id, al_vnf_dict = _find_vnf_ins(al_mes)
|
|
if not al_ns_id:
|
|
continue
|
|
ns_candidate[al_mes['id']][al_ns_id] = dict()
|
|
for req_vnf_dict in req_vnf_list:
|
|
for vnf_name, al_vnf_id in al_vnf_dict.items():
|
|
if req_vnf_dict['name'] == vnf_name:
|
|
# Todo: remember to change this with VM capacity
|
|
len_diff =\
|
|
len([lend for lend in
|
|
al_mes['reused'][vnf_name]
|
|
if lend > 0])
|
|
avail = len_diff - req_vnf_dict['nf_ins']
|
|
ns_candidate[al_mes['id']][al_ns_id].\
|
|
update({vnf_name: avail})
|
|
|
|
ns_cds = dict()
|
|
deep_ns = dict()
|
|
for mesid, ns_data_dict in ns_candidate.items():
|
|
for nsid, resev_dict in ns_data_dict.items():
|
|
if len(resev_dict) == len(req_vnf_list):
|
|
nf_ins_list =\
|
|
[nf_ins for nf_name, nf_ins in
|
|
resev_dict.items() if nf_ins >= 0]
|
|
if len(nf_ins_list) == len(req_vnf_list):
|
|
total_ins = sum(nf_ins_list)
|
|
ns_cds[mesid] = total_ins
|
|
else:
|
|
extra_nf_ins_list =\
|
|
[-nf_ins for nf_name, nf_ins in
|
|
resev_dict.items() if nf_ins < 0]
|
|
total_ins = sum(extra_nf_ins_list)
|
|
deep_ns[mesid] = total_ins
|
|
if ns_cds:
|
|
selected_mes1 = min(ns_cds, key=ns_cds.get)
|
|
is_accepted = True
|
|
return is_accepted, selected_mes1, None
|
|
if deep_ns:
|
|
selected_mes2 = min(deep_ns, key=deep_ns.get)
|
|
is_accepted = True
|
|
return is_accepted, selected_mes2, ns_candidate[selected_mes2]
|
|
|
|
return is_accepted, None, None
|
|
|
|
build_nsd_dict = dict()
|
|
if mesd_dict['imports'].get('nsds'):
|
|
# For framework evaluation
|
|
nsd_template = mesd_dict['imports']['nsds']['nsd_templates']
|
|
if isinstance(nsd_template, dict):
|
|
if nsd_template.get('requirements'):
|
|
req_nf_dict = nsd_template['requirements']
|
|
req_nf_list = list()
|
|
for vnf_dict in req_nf_dict:
|
|
# Todo: make the requests more natural
|
|
req_nf_list.append(
|
|
{'name': vnf_dict['name'],
|
|
'nf_ins': int(vnf_dict['vnfd_template'][5])})
|
|
is_accepted, cd_mes_id, cd_vnf_dict =\
|
|
_run_meso_algorithm(req_nf_list)
|
|
if is_accepted:
|
|
new_mesd_dict = dict()
|
|
ref_mesd_dict = copy.deepcopy(mesd_dict)
|
|
ref_mesd_dict['imports']['nsds']['nsd_templates']['requirements'] = \
|
|
req_nf_list
|
|
new_mesd_dict['mes'] = dict()
|
|
new_mesd_dict['mes'] =\
|
|
{'mesd_template': yaml.safe_dump(ref_mesd_dict)}
|
|
self.update_mes(context, cd_mes_id, new_mesd_dict)
|
|
return cd_mes_id
|
|
else:
|
|
# Create the inline NS with the following template
|
|
import_list = list()
|
|
node_dict = dict()
|
|
for vnfd in req_nf_dict:
|
|
import_list.append(vnfd['vnfd_template'])
|
|
node = 'tosca.nodes.nfv.' + vnfd['name']
|
|
node_dict[vnfd['name']] = {'type': node}
|
|
build_nsd_dict['tosca_definitions_version'] =\
|
|
'tosca_simple_profile_for_nfv_1_0_0'
|
|
build_nsd_dict['description'] = mes_info['description']
|
|
build_nsd_dict['imports'] = import_list
|
|
build_nsd_dict['topology_template'] = dict()
|
|
build_nsd_dict['topology_template']['node_templates'] =\
|
|
node_dict
|
|
|
|
nsds = mesd['attributes'].get('nsds')
|
|
mes_info['mes_mapping']['NS'] = list()
|
|
if nsds:
|
|
nsds_list = nsds.split('-')
|
|
for nsd in nsds_list:
|
|
ns_name = nsd + '-' + name + '-' + uuidutils.generate_uuid() # noqa
|
|
nsd_instance = self._nfv_drivers.invoke(
|
|
nfv_driver,
|
|
'nsd_get_by_name',
|
|
nsd_name=nsd,
|
|
auth_attr=vim_res['vim_auth'],)
|
|
if nsd_instance:
|
|
ns_arg = {'ns': {'nsd_id': nsd_instance['id'],
|
|
'name': ns_name}}
|
|
ns_id = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_create',
|
|
ns_dict=ns_arg,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
mes_info['mes_mapping']['NS'].append(ns_id)
|
|
if build_nsd_dict:
|
|
ns_name = 'nsd' + name + '-' + uuidutils.generate_uuid()
|
|
ns_arg = {'ns': {'nsd_template': build_nsd_dict,
|
|
'name': ns_name,
|
|
'description': mes_info['description'],
|
|
'vim_id': '',
|
|
'tenant_id': mes_info['tenant_id'],
|
|
'attributes': {}}}
|
|
ns_id = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_create',
|
|
ns_dict=ns_arg,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
mes_info['mes_mapping']['NS'].append(ns_id)
|
|
|
|
vnffgds = mesd['attributes'].get('vnffgds')
|
|
if mesd_dict['imports'].get('vnffgds'):
|
|
vnffgds_list = vnffgds.split('-')
|
|
mes_info['mes_mapping']['VNFFG'] = list()
|
|
for vnffgd in vnffgds_list:
|
|
vnffg_name = vnffgds + '-' + name + '-' + uuidutils.generate_uuid() # noqa
|
|
vnffgd_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffgd_get_by_name',
|
|
vnffgd_name=vnffgd,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
if vnffgd_instance:
|
|
vnffg_arg = {'vnffg': {'vnffgd_id': vnffgd_instance['id'], 'name': vnffg_name}} # noqa
|
|
vnffg_id = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_create',
|
|
vnffg_dict=vnffg_arg,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
mes_info['mes_mapping']['VNFFG'].append(vnffg_id)
|
|
|
|
# meca_id = dict()
|
|
# Create MEAs using MEO APIs
|
|
try:
|
|
meca_name = 'meca' + '-' + name + '-' + uuidutils.generate_uuid()
|
|
# Separate the imports out from template
|
|
mead_tpl_dict = dict()
|
|
mead_tpl_dict['imports'] =\
|
|
mesd_dict['imports']['meads']['mead_templates']
|
|
mecad_dict = copy.deepcopy(mesd_dict)
|
|
mecad_dict.pop('imports')
|
|
mecad_dict.update(mead_tpl_dict)
|
|
LOG.debug('mesd %s', mecad_dict)
|
|
meca_arg = {'meca': {'mecad_template': mecad_dict, 'name': meca_name, # noqa
|
|
'description': mes_info['description'],
|
|
'tenant_id': mes_info['tenant_id'],
|
|
'vim_id': mes_info['vim_id'],
|
|
'attributes': {}}}
|
|
meca_dict = meo_plugin.create_meca(context, meca_arg)
|
|
mes_info['mes_mapping']['MECA'] = meca_dict['id']
|
|
except Exception as e:
|
|
LOG.error('Error while creating the MECAs: %s', e)
|
|
# Call Tacker client driver
|
|
|
|
mes_dict = super(MesoPlugin, self).create_mes(context, mes)
|
|
|
|
def _create_mes_wait(self_obj, mes_id):
|
|
args = dict()
|
|
mes_status = "ACTIVE"
|
|
ns_status = "PENDING_CREATE"
|
|
vnffg_status = "PENDING_CREATE"
|
|
mec_status = "PENDING_CREATE"
|
|
ns_retries = NS_RETRIES
|
|
mec_retries = MEC_RETRIES
|
|
vnffg_retries = VNFFG_RETRIES
|
|
mes_mapping = self.get_mes(context, mes_id)['mes_mapping']
|
|
# Check MECA
|
|
meca_id = mes_mapping['MECA']
|
|
while mec_status == "PENDING_CREATE" and mec_retries > 0:
|
|
time.sleep(MEC_RETRY_WAIT)
|
|
mec_status = meo_plugin.get_meca(context, meca_id)['status']
|
|
LOG.debug('status: %s', mec_status)
|
|
if mec_status == 'ACTIVE' or mec_status == 'ERROR':
|
|
break
|
|
mec_retries = mec_retries - 1
|
|
error_reason = None
|
|
if mec_retries == 0 and mec_status == 'PENDING_CREATE':
|
|
error_reason = _(
|
|
"MES creation is not completed within"
|
|
" {wait} seconds as creation of MECA").format(
|
|
wait=MEC_RETRIES * MEC_RETRY_WAIT)
|
|
# Check NS/VNFFG status
|
|
if mes_mapping.get('NS'):
|
|
ns_list = mes_mapping['NS']
|
|
while ns_status == "PENDING_CREATE" and ns_retries > 0:
|
|
time.sleep(NS_RETRY_WAIT)
|
|
# Todo: support multiple NSs
|
|
ns_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_get',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
ns_status = ns_instance['status']
|
|
LOG.debug('status: %s', ns_status)
|
|
if ns_status == 'ACTIVE' or ns_status == 'ERROR':
|
|
break
|
|
ns_retries = ns_retries - 1
|
|
error_reason = None
|
|
if ns_retries == 0 and ns_status == 'PENDING_CREATE':
|
|
error_reason = _(
|
|
"MES creation is not completed within"
|
|
" {wait} seconds as creation of NS(s)").format(
|
|
wait=NS_RETRIES * NS_RETRY_WAIT)
|
|
|
|
# Determine args
|
|
ns_cd = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_get',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
ns_instance_dict = ns_cd['mgmt_urls']
|
|
ns_instance_list = ast.literal_eval(ns_instance_dict)
|
|
args['NS'] = dict()
|
|
|
|
for vnf_name, mgmt_url_list in ns_instance_list.items():
|
|
# Todo: remember to change this with VM capacity
|
|
vm_capacity = VM_CAPA[vnf_name]
|
|
orig = [vm_capacity] * len(mgmt_url_list)
|
|
args['NS'][vnf_name] = [(val - 1) for val in orig]
|
|
|
|
if mes_mapping.get('VNFFG'):
|
|
while vnffg_status == "PENDING_CREATE" and vnffg_retries > 0:
|
|
time.sleep(VNFFG_RETRY_WAIT)
|
|
vnffg_list = mes_mapping['VNFFG']
|
|
# Todo: support multiple VNFFGs
|
|
vnffg_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_get',
|
|
vnffg_id=vnffg_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
vnffg_status = vnffg_instance['status']
|
|
LOG.debug('status: %s', vnffg_status)
|
|
if vnffg_status == 'ACTIVE' or vnffg_status == 'ERROR':
|
|
break
|
|
vnffg_retries = vnffg_retries - 1
|
|
error_reason = None
|
|
if vnffg_retries == 0 and vnffg_status == 'PENDING_CREATE':
|
|
error_reason = _(
|
|
"MES creation is not completed within"
|
|
" {wait} seconds as creation of VNFFG(s)").format(
|
|
wait=VNFFG_RETRIES * VNFFG_RETRY_WAIT)
|
|
if mec_status == "ERROR" or ns_status == "ERROR" or vnffg_status == "ERROR": # noqa
|
|
mes_status = "ERROR"
|
|
if error_reason:
|
|
mes_status = "PENDING_CREATE"
|
|
|
|
super(MesoPlugin, self).create_mes_post(context, mes_id, mes_status, error_reason, args) # noqa
|
|
self.spawn_n(_create_mes_wait, self, mes_dict['id'])
|
|
return mes_dict
|
|
|
|
@log.log
|
|
def _update_params(self, original, paramvalues):
|
|
for key, value in (original).items():
|
|
if not isinstance(value, dict) or 'get_input' not in str(value):
|
|
pass
|
|
elif isinstance(value, dict):
|
|
if 'get_input' in value:
|
|
if value['get_input'] in paramvalues:
|
|
original[key] = paramvalues[value['get_input']]
|
|
else:
|
|
LOG.debug('Key missing Value: %s', key)
|
|
raise cs.InputValuesMissing(key=key)
|
|
else:
|
|
self._update_params(value, paramvalues)
|
|
|
|
@log.log
|
|
def _process_parameterized_input(self, attrs, mesd_dict):
|
|
param_vattrs_dict = attrs.pop('param_values', None)
|
|
if param_vattrs_dict:
|
|
for node in \
|
|
mesd_dict['topology_template']['node_templates'].values():
|
|
if 'get_input' in str(node):
|
|
self._update_params(node, param_vattrs_dict['mesd'])
|
|
else:
|
|
raise cs.ParamYAMLInputMissing()
|
|
|
|
@log.log
|
|
def delete_mes(self, context, mes_id):
|
|
mes = super(MesoPlugin, self).get_mes(context, mes_id)
|
|
mesd = self.get_mesd(context, mes['mesd_id'])
|
|
mesd_dict = yaml.safe_load(mesd['attributes']['mesd'])
|
|
vim_res = self.vim_client.get_vim(context, mes['vim_id'])
|
|
mes_mapping = mes['mes_mapping']
|
|
meca_id = mes_mapping['MECA']
|
|
meo_plugin = manager.ApmecManager.get_service_plugins()['MEO']
|
|
try:
|
|
meca_id = meo_plugin.delete_meca(context, meca_id)
|
|
except Exception as e:
|
|
LOG.error('Error while deleting the MECA(s): %s', e)
|
|
|
|
if mes_mapping.get('NS'):
|
|
# Todo: support multiple NSs
|
|
ns_id = mes_mapping['NS'][0]
|
|
nfv_driver = None
|
|
if mesd_dict['imports'].get('nsds'):
|
|
nfv_driver = mesd_dict['imports']['nsds']['nfv_driver']
|
|
nfv_driver = nfv_driver.lower()
|
|
if not nfv_driver:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
try:
|
|
self._nfv_drivers.invoke(
|
|
nfv_driver,
|
|
'ns_delete',
|
|
ns_id=ns_id,
|
|
auth_attr=vim_res['vim_auth'])
|
|
except Exception as e:
|
|
LOG.error('Error while deleting the NS(s): %s', e)
|
|
if mes_mapping.get('VNFFG'):
|
|
# Todo: support multiple VNFFGs
|
|
vnffg_id = mes_mapping['VNFFG'][0]
|
|
nfv_driver = None
|
|
if mesd_dict['imports'].get('vnffgds'):
|
|
nfv_driver = mesd_dict['imports']['vnffgds']['nfv_driver']
|
|
nfv_driver = nfv_driver.lower()
|
|
if not nfv_driver:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
try:
|
|
self._nfv_drivers.invoke(
|
|
nfv_driver,
|
|
'vnffg_delete',
|
|
vnffg_id=vnffg_id,
|
|
auth_attr=vim_res['vim_auth'])
|
|
except Exception as e:
|
|
LOG.error('Error while deleting the VNFFG(s): %s', e)
|
|
|
|
super(MesoPlugin, self).delete_mes(context, mes_id)
|
|
|
|
def _delete_mes_wait(mes_id):
|
|
ns_status = "PENDING_DELETE"
|
|
vnffg_status = "PENDING_DELETE"
|
|
mec_status = "PENDING_DELETE"
|
|
ns_retries = NS_RETRIES
|
|
mec_retries = MEC_RETRIES
|
|
vnffg_retries = VNFFG_RETRIES
|
|
error_reason_meca = None
|
|
error_reason_ns = None
|
|
error_reason_vnffg = None
|
|
# Check MECA
|
|
while mec_status == "PENDING_DELETE" and mec_retries > 0:
|
|
time.sleep(MEC_RETRY_WAIT)
|
|
meca_id = mes_mapping['MECA']
|
|
meca_list = meo_plugin.get_mecas(context)
|
|
is_deleted = True
|
|
for meca in meca_list:
|
|
if meca_id in meca['id']:
|
|
is_deleted = False
|
|
if is_deleted:
|
|
break
|
|
mec_status = meo_plugin.get_meca(context, meca_id)['status']
|
|
LOG.debug('status: %s', mec_status)
|
|
if mec_status == 'ERROR':
|
|
break
|
|
mec_retries = mec_retries - 1
|
|
if mec_retries == 0 and mec_status == 'PENDING_DELETE':
|
|
error_reason_meca = _(
|
|
"MES deletion is not completed within"
|
|
" {wait} seconds as deletion of MECA").format(
|
|
wait=MEC_RETRIES * MEC_RETRY_WAIT)
|
|
# Check NS/VNFFG status
|
|
if mes_mapping.get('NS'):
|
|
while ns_status == "PENDING_DELETE" and ns_retries > 0:
|
|
time.sleep(NS_RETRY_WAIT)
|
|
ns_list = mes_mapping['NS']
|
|
# Todo: support multiple NSs
|
|
is_existed = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_check',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
if not is_existed:
|
|
break
|
|
ns_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_get',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
ns_status = ns_instance['status']
|
|
LOG.debug('status: %s', ns_status)
|
|
if ns_status == 'ERROR':
|
|
break
|
|
ns_retries = ns_retries - 1
|
|
if ns_retries == 0 and ns_status == 'PENDING_DELETE':
|
|
error_reason_ns = _(
|
|
"MES deletion is not completed within"
|
|
" {wait} seconds as deletion of NS(s)").format(
|
|
wait=NS_RETRIES * NS_RETRY_WAIT)
|
|
if mes_mapping.get('VNFFG'):
|
|
while vnffg_status == "PENDING_DELETE" and vnffg_retries > 0:
|
|
time.sleep(VNFFG_RETRY_WAIT)
|
|
vnffg_list = mes_mapping['VNFFG']
|
|
# Todo: support multiple VNFFGs
|
|
is_existed = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_check',
|
|
vnffg_id=vnffg_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
if not is_existed:
|
|
break
|
|
vnffg_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_get',
|
|
vnffg_id=vnffg_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
vnffg_status = vnffg_instance['status']
|
|
LOG.debug('status: %s', vnffg_status)
|
|
if vnffg_status == 'ERROR':
|
|
break
|
|
vnffg_retries = vnffg_retries - 1
|
|
if vnffg_retries == 0 and vnffg_status == 'PENDING_DELETE':
|
|
error_reason_vnffg = _(
|
|
"MES deletion is not completed within"
|
|
" {wait} seconds as deletion of VNFFG(s)").format(
|
|
wait=VNFFG_RETRIES * VNFFG_RETRY_WAIT)
|
|
error = False
|
|
if mec_status == "ERROR" or ns_status == "ERROR" or vnffg_status == "ERROR": # noqa
|
|
error = True
|
|
error_reason = None
|
|
for reason in [error_reason_meca, error_reason_ns, error_reason_vnffg]: # noqa
|
|
error_reason = reason if reason else None
|
|
|
|
super(MesoPlugin, self).delete_mes_post(
|
|
context, mes_id, error_reason=error_reason, error=error)
|
|
self.spawn_n(_delete_mes_wait, mes['id'])
|
|
return mes['id']
|
|
|
|
def update_mes(self, context, mes_id, mes):
|
|
args = dict()
|
|
mes_info = mes['mes']
|
|
old_mes = super(MesoPlugin, self).get_mes(context, mes_id)
|
|
name = old_mes['name']
|
|
lftover = dict()
|
|
# vm_capacity = 3
|
|
# create inline mesd if given by user
|
|
|
|
def _update_nsd_template(req_mesd_dict):
|
|
build_nsd_dict = dict()
|
|
if req_mesd_dict['imports'].get('nsds'):
|
|
args['NS'] = dict()
|
|
# Todo: Support multiple NSs
|
|
# For framework evaluation
|
|
nsd_templates = req_mesd_dict['imports']['nsds']['nsd_templates'] # noqa
|
|
if isinstance(nsd_templates, dict):
|
|
if nsd_templates.get('requirements'):
|
|
old_reused = old_mes['reused']
|
|
vnf_mapping_list = nsd_templates['requirements']
|
|
for vnf_mapping_dict in vnf_mapping_list:
|
|
for old_vnf_name, old_nfins_list in old_reused.items(): # noqa
|
|
if vnf_mapping_dict['name'] == old_vnf_name:
|
|
len_diff = len([lend for lend in old_nfins_list if lend > 0]) # noqa
|
|
diff = len_diff - vnf_mapping_dict['nf_ins'] # noqa
|
|
if diff < 0:
|
|
lftover.update({old_vnf_name: -diff})
|
|
vm_capacity = VM_CAPA[old_vnf_name]
|
|
old_reused[old_vnf_name].extend([vm_capacity] * (-diff)) # noqa
|
|
# old_reused[old_vnf_name] = diff
|
|
temp = vnf_mapping_dict['nf_ins']
|
|
for index, nfins in enumerate(old_nfins_list): # noqa
|
|
if nfins > 0:
|
|
old_nfins_list[index] = old_nfins_list[index] - 1 # noqa
|
|
temp = temp - 1
|
|
if temp == 0:
|
|
break
|
|
|
|
formal_req = list()
|
|
for nf_name, nf_ins in lftover.items():
|
|
vnfd_name = 'vnfd' + nf_name[3] + str(nf_ins)
|
|
formal_req.append(vnfd_name)
|
|
|
|
if formal_req:
|
|
build_nsd_dict['tosca_definitions_version'] = 'tosca_simple_profile_for_nfv_1_0_0' # noqa
|
|
build_nsd_dict['description'] = old_mes['description']
|
|
build_nsd_dict['imports'] = formal_req
|
|
build_nsd_dict['topology_template'] = dict()
|
|
build_nsd_dict['topology_template']['node_templates'] = dict() # noqa
|
|
for nf_name, nf_ins in lftover.items():
|
|
node = 'tosca.nodes.nfv.' + nf_name
|
|
node_dict = dict()
|
|
node_dict['type'] = node
|
|
build_nsd_dict['topology_template']['node_templates'].update({nf_name: node_dict}) # noqa
|
|
return build_nsd_dict
|
|
|
|
if mes_info.get('mesd_template'):
|
|
# Build vnf_dict here
|
|
mes_name = utils.generate_resource_name(name, 'inline')
|
|
mesd = {'mesd': {'tenant_id': old_mes['tenant_id'],
|
|
'name': mes_name,
|
|
'attributes': {
|
|
'mesd': mes_info['mesd_template']},
|
|
'template_source': 'inline',
|
|
'description': old_mes['description']}}
|
|
try:
|
|
mes_info['mesd_id'] = \
|
|
self.create_mesd(context, mesd).get('id')
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
super(MesoPlugin, self)._update_mes_status(context, mes_id, constants.ACTIVE) # noqa
|
|
|
|
mesd = self.get_mesd(context, mes_info['mesd_id'])
|
|
mesd_dict = yaml.safe_load(mesd['attributes']['mesd'])
|
|
new_mesd_mapping = mesd['mesd_mapping']
|
|
region_name = mes.setdefault('placement_attr', {}).get(
|
|
'region_name', None)
|
|
vim_res = self.vim_client.get_vim(context, old_mes['vim_id'],
|
|
region_name)
|
|
|
|
if mesd_dict['imports'].get('meads'):
|
|
# Update MECA
|
|
meo_plugin = manager.ApmecManager.get_service_plugins()['MEO']
|
|
# Build the MECA template here
|
|
mead_tpl_dict = dict()
|
|
mead_tpl_dict['imports'] =\
|
|
mesd_dict['imports']['meads']['mead_templates']
|
|
mecad_dict = copy.deepcopy(mesd_dict)
|
|
mecad_dict.pop('imports')
|
|
mecad_dict.update(mead_tpl_dict)
|
|
mecad_arg = {'meca': {'mecad_template': mecad_dict}}
|
|
old_meca_id = old_mes['mes_mapping']['MECA']
|
|
meca_id = meo_plugin.update_meca(context, old_meca_id, mecad_arg) # noqa
|
|
|
|
if mesd_dict['imports'].get('nsds'):
|
|
nfv_driver = None
|
|
nfv_driver = mesd_dict['imports']['nsds'].get('nfv_driver')
|
|
if not nfv_driver:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
nfv_driver = nfv_driver.lower()
|
|
|
|
req_mesd_dict = yaml.safe_load(mes_info['mesd_template'])
|
|
new_nsd_template = _update_nsd_template(req_mesd_dict)
|
|
nsd_template = None
|
|
if isinstance(new_mesd_mapping.get('NSD'), list):
|
|
nsd_name = new_mesd_mapping['NSD'][0]
|
|
nsd_dict = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'nsd_get_by_name',
|
|
nsd_name=nsd_name,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
nsd_template = yaml.safe_load(nsd_dict['attributes']['nsd'])
|
|
actual_nsd_template = new_nsd_template if new_nsd_template else nsd_template # noqa
|
|
if actual_nsd_template:
|
|
old_ns_id = old_mes['mes_mapping']['NS'][0]
|
|
ns_arg = {'ns': {'nsd_template': actual_nsd_template}}
|
|
self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_update',
|
|
ns_id=old_ns_id,
|
|
ns_dict=ns_arg,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
|
|
if mesd_dict['imports'].get('vnffgds'):
|
|
# Todo: Support multiple VNFFGs
|
|
nfv_driver = None
|
|
nfv_driver = mesd_dict['imports']['nsds'].get('nfv_driver')
|
|
if not nfv_driver:
|
|
raise meso.NFVDriverNotFound(mesd_name=mesd_dict['name'])
|
|
nfv_driver = nfv_driver.lower()
|
|
vnffgd_name = new_mesd_mapping['VNFFGD'][0]
|
|
vnffgd_dict = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffgd_get_by_name',
|
|
vnffgd_name=vnffgd_name,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
vnffgd_template = yaml.safe_load(
|
|
vnffgd_dict['attributes']['vnffgd'])
|
|
old_vnffg_id = old_mes['mes_mapping']['VNFFG'][0]
|
|
vnffg_arg = {'vnffg': {'vnffgd_template': vnffgd_template}}
|
|
self._nfv_drivers.invoke(
|
|
nfv_driver,
|
|
'vnffg_update',
|
|
vnffg_id=old_vnffg_id,
|
|
vnffg_dict=vnffg_arg,
|
|
auth_attr=vim_res['vim_auth'], )
|
|
|
|
mes_dict = super(MesoPlugin, self)._update_mes_pre(context, mes_id)
|
|
|
|
def _update_mes_wait(self_obj, mes_id):
|
|
args = dict()
|
|
mes_status = "ACTIVE"
|
|
ns_status = "PENDING_UPDATE"
|
|
vnffg_status = "PENDING_UPDATE"
|
|
mec_status = "PENDING_UPDATE"
|
|
ns_retries = NS_RETRIES
|
|
mec_retries = MEC_RETRIES
|
|
vnffg_retries = VNFFG_RETRIES
|
|
error_reason_meca = None
|
|
error_reason_ns = None
|
|
error_reason_vnffg = None
|
|
# Check MECA
|
|
if mesd_dict['imports'].get('meads'):
|
|
while mec_status == "PENDING_UPDATE" and mec_retries > 0:
|
|
time.sleep(MEC_RETRY_WAIT)
|
|
meca_id = old_mes['mes_mapping']['MECA']
|
|
meca_list = meo_plugin.get_mecas(context)
|
|
is_deleted = True
|
|
for meca in meca_list:
|
|
if meca_id in meca['id']:
|
|
is_deleted = False
|
|
if is_deleted:
|
|
break
|
|
mec_status = meo_plugin.get_meca(context, meca_id)['status'] # noqa
|
|
LOG.debug('status: %s', mec_status)
|
|
if mec_status == 'ERROR':
|
|
break
|
|
mec_retries = mec_retries - 1
|
|
if mec_retries == 0 and mec_status == 'PENDING_UPDATE':
|
|
error_reason_meca = _(
|
|
"MES update is not completed within"
|
|
" {wait} seconds as update of MECA").format(
|
|
wait=MEC_RETRIES * MEC_RETRY_WAIT)
|
|
# Check NS/VNFFG status
|
|
if mesd_dict['imports'].get('nsds'):
|
|
while ns_status == "PENDING_UPDATE" and ns_retries > 0:
|
|
time.sleep(NS_RETRY_WAIT)
|
|
ns_list = old_mes['mes_mapping']['NS']
|
|
# Todo: support multiple NSs
|
|
is_existed = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_check',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
if not is_existed:
|
|
break
|
|
ns_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'ns_get',
|
|
ns_id=ns_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
ns_status = ns_instance['status']
|
|
LOG.debug('status: %s', ns_status)
|
|
if ns_status == 'ERROR':
|
|
break
|
|
ns_retries = ns_retries - 1
|
|
if ns_retries == 0 and ns_status == 'PENDING_UPDATE':
|
|
error_reason_ns = _(
|
|
"MES update is not completed within"
|
|
" {wait} seconds as update of NS(s)").format(
|
|
wait=NS_RETRIES * NS_RETRY_WAIT)
|
|
|
|
if mesd_dict['imports'].get('vnffgds'):
|
|
while vnffg_status == "PENDING_UPDATE" and vnffg_retries > 0:
|
|
time.sleep(VNFFG_RETRY_WAIT)
|
|
vnffg_list = old_mes['mes_mapping']['VNFFG']
|
|
# Todo: support multiple VNFFGs
|
|
is_existed = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_check',
|
|
vnffg_id=vnffg_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
if not is_existed:
|
|
break
|
|
vnffg_instance = self._nfv_drivers.invoke(
|
|
nfv_driver, # How to tell it is Tacker
|
|
'vnffg_get',
|
|
vnffg_id=vnffg_list[0],
|
|
auth_attr=vim_res['vim_auth'], )
|
|
vnffg_status = vnffg_instance['status']
|
|
LOG.debug('status: %s', vnffg_status)
|
|
if vnffg_status == 'ERROR':
|
|
break
|
|
vnffg_retries = vnffg_retries - 1
|
|
if vnffg_retries == 0 and vnffg_status == 'PENDING_UPDATE':
|
|
error_reason_vnffg = _(
|
|
"MES update is not completed within"
|
|
" {wait} seconds as update of VNFFG(s)").format(
|
|
wait=VNFFG_RETRIES * VNFFG_RETRY_WAIT)
|
|
args['NS'] = old_mes['reused']
|
|
if mec_status == "ERROR" or ns_status == "ERROR" or vnffg_status == "ERROR": # noqa
|
|
mes_status = "ERROR"
|
|
error_reason = None
|
|
for reason in [error_reason_meca, error_reason_ns, error_reason_vnffg]: # noqa
|
|
if reason:
|
|
error_reason = reason
|
|
mes_status = "PENDING_UPDATE"
|
|
super(MesoPlugin, self)._update_mes_post(context, mes_id, error_reason, mes_status, args) # noqa
|
|
|
|
self.spawn_n(_update_mes_wait, self, mes_dict['id'])
|
|
return mes_dict
|