Implements VNFFG into NFVO

implements blueprint: tacker-vnffg

Change-Id: I9e2fd8b14fd6eaf05aa7813b5f0fab6daa4abd43
Signed-off-by: Tim Rozet <trozet@redhat.com>
changes/22/344522/26
Tim Rozet 6 years ago
parent d3f791591a
commit 8b479f4268
  1. 1
      requirements.txt
  2. 145
      tacker/db/migration/alembic_migrations/versions/507122918800_adds_vnffg.py
  3. 2
      tacker/db/migration/alembic_migrations/versions/HEAD
  4. 1
      tacker/db/migration/models/head.py
  5. 908
      tacker/db/nfvo/vnffg_db.py
  6. 397
      tacker/extensions/nfvo.py
  7. 0
      tacker/extensions/nfvo_plugins/__init__.py
  8. 83
      tacker/extensions/nfvo_plugins/vnffg.py
  9. 11
      tacker/nfvo/drivers/vim/abstract_vim_driver.py
  10. 69
      tacker/nfvo/drivers/vim/openstack_driver.py
  11. 72
      tacker/nfvo/drivers/vnffg/sfc_drivers/noop.py
  12. 224
      tacker/nfvo/nfvo_plugin.py
  13. 32
      tacker/tests/unit/db/utils.py
  14. 41
      tacker/tests/unit/vm/infra_drivers/heat/data/tosca_invalid_vnffgd_template.yaml
  15. 41
      tacker/tests/unit/vm/infra_drivers/heat/data/tosca_vnffgd_template.yaml
  16. 32
      tacker/tests/unit/vm/infra_drivers/heat/data/vnffgd_template.yaml
  17. 286
      tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py
  18. 170
      tacker/vnfm/tosca/lib/tacker_nfv_defs.yaml

@ -37,6 +37,7 @@ oslo.serialization>=1.10.0 # Apache-2.0
oslo.service>=1.10.0 # Apache-2.0
oslo.utils>=3.16.0 # Apache-2.0
oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
python-neutronclient>=5.1.0 # Apache-2.0
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
tosca-parser>=0.5.0 # Apache-2.0
heat-translator>=0.4.0 # Apache-2.0

@ -0,0 +1,145 @@
# 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.
#
"""adds_VNFFG
Revision ID: 507122918800
Revises: 4ee19c8a6d0a
Create Date: 2016-07-29 21:48:18.816277
"""
# revision identifiers, used by Alembic.
revision = '507122918800'
down_revision = '4ee19c8a6d0a'
import sqlalchemy as sa
from alembic import op
from tacker.db.types import Json
def upgrade(active_plugins=None, options=None):
op.create_table(
'vnffgtemplates',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('template', Json),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
op.create_table(
'vnffgs',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('vnffgd_id', sa.String(length=36), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.Column('vnf_mapping', Json),
sa.ForeignKeyConstraint(['vnffgd_id'], ['vnffgtemplates.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
op.create_table(
'vnffgnfps',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('vnffg_id', sa.String(length=36), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.Column('path_id', sa.String(length=255), nullable=False),
sa.Column('symmetrical', sa.Boolean, default=False),
sa.ForeignKeyConstraint(['vnffg_id'], ['vnffgs.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
op.create_table(
'vnffgchains',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('instance_id', sa.String(length=255), nullable=True),
sa.Column('nfp_id', sa.String(length=36), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.Column('path_id', sa.String(length=255), nullable=False),
sa.Column('symmetrical', sa.Boolean, default=False),
sa.Column('chain', Json),
sa.ForeignKeyConstraint(['nfp_id'], ['vnffgnfps.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
op.create_table(
'vnffgclassifiers',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('nfp_id', sa.String(length=36), nullable=False),
sa.Column('instance_id', sa.String(length=255), nullable=True),
sa.Column('chain_id', sa.String(length=36), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.ForeignKeyConstraint(['nfp_id'], ['vnffgnfps.id'], ),
sa.ForeignKeyConstraint(['chain_id'], ['vnffgchains.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
op.create_table(
'aclmatchcriterias',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('vnffgc_id', sa.String(length=36), nullable=False),
sa.Column('eth_src', sa.String(length=36), nullable=True),
sa.Column('eth_dst', sa.String(length=36), nullable=True),
sa.Column('eth_type', sa.String(length=36), nullable=True),
sa.Column('vlan_id', sa.Integer, nullable=True),
sa.Column('vlan_pcp', sa.Integer, nullable=True),
sa.Column('mpls_label', sa.Integer, nullable=True),
sa.Column('mpls_tc', sa.Integer, nullable=True),
sa.Column('ip_dscp', sa.Integer, nullable=True),
sa.Column('ip_ecn', sa.Integer, nullable=True),
sa.Column('ip_src_prefix', sa.String(length=36), nullable=True),
sa.Column('ip_dst_prefix', sa.String(length=36), nullable=True),
sa.Column('source_port_min', sa.Integer, nullable=True),
sa.Column('source_port_max', sa.Integer, nullable=True),
sa.Column('destination_port_min', sa.Integer, nullable=True),
sa.Column('destination_port_max', sa.Integer, nullable=True),
sa.Column('ip_proto', sa.Integer, nullable=True),
sa.Column('network_id', sa.String(length=36), nullable=True),
sa.Column('network_src_port_id', sa.String(length=36), nullable=True),
sa.Column('network_dst_port_id', sa.String(length=36), nullable=True),
sa.Column('tenant_id', sa.String(length=64), nullable=True),
sa.Column('icmpv4_type', sa.Integer, nullable=True),
sa.Column('icmpv4_code', sa.Integer, nullable=True),
sa.Column('arp_op', sa.Integer, nullable=True),
sa.Column('arp_spa', sa.Integer, nullable=True),
sa.Column('arp_tpa', sa.Integer, nullable=True),
sa.Column('arp_sha', sa.Integer, nullable=True),
sa.Column('arp_tha', sa.Integer, nullable=True),
sa.Column('ipv6_src', sa.String(36), nullable=True),
sa.Column('ipv6_dst', sa.String(36), nullable=True),
sa.Column('ipv6_flabel', sa.Integer, nullable=True),
sa.Column('icmpv6_type', sa.Integer, nullable=True),
sa.Column('icmpv6_code', sa.Integer, nullable=True),
sa.Column('ipv6_nd_target', sa.String(36), nullable=True),
sa.Column('ipv6_nd_sll', sa.String(36), nullable=True),
sa.Column('ipv6_nd_tll', sa.String(36), nullable=True),
sa.ForeignKeyConstraint(['vnffgc_id'], ['vnffgclassifiers.id'], ),
sa.PrimaryKeyConstraint('id'),
)

@ -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 vnffg_db # noqa
from tacker.db.vm import vm_db # noqa

@ -0,0 +1,908 @@
# Copyright 2016 Red Hat 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 random
import sqlalchemy as sa
import uuid
from oslo_log import log as logging
from sqlalchemy import orm
from sqlalchemy.orm import exc as orm_exc
from tacker._i18n import _
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 import nfvo
from tacker.extensions.nfvo_plugins import vnffg
from tacker import manager
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)
_VALID_VNFFG_UPDATE_ATTRIBUTES = ('name', 'description', 'vnf_mapping')
_VALID_SFC_UPDATE_ATTRIBUTES = ('chain', 'symmetrical')
_VALID_FC_UPDATE_ATTRIBUTES = ()
MATCH_CRITERIA = (
'eth_type', 'eth_src', 'eth_dst', 'vlan_id', 'vlan_pcp', 'mpls_label',
'mpls_tc', 'ip_dscp', 'ip_ecn', 'ip_src_prefix', 'ip_dst_prefix',
'ip_proto', 'destination_port_range', 'source_port_range',
'network_src_port_id', 'network_dst_port_id', 'network_id', 'network_name',
'tenant_id', 'icmpv4_type', 'icmpv4_code', 'arp_op', 'arp_spa',
'arp_tpa', 'arp_sha', 'arp_tha', 'ipv6_src', 'ipv6_dst', 'ipv6_flabel',
'icmpv6_type', 'icmpv6_code', 'ipv6_nd_target', 'ipv6_nd_sll',
'ipv6_nd_tll')
MATCH_DB_KEY_LIST = (
'eth_type', 'eth_src', 'eth_dst', 'vlan_id', 'vlan_pcp', 'mpls_label',
'mpls_tc', 'ip_dscp', 'ip_ecn', 'ip_src_prefix', 'ip_dst_prefix',
'ip_proto', 'destination_port_min', 'destination_port_max',
'source_port_min', 'source_port_max', 'network_src_port_id',
'network_dst_port_id', 'network_id', 'tenant_id', 'icmpv4_type',
'icmpv4_code', 'arp_op', 'arp_spa', 'arp_tpa', 'arp_sha', 'arp_tha',
'ipv6_src', 'ipv6_dst', 'ipv6_flabel', 'icmpv6_type', 'icmpv6_code',
'ipv6_nd_target', 'ipv6_nd_sll', 'ipv6_nd_tll'
)
CP = 'connection_points'
class VnffgTemplate(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
"""Represents template to create a VNF Forwarding Graph."""
# Descriptive name
name = sa.Column(sa.String(255), nullable=False)
description = sa.Column(sa.Text)
# Vnffg template
template = sa.Column(types.Json)
class Vnffg(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""VNF Forwarding Graph Data Model"""
name = sa.Column(sa.String(255), nullable=False)
description = sa.Column(sa.String(255), nullable=True)
# List of associated NFPs
forwarding_paths = orm.relationship("VnffgNfp", backref="vnffg")
vnffgd_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgtemplates.id'))
vnffgd = orm.relationship('VnffgTemplate')
status = sa.Column(sa.String(255), nullable=False)
# Mapping of VNFD to VNF instance names
vnf_mapping = sa.Column(types.Json)
class VnffgNfp(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""Network Forwarding Path Data Model"""
name = sa.Column(sa.String(255), nullable=False)
vnffg_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgs.id'),
nullable=False)
classifier = orm.relationship('VnffgClassifier', backref='nfp',
uselist=False)
chain = orm.relationship('VnffgChain', backref='nfp',
uselist=False)
status = sa.Column(sa.String(255), nullable=False)
path_id = sa.Column(sa.String(255), nullable=False)
# symmetry of forwarding path
symmetrical = sa.Column(sa.Boolean(), default=False)
class VnffgChain(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""Service Function Chain Data Model"""
status = sa.Column(sa.String(255), nullable=False)
instance_id = sa.Column(sa.String(255), nullable=True)
# symmetry of forwarding path
symmetrical = sa.Column(sa.Boolean(), default=False)
# chain
chain = sa.Column(types.Json)
path_id = sa.Column(sa.String(255), nullable=False)
nfp_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgnfps.id'))
class VnffgClassifier(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""VNFFG NFP Classifier Data Model"""
status = sa.Column(sa.String(255), nullable=False)
instance_id = sa.Column(sa.String(255), nullable=True)
chain_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgchains.id'))
chain = orm.relationship('VnffgChain', backref='classifier',
uselist=False, foreign_keys=[chain_id])
nfp_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgnfps.id'))
# match criteria
match = orm.relationship('ACLMatchCriteria')
class ACLMatchCriteria(model_base.BASE, models_v1.HasId):
"""Represents ACL match criteria of a classifier."""
vnffgc_id = sa.Column(types.Uuid, sa.ForeignKey('vnffgclassifiers.id'))
eth_src = sa.Column(sa.String(36), nullable=True)
eth_dst = sa.Column(sa.String(36), nullable=True)
eth_type = sa.Column(sa.String(36), nullable=True)
vlan_id = sa.Column(sa.Integer, nullable=True)
vlan_pcp = sa.Column(sa.Integer, nullable=True)
mpls_label = sa.Column(sa.Integer, nullable=True)
mpls_tc = sa.Column(sa.Integer, nullable=True)
ip_dscp = sa.Column(sa.Integer, nullable=True)
ip_ecn = sa.Column(sa.Integer, nullable=True)
ip_src_prefix = sa.Column(sa.String(36), nullable=True)
ip_dst_prefix = sa.Column(sa.String(36), nullable=True)
source_port_min = sa.Column(sa.Integer, nullable=True)
source_port_max = sa.Column(sa.Integer, nullable=True)
destination_port_min = sa.Column(sa.Integer, nullable=True)
destination_port_max = sa.Column(sa.Integer, nullable=True)
ip_proto = sa.Column(sa.Integer, nullable=True)
network_id = sa.Column(types.Uuid, nullable=True)
network_src_port_id = sa.Column(types.Uuid, nullable=True)
network_dst_port_id = sa.Column(types.Uuid, nullable=True)
tenant_id = sa.Column(sa.String(64), nullable=True)
icmpv4_type = sa.Column(sa.Integer, nullable=True)
icmpv4_code = sa.Column(sa.Integer, nullable=True)
arp_op = sa.Column(sa.Integer, nullable=True)
arp_spa = sa.Column(sa.String(36), nullable=True)
arp_tpa = sa.Column(sa.String(36), nullable=True)
arp_sha = sa.Column(sa.String(36), nullable=True)
arp_tha = sa.Column(sa.String(36), nullable=True)
ipv6_src = sa.Column(sa.String(36), nullable=True)
ipv6_dst = sa.Column(sa.String(36), nullable=True)
ipv6_flabel = sa.Column(sa.Integer, nullable=True)
icmpv6_type = sa.Column(sa.Integer, nullable=True)
icmpv6_code = sa.Column(sa.Integer, nullable=True)
ipv6_nd_target = sa.Column(sa.String(36), nullable=True)
ipv6_nd_sll = sa.Column(sa.String(36), nullable=True)
ipv6_nd_tll = sa.Column(sa.String(36), nullable=True)
class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
def __init__(self):
super(VnffgPluginDbMixin, self).__init__()
def create_vnffg(self, context, vnffg):
vnffg_dict = self._create_vnffg_pre(context, vnffg)
sfc_instance = str(uuid.uuid4())
fc_instance = str(uuid.uuid4())
self._create_vnffg_post(context, sfc_instance,
fc_instance, vnffg_dict)
self._create_vnffg_status(context, vnffg_dict)
return vnffg_dict
def get_vnffg(self, context, vnffg_id, fields=None):
vnffg_db = self._get_resource(context, Vnffg, vnffg_id)
return self._make_vnffg_dict(vnffg_db, fields)
def get_vnffgs(self, context, filters=None, fields=None):
return self._get_collection(context, Vnffg, self._make_vnffg_dict,
filters=filters, fields=fields)
def update_vnffg(self, context, vnffg_id, vnffg):
vnffg_dict = self._update_vnffg_pre(context, vnffg_id)
self._update_vnffg_post(context, vnffg_id, constants.ACTIVE, vnffg)
return vnffg_dict
def delete_vnffg(self, context, vnffg_id):
self._delete_vnffg_pre(context, vnffg_id)
self._delete_vnffg_post(context, vnffg_id, False)
def create_vnffgd(self, context, vnffgd):
template = vnffgd['vnffgd']
LOG.debug(_('template %s'), template)
tenant_id = self._get_tenant_id_for_create(context, template)
with context.session.begin(subtransactions=True):
template_id = str(uuid.uuid4())
template_db = VnffgTemplate(
id=template_id,
tenant_id=tenant_id,
name=template.get('name'),
description=template.get('description'),
template=template.get('template'))
context.session.add(template_db)
LOG.debug(_('template_db %(template_db)s'),
{'template_db': template_db})
return self._make_template_dict(template_db)
def get_vnffgd(self, context, vnffgd_id, fields=None):
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
return self._make_template_dict(template_db, fields)
def get_vnffgds(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgTemplate,
self._make_template_dict,
filters=filters, fields=fields)
def delete_vnffgd(self, context, vnffgd_id):
with context.session.begin(subtransactions=True):
vnffg_db = context.session.query(Vnffg).filter_by(
vnffgd_id=vnffgd_id).first()
if vnffg_db is not None:
raise nfvo.VnffgdInUse(vnffgd_id=vnffgd_id)
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
context.session.delete(template_db)
def get_classifier(self, context, classifier_id, fields=None):
classifier_db = self._get_resource(context, VnffgClassifier,
classifier_id)
return self._make_classifier_dict(classifier_db, fields)
def get_classifiers(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgClassifier,
self._make_classifier_dict,
filters=filters, fields=fields)
def get_nfp(self, context, nfp_id, fields=None):
nfp_db = self._get_resource(context, VnffgNfp, nfp_id)
return self._make_nfp_dict(nfp_db, fields)
def get_nfps(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgNfp,
self._make_nfp_dict,
filters=filters, fields=fields)
def get_sfc(self, context, sfc_id, fields=None):
chain_db = self._get_resource(context, VnffgChain, sfc_id)
return self._make_chain_dict(chain_db, fields)
def get_sfcs(self, context, filters=None, fields=None):
return self._get_collection(context, VnffgChain,
self._make_chain_dict,
filters=filters, fields=fields)
# called internally, not by REST API
def _create_vnffg_pre(self, context, vnffg):
vnffg = vnffg['vnffg']
LOG.debug(_('vnffg %s'), vnffg)
tenant_id = self._get_tenant_id_for_create(context, vnffg)
name = vnffg.get('name')
vnffg_id = vnffg.get('id') or str(uuid.uuid4())
template_id = vnffg['vnffgd_id']
symmetrical = vnffg['symmetrical']
with context.session.begin(subtransactions=True):
template_db = self._get_resource(context, VnffgTemplate,
template_id)
LOG.debug(_('vnffg template %s'), template_db)
vnf_members = self._get_vnffg_property(template_db,
'constituent_vnfs')
LOG.debug(_('Constituent VNFs: %s'), vnf_members)
vnf_mapping = self._get_vnf_mapping(context, vnffg.get(
'vnf_mapping'), vnf_members)
LOG.debug(_('VNF Mapping: %s'), vnf_mapping)
# create NFP dict
nfp_dict = self._create_nfp_pre(template_db)
vnffg_db = Vnffg(id=vnffg_id,
tenant_id=tenant_id,
name=name,
description=template_db.description,
vnf_mapping=vnf_mapping,
vnffgd_id=template_id,
status=constants.PENDING_CREATE)
context.session.add(vnffg_db)
nfp_id = str(uuid.uuid4())
sfc_id = str(uuid.uuid4())
classifier_id = str(uuid.uuid4())
nfp_db = VnffgNfp(id=nfp_id, vnffg_id=vnffg_id,
tenant_id=tenant_id,
name=nfp_dict['name'],
status=constants.PENDING_CREATE,
path_id=nfp_dict['path_id'],
symmetrical=symmetrical)
context.session.add(nfp_db)
chain = self._create_port_chain(context, vnf_mapping, template_db,
nfp_dict['name'])
LOG.debug(_('chain: %s'), chain)
sfc_db = VnffgChain(id=sfc_id,
tenant_id=tenant_id,
status=constants.PENDING_CREATE,
symmetrical=symmetrical,
chain=chain,
nfp_id=nfp_id,
path_id=nfp_dict['path_id'])
context.session.add(sfc_db)
sfcc_db = VnffgClassifier(id=classifier_id,
tenant_id=tenant_id,
status=constants.PENDING_CREATE,
nfp_id=nfp_id,
chain_id=sfc_id)
context.session.add(sfcc_db)
match = self._policy_to_acl_criteria(context, template_db,
nfp_dict['name'],
vnf_mapping)
LOG.debug(_('acl_match %s'), match)
match_db_table = ACLMatchCriteria(
id=str(uuid.uuid4()),
vnffgc_id=classifier_id,
**match)
context.session.add(match_db_table)
return self._make_vnffg_dict(vnffg_db)
@staticmethod
def _create_nfp_pre(template_db):
template = template_db.template['vnffgd']['topology_template']
nfp_dict = dict()
vnffg_name = template['groups'].keys()[0]
# we assume only one NFP for initial implementation
nfp_dict['name'] = template['groups'][vnffg_name]['members'][0]
nfp_dict['path_id'] = template['node_templates'][nfp_dict['name']][
'properties']['id']
if not nfp_dict['path_id']:
# TODO(trozet): do we need to check if this path ID is already
# taken by another VNFFG
nfp_dict['path_id'] = random.randint(1, 16777216)
return nfp_dict
def _create_port_chain(self, context, vnf_mapping, template_db, nfp_name):
"""Creates a list of physical port ids to represent an ordered chain
:param context: SQL session context
:param vnf_mapping: dict of VNFD to VNF instance mappings
:param template_db: VNFFG Descriptor
:param nfp_name: name of the forwarding path with chain requirements
:return: list of port chain including vnf name and list of CPs
"""
chain_list = []
prev_forwarder = None
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
# Build the list of logical chain representation
logical_chain = self._get_nfp_attribute(template_db.template,
nfp_name, 'path')
# Build physical port chain
for element in logical_chain:
if element['forwarder'] not in vnf_mapping.keys():
raise nfvo.NfpForwarderNotFoundException(vnfd=element[
'forwarder'],
mapping=vnf_mapping)
# TODO(trozet): validate CP in VNFD has forwarding capability
# Find VNF resources
vnf = vnfm_plugin.get_vnf_resources(context,
vnf_mapping[element[
'forwarder']]
)
vnf_info = vnfm_plugin.get_vnf(context,
vnf_mapping[element['forwarder']])
vnf_cp = None
for resource in vnf:
if resource['name'] == element['capability']:
vnf_cp = resource['id']
break
if vnf_cp is None:
raise nfvo.VnffgCpNotFoundException(cp_id=element[
'capability'], vnf_id=vnf_mapping[element['forwarder']])
# Check if this is a new VNF entry in the chain
if element['forwarder'] != prev_forwarder:
chain_list.append({'name': vnf_info['name'],
CP: [vnf_cp]})
prev_forwarder = element['forwarder']
# Must be an egress CP
else:
if len(chain_list[-1][CP]) > 1:
raise nfvo.NfpRequirementsException(vnfd=element[
'forwarder'])
else:
chain_list[-1]['connection_points'].append(vnf_cp)
return chain_list
@staticmethod
def _get_vnffg_property(template_db, vnffg_property):
template = template_db.template['vnffgd']['topology_template']
vnffg_name = template['groups'].keys()[0]
try:
return template['groups'][vnffg_name]['properties'][vnffg_property]
except KeyError:
raise nfvo.VnffgPropertyNotFoundException(
vnffg_property=vnffg_property)
@staticmethod
def _get_nfp_attribute(template, nfp, attribute):
"""Finds any attribute of an NFP described in a template
:param template: VNFFGD template
:param nfp: name of NFP
:param attribute: attribute to find
:return: value of attribute from template
"""
template = template['vnffgd']['topology_template']
try:
attr_val = VnffgPluginDbMixin._search_value(
template['node_templates'][nfp], attribute)
if attr_val is None:
print(template['node_templates'][nfp])
raise nfvo.NfpAttributeNotFoundException(attribute=attribute)
else:
return attr_val
except KeyError:
raise nfvo.NfpAttributeNotFoundException(attribute=attribute)
@staticmethod
def _search_value(search_dict, search_key):
for k, v in search_dict.iteritems():
if k == search_key:
return v
elif isinstance(v, dict):
val = VnffgPluginDbMixin._search_value(v, search_key)
if val is not None:
return val
def _get_vnf_mapping(self, context, vnf_mapping, vnf_members):
"""Creates/validates a mapping of VNFD names to VNF IDs for NFP.
:param context: SQL session context
:param vnf_mapping: dict of requested VNFD:VNF_ID mappings
:param vnf_members: list of constituent VNFs from a VNFFG
:return: dict of VNFD:VNF_ID mappings
"""
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
new_mapping = dict()
for vnfd in vnf_members:
# there should only be one ID returned for a unique name
try:
vnfd_id = vnfm_plugin.get_vnfds(context, {'name': [vnfd]},
fields=['id']).pop()['id']
except Exception:
raise nfvo.VnffgdVnfdNotFoundException(vnfd_name=vnfd)
if vnfd_id is None:
raise nfvo.VnffgdVnfdNotFoundException(vnfd_name=vnfd)
else:
# if no VNF mapping, we need to abstractly look for instances
# that match VNFD
if vnf_mapping is None or vnfd not in vnf_mapping.keys():
# find suitable VNFs from vnfd_id
LOG.debug(_('Searching VNFS with id %s'), vnfd_id)
vnf_list = vnfm_plugin.get_vnfs(context,
{'vnfd_id': [vnfd_id]},
fields=['id'])
if vnf_list is None:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
else:
LOG.debug(_('Matching VNFs found %s'), vnf_list)
vnf_list = [vnf['id'] for vnf in vnf_list]
if len(vnf_list) > 1:
new_mapping[vnfd] = random.choice(vnf_list)
else:
new_mapping[vnfd] = vnf_list[0]
# if VNF mapping, validate instances exist and match the VNFD
else:
vnf_vnfd = vnfm_plugin.get_vnf(context, vnf_mapping[vnfd],
fields=['vnfd_id'])
if vnf_vnfd is not None:
vnf_vnfd_id = vnf_vnfd['vnfd_id']
else:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
if vnfd_id != vnf_vnfd_id:
raise nfvo.VnffgInvalidMappingException(vnfd_name=vnfd)
else:
new_mapping[vnfd] = vnf_mapping.pop(vnfd)
self._validate_vim(context, new_mapping.values())
return new_mapping
def _validate_vim(self, context, vnfs):
"""Validates all VNFs are in the same VIM
:param context: SQL Session Context
:param vnfs: List of VNF instance IDs
:return: None
"""
LOG.debug(_('validating vim for vnfs %s'), vnfs)
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
vim_id = None
for vnf in vnfs:
vnf_dict = vnfm_plugin.get_vnf(context, vnf)
if vim_id is None:
vim_id = vnf_dict['vim_id']
elif vnf_dict['vim_id'] != vim_id:
raise nfvo.VnffgVimMappingException(vnf_id=vnf, vim_id=vim_id)
def _policy_to_acl_criteria(self, context, template_db, nfp_name,
vnf_mapping):
template = template_db.template['vnffgd']['topology_template']
nfp = template['node_templates'][nfp_name]
try:
policy = nfp['properties']['policy']
except KeyError:
raise nfvo.NfpPolicyNotFoundException(policy=nfp)
if 'type' in policy:
if policy['type'] != 'ACL':
raise nfvo.NfpPolicyTypeError(type=policy['type'])
if 'criteria' not in policy:
raise nfvo.NfpPolicyCriteriaError(error="Missing criteria in "
"policy")
match = dict()
for criteria in policy['criteria']:
for key, val in criteria.iteritems():
if key in MATCH_CRITERIA:
match.update(self._convert_criteria(context, key, val,
vnf_mapping))
else:
raise nfvo.NfpPolicyCriteriaError(error="Unsupported "
"criteria: "
"{}".format(key))
return match
def _convert_criteria(self, context, criteria, value, vnf_mapping):
"""Method is used to convert criteria to proper db value from template
:param context: SQL session context
:param criteria: input criteria name
:param value: input value
:param vnf_mapping: mapping of VNFD to VNF instances
:return: converted dictionary
"""
if criteria.endswith('_range'):
prefix = criteria[:-6]
criteria_min = prefix + "_min"
criteria_max = prefix + "_max"
try:
min_val, max_val = value.split('-')
except ValueError:
raise nfvo.NfpPolicyCriteriaError(error="Range missing or "
"incorrect for "
"%s".format(criteria))
return {criteria_min: int(min_val), criteria_max: int(max_val)}
elif criteria.endswith('_name'):
prefix = criteria[:-5]
vnf_id = vnf_mapping.values()[0]
new_value = self._vim_resource_name_to_id(context, prefix, value,
vnf_id)
new_name = prefix + "_id"
return {new_name: new_value}
else:
return {criteria: value}
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
"""Converts a VIM resource name to its ID
:param context: SQL session context
:param resource: resource type to find (network, subnet, etc)
:param name: name of the resource to find its ID
:param vnf_id: A VNF instance ID that is part of the chain to which
the classifier will apply to
:return: ID of the resource name
"""
# this should be overridden with driver call to find ID given name
# for resource
return str(uuid.uuid4())
# called internally, not by REST API
# instance_id = None means error on creation
def _create_vnffg_post(self, context, sfc_instance_id,
fc_instance_id, vnffg_dict):
LOG.debug(_('SFC created instance is %s'), sfc_instance_id)
LOG.debug(_('Flow Classifier created instance is %s'),
fc_instance_id)
nfp_dict = self.get_nfp(context, vnffg_dict['forwarding_paths'])
sfc_id = nfp_dict['chain_id']
classifier_id = nfp_dict['classifier_id']
with context.session.begin(subtransactions=True):
query = (self._model_query(context, VnffgChain).
filter(VnffgChain.id == sfc_id).
filter(VnffgChain.status == constants.PENDING_CREATE).
one())
query.update({'instance_id': sfc_instance_id})
if sfc_instance_id is None:
query.update({'status': constants.ERROR})
else:
query.update({'status': constants.ACTIVE})
query = (self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == classifier_id).
filter(VnffgClassifier.status ==
constants.PENDING_CREATE).
one())
query.update({'instance_id': fc_instance_id})
if fc_instance_id is None:
query.update({'status': constants.ERROR})
else:
query.update({'status': constants.ACTIVE})
def _create_vnffg_status(self, context, vnffg):
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
classifier = self.get_classifier(context, nfp['classifier_id'])
if classifier['status'] == constants.ERROR or chain['status'] ==\
constants.ERROR:
self._update_all_status(context, vnffg['id'], nfp['id'],
constants.ERROR)
elif classifier['status'] == constants.ACTIVE and \
chain['status'] == constants.ACTIVE:
self._update_all_status(context, vnffg['id'], nfp['id'],
constants.ACTIVE)
def _update_all_status(self, context, vnffg_id, nfp_id, status):
with context.session.begin(subtransactions=True):
query = (self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg_id))
query.update({'status': status})
nfp_query = (self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp_id))
nfp_query.update({'status': status})
def _make_vnffg_dict(self, vnffg_db, fields=None):
LOG.debug(_('vnffg_db %s'), vnffg_db)
LOG.debug(_('vnffg_db nfp %s'), vnffg_db.forwarding_paths)
res = {
'forwarding_paths': vnffg_db.forwarding_paths[0]['id']
}
key_list = ('id', 'tenant_id', 'name', 'description',
'vnf_mapping', 'status', 'vnffgd_id')
res.update((key, vnffg_db[key]) for key in key_list)
return self._fields(res, fields)
def _update_vnffg_pre(self, context, vnffg_id):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
sfc = self.get_sfc(context, nfp['chain_id'])
fc = self.get_classifier(context, nfp['classifier_id'])
with context.session.begin(subtransactions=True):
vnffg_db = self._get_vnffg_db(context, vnffg['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
self._get_nfp_db(context, nfp['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
self._get_sfc_db(context, sfc['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
self._get_classifier_db(context, fc['id'], _ACTIVE_UPDATE,
constants.PENDING_UPDATE)
return self._make_vnffg_dict(vnffg_db)
def _update_vnffg_post(self, context, vnffg_id, new_status,
new_vnffg=None):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
with context.session.begin(subtransactions=True):
query = (self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg['id']).
filter(Vnffg.status == constants.PENDING_UPDATE))
query.update({'status': new_status})
nfp_query = (self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp['id']).
filter(VnffgNfp.status == constants.PENDING_UPDATE))
nfp_query.update({'status': new_status})
if new_vnffg is not None:
for key in _VALID_VNFFG_UPDATE_ATTRIBUTES:
query.update({key: new_vnffg[key]})
nfp_query.update({'symmetrical': new_vnffg['symmetrical']})
def _update_sfc_post(self, context, sfc_id, new_status, new_sfc=None):
with context.session.begin(subtransactions=True):
sfc_query = (self._model_query(context, VnffgChain).
filter(VnffgChain.id == sfc_id).
filter(VnffgChain.status == constants.PENDING_UPDATE))
sfc_query.update({'status': new_status})
if new_sfc is not None:
for key in _VALID_SFC_UPDATE_ATTRIBUTES:
sfc_query.update({key: new_sfc[key]})
def _update_classifier_post(self, context, sfc_id, new_status,
new_fc=None):
with context.session.begin(subtransactions=True):
fc_query = (self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == sfc_id).
filter(VnffgClassifier.status ==
constants.PENDING_UPDATE))
fc_query.update({'status': new_status})
if new_fc is not None:
for key in _VALID_FC_UPDATE_ATTRIBUTES:
fc_query.update({key: new_fc[key]})
def _get_vnffg_db(self, context, vnffg_id, current_statuses, new_status):
try:
vnffg_db = (
self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg_id).
filter(Vnffg.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.VnffgNotFoundException(vnffg_id=vnffg_id)
if vnffg_db.status == constants.PENDING_UPDATE:
raise nfvo.VnffgInUse(vnffg_id=vnffg_id)
vnffg_db.update({'status': new_status})
return vnffg_db
def _get_nfp_db(self, context, nfp_id, current_statuses, new_status):
try:
nfp_db = (
self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp_id).
filter(VnffgNfp.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.NfpNotFoundException(nfp_id=nfp_id)
if nfp_db.status == constants.PENDING_UPDATE:
raise nfvo.NfpInUse(nfp_id=nfp_id)
nfp_db.update({'status': new_status})
return nfp_db
def _get_sfc_db(self, context, sfc_id, current_statuses, new_status):
try:
sfc_db = (
self._model_query(context, VnffgChain).
filter(VnffgChain.id == sfc_id).
filter(VnffgChain.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.SfcNotFoundException(sfc_id=sfc_id)
if sfc_db.status == constants.PENDING_UPDATE:
raise nfvo.SfcInUse(sfc_id=sfc_id)
sfc_db.update({'status': new_status})
return sfc_db
def _get_classifier_db(self, context, fc_id, current_statuses, new_status):
try:
fc_db = (
self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == fc_id).
filter(VnffgClassifier.status.in_(current_statuses)).
with_lockmode('update').one())
except orm_exc.NoResultFound:
raise nfvo.ClassifierNotFoundException(fc_id=fc_id)
if fc_db.status == constants.PENDING_UPDATE:
raise nfvo.ClassifierInUse(fc_id=fc_id)
fc_db.update({'status': new_status})
return fc_db
def _delete_vnffg_pre(self, context, vnffg_id):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
classifier = self.get_classifier(context, nfp['classifier_id'])
with context.session.begin(subtransactions=True):
vnffg_db = self._get_vnffg_db(
context, vnffg['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
self._get_nfp_db(context, nfp['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
self._get_sfc_db(context, chain['id'], _ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
self._get_classifier_db(context, classifier['id'],
_ACTIVE_UPDATE_ERROR_DEAD,
constants.PENDING_DELETE)
return self._make_vnffg_dict(vnffg_db)
def _delete_vnffg_post(self, context, vnffg_id, error):
vnffg = self.get_vnffg(context, vnffg_id)
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
chain = self.get_sfc(context, nfp['chain_id'])
classifier = self.get_classifier(context, nfp['classifier_id'])
with context.session.begin(subtransactions=True):
vnffg_query = (
self._model_query(context, Vnffg).
filter(Vnffg.id == vnffg['id']).
filter(Vnffg.status == constants.PENDING_DELETE))
nfp_query = (
self._model_query(context, VnffgNfp).
filter(VnffgNfp.id == nfp['id']).
filter(VnffgNfp.status == constants.PENDING_DELETE))
sfc_query = (
self._model_query(context, VnffgChain).
filter(VnffgChain.id == chain['id']).
filter(VnffgChain.status == constants.PENDING_DELETE))
fc_query = (
self._model_query(context, VnffgClassifier).
filter(VnffgClassifier.id == classifier['id']).
filter(VnffgClassifier.status == constants.PENDING_DELETE))
match_query = (
self._model_query(context, ACLMatchCriteria).
filter(ACLMatchCriteria.vnffgc_id == classifier['id']))
if error:
vnffg_query.update({'status': constants.ERROR})
nfp_query.update({'status': constants.ERROR})
sfc_query.update({'status': constants.ERROR})
fc_query.update({'status': constants.ERROR})
else:
match_query.delete()
fc_query.delete()
sfc_query.delete()
nfp_query.delete()
vnffg_query.delete()
def _make_template_dict(self, template, fields=None):
res = {}
key_list = ('id', 'tenant_id', 'name', 'description', 'template')
res.update((key, template[key]) for key in key_list)
return self._fields(res, fields)
def _make_acl_match_dict(self, acl_match_db):
key_list = MATCH_DB_KEY_LIST
return {key: entry[key] for key in key_list for entry in acl_match_db
if entry[key]}
def _make_classifier_dict(self, classifier_db, fields=None):
LOG.debug(_('classifier_db %s'), classifier_db)
LOG.debug(_('classifier_db match %s'), classifier_db.match)
res = {
'match': self._make_acl_match_dict(classifier_db.match)
}
key_list = ('id', 'tenant_id', 'instance_id', 'status', 'chain_id',
'nfp_id')
res.update((key, classifier_db[key]) for key in key_list)
return self._fields(res, fields)
def _make_nfp_dict(self, nfp_db, fields=None):
LOG.debug(_('nfp_db %s'), nfp_db)
res = {'chain_id': nfp_db.chain['id'],
'classifier_id': nfp_db.classifier['id']}
key_list = ('name', 'id', 'tenant_id', 'symmetrical', 'status',
'path_id')
res.update((key, nfp_db[key]) for key in key_list)
return self._fields(res, fields)
def _make_chain_dict(self, chain_db, fields=None):
LOG.debug(_('chain_db %s'), chain_db)
res = {}
key_list = ('id', 'tenant_id', 'symmetrical', 'status', 'chain',
'path_id', 'nfp_id', 'instance_id')
res.update((key, chain_db[key]) for key in key_list)
return self._fields(res, fields)
def _get_resource(self, context, model, res_id):
try:
return self._get_by_id(context, model, res_id)
except orm_exc.NoResultFound:
if issubclass(model, Vnffg):
raise nfvo.VnffgNotFoundException(vnffg_id=res_id)
elif issubclass(model, VnffgClassifier):
raise nfvo.ClassifierNotFoundException(classifier_id=res_id)
if issubclass(model, VnffgTemplate):
raise nfvo.VnffgdNotFoundException(vnffgd_id=res_id)
if issubclass(model, VnffgChain):
raise nfvo.SfcNotFoundException(sfc_id=res_id)
else:
raise

@ -17,6 +17,7 @@ import abc
import six
from tacker._i18n import _
from tacker.api import extensions
from tacker.api.v1 import attributes as attr
from tacker.api.v1 import resource_helper
@ -73,6 +74,151 @@ class VimDuplicateUrlException(exceptions.TackerException):
"duplicate VIM")
class VimUnsupportedResourceTypeException(exceptions.TackerException):
message = _("Resource type %(type) is unsupported by VIM")
class VimGetResourceException(exceptions.TackerException):
message = _("Error while trying to issue %(cmd)s to find resource type "
"%(type)s")
class VimFromVnfNotFoundException(exceptions.NotFound):
message = _('VIM from VNF %(vnf_id)s could not be found')
class ToscaParserFailed(exceptions.InvalidInput):
message = _("tosca-parser failed: - %(error_msg_details)s")
class VnffgdInvalidTemplate(exceptions.InvalidInput):
message = _("Invalid VNFFG template input: %(template)s")
class VnffgdDuplicateForwarderException(exceptions.InvalidInput):
message = _("Invalid Forwarding Path contains duplicate forwarder not in "
"order: %(forwarder)s")
class VnffgdDuplicateCPException(exceptions.InvalidInput):
message = _("Invalid Forwarding Path contains duplicate connection point "
": %(cp)s")
class VnffgdVnfdNotFoundException(exceptions.NotFound):
message = _("Specified VNFD %(vnfd_name)s in VNFFGD does not exist. "
"Please create VNFDs before creating VNFFG")
class VnffgdCpNotFoundException(exceptions.NotFound):
message = _("Specified CP %(cp_id)s could not be found in VNFD "
"%(vnfd_name)s. Please check VNFD for correct Connection "
"Point.")
class VnffgdCpNoForwardingException(exceptions.TackerException):
message = _("Specified CP %(cp_id)s in VNFD %(vnfd_name)s "
"does not have forwarding capability, which is required to be "
"included in forwarding path")
class VnffgdInUse(exceptions.InUse):
message = _('VNFFGD %(vnffgd_id)s is still in use')
class VnffgdNotFoundException(exceptions.NotFound):
message = _('VNFFG Template %(vnffgd_id)s could not be found')
class VnffgCreateFailed(exceptions.TackerException):
message = _('Creating VNFFG based on %(vnffgd_id)s failed')
class VnffgInvalidMappingException(exceptions.TackerException):
message = _("Matching VNF Instance for VNFD %(vnfd_name)s could not be "
"found. Please create an instance of this VNFD before "
"creating/updating VNFFG.")
class VnffgVimMappingException(exceptions.TackerException):
message = _("VNF Instance VNF %(vnf_id)s does not match VIM ID %(vim_id).")
class VnffgPropertyNotFoundException(exceptions.NotFound):
message = _('VNFFG Property %(vnffg_property)s could not be found')
class VnffgCpNotFoundException(exceptions.NotFound):
message = _("Specified CP %(cp_id)s could not be found in VNF "
"%(vnf_id)s.")
class VnffgNotFoundException(exceptions.NotFound):
message = _('VNFFG %(vnffg_id)s could not be found')
class VnffgInUse(exceptions.InUse):
message = _('VNFFG %(vnffg_id)s is still in use')
class VnffgVnfNotFoundException(exceptions.NotFound):
message = _("Specified VNF instance %(vnf_name)s in VNF Mapping could not "
"be found")
class VnffgDeleteFailed(exceptions.TackerException):
message = _('Deleting VNFFG %(vnffg_id)s failed')
class NfpAttributeNotFoundException(exceptions.NotFound):
message = _('NFP attribute %(attribute)s could not be found')
class NfpNotFoundException(exceptions.NotFound):
message = _('NFP %(nfp_id)s could not be found')
class NfpInUse(exceptions.InUse):
message = _('NFP %(nfp_id)s is still in use')
class NfpPolicyCriteriaError(exceptions.PolicyCheckError):
message = _('%(error)s in policy')
class NfpPolicyNotFoundException(exceptions.NotFound):
message = _('Policy not found in NFP %(nfp)s')
class NfpPolicyTypeError(exceptions.PolicyCheckError):
message = _('Unsupported Policy Type: %(type)s')
class NfpForwarderNotFoundException(exceptions.NotFound):
message = _('VNFD Forwarder %(vnfd)s not found in VNF Mapping %(mapping)s')
class NfpRequirementsException(exceptions.TackerException):
message = _('VNFD Forwarder %(vnfd) specified more than twice in '
'requirements path')
class SfcInUse(exceptions.InUse):
message = _('SFC %(sfc_id)s is still in use')
class SfcNotFoundException(exceptions.NotFound):
message = _('Service Function Chain %(sfc_id)s could not be found')
class ClassifierInUse(exceptions.InUse):
message = _('Classifier %(classifier_id)s is still in use')
class ClassifierNotFoundException(exceptions.NotFound):
message = _('Classifier %(classifier_id)s could not be found')
RESOURCE_ATTRIBUTE_MAP = {
'vims': {
@ -162,6 +308,257 @@ RESOURCE_ATTRIBUTE_MAP = {
'allow_put': False,
'is_visible': True,
},
},
'vnffgds': {
'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': {&#