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
This commit is contained in:
parent
c41e2d080d
commit
bff90fa0b1
356
apmec/db/meso/meso_db.py
Normal file
356
apmec/db/meso/meso_db.py
Normal file
@ -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)
|
@ -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')
|
320
apmec/extensions/meso.py
Normal file
320
apmec/extensions/meso.py
Normal file
@ -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
|
0
apmec/meso/__init__.py
Normal file
0
apmec/meso/__init__.py
Normal file
0
apmec/meso/drivers/__init__.py
Normal file
0
apmec/meso/drivers/__init__.py
Normal file
0
apmec/meso/drivers/nfv_drivers/__init__.py
Normal file
0
apmec/meso/drivers/nfv_drivers/__init__.py
Normal file
44
apmec/meso/drivers/nfv_drivers/abstract_driver.py
Normal file
44
apmec/meso/drivers/nfv_drivers/abstract_driver.py
Normal file
@ -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
|
278
apmec/meso/drivers/nfv_drivers/tacker_driver.py
Normal file
278
apmec/meso/drivers/nfv_drivers/tacker_driver.py
Normal file
@ -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)
|
943
apmec/meso/meso_plugin.py
Normal file
943
apmec/meso/meso_plugin.py
Normal file
@ -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
|
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user