From bff90fa0b1a5dd4e6806aa96271b796348680466 Mon Sep 17 00:00:00 2001 From: Tung Doan Date: Fri, 31 Aug 2018 06:42:54 -0700 Subject: [PATCH] Add MESO component that allows: - Apmec to collaborate with other orchestration frameworks (e.g, NFV framework) - for oprators to excecute smart placement decisions of MEC applications Change-Id: I28fe23029f11632f1909967238b9fd440aa6f936 --- .../meo_plugins => db/meso}/__init__.py | 0 apmec/db/meso/meso_db.py | 356 +++++++ apmec/extensions/meo_plugins/edge_service.py | 61 -- apmec/extensions/meso.py | 320 ++++++ apmec/meso/__init__.py | 0 apmec/meso/drivers/__init__.py | 0 apmec/meso/drivers/nfv_drivers/__init__.py | 0 .../drivers/nfv_drivers/abstract_driver.py | 44 + .../meso/drivers/nfv_drivers/tacker_driver.py | 278 ++++++ apmec/meso/meso_plugin.py | 943 ++++++++++++++++++ etc/config-generator.conf | 4 +- setup.cfg | 5 + 12 files changed, 1949 insertions(+), 62 deletions(-) rename apmec/{extensions/meo_plugins => db/meso}/__init__.py (100%) create mode 100644 apmec/db/meso/meso_db.py delete mode 100644 apmec/extensions/meo_plugins/edge_service.py create mode 100644 apmec/extensions/meso.py create mode 100644 apmec/meso/__init__.py create mode 100644 apmec/meso/drivers/__init__.py create mode 100644 apmec/meso/drivers/nfv_drivers/__init__.py create mode 100644 apmec/meso/drivers/nfv_drivers/abstract_driver.py create mode 100644 apmec/meso/drivers/nfv_drivers/tacker_driver.py create mode 100644 apmec/meso/meso_plugin.py diff --git a/apmec/extensions/meo_plugins/__init__.py b/apmec/db/meso/__init__.py similarity index 100% rename from apmec/extensions/meo_plugins/__init__.py rename to apmec/db/meso/__init__.py diff --git a/apmec/db/meso/meso_db.py b/apmec/db/meso/meso_db.py new file mode 100644 index 0000000..bc4c2f4 --- /dev/null +++ b/apmec/db/meso/meso_db.py @@ -0,0 +1,356 @@ +# 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. + + +from datetime import datetime + +from oslo_db.exception import DBDuplicateEntry +from oslo_log import log as logging +from oslo_utils import timeutils +from oslo_utils import uuidutils + +import sqlalchemy as sa +from sqlalchemy import orm +from sqlalchemy.orm import exc as orm_exc +from sqlalchemy import schema + +from apmec.common import exceptions +from apmec.db.common_services import common_services_db_plugin +from apmec.db import db_base +from apmec.db import model_base +from apmec.db import models_v1 +from apmec.db import types +from apmec.extensions import meso +from apmec.plugins.common import constants + +LOG = logging.getLogger(__name__) +_ACTIVE_UPDATE = (constants.ACTIVE, constants.PENDING_UPDATE) +_ACTIVE_UPDATE_ERROR_DEAD = ( + constants.PENDING_CREATE, constants.ACTIVE, constants.PENDING_UPDATE, + constants.ERROR, constants.DEAD) +CREATE_STATES = (constants.PENDING_CREATE, constants.DEAD) + + +########################################################################### +# db tables + +class MESD(model_base.BASE, models_v1.HasId, models_v1.HasTenant, + models_v1.Audit): + """Represents MESD to create MES.""" + + __tablename__ = 'mesd' + # Descriptive name + name = sa.Column(sa.String(255), nullable=False) + description = sa.Column(sa.Text) + # Mesd template source - onboarded + template_source = sa.Column(sa.String(255), server_default='onboarded') + mesd_mapping = sa.Column(types.Json, nullable=True) + # (key, value) pair to spin up + attributes = orm.relationship('MESDAttribute', + backref='mesd') + + __table_args__ = ( + schema.UniqueConstraint( + "tenant_id", + "name", + name="uniq_mesd0tenant_id0name"), + ) + + +class MESDAttribute(model_base.BASE, models_v1.HasId): + """Represents attributes necessary for creation of mes in (key, value) pair + + """ + + __tablename__ = 'mesd_attribute' + mesd_id = sa.Column(types.Uuid, sa.ForeignKey('mesd.id'), + nullable=False) + key = sa.Column(sa.String(255), nullable=False) + value = sa.Column(sa.TEXT(65535), nullable=True) + + +class MES(model_base.BASE, models_v1.HasId, models_v1.HasTenant, + models_v1.Audit): + """Represents network services that deploys services. + + """ + + __tablename__ = 'mes' + mesd_id = sa.Column(types.Uuid, sa.ForeignKey('mesd.id')) + mesd = orm.relationship('MESD') + mes_mapping = sa.Column(types.Json, nullable=True) + reused = sa.Column(types.Json, nullable=True) + name = sa.Column(sa.String(255), nullable=False) + description = sa.Column(sa.Text, nullable=True) + + # Dict of MEA details that network service launches + mea_ids = sa.Column(sa.TEXT(65535), nullable=True) + + # Dict of mgmt urls that network servic launches + mgmt_urls = sa.Column(sa.TEXT(65535), nullable=True) + + status = sa.Column(sa.String(64), nullable=False) + vim_id = sa.Column(types.Uuid, sa.ForeignKey('vims.id'), nullable=False) + error_reason = sa.Column(sa.Text, nullable=True) + + __table_args__ = ( + schema.UniqueConstraint( + "tenant_id", + "name", + name="uniq_mes0tenant_id0name"), + ) + + +class MESOPluginDb(meso.MESOPluginBase, db_base.CommonDbMixin): + + def __init__(self): + super(MESOPluginDb, self).__init__() + self._cos_db_plg = common_services_db_plugin.CommonServicesPluginDb() + + def _get_resource(self, context, model, id): + try: + return self._get_by_id(context, model, id) + except orm_exc.NoResultFound: + if issubclass(model, MESD): + raise meso.MESDNotFound(mesd_id=id) + if issubclass(model, MES): + raise meso.MESNotFound(mes_id=id) + else: + raise + + def _get_mes_db(self, context, mes_id, current_statuses, new_status): + try: + mes_db = ( + self._model_query(context, MES). + filter(MES.id == mes_id). + filter(MES.status.in_(current_statuses)). + with_lockmode('update').one()) + except orm_exc.NoResultFound: + raise meso.MESNotFound(mes_id=mes_id) + mes_db.update({'status': new_status}) + return mes_db + + def _make_attributes_dict(self, attributes_db): + return dict((attr.key, attr.value) for attr in attributes_db) + + def _make_mesd_dict(self, mesd, fields=None): + res = { + 'attributes': self._make_attributes_dict(mesd['attributes']), + } + key_list = ('id', 'tenant_id', 'name', 'description', 'mesd_mapping', + 'created_at', 'updated_at', 'template_source') + res.update((key, mesd[key]) for key in key_list) + return self._fields(res, fields) + + def _make_dev_attrs_dict(self, dev_attrs_db): + return dict((arg.key, arg.value) for arg in dev_attrs_db) + + def _make_mes_dict(self, mes_db, fields=None): + LOG.debug('mes_db %s', mes_db) + res = {} + key_list = ('id', 'tenant_id', 'mesd_id', 'name', 'description', 'mes_mapping', # noqa + 'mea_ids', 'status', 'mgmt_urls', 'error_reason', 'reused', + 'vim_id', 'created_at', 'updated_at') + res.update((key, mes_db[key]) for key in key_list) + return self._fields(res, fields) + + def create_mesd(self, context, mesd): + mesd = mesd['mesd'] + LOG.debug('mesd %s', mesd) + tenant_id = self._get_tenant_id_for_create(context, mesd) + template_source = mesd.get('template_source') + + try: + with context.session.begin(subtransactions=True): + mesd_id = uuidutils.generate_uuid() + mesd_db = MESD( + id=mesd_id, + tenant_id=tenant_id, + name=mesd.get('name'), + description=mesd.get('description'), + mesd_mapping=mesd.get('mesd_mapping'), + deleted_at=datetime.min, + template_source=template_source) + context.session.add(mesd_db) + for (key, value) in mesd.get('attributes', {}).items(): + attribute_db = MESDAttribute( + id=uuidutils.generate_uuid(), + mesd_id=mesd_id, + key=key, + value=value) + context.session.add(attribute_db) + except DBDuplicateEntry as e: + raise exceptions.DuplicateEntity( + _type="mesd", + entry=e.columns) + LOG.debug('mesd_db %(mesd_db)s %(attributes)s ', + {'mesd_db': mesd_db, + 'attributes': mesd_db.attributes}) + mesd_dict = self._make_mesd_dict(mesd_db) + LOG.debug('mesd_dict %s', mesd_dict) + self._cos_db_plg.create_event( + context, res_id=mesd_dict['id'], + res_type=constants.RES_TYPE_MESD, + res_state=constants.RES_EVT_ONBOARDED, + evt_type=constants.RES_EVT_CREATE, + tstamp=mesd_dict[constants.RES_EVT_CREATED_FLD]) + return mesd_dict + + def delete_mesd(self, + context, + mesd_id, + soft_delete=True): + with context.session.begin(subtransactions=True): + mess_db = context.session.query(MES).filter_by( + mesd_id=mesd_id).first() + if mess_db is not None and mess_db.deleted_at is None: + raise meso.MESDInUse(mesd_id=mesd_id) + + mesd_db = self._get_resource(context, MESD, + mesd_id) + if soft_delete: + mesd_db.update({'deleted_at': timeutils.utcnow()}) + self._cos_db_plg.create_event( + context, res_id=mesd_db['id'], + res_type=constants.RES_TYPE_MESD, + res_state=constants.RES_EVT_NA_STATE, + evt_type=constants.RES_EVT_DELETE, + tstamp=mesd_db[constants.RES_EVT_DELETED_FLD]) + else: + context.session.query(MESDAttribute).filter_by( + mesd_id=mesd_id).delete() + context.session.delete(mesd_db) + + def get_mesd(self, context, mesd_id, fields=None): + mesd_db = self._get_resource(context, MESD, mesd_id) + return self._make_mesd_dict(mesd_db) + + def get_mesds(self, context, filters, fields=None): + if ('template_source' in filters) and \ + (filters['template_source'][0] == 'all'): + filters.pop('template_source') + return self._get_collection(context, MESD, + self._make_mesd_dict, + filters=filters, fields=fields) + + # reference implementation. needs to be overrided by subclass + def create_mes(self, context, mes): + LOG.debug('mes %s', mes) + mes = mes['mes'] + tenant_id = self._get_tenant_id_for_create(context, mes) + mesd_id = mes['mesd_id'] + vim_id = mes['vim_id'] + name = mes.get('name') + mes_mapping = mes['mes_mapping'] + mes_id = uuidutils.generate_uuid() + try: + with context.session.begin(subtransactions=True): + mesd_db = self._get_resource(context, MESD, + mesd_id) + mes_db = MES(id=mes_id, + tenant_id=tenant_id, + name=name, + description=mesd_db.description, + mea_ids=None, + status=constants.PENDING_CREATE, + mes_mapping=mes_mapping, + reused=None, + mgmt_urls=None, + mesd_id=mesd_id, + vim_id=vim_id, + error_reason=None, + deleted_at=datetime.min) + context.session.add(mes_db) + except DBDuplicateEntry as e: + raise exceptions.DuplicateEntity( + _type="mes", + entry=e.columns) + return self._make_mes_dict(mes_db) + + def create_mes_post(self, context, mes_id, + mes_status, error_reason, args): + LOG.debug('mes ID %s', mes_id) + with context.session.begin(subtransactions=True): + mes_db = self._get_resource(context, MES, + mes_id) + mes_db.update({'status': mes_status}) + mes_db.update({'error_reason': error_reason}) + mes_db.update({'updated_at': timeutils.utcnow()}) + mes_db.update({'reused': args.get("NS")}) + mes_dict = self._make_mes_dict(mes_db) + return mes_dict + + # reference implementation. needs to be overrided by subclass + def delete_mes(self, context, mes_id): + with context.session.begin(subtransactions=True): + mes_db = self._get_mes_db( + context, mes_id, _ACTIVE_UPDATE_ERROR_DEAD, + constants.PENDING_DELETE) + deleted_mes_db = self._make_mes_dict(mes_db) + return deleted_mes_db + + def delete_mes_post(self, context, mes_id, + error_reason, soft_delete=True, error=False): + mes = self.get_mes(context, mes_id) + mesd_id = mes.get('mesd_id') + with context.session.begin(subtransactions=True): + query = ( + self._model_query(context, MES). + filter(MES.id == mes_id). + filter(MES.status == constants.PENDING_DELETE)) + if error: + query.update({'status': constants.ERROR}) + if error_reason: + query.update({'error_reason': error_reason}) + else: + if soft_delete: + deleted_time_stamp = timeutils.utcnow() + query.update({'deleted_at': deleted_time_stamp}) + + else: + query.delete() + template_db = self._get_resource(context, MESD, mesd_id) + if template_db.get('template_source') == 'inline': + self.delete_mesd(context, mesd_id) + + def get_mes(self, context, mes_id, fields=None): + mes_db = self._get_resource(context, MES, mes_id) + return self._make_mes_dict(mes_db) + + def get_mess(self, context, filters=None, fields=None): + return self._get_collection(context, MES, + self._make_mes_dict, + filters=filters, fields=fields) + + def _update_mes_pre(self, context, mes_id): + with context.session.begin(subtransactions=True): + mes_db = self._get_mes_db(context, mes_id, _ACTIVE_UPDATE, constants.PENDING_UPDATE) # noqa + return self._make_mes_dict(mes_db) + + def _update_mes_post(self, context, mes_id, error_reason, mes_status, args): # noqa + with context.session.begin(subtransactions=True): + mes_db = self._get_resource(context, MES, mes_id) + mes_db.update({'status': mes_status}) + mes_db.update({'error_reason': error_reason}) + mes_db.update({'updated_at': timeutils.utcnow()}) + mes_db.update({'reused': args.get('NS')}) + mes_dict = self._make_mes_dict(mes_db) + return mes_dict + + def _update_mes_status(self, context, mes_id, new_status): + with context.session.begin(subtransactions=True): + mes_db = self._get_mes_db(context, mes_id, _ACTIVE_UPDATE, new_status) # noqa + return self._make_mes_dict(mes_db) + + def update_mes(self, context, mes_id, mes): + self._update_mes_pre(context, mes_id) + self._update_mes_post(context, mes_id, constants.ACTIVE, None) diff --git a/apmec/extensions/meo_plugins/edge_service.py b/apmec/extensions/meo_plugins/edge_service.py deleted file mode 100644 index 0e887f5..0000000 --- a/apmec/extensions/meo_plugins/edge_service.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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 abc -import six - -from apmec.common import exceptions -from apmec.services import service_base - - -@six.add_metaclass(abc.ABCMeta) -class MESPluginBase(service_base.MECPluginBase): - - @abc.abstractmethod - def create_mesd(self, context, mesd): - pass - - @abc.abstractmethod - def delete_mesd(self, context, mesd_id): - pass - - @abc.abstractmethod - def get_mesd(self, context, mesd_id, fields=None): - pass - - @abc.abstractmethod - def get_mesds(self, context, filters=None, fields=None): - pass - - @abc.abstractmethod - def create_mes(self, context, mes): - pass - - @abc.abstractmethod - def get_mess(self, context, filters=None, fields=None): - pass - - @abc.abstractmethod - def get_mes(self, context, mes_id, fields=None): - pass - - @abc.abstractmethod - def delete_mes(self, context, mes_id): - pass - - -class MESDNotFound(exceptions.NotFound): - message = _('MESD %(mesd_id)s could not be found') - - -class MESNotFound(exceptions.NotFound): - message = _('MES %(mes_id)s could not be found') diff --git a/apmec/extensions/meso.py b/apmec/extensions/meso.py new file mode 100644 index 0000000..021388d --- /dev/null +++ b/apmec/extensions/meso.py @@ -0,0 +1,320 @@ +# Copyright 2016 Brocade Communications Systems 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 abc + +import six + +from apmec._i18n import _ +from apmec.api import extensions +from apmec.api.v1 import attributes as attr +from apmec.api.v1 import resource_helper +from apmec.common import exceptions +from apmec.plugins.common import constants +from apmec.services import service_base + + +class MESDInUse(exceptions.InUse): + message = _('MESD %(mesd_id)s is still in use') + + +class MESInUse(exceptions.InUse): + message = _('MES %(mes_id)s is still in use') + + +class NoTasksException(exceptions.ApmecException): + message = _('No tasks to run for %(action)s on %(resource)s') + + +class MESDNotFound(exceptions.NotFound): + message = _('MESD %(mesd_id)s could not be found') + + +class MESNotFound(exceptions.NotFound): + message = _('MES %(mes_id)s could not be found') + + +class ToscaParserFailed(exceptions.InvalidInput): + message = _("tosca-parser failed: - %(error_msg_details)s") + + +class NSDNotFound(exceptions.NotFound): + message = _('NSD template(s) not existed for MESD %(mesd_name)') + + +class VNFFGDNotFound(exceptions.NotFound): + message = _('VNFFGD template(s) not existed for MESD %(mesd_name)') + + +class MECDriverNotfound(exceptions.NotFound): + message = _('MEC driver not specified for the MESD %(mesd_name)') + + +class NFVDriverNotFound(exceptions.NotFound): + message = _('NFV driver is not specified for the MESD %(mesd_name)') + + +RESOURCE_ATTRIBUTE_MAP = { + 'mesds': { + 'id': { + 'allow_post': False, + 'allow_put': False, + 'validate': {'type:uuid': None}, + 'is_visible': True, + 'primary_key': True, + }, + 'tenant_id': { + 'allow_post': True, + 'allow_put': False, + 'validate': {'type:string': None}, + 'required_by_policy': True, + 'is_visible': True, + }, + 'name': { + 'allow_post': True, + 'allow_put': True, + 'validate': {'type:string': None}, + 'is_visible': True, + }, + 'description': { + 'allow_post': True, + 'allow_put': True, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '', + }, + 'mesd_mapping': { + 'allow_post': False, + 'allow_put': False, + 'convert_to': attr.convert_none_to_empty_dict, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': '', + }, + 'created_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'updated_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'attributes': { + 'allow_post': True, + 'allow_put': False, + 'convert_to': attr.convert_none_to_empty_dict, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': None, + }, + 'template_source': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + 'default': 'onboarded' + }, + + }, + + 'mess': { + 'id': { + 'allow_post': False, + 'allow_put': False, + 'validate': {'type:uuid': None}, + 'is_visible': True, + 'primary_key': True, + }, + 'tenant_id': { + 'allow_post': True, + 'allow_put': False, + 'validate': {'type:string': None}, + 'required_by_policy': True, + 'is_visible': True, + }, + 'name': { + 'allow_post': True, + 'allow_put': True, + 'validate': {'type:string': None}, + 'is_visible': True, + }, + 'description': { + 'allow_post': True, + 'allow_put': True, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '', + }, + 'created_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'updated_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'mes_mapping': { + 'allow_post': False, + 'allow_put': False, + 'convert_to': attr.convert_none_to_empty_dict, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': '', + }, + 'reused': { + 'allow_post': False, + 'allow_put': False, + 'convert_to': attr.convert_none_to_empty_dict, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': '', + }, + 'mesd_id': { + 'allow_post': True, + 'allow_put': False, + 'validate': {'type:uuid': None}, + 'is_visible': True, + 'default': None, + }, + 'vim_id': { + 'allow_post': True, + 'allow_put': False, + 'validate': {'type:string': None}, + 'is_visible': True, + 'default': '', + }, + 'status': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'error_reason': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'attributes': { + 'allow_post': True, + 'allow_put': False, + 'convert_to': attr.convert_none_to_empty_dict, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': None, + }, + 'mesd_template': { + 'allow_post': True, + 'allow_put': True, + 'validate': {'type:dict_or_nodata': None}, + 'is_visible': True, + 'default': None, + }, + }, + +} + + +class Meso(extensions.ExtensionDescriptor): + @classmethod + def get_name(cls): + return 'MEC Service Orchestrator' + + @classmethod + def get_alias(cls): + return 'MESO' + + @classmethod + def get_description(cls): + return "Extension for MEC Service Orchestrator" + + @classmethod + def get_namespace(cls): + return 'http://wiki.openstack.org/Apmec' + + @classmethod + def get_updated(cls): + return "2015-12-21T10:00:00-00:00" + + @classmethod + def get_resources(cls): + special_mappings = {} + plural_mappings = resource_helper.build_plural_mappings( + special_mappings, RESOURCE_ATTRIBUTE_MAP) + attr.PLURALS.update(plural_mappings) + return resource_helper.build_resource_info( + plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.MESO, + translate_name=True) + + @classmethod + def get_plugin_interface(cls): + return MESOPluginBase + + def update_attributes_map(self, attributes): + super(Meso, self).update_attributes_map( + attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) + + def get_extended_resources(self, version): + version_map = {'1.0': RESOURCE_ATTRIBUTE_MAP} + return version_map.get(version, {}) + + +@six.add_metaclass(abc.ABCMeta) +class MESOPluginBase(service_base.MECPluginBase): + def get_plugin_name(self): + return constants.MESO + + def get_plugin_type(self): + return constants.MESO + + def get_plugin_description(self): + return 'Apmec MEC service Orchestrator plugin' + + @abc.abstractmethod + def create_mesd(self, context, mesd): + pass + + @abc.abstractmethod + def delete_mesd(self, context, mesd_id): + pass + + @abc.abstractmethod + def get_mesd(self, context, mesd_id, fields=None): + pass + + @abc.abstractmethod + def get_mesds(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def create_mes(self, context, mes): + pass + + @abc.abstractmethod + def get_mess(self, context, filters=None, fields=None): + pass + + @abc.abstractmethod + def get_mes(self, context, mes_id, fields=None): + pass + + @abc.abstractmethod + def delete_mes(self, context, mes_id): + pass + + @abc.abstractmethod + def update_mes(self, context, mes_id, mes): + pass diff --git a/apmec/meso/__init__.py b/apmec/meso/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apmec/meso/drivers/__init__.py b/apmec/meso/drivers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apmec/meso/drivers/nfv_drivers/__init__.py b/apmec/meso/drivers/nfv_drivers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apmec/meso/drivers/nfv_drivers/abstract_driver.py b/apmec/meso/drivers/nfv_drivers/abstract_driver.py new file mode 100644 index 0000000..23e2ba4 --- /dev/null +++ b/apmec/meso/drivers/nfv_drivers/abstract_driver.py @@ -0,0 +1,44 @@ +# 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 abc + +import six + +from apmec.api import extensions + + +@six.add_metaclass(abc.ABCMeta) +class NfvAbstractDriver(extensions.PluginInterface): + + @abc.abstractmethod + def get_type(self): + """Get NFV Driver type + + Return one of predefined types of NFV drivers. + """ + pass + + @abc.abstractmethod + def get_name(self): + """Get NFV driver name + + Return a symbolic name for the NFV driver. + """ + pass + + @abc.abstractmethod + def get_description(self): + pass diff --git a/apmec/meso/drivers/nfv_drivers/tacker_driver.py b/apmec/meso/drivers/nfv_drivers/tacker_driver.py new file mode 100644 index 0000000..5dc5b0c --- /dev/null +++ b/apmec/meso/drivers/nfv_drivers/tacker_driver.py @@ -0,0 +1,278 @@ +# 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. + +from keystoneauth1 import identity +from keystoneauth1 import session +from oslo_config import cfg +from oslo_log import log as logging + +from apmec.meso.drivers.nfv_drivers import abstract_driver + +from tackerclient.v1_0 import client as tacker_client + +LOG = logging.getLogger(__name__) +CONF = cfg.CONF + + +class Tacker_Driver(abstract_driver.NfvAbstractDriver): + """Driver for NFV + + OpenStack driver handles interactions with local as well as + remote OpenStack instances. The driver invokes keystone service for VIM + authorization and validation. The driver is also responsible for + discovering placement attributes such as regions, availability zones + """ + + def get_type(self): + return 'tacker' + + def get_name(self): + return 'OpenStack Tacker Driver' + + def get_description(self): + return 'OpenStack Tacker Driver' + + def nsd_create(self, auth_attr, nsd_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.nsd_create(nsd_dict) + + def nsd_get_by_name(self, auth_attr, nsd_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.nsd_get_by_name(nsd_name) + + def nsd_get(self, auth_attr, nsd_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.nsd_get(nsd_id) + + def ns_create(self, auth_attr, ns_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_create(ns_dict) + + def ns_get_by_name(self, auth_attr, ns_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_get(ns_name) + + def ns_get(self, auth_attr, ns_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_get(ns_id) + + def ns_check(self, auth_attr, ns_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_check(ns_id) + + def ns_delete_by_name(self, auth_attr, ns_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_delete(ns_name) + + def ns_delete(self, auth_attr, ns_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_delete(ns_id) + + def ns_update(self, auth_attr, ns_id, ns_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_update(ns_id, ns_dict) + + def vnfd_create(self, auth_attr, vnfd_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnfd_create(vnfd_dict) + + def vnf_create(self, auth_attr, vnf_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnf_create(vnf_dict) + + def vnf_get(self, auth_attr, vnf_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnf_get(vnf_id) + + def vnfd_get(self, auth_attr, vnfd_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnfd_get(vnfd_id) + + def vnffgd_get_by_name(self, auth_attr, vnffgd_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffgd_get(vnffgd_name) + + def vnffgd_get(self, auth_attr, vnffgd_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffgd_get(vnffgd_id) + + def vnffg_create(self, auth_attr, vnffg_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_create(vnffg_dict) + + def vnffg_get_by_name(self, auth_attr, vnffg_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_get(vnffg_name) + + def vnffg_get(self, auth_attr, vnffg_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_get(vnffg_id) + + def vnffg_delete_by_name(self, auth_attr, vnffg_name): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_delete(vnffg_name) + + def vnffg_delete(self, auth_attr, vnffg_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_delete(vnffg_id) + + def vnffg_check(self, auth_attr, vnffg_id): + tacker_client = TackerClient(auth_attr) + return tacker_client.vnffg_check(vnffg_id) + + def vnffg_update(self, auth_attr, vnffg_id, vnffg_dict): + tacker_client = TackerClient(auth_attr) + return tacker_client.ns_update(vnffg_id, vnffg_dict) + + +class TackerClient(object): + """Tacker Client class for VNFM and NFVO negotiation""" + + def __init__(self, auth_attr): + auth = identity.Password(**auth_attr) + sess = session.Session(auth=auth) + self.client = tacker_client.Client(session=sess) + + def nsd_create(self, nsd_dict): + nsd_instance = self.client.create_nsd(body=nsd_dict) + if nsd_instance: + return nsd_instance['nsd']['id'] + else: + return None + + def nsd_get_by_name(self, nsd_name): + nsd_dict = self.client.list_nsds() + nsd_list = nsd_dict['nsds'] + nsd_dict = None + for nsd in nsd_list: + if nsd['name'] == nsd_name: + nsd_dict = nsd + return nsd_dict + + def nsd_get(self, nsd_id): + nsd_dict = self.client.show_nsd(nsd_id) + return nsd_dict['nsd'] + + def ns_create(self, ns_dict): + ns_instance = self.client.create_ns(body=ns_dict) + if ns_instance: + return ns_instance['ns']['id'] + else: + return None + + def ns_get_by_name(self, ns_name): + ns_dict = self.client.list_nsds() + ns_list = ns_dict['nss'] + ns_id = None + for ns in ns_list: + if ns['name'] == ns_name: + ns_id = ns['id'] + return ns_id + + def ns_get(self, ns_id): + ns_instance = self.client.show_ns(ns_id) + return ns_instance['ns'] + + def ns_delete_by_name(self, ns_name): + ns_id = self.ns_get_by_name(ns_name) + if ns_id: + self.client.delete_ns(ns_id) + + def ns_check(self, ns_id): + ns_dict = self.client.list_nss() + ns_list = ns_dict['nss'] + check = False + for ns in ns_list: + if ns['id'] == ns_id: + check = True + return check + + def ns_delete(self, ns_id): + return self.client.delete_ns(ns_id) + + def ns_update(self, ns_id, ns_dict): + return self.client.update_ns(ns_id, ns_dict) + + def vnfd_create(self, vnfd_dict): + vnfd_instance = self.client.create_vnfd(body=vnfd_dict) + if vnfd_instance: + return vnfd_instance['vnf']['id'] + else: + return None + + def vnf_create(self, vnf_dict): + vnf_instance = self.client.create_vnf(body=vnf_dict) + if vnf_instance: + return vnf_instance['vnf']['id'] + else: + return None + + def vnf_get(self, vnf_id): + vnf_instance = self.client.show_vnf(vnf_id) + return vnf_instance['vnf'] + + def vnfd_get(self, vnfd_id): + vnfd_instance = self.client.show_vnfd(vnfd_id) + return vnfd_instance['vnfd'] + + def vnffgd_get_by_name(self, vnffgd_name): + vnffgd_dict = self.client.list_vnffgds() + vnffgd_list = vnffgd_dict['vnffgds'] + vnffgd_dict = None + for vnffgd in vnffgd_list: + if vnffgd['name'] == vnffgd_name: + vnffgd_dict = vnffgd + return vnffgd_dict + + def vnffgd_get(self, vnffgd_id): + vnffgd_instance = self.client.show_vnffgd(vnffgd_id) + return vnffgd_instance['vnffgd'] + + def vnffg_create(self, vnffgd_dict): + vnffg_instance = self.client.create_vnffg(body=vnffgd_dict) + if vnffg_instance: + return vnffg_instance['vnffg']['id'] + else: + return None + + def vnffg_get_by_name(self, vnffg_name): + vnffg_dict = self.client.list_vnffgs() + vnffg_list = vnffg_dict['vnffgs'] + vnffg_id = None + for vnffg in vnffg_list: + if vnffg['name'] == vnffg_name: + vnffg_id = vnffg['id'] + return vnffg_id + + def vnffg_get(self, vnffg_id): + vnffg_instance = self.client.show_vnffg(vnffg_id) + return vnffg_instance['vnffg'] + + def vnffg_delete_by_name(self, vnffg_name): + vnffg_id = self.vnffg_get_by_name(vnffg_name) + if vnffg_id: + self.client.delete_vnffg(vnffg_id) + + def vnffg_delete(self, vnffg_id): + return self.client.delete_vnffg(vnffg_id) + + def vnffg_check(self, vnffg_id): + vnffg_dict = self.client.list_vnffgs() + vnffg_list = vnffg_dict['vnffgs'] + check = False + for vnffg in vnffg_list: + if vnffg['id'] == vnffg_id: + check = True + return check + + def vnffg_update(self, vnffg_id, vnffg_dict): + return self.client.update_ns(vnffg_id, vnffg_dict) diff --git a/apmec/meso/meso_plugin.py b/apmec/meso/meso_plugin.py new file mode 100644 index 0000000..6d0aa8b --- /dev/null +++ b/apmec/meso/meso_plugin.py @@ -0,0 +1,943 @@ +# 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 diff --git a/etc/config-generator.conf b/etc/config-generator.conf index daf02fa..9b3d840 100644 --- a/etc/config-generator.conf +++ b/etc/config-generator.conf @@ -4,6 +4,8 @@ wrap_width = 79 namespace = apmec.common.config namespace = apmec.wsgi namespace = apmec.service +namespace = apmec.meso.meso_plugin +namespace = apmec.meso.drivers.nfv_drivers.tacker_driver namespace = apmec.meo.meo_plugin namespace = apmec.meo.drivers.vim.openstack_driver namespace = apmec.keymgr @@ -24,4 +26,4 @@ namespace = oslo.messaging namespace = oslo.db namespace = oslo.log namespace = oslo.policy -namespace = oslo.service.service +namespace = oslo.service.service \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index b4ab35e..28ca61c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,7 +44,10 @@ apmec.service_plugins = dummy = apmec.tests.unit.dummy_plugin:DummyServicePlugin mem = apmec.mem.plugin:MEMPlugin meo = apmec.meo.meo_plugin:MeoPlugin + meso = apmec.meso.meso_plugin:MesoPlugin commonservices = apmec.plugins.common_services.common_services_plugin:CommonServicesPlugin +apmec.meso.drivers = + tacker = apmec.meso.drivers.nfv_drivers.tacker_driver:Tacker_Driver apmec.meo.vim.drivers = openstack = apmec.meo.drivers.vim.openstack_driver:OpenStack_Driver apmec.openstack.common.cache.backends = @@ -71,6 +74,8 @@ oslo.config.opts = apmec.service = apmec.service:config_opts apmec.meo.meo_plugin = apmec.meo.meo_plugin:config_opts apmec.meo.drivers.vim.openstack_driver = apmec.meo.drivers.vim.openstack_driver:config_opts + apmec.meso.meso_plugin = apmec.meso.meso_plugin:config_opts + apmec.mseo.drivers.nfv_drivers.tacker_driver = apmec.meso.drivers.nfv_drivers.tacker_driver:config_opts apmec.keymgr = apmec.keymgr:config_opts apmec.mem.monitor = apmec.mem.monitor:config_opts apmec.mem.plugin = apmec.mem.plugin:config_opts