Implement NSD Support - Add CRUD operations for NSD
Co-Authored-By: Dharmendra Kushwaha<dharmendra.kushwaha@nectechnologies.in> Partially-implements: blueprint nsd-support Change-Id: Iff51926ce9f3b96f83a51ef06b4b464e4696d777
This commit is contained in:
parent
53c1c0464b
commit
5f46c2b030
@ -41,3 +41,4 @@ tosca-parser>=0.7.0 # Apache-2.0
|
||||
heat-translator>=0.4.0 # Apache-2.0
|
||||
cryptography!=1.3.0,>=1.0 # BSD/Apache-2.0
|
||||
paramiko>=2.0 # LGPLv2.1+
|
||||
python-mistralclient>=2.0.0 # Apache-2.0
|
||||
|
@ -0,0 +1,73 @@
|
||||
# Copyright 2016 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""create of Network service tables
|
||||
|
||||
Revision ID: 0ad3bbce1c18
|
||||
Revises: 0ae5b1ce3024
|
||||
Create Date: 2016-12-17 19:41:01.906138
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0ad3bbce1c18'
|
||||
down_revision = '8f7145914cb0'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from tacker.db import types
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.create_table('nsd',
|
||||
sa.Column('tenant_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('id', types.Uuid(length=36), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('vnfds', types.Json, nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
op.create_table('ns',
|
||||
sa.Column('tenant_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('id', types.Uuid(length=36), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('nsd_id', types.Uuid(length=36), nullable=True),
|
||||
sa.Column('vim_id', sa.String(length=64), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('vnf_ids', sa.TEXT(length=65535), nullable=True),
|
||||
sa.Column('mgmt_urls', sa.TEXT(length=65535), nullable=True),
|
||||
sa.Column('status', sa.String(length=64), nullable=False),
|
||||
sa.Column('error_reason', sa.Text(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['nsd_id'], ['nsd.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
||||
op.create_table('nsd_attribute',
|
||||
sa.Column('id', types.Uuid(length=36), nullable=False),
|
||||
sa.Column('nsd_id', types.Uuid(length=36), nullable=False),
|
||||
sa.Column('key', sa.String(length=255), nullable=False),
|
||||
sa.Column('value', sa.TEXT(length=65535), nullable=True),
|
||||
sa.ForeignKeyConstraint(['nsd_id'], ['nsd.id'], ),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
@ -1 +1 @@
|
||||
8f7145914cb0
|
||||
0ad3bbce1c18
|
||||
|
@ -23,6 +23,7 @@ Based on this comparison database can be healed with healing migration.
|
||||
|
||||
from tacker.db import model_base
|
||||
from tacker.db.nfvo import nfvo_db # noqa
|
||||
from tacker.db.nfvo import ns_db # noqa
|
||||
from tacker.db.nfvo import vnffg_db # noqa
|
||||
from tacker.db.vnfm import vnfm_db # noqa
|
||||
|
||||
|
192
tacker/db/nfvo/ns_db.py
Normal file
192
tacker/db/nfvo/ns_db.py
Normal file
@ -0,0 +1,192 @@
|
||||
# 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 uuid
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
||||
from tacker.db.common_services import common_services_db
|
||||
from tacker.db import db_base
|
||||
from tacker.db import model_base
|
||||
from tacker.db import models_v1
|
||||
from tacker.db import types
|
||||
from tacker.extensions.nfvo_plugins import network_service
|
||||
from tacker.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 NSD(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
|
||||
models_v1.Audit):
|
||||
"""Represents NSD to create NS."""
|
||||
|
||||
__tablename__ = 'nsd'
|
||||
# Descriptive name
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
description = sa.Column(sa.Text)
|
||||
vnfds = sa.Column(types.Json, nullable=True)
|
||||
|
||||
# (key, value) pair to spin up
|
||||
attributes = orm.relationship('NSDAttribute',
|
||||
backref='nsd')
|
||||
|
||||
|
||||
class NSDAttribute(model_base.BASE, models_v1.HasId):
|
||||
"""Represents attributes necessary for creation of ns in (key, value) pair
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = 'nsd_attribute'
|
||||
nsd_id = sa.Column(types.Uuid, sa.ForeignKey('nsd.id'),
|
||||
nullable=False)
|
||||
key = sa.Column(sa.String(255), nullable=False)
|
||||
value = sa.Column(sa.TEXT(65535), nullable=True)
|
||||
|
||||
|
||||
class NS(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
|
||||
models_v1.Audit):
|
||||
"""Represents network services that deploys services.
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = 'ns'
|
||||
nsd_id = sa.Column(types.Uuid, sa.ForeignKey('nsd.id'))
|
||||
nsd = orm.relationship('NSD')
|
||||
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
description = sa.Column(sa.Text, nullable=True)
|
||||
|
||||
# Dict of VNF details that network service launches
|
||||
vnf_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)
|
||||
|
||||
|
||||
class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
|
||||
def __init__(self):
|
||||
super(NSPluginDb, self).__init__()
|
||||
self._cos_db_plg = common_services_db.CommonServicesPluginDb()
|
||||
|
||||
def _make_attributes_dict(self, attributes_db):
|
||||
return dict((attr.key, attr.value) for attr in attributes_db)
|
||||
|
||||
def _make_nsd_dict(self, nsd, fields=None):
|
||||
res = {
|
||||
'attributes': self._make_attributes_dict(nsd['attributes']),
|
||||
}
|
||||
key_list = ('id', 'tenant_id', 'name', 'description',
|
||||
'created_at', 'updated_at', 'vnfds')
|
||||
res.update((key, nsd[key]) for key in key_list)
|
||||
return self._fields(res, fields)
|
||||
|
||||
def create_nsd(self, context, nsd):
|
||||
vnfds = nsd['vnfds']
|
||||
nsd = nsd['nsd']
|
||||
LOG.debug(_('nsd %s'), nsd)
|
||||
tenant_id = self._get_tenant_id_for_create(context, nsd)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
nsd_id = str(uuid.uuid4())
|
||||
nsd_db = NSD(
|
||||
id=nsd_id,
|
||||
tenant_id=tenant_id,
|
||||
name=nsd.get('name'),
|
||||
vnfds=vnfds,
|
||||
description=nsd.get('description'))
|
||||
context.session.add(nsd_db)
|
||||
for (key, value) in nsd.get('attributes', {}).items():
|
||||
attribute_db = NSDAttribute(
|
||||
id=str(uuid.uuid4()),
|
||||
nsd_id=nsd_id,
|
||||
key=key,
|
||||
value=value)
|
||||
context.session.add(attribute_db)
|
||||
|
||||
LOG.debug(_('nsd_db %(nsd_db)s %(attributes)s '),
|
||||
{'nsd_db': nsd_db,
|
||||
'attributes': nsd_db.attributes})
|
||||
nsd_dict = self._make_nsd_dict(nsd_db)
|
||||
LOG.debug(_('nsd_dict %s'), nsd_dict)
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=nsd_dict['id'],
|
||||
res_type=constants.RES_TYPE_NSD,
|
||||
res_state=constants.RES_EVT_ONBOARDED,
|
||||
evt_type=constants.RES_EVT_CREATE,
|
||||
tstamp=nsd_dict[constants.RES_EVT_CREATED_FLD])
|
||||
return nsd_dict
|
||||
|
||||
def delete_nsd(self,
|
||||
context,
|
||||
nsd_id,
|
||||
soft_delete=True):
|
||||
with context.session.begin(subtransactions=True):
|
||||
nss_db = context.session.query(NS).filter_by(
|
||||
nsd_id=nsd_id).first()
|
||||
if nss_db is not None and nss_db.deleted_at is None:
|
||||
raise network_service.NSDInUse(
|
||||
nsd_id=nsd_id)
|
||||
|
||||
nsd_db = self._get_resource(context, NSD,
|
||||
nsd_id)
|
||||
if soft_delete:
|
||||
nsd_db.update({'deleted_at': timeutils.utcnow()})
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=nsd_db['id'],
|
||||
res_type=constants.RES_TYPE_NSD,
|
||||
res_state=constants.RES_EVT_NA_STATE,
|
||||
evt_type=constants.RES_EVT_DELETE,
|
||||
tstamp=nsd_db[constants.RES_EVT_DELETED_FLD])
|
||||
else:
|
||||
context.session.query(NSDAttribute).filter_by(
|
||||
nsd_id=nsd_id).delete()
|
||||
context.session.delete(nsd_db)
|
||||
|
||||
def get_nsd(self, context, nsd_id, fields=None):
|
||||
nsd_db = self._get_resource(context, NSD, nsd_id)
|
||||
return self._make_nsd_dict(nsd_db)
|
||||
|
||||
def get_nsds(self, context, filters, fields=None):
|
||||
return self._get_collection(context, NSD,
|
||||
self._make_nsd_dict,
|
||||
filters=filters, fields=fields)
|
||||
|
||||
# reference implementation. needs to be overrided by subclass
|
||||
def create_ns(self, context, ns):
|
||||
return {'nsd': {}}
|
||||
|
||||
# reference implementation. needs to be overrided by subclass
|
||||
def delete_ns(self, context, ns_id, soft_delete=True):
|
||||
pass
|
||||
|
||||
def get_ns(self, context, ns_id, fields=None):
|
||||
pass
|
||||
|
||||
def get_nss(self, context, filters=None, fields=None):
|
||||
pass
|
@ -18,6 +18,7 @@ import uuid
|
||||
|
||||
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
|
||||
@ -156,7 +157,9 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
|
||||
def _get_resource(self, context, model, id):
|
||||
try:
|
||||
return self._get_by_id(context, model, id)
|
||||
if uuidutils.is_uuid_like(id):
|
||||
return self._get_by_id(context, model, id)
|
||||
return self._get_by_name(context, model, id)
|
||||
except orm_exc.NoResultFound:
|
||||
if issubclass(model, VNFD):
|
||||
raise vnfm.VNFDNotFound(vnfd_id=id)
|
||||
@ -254,7 +257,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=vnfd_dict['id'],
|
||||
res_type=constants.RES_TYPE_VNFD,
|
||||
res_state=constants.RES_EVT_VNFD_ONBOARDED,
|
||||
res_state=constants.RES_EVT_ONBOARDED,
|
||||
evt_type=constants.RES_EVT_CREATE,
|
||||
tstamp=vnfd_dict[constants.RES_EVT_CREATED_FLD])
|
||||
return vnfd_dict
|
||||
@ -270,7 +273,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=vnfd_dict['id'],
|
||||
res_type=constants.RES_TYPE_VNFD,
|
||||
res_state=constants.RES_EVT_VNFD_NA_STATE,
|
||||
res_state=constants.RES_EVT_NA_STATE,
|
||||
evt_type=constants.RES_EVT_UPDATE,
|
||||
tstamp=vnfd_dict[constants.RES_EVT_UPDATED_FLD])
|
||||
return vnfd_dict
|
||||
@ -295,7 +298,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=vnfd_db['id'],
|
||||
res_type=constants.RES_TYPE_VNFD,
|
||||
res_state=constants.RES_EVT_VNFD_NA_STATE,
|
||||
res_state=constants.RES_EVT_NA_STATE,
|
||||
evt_type=constants.RES_EVT_DELETE,
|
||||
tstamp=vnfd_db[constants.RES_EVT_DELETED_FLD])
|
||||
else:
|
||||
|
@ -209,6 +209,9 @@ class ClassifierNotFoundException(exceptions.NotFound):
|
||||
message = _('Classifier %(classifier_id)s could not be found')
|
||||
|
||||
|
||||
class NSDInUse(exceptions.InUse):
|
||||
message = _('NSD %(nsd_id)s is still in use')
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
|
||||
'vims': {
|
||||
@ -549,7 +552,152 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
'nsds': {
|
||||
'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,
|
||||
},
|
||||
'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,
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
'nss': {
|
||||
'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,
|
||||
},
|
||||
'vnf_ids': {
|
||||
'allow_post': True,
|
||||
'allow_put': False,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True,
|
||||
'default': '',
|
||||
},
|
||||
'nsd_id': {
|
||||
'allow_post': True,
|
||||
'allow_put': False,
|
||||
'validate': {'type:uuid': None},
|
||||
'is_visible': True,
|
||||
},
|
||||
'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,
|
||||
},
|
||||
'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,
|
||||
},
|
||||
'mgmt_urls': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'convert_to': attr.convert_none_to_empty_dict,
|
||||
'validate': {'type:dict_or_nodata': None},
|
||||
'is_visible': True,
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
52
tacker/extensions/nfvo_plugins/network_service.py
Normal file
52
tacker/extensions/nfvo_plugins/network_service.py
Normal file
@ -0,0 +1,52 @@
|
||||
# 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 tacker.services import service_base
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class NSPluginBase(service_base.NFVPluginBase):
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_nsd(self, context, nsd):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_nsd(self, context, nsd_id):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nsd(self, context, nsd_id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nsds(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_ns(self, context, ns):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nss(self, context, filters=None, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_ns(self, context, ns_id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_ns(self, context, ns_id):
|
||||
pass
|
@ -149,6 +149,21 @@ class MetadataNotMatched(exceptions.InvalidInput):
|
||||
message = _("Metadata for alarm policy is not matched")
|
||||
|
||||
|
||||
class InvalidSubstitutionMapping(exceptions.InvalidInput):
|
||||
message = _("Input for substitution mapping requirements are not"
|
||||
" valid for %(requirement)s. They must be in the form"
|
||||
" of list with two entries")
|
||||
|
||||
|
||||
class SMRequirementMissing(exceptions.InvalidInput):
|
||||
message = _("All the requirements for substitution_mappings are not"
|
||||
" provided. Missing requirement for %(requirement)s")
|
||||
|
||||
|
||||
class InvalidParamsForSM(exceptions.InvalidInput):
|
||||
message = _("Please provide parameters for substitution mappings")
|
||||
|
||||
|
||||
def _validate_service_type_list(data, valid_values=None):
|
||||
if not isinstance(data, list):
|
||||
msg = _("invalid data format for service list: '%s'") % data
|
||||
|
@ -18,12 +18,15 @@ import os
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from cryptography import fernet
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
from oslo_utils import strutils
|
||||
from tempfile import mkstemp
|
||||
from toscaparser.tosca_template import ToscaTemplate
|
||||
|
||||
from tacker._i18n import _
|
||||
from tacker.common import driver_manager
|
||||
@ -32,6 +35,7 @@ from tacker.common import log
|
||||
from tacker.common import utils
|
||||
from tacker import context as t_context
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
from tacker.db.nfvo import ns_db
|
||||
from tacker.db.nfvo import vnffg_db
|
||||
from tacker.extensions import nfvo
|
||||
from tacker import manager
|
||||
@ -48,7 +52,8 @@ def config_opts():
|
||||
return [('nfvo_vim', NfvoPlugin.OPTS)]
|
||||
|
||||
|
||||
class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||
class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
ns_db.NSPluginDb):
|
||||
"""NFVO reference plugin for NFVO extension
|
||||
|
||||
Implements the NFVO extension and defines public facing APIs for VIM
|
||||
@ -404,3 +409,74 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||
vim_auth=vim_obj['auth_cred'],
|
||||
resource_type=resource,
|
||||
resource_name=name)
|
||||
|
||||
@log.log
|
||||
def create_nsd(self, context, nsd):
|
||||
nsd_data = nsd['nsd']
|
||||
template = nsd_data['attributes'].get('nsd')
|
||||
if isinstance(template, dict):
|
||||
nsd_data['attributes']['nsd'] = yaml.safe_dump(
|
||||
template)
|
||||
LOG.debug(_('nsd %s'), nsd_data)
|
||||
|
||||
name = nsd_data['name']
|
||||
if self._get_by_name(context, ns_db.NSD, name):
|
||||
raise exceptions.DuplicateResourceName(resource='NSD', name=name)
|
||||
|
||||
self._parse_template_input(context, nsd)
|
||||
return super(NfvoPlugin, self).create_nsd(
|
||||
context, nsd)
|
||||
|
||||
def _parse_template_input(self, context, nsd):
|
||||
nsd_dict = nsd['nsd']
|
||||
nsd_yaml = nsd_dict['attributes'].get('nsd')
|
||||
inner_nsd_dict = yaml.load(nsd_yaml)
|
||||
nsd['vnfds'] = dict()
|
||||
LOG.debug(_('nsd_dict: %s'), inner_nsd_dict)
|
||||
|
||||
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||
vnfd_imports = inner_nsd_dict['imports']
|
||||
inner_nsd_dict['imports'] = []
|
||||
new_files = []
|
||||
for vnfd_name in vnfd_imports:
|
||||
vnfd = vnfm_plugin.get_vnfd(context, vnfd_name)
|
||||
# Copy VNF types and VNF names
|
||||
sm_dict = yaml.load(vnfd['attributes']['vnfd'])[
|
||||
'topology_template'][
|
||||
'substitution_mappings']
|
||||
nsd['vnfds'][sm_dict['node_type']] = vnfd['name']
|
||||
# Ugly Hack to validate the child templates
|
||||
# TODO(tbh): add support in tosca-parser to pass child
|
||||
# templates as dict
|
||||
fd, temp_path = mkstemp()
|
||||
with open(temp_path, 'w') as fp:
|
||||
fp.write(vnfd['attributes']['vnfd'])
|
||||
os.close(fd)
|
||||
new_files.append(temp_path)
|
||||
inner_nsd_dict['imports'].append(temp_path)
|
||||
# Prepend the tacker_defs.yaml import file with the full
|
||||
# path to the file
|
||||
toscautils.updateimports(inner_nsd_dict)
|
||||
|
||||
try:
|
||||
ToscaTemplate(a_file=False,
|
||||
yaml_dict_tpl=inner_nsd_dict)
|
||||
except Exception as e:
|
||||
LOG.exception(_("tosca-parser error: %s"), str(e))
|
||||
raise nfvo.ToscaParserFailed(error_msg_details=str(e))
|
||||
finally:
|
||||
for file_path in new_files:
|
||||
os.remove(file_path)
|
||||
inner_nsd_dict['imports'] = vnfd_imports
|
||||
|
||||
if ('description' not in nsd_dict or
|
||||
nsd_dict['description'] == ''):
|
||||
nsd_dict['description'] = inner_nsd_dict.get(
|
||||
'description', '')
|
||||
if (('name' not in nsd_dict or
|
||||
not len(nsd_dict['name'])) and
|
||||
'metadata' in inner_nsd_dict):
|
||||
nsd_dict['name'] = inner_nsd_dict['metadata'].get(
|
||||
'template_name', '')
|
||||
|
||||
LOG.debug(_('nsd %s'), nsd)
|
||||
|
@ -56,6 +56,7 @@ POLICY_ALARMING = 'tosca.policies.tacker.Alarming'
|
||||
DEFAULT_ALARM_ACTIONS = ['respawn', 'log', 'log_and_kill', 'notify']
|
||||
|
||||
RES_TYPE_VNFD = "vnfd"
|
||||
RES_TYPE_NSD = "nsd"
|
||||
RES_TYPE_VNF = "vnf"
|
||||
RES_TYPE_VIM = "vim"
|
||||
|
||||
@ -64,8 +65,8 @@ RES_EVT_DELETE = "DELETE"
|
||||
RES_EVT_UPDATE = "UPDATE"
|
||||
RES_EVT_MONITOR = "MONITOR"
|
||||
RES_EVT_SCALE = "SCALE"
|
||||
RES_EVT_VNFD_NA_STATE = "Not Applicable"
|
||||
RES_EVT_VNFD_ONBOARDED = "OnBoarded"
|
||||
RES_EVT_NA_STATE = "Not Applicable"
|
||||
RES_EVT_ONBOARDED = "OnBoarded"
|
||||
|
||||
RES_EVT_CREATED_FLD = "created_at"
|
||||
RES_EVT_DELETED_FLD = "deleted_at"
|
||||
|
@ -37,14 +37,14 @@ class VnfdTestCreate(base.BaseTackerTest):
|
||||
vnfd_id = vnfd_instance['vnfd']['id']
|
||||
self.verify_vnfd_events(
|
||||
vnfd_id, evt_constants.RES_EVT_CREATE,
|
||||
evt_constants.RES_EVT_VNFD_ONBOARDED)
|
||||
evt_constants.RES_EVT_ONBOARDED)
|
||||
|
||||
try:
|
||||
self.client.delete_vnfd(vnfd_id)
|
||||
except Exception:
|
||||
assert False, "vnfd Delete failed"
|
||||
self.verify_vnfd_events(vnfd_id, evt_constants.RES_EVT_DELETE,
|
||||
evt_constants.RES_EVT_VNFD_NA_STATE)
|
||||
evt_constants.RES_EVT_NA_STATE)
|
||||
|
||||
def test_tosca_vnfd(self):
|
||||
self._test_create_list_delete_tosca_vnfd('sample-tosca-vnfd.yaml',
|
||||
|
@ -33,7 +33,7 @@ class VnfmTestParam(base.BaseTackerTest):
|
||||
self.assertIsNotNone(vnfd_id)
|
||||
self.verify_vnfd_events(
|
||||
vnfd_id, evt_constants.RES_EVT_CREATE,
|
||||
evt_constants.RES_EVT_VNFD_ONBOARDED)
|
||||
evt_constants.RES_EVT_ONBOARDED)
|
||||
return vnfd_instance
|
||||
|
||||
def _test_vnfd_delete(self, vnfd_instance):
|
||||
@ -45,7 +45,7 @@ class VnfmTestParam(base.BaseTackerTest):
|
||||
except Exception:
|
||||
assert False, "vnfd Delete failed"
|
||||
self.verify_vnfd_events(vnfd_id, evt_constants.RES_EVT_DELETE,
|
||||
evt_constants.RES_EVT_VNFD_NA_STATE)
|
||||
evt_constants.RES_EVT_NA_STATE)
|
||||
try:
|
||||
vnfd_d = self.client.show_vnfd(vnfd_id)
|
||||
except Exception:
|
||||
|
@ -41,6 +41,7 @@ vnfd_alarm_respawn_tosca_template = _get_template(
|
||||
'test_tosca_vnfd_alarm_respawn.yaml')
|
||||
vnfd_alarm_scale_tosca_template = _get_template(
|
||||
'test_tosca_vnfd_alarm_scale.yaml')
|
||||
nsd_tosca_template = yaml.load(_get_template('tosca_nsd_template.yaml'))
|
||||
|
||||
|
||||
def get_dummy_vnfd_obj():
|
||||
@ -177,3 +178,11 @@ def get_dummy_vnffg_obj_vnf_mapping():
|
||||
'VNF3': '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
||||
},
|
||||
'symmetrical': False}}
|
||||
|
||||
|
||||
def get_dummy_nsd_obj():
|
||||
return {'nsd': {'description': 'dummy nsd description',
|
||||
'name': 'dummy_NSD',
|
||||
'tenant_id': u'8819a1542a5948b68f94d4be0fd50496',
|
||||
'template': {},
|
||||
'attributes': {u'nsd': nsd_tosca_template}}}
|
||||
|
@ -0,0 +1,38 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
imports:
|
||||
- VNF1
|
||||
- VNF2
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
vl1_name:
|
||||
type: string
|
||||
description: name of VL1 virtuallink
|
||||
default: net_mgmt
|
||||
vl2_name:
|
||||
type: string
|
||||
description: name of VL2 virtuallink
|
||||
default: net0
|
||||
|
||||
node_templates:
|
||||
VNF1:
|
||||
type: tosca.nodes.nfv.VNF1
|
||||
requirements:
|
||||
- virtualLink1: VL1
|
||||
- virtualLink2: VL2
|
||||
|
||||
VNF2:
|
||||
type: tosca.nodes.nfv.VNF2
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl1_name}
|
||||
vendor: tacker
|
||||
|
||||
VL2:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl2_name}
|
||||
vendor: tacker
|
||||
|
@ -69,7 +69,7 @@ class FakeVNFMPlugin(mock.Mock):
|
||||
elif {'name': ['VNF3']} in args:
|
||||
return [{'id': self.vnf3_vnfd_id}]
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
|
||||
def get_vnfs(self, *args, **kwargs):
|
||||
if {'vnfd_id': [self.vnf1_vnfd_id]} in args:
|
||||
@ -432,3 +432,13 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||
fc_id=mock.ANY,
|
||||
auth_attr=mock.ANY)
|
||||
|
||||
# def test_create_nsd(self):
|
||||
# nsd_obj = utils.get_dummy_nsd_obj()
|
||||
# with patch.object(TackerManager, 'get_service_plugins') as \
|
||||
# mock_plugins:
|
||||
# mock_plugins.return_value = {'VNFM': FakeVNFMPlugin()}
|
||||
# mock.patch('tacker.common.driver_manager.DriverManager',
|
||||
# side_effect=FakeDriverManager()).start()
|
||||
# result = self.nfvo_plugin.create_nsd(self.context, nsd_obj)
|
||||
# self.assertIsNotNone(result)
|
||||
|
@ -226,7 +226,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
mock_update_imports.assert_called_once_with(yaml_dict)
|
||||
self._cos_db_plugin.create_event.assert_called_once_with(
|
||||
self.context, evt_type=constants.RES_EVT_CREATE, res_id=mock.ANY,
|
||||
res_state=constants.RES_EVT_VNFD_ONBOARDED,
|
||||
res_state=constants.RES_EVT_ONBOARDED,
|
||||
res_type=constants.RES_TYPE_VNFD, tstamp=mock.ANY)
|
||||
|
||||
def test_create_vnfd_no_service_types(self):
|
||||
|
@ -289,6 +289,9 @@ class TOSCAToHOT(object):
|
||||
raise vnfm.ParamYAMLNotWellFormed(error_msg_details=str(e))
|
||||
|
||||
toscautils.updateimports(vnfd_dict)
|
||||
if 'substitution_mappings' in str(vnfd_dict):
|
||||
toscautils.check_for_substitution_mappings(vnfd_dict,
|
||||
parsed_params)
|
||||
|
||||
try:
|
||||
tosca = tosca_template.ToscaTemplate(parsed_params=parsed_params,
|
||||
|
@ -99,6 +99,38 @@ def updateimports(template):
|
||||
LOG.debug(_("%s"), path)
|
||||
|
||||
|
||||
@log.log
|
||||
def check_for_substitution_mappings(template, params):
|
||||
sm_dict = params.get('substitution_mappings')
|
||||
if not sm_dict:
|
||||
raise vnfm.InvalidParamsForSM()
|
||||
del params['substitution_mappings']
|
||||
requirements = sm_dict.get('requirements')
|
||||
if not requirements:
|
||||
pass
|
||||
node_tpl = template['topology_template']['node_templates']
|
||||
req_dict_tpl = template['topology_template']['substitution_mappings'][
|
||||
'requirements']
|
||||
for req_name, req_val in iteritems(req_dict_tpl):
|
||||
if req_name not in requirements:
|
||||
raise vnfm.SMRequirementMissing(requirement=req_name)
|
||||
if not isinstance(req_val, list):
|
||||
raise vnfm.InvalidSubstitutionMapping(requirement=req_name)
|
||||
try:
|
||||
node_name = req_val[0]
|
||||
node_req = req_val[1]
|
||||
|
||||
node_tpl[node_name]['requirements'].append({
|
||||
node_req: {
|
||||
'node': requirements[req_name]
|
||||
}
|
||||
})
|
||||
node_tpl[requirements[req_name]] = \
|
||||
sm_dict[requirements[req_name]]
|
||||
except Exception:
|
||||
raise vnfm.InvalidSubstitutionMapping(requirement=req_name)
|
||||
|
||||
|
||||
@log.log
|
||||
def get_vdu_monitoring(template):
|
||||
monitoring_dict = {}
|
||||
|
Loading…
Reference in New Issue
Block a user