# 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