Merge "Implements VNFFG into NFVO"
This commit is contained in:
commit
4ce287946a
@ -37,6 +37,7 @@ oslo.serialization>=1.10.0 # Apache-2.0
|
|||||||
oslo.service>=1.10.0 # Apache-2.0
|
oslo.service>=1.10.0 # Apache-2.0
|
||||||
oslo.utils>=3.16.0 # Apache-2.0
|
oslo.utils>=3.16.0 # Apache-2.0
|
||||||
oslosphinx!=3.4.0,>=2.5.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
|
python-novaclient!=2.33.0,>=2.29.0 # Apache-2.0
|
||||||
tosca-parser>=0.5.0 # Apache-2.0
|
tosca-parser>=0.5.0 # Apache-2.0
|
||||||
heat-translator>=0.4.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'),
|
||||||
|
)
|
@ -1 +1 @@
|
|||||||
4ee19c8a6d0a
|
507122918800
|
||||||
|
@ -23,6 +23,7 @@ Based on this comparison database can be healed with healing migration.
|
|||||||
|
|
||||||
from tacker.db import model_base
|
from tacker.db import model_base
|
||||||
from tacker.db.nfvo import nfvo_db # noqa
|
from tacker.db.nfvo import nfvo_db # noqa
|
||||||
|
from tacker.db.nfvo import vnffg_db # noqa
|
||||||
from tacker.db.vm import vm_db # noqa
|
from tacker.db.vm import vm_db # noqa
|
||||||
|
|
||||||
|
|
||||||
|
908
tacker/db/nfvo/vnffg_db.py
Normal file
908
tacker/db/nfvo/vnffg_db.py
Normal file
@ -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
|
import six
|
||||||
|
|
||||||
|
from tacker._i18n import _
|
||||||
from tacker.api import extensions
|
from tacker.api import extensions
|
||||||
from tacker.api.v1 import attributes as attr
|
from tacker.api.v1 import attributes as attr
|
||||||
from tacker.api.v1 import resource_helper
|
from tacker.api.v1 import resource_helper
|
||||||
@ -73,6 +74,151 @@ class VimDuplicateUrlException(exceptions.TackerException):
|
|||||||
"duplicate VIM")
|
"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 = {
|
RESOURCE_ATTRIBUTE_MAP = {
|
||||||
|
|
||||||
'vims': {
|
'vims': {
|
||||||
@ -162,6 +308,257 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'allow_put': False,
|
'allow_put': False,
|
||||||
'is_visible': True,
|
'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': {'type:string': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'default': '',
|
||||||
|
},
|
||||||
|
'template': {
|
||||||
|
'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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'vnffgs': {
|
||||||
|
'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
|
||||||
|
},
|
||||||
|
'vnffgd_id': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'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': '',
|
||||||
|
},
|
||||||
|
'vnf_mapping': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': True,
|
||||||
|
'convert_to': attr.convert_none_to_empty_dict,
|
||||||
|
'validate': {'type:dict_or_nodata': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'default': None,
|
||||||
|
},
|
||||||
|
'symmetrical': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': True,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:boolean': None},
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'forwarding_paths': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
'nfps': {
|
||||||
|
'id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'primary_key': True
|
||||||
|
},
|
||||||
|
'tenant_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'required_by_policy': True,
|
||||||
|
'is_visible': True
|
||||||
|
},
|
||||||
|
'vnffg_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'name': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'classifier_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'chain_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'path_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'symmetrical': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:boolean': None},
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'sfcs': {
|
||||||
|
'id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'primary_key': True
|
||||||
|
},
|
||||||
|
'tenant_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'required_by_policy': True,
|
||||||
|
'is_visible': True
|
||||||
|
},
|
||||||
|
'nfp_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'instance_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'chain': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'path_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'symmetrical': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:boolean': None},
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'classifiers': {
|
||||||
|
'id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'primary_key': True
|
||||||
|
},
|
||||||
|
'tenant_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'required_by_policy': True,
|
||||||
|
'is_visible': True
|
||||||
|
},
|
||||||
|
'nfp_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'instance_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:uuid': None},
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'match': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'chain_id': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
|
'status': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
0
tacker/extensions/nfvo_plugins/__init__.py
Normal file
0
tacker/extensions/nfvo_plugins/__init__.py
Normal file
83
tacker/extensions/nfvo_plugins/vnffg.py
Normal file
83
tacker/extensions/nfvo_plugins/vnffg.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# 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 abc
|
||||||
|
import six
|
||||||
|
|
||||||
|
from tacker.services import service_base
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class VNFFGPluginBase(service_base.NFVPluginBase):
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_vnffgd(self, context, vnffgd):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete_vnffgd(self, context, vnffgd_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vnffgd(self, context, vnffgd_id, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vnffgds(self, context, filters=None, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_vnffg(self, context, vnffg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vnffgs(self, context, filters=None, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vnffg(self, context, vnffg_id, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def update_vnffg(self, context, vnffg_id, vnffg):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def delete_vnffg(self, context, vnffg_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_nfp(self, context, nfp_id, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_nfps(self, context, filters=None, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_sfcs(self, context, filters=None, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_sfc(self, context, sfc_id, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_classifiers(self, context, filters=None, fields=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_classifier(self, context, classifier_id, fields=None):
|
||||||
|
pass
|
@ -90,3 +90,14 @@ class VimAbstractDriver(extensions.PluginInterface):
|
|||||||
Checks the health status of VIM and return a boolean value
|
Checks the health status of VIM and return a boolean value
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_vim_resource_id(self, vim_obj, resource_type, resource_name):
|
||||||
|
"""Parses a VIM resource ID from a given type and name
|
||||||
|
|
||||||
|
:param vim_obj: VIM information
|
||||||
|
:param resource_type: type of resource, such as network, compute
|
||||||
|
:param resource_name: name of resource, such at "test-network"
|
||||||
|
:return: ID of of resource
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from keystoneclient.auth.identity import v2
|
||||||
|
from keystoneclient.auth.identity import v3
|
||||||
from keystoneclient import exceptions
|
from keystoneclient import exceptions
|
||||||
|
from keystoneclient import session
|
||||||
|
from neutronclient.v2_0 import client as neutron_client
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from tacker._i18n import _LW
|
from tacker._i18n import _LW, _
|
||||||
from tacker.agent.linux import utils as linux_utils
|
from tacker.agent.linux import utils as linux_utils
|
||||||
from tacker.common import log
|
from tacker.common import log
|
||||||
from tacker.extensions import nfvo
|
from tacker.extensions import nfvo
|
||||||
@ -46,6 +50,11 @@ OPENSTACK_OPTS = [
|
|||||||
cfg.CONF.register_opts(OPTS, 'vim_keys')
|
cfg.CONF.register_opts(OPTS, 'vim_keys')
|
||||||
cfg.CONF.register_opts(OPENSTACK_OPTS, 'vim_monitor')
|
cfg.CONF.register_opts(OPENSTACK_OPTS, 'vim_monitor')
|
||||||
|
|
||||||
|
_VALID_RESOURCE_TYPES = {'network': {'client': neutron_client.Client,
|
||||||
|
'cmd': 'list_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def config_opts():
|
def config_opts():
|
||||||
return [('vim_keys', OPTS), ('vim_monitor', OPENSTACK_OPTS)]
|
return [('vim_keys', OPTS), ('vim_monitor', OPENSTACK_OPTS)]
|
||||||
@ -78,10 +87,15 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver):
|
|||||||
|
|
||||||
Initialize keystoneclient with provided authentication attributes.
|
Initialize keystoneclient with provided authentication attributes.
|
||||||
"""
|
"""
|
||||||
|
auth_url = vim_obj['auth_url']
|
||||||
|
keystone_version = self._validate_auth_url(auth_url)
|
||||||
|
auth_cred = self._get_auth_creds(keystone_version, vim_obj)
|
||||||
|
return self._initialize_keystone(keystone_version, auth_cred)
|
||||||
|
|
||||||
|
def _get_auth_creds(self, keystone_version, vim_obj):
|
||||||
auth_url = vim_obj['auth_url']
|
auth_url = vim_obj['auth_url']
|
||||||
auth_cred = vim_obj['auth_cred']
|
auth_cred = vim_obj['auth_cred']
|
||||||
vim_project = vim_obj['vim_project']
|
vim_project = vim_obj['vim_project']
|
||||||
keystone_version = self._validate_auth_url(auth_url)
|
|
||||||
|
|
||||||
if keystone_version not in auth_url:
|
if keystone_version not in auth_url:
|
||||||
vim_obj['auth_url'] = auth_url + '/' + keystone_version
|
vim_obj['auth_url'] = auth_url + '/' + keystone_version
|
||||||
@ -97,7 +111,15 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver):
|
|||||||
auth_cred.pop('user_domain_name', None)
|
auth_cred.pop('user_domain_name', None)
|
||||||
auth_cred.pop('user_id', None)
|
auth_cred.pop('user_id', None)
|
||||||
auth_cred['auth_url'] = vim_obj['auth_url']
|
auth_cred['auth_url'] = vim_obj['auth_url']
|
||||||
return self._initialize_keystone(keystone_version, auth_cred)
|
return auth_cred
|
||||||
|
|
||||||
|
def _get_auth_plugin(self, version, **kwargs):
|
||||||
|
if version == 'v2.0':
|
||||||
|
auth_plugin = v2.Password(**kwargs)
|
||||||
|
else:
|
||||||
|
auth_plugin = v3.Password(**kwargs)
|
||||||
|
|
||||||
|
return auth_plugin
|
||||||
|
|
||||||
def _validate_auth_url(self, auth_url):
|
def _validate_auth_url(self, auth_url):
|
||||||
try:
|
try:
|
||||||
@ -207,3 +229,44 @@ class OpenStack_Driver(abstract_vim_driver.VimAbstractDriver):
|
|||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
LOG.warning(_LW("Cannot ping ip address: %s"), vim_ip)
|
LOG.warning(_LW("Cannot ping ip address: %s"), vim_ip)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def get_vim_resource_id(self, vim_obj, resource_type, resource_name):
|
||||||
|
"""Locates openstack resource by type/name and returns ID
|
||||||
|
|
||||||
|
:param vim_obj: VIM info used to access openstack instance
|
||||||
|
:param resource_type: type of resource to find
|
||||||
|
:param resource_name: name of resource to locate
|
||||||
|
:return: ID of resource
|
||||||
|
"""
|
||||||
|
if resource_type in _VALID_RESOURCE_TYPES.keys():
|
||||||
|
client_type = _VALID_RESOURCE_TYPES[resource_type]['client']
|
||||||
|
cmd_prefix = _VALID_RESOURCE_TYPES[resource_type]['cmd']
|
||||||
|
else:
|
||||||
|
raise nfvo.VimUnsupportedResourceTypeException(type=resource_type)
|
||||||
|
|
||||||
|
client = self._get_client(vim_obj, client_type)
|
||||||
|
cmd = str(cmd_prefix) + str(resource_name)
|
||||||
|
try:
|
||||||
|
resources = getattr(client, "%s" % cmd)()
|
||||||
|
LOG.debug(_('resources output %s'), resources)
|
||||||
|
for resource in resources[resource_type]:
|
||||||
|
if resource['name'] == resource_name:
|
||||||
|
return resource['id']
|
||||||
|
except Exception:
|
||||||
|
raise nfvo.VimGetResourceException(cmd=cmd, type=resource_type)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def _get_client(self, vim_obj, client_type):
|
||||||
|
"""Initializes and returns an openstack client
|
||||||
|
|
||||||
|
:param vim_obj: VIM Information
|
||||||
|
:param client_type: openstack client to initialize
|
||||||
|
:return: initialized client
|
||||||
|
"""
|
||||||
|
auth_url = vim_obj['auth_url']
|
||||||
|
keystone_version = self._validate_auth_url(auth_url)
|
||||||
|
auth_cred = self._get_auth_creds(keystone_version, vim_obj)
|
||||||
|
auth_plugin = self._get_auth_plugin(keystone_version, **auth_cred)
|
||||||
|
sess = session.Session(auth=auth_plugin)
|
||||||
|
return client_type(session=sess)
|
||||||
|
72
tacker/nfvo/drivers/vnffg/sfc_drivers/noop.py
Normal file
72
tacker/nfvo/drivers/vnffg/sfc_drivers/noop.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from tacker.common import log
|
||||||
|
from tacker.nfvo.drivers.vnffg.sfc_drivers import abstract_driver
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class VNFFGNoop(abstract_driver.SfcAbstractDriver):
|
||||||
|
|
||||||
|
"""Noop driver for VNFFG tests"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(VNFFGNoop, self).__init__()
|
||||||
|
self._instances = set()
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return 'noop'
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return 'noop'
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
return 'VNFFG Noop driver'
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def create_chain(self, fc_id, vnfs, auth_attr=None):
|
||||||
|
instance_id = str(uuid.uuid4())
|
||||||
|
self._instances.add(instance_id)
|
||||||
|
return instance_id
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def update_chain(self, chain_id, fc_ids, vnfs, auth_attr=None):
|
||||||
|
if chain_id not in self._instances:
|
||||||
|
LOG.debug(_('Chain not found'))
|
||||||
|
raise ValueError('No chain instance %s' % chain_id)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def delete_chain(self, chain_id, auth_attr=None):
|
||||||
|
self._instances.remove(chain_id)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def create_flow_classifier(self, fc, auth_attr=None):
|
||||||
|
instance_id = str(uuid.uuid4())
|
||||||
|
self._instances.add(instance_id)
|
||||||
|
return instance_id
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def update_flow_classifier(self, fc_id, fc, auth_attr=None):
|
||||||
|
if fc_id not in self._instances:
|
||||||
|
LOG.debug(_('FC not found'))
|
||||||
|
raise ValueError('No FC instance %s' % fc_id)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def delete_flow_classifier(self, fc_id, auth_attr=None):
|
||||||
|
self._instances.remove(fc_id)
|
@ -23,11 +23,18 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import excutils
|
from oslo_utils import excutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
|
|
||||||
|
from tacker._i18n import _
|
||||||
from tacker.common import driver_manager
|
from tacker.common import driver_manager
|
||||||
from tacker.common import log
|
from tacker.common import log
|
||||||
from tacker.common import utils
|
from tacker.common import utils
|
||||||
from tacker import context as t_context
|
from tacker import context as t_context
|
||||||
from tacker.db.nfvo import nfvo_db
|
from tacker.db.nfvo import nfvo_db
|
||||||
|
from tacker.db.nfvo import vnffg_db
|
||||||
|
from tacker.extensions import nfvo
|
||||||
|
from tacker import manager
|
||||||
|
from tacker.plugins.common import constants
|
||||||
|
from tacker.vnfm.tosca import utils as toscautils
|
||||||
|
from toscaparser import tosca_template
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -36,7 +43,7 @@ def config_opts():
|
|||||||
return [('nfvo', NfvoPlugin.OPTS)]
|
return [('nfvo', NfvoPlugin.OPTS)]
|
||||||
|
|
||||||
|
|
||||||
class NfvoPlugin(nfvo_db.NfvoPluginDb):
|
class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin):
|
||||||
"""NFVO reference plugin for NFVO extension
|
"""NFVO reference plugin for NFVO extension
|
||||||
|
|
||||||
Implements the NFVO extension and defines public facing APIs for VIM
|
Implements the NFVO extension and defines public facing APIs for VIM
|
||||||
@ -138,3 +145,218 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb):
|
|||||||
t_context.get_admin_context(),
|
t_context.get_admin_context(),
|
||||||
vim_id, status)
|
vim_id, status)
|
||||||
self._created_vims[vim_id]["status"] = status
|
self._created_vims[vim_id]["status"] = status
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def validate_tosca(self, template):
|
||||||
|
if "tosca_definitions_version" not in template:
|
||||||
|
raise nfvo.ToscaParserFailed(
|
||||||
|
error_msg_details='tosca_definitions_version missing in '
|
||||||
|
'template'
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.debug(_('template yaml: %s'), template)
|
||||||
|
|
||||||
|
toscautils.updateimports(template)
|
||||||
|
|
||||||
|
try:
|
||||||
|
tosca_template.ToscaTemplate(
|
||||||
|
a_file=False, yaml_dict_tpl=template)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.exception(_("tosca-parser error: %s"), str(e))
|
||||||
|
raise nfvo.ToscaParserFailed(error_msg_details=str(e))
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def create_vnffgd(self, context, vnffgd):
|
||||||
|
template = vnffgd['vnffgd']
|
||||||
|
|
||||||
|
if 'vnffgd' not in template.get('template'):
|
||||||
|
raise nfvo.VnffgdInvalidTemplate(template=template.get('template'))
|
||||||
|
else:
|
||||||
|
self.validate_tosca(template['template']['vnffgd'])
|
||||||
|
temp = template['template']['vnffgd']['topology_template']
|
||||||
|
vnffg_name = temp['groups'].keys()[0]
|
||||||
|
nfp_name = temp['groups'][vnffg_name]['members'][0]
|
||||||
|
path = self._get_nfp_attribute(template['template'], nfp_name,
|
||||||
|
'path')
|
||||||
|
prev_element = None
|
||||||
|
known_forwarders = set()
|
||||||
|
for element in path:
|
||||||
|
if element.get('forwarder') in known_forwarders:
|
||||||
|
if prev_element is not None and element.get('forwarder')\
|
||||||
|
!= prev_element['forwarder']:
|
||||||
|
raise nfvo.VnffgdDuplicateForwarderException(
|
||||||
|
forwarder=element.get('forwarder')
|
||||||
|
)
|
||||||
|
elif prev_element is not None and element.get(
|
||||||
|
'capability') == prev_element['capability']:
|
||||||
|
raise nfvo.VnffgdDuplicateCPException(
|
||||||
|
cp=element.get('capability')
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
known_forwarders.add(element.get('forwarder'))
|
||||||
|
prev_element = element
|
||||||
|
return super(NfvoPlugin, self).create_vnffgd(context, vnffgd)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def create_vnffg(self, context, vnffg):
|
||||||
|
vnffg_dict = super(NfvoPlugin, self)._create_vnffg_pre(context, vnffg)
|
||||||
|
nfp = super(NfvoPlugin, self).get_nfp(context,
|
||||||
|
vnffg_dict['forwarding_paths'])
|
||||||
|
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
|
||||||
|
match = super(NfvoPlugin, self).get_classifier(context,
|
||||||
|
nfp['classifier_id'],
|
||||||
|
fields='match')
|
||||||
|
# grab the first VNF to check it's VIM type
|
||||||
|
# we have already checked that all VNFs are in the same VIM
|
||||||
|
vim_auth = self._get_vim_from_vnf(context,
|
||||||
|
vnffg_dict['vnf_mapping'].values()[0]
|
||||||
|
)
|
||||||
|
# TODO(trozet): figure out what auth info we actually need to pass
|
||||||
|
# to the driver. Is it a session, or is full vim obj good enough?
|
||||||
|
driver_type = vim_auth['type']
|
||||||
|
try:
|
||||||
|
fc_id = self._vim_drivers.invoke(driver_type,
|
||||||
|
'create_flow_classifier',
|
||||||
|
fc=match, auth_attr=vim_auth,
|
||||||
|
symmetrical=sfc['symmetrical'])
|
||||||
|
sfc_id = self._vim_drivers.invoke(driver_type, 'create_chain',
|
||||||
|
vnfs=sfc['chain'], fc_id=fc_id,
|
||||||
|
symmetrical=sfc['symmetrical'],
|
||||||
|
auth_attr=vim_auth)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
self.delete_vnffg(context, vnffg_id=vnffg_dict['id'])
|
||||||
|
super(NfvoPlugin, self)._create_vnffg_post(context, sfc_id, fc_id,
|
||||||
|
vnffg_dict)
|
||||||
|
super(NfvoPlugin, self)._create_vnffg_status(context, vnffg_dict)
|
||||||
|
return vnffg_dict
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def update_vnffg(self, context, vnffg_id, vnffg):
|
||||||
|
vnffg_dict = super(NfvoPlugin, self)._update_vnffg_pre(context,
|
||||||
|
vnffg_id)
|
||||||
|
new_vnffg = vnffg['vnffg']
|
||||||
|
LOG.debug(_('vnffg update: %s'), vnffg)
|
||||||
|
nfp = super(NfvoPlugin, self).get_nfp(context,
|
||||||
|
vnffg_dict['forwarding_paths'])
|
||||||
|
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
|
||||||
|
|
||||||
|
fc = super(NfvoPlugin, self).get_classifier(context,
|
||||||
|
nfp['classifier_id'])
|
||||||
|
template_db = self._get_resource(context, vnffg_db.VnffgTemplate,
|
||||||
|
vnffg_dict['vnffgd_id'])
|
||||||
|
vnf_members = self._get_vnffg_property(template_db,
|
||||||
|
'constituent_vnfs')
|
||||||
|
new_vnffg['vnf_mapping'] = super(NfvoPlugin, self)._get_vnf_mapping(
|
||||||
|
context, new_vnffg.get('vnf_mapping'), vnf_members)
|
||||||
|
template_id = vnffg_dict['vnffgd_id']
|
||||||
|
template_db = self._get_resource(context, vnffg_db.VnffgTemplate,
|
||||||
|
template_id)
|
||||||
|
# functional attributes that allow update are vnf_mapping,
|
||||||
|
# and symmetrical. Therefore we need to figure out the new chain if
|
||||||
|
# it was updated by new vnf_mapping. Symmetrical is handled by driver.
|
||||||
|
|
||||||
|
chain = super(NfvoPlugin, self)._create_port_chain(context,
|
||||||
|
new_vnffg[
|
||||||
|
'vnf_mapping'],
|
||||||
|
template_db,
|
||||||
|
nfp['name'])
|
||||||
|
LOG.debug(_('chain update: %s'), chain)
|
||||||
|
sfc['chain'] = chain
|
||||||
|
sfc['symmetrical'] = new_vnffg['symmetrical']
|
||||||
|
vim_auth = self._get_vim_from_vnf(context,
|
||||||
|
vnffg_dict['vnf_mapping'].values()[0]
|
||||||
|
)
|
||||||
|
driver_type = vim_auth['type']
|
||||||
|
try:
|
||||||
|
# we don't support updating the match criteria in first iteration
|
||||||
|
# so this is essentially a noop. Good to keep for future use
|
||||||
|
# though.
|
||||||
|
self._vim_drivers.invoke(driver_type, 'update_flow_classifier',
|
||||||
|
fc_id=fc['instance_id'], fc=fc['match'],
|
||||||
|
auth_attr=vim_auth,
|
||||||
|
symmetrical=new_vnffg['symmetrical'])
|
||||||
|
self._vim_drivers.invoke(driver_type, 'update_chain',
|
||||||
|
vnfs=sfc['chain'],
|
||||||
|
fc_ids=[fc['instance_id']],
|
||||||
|
chain_id=sfc['instance_id'],
|
||||||
|
auth_attr=vim_auth,
|
||||||
|
symmetrical=new_vnffg['symmetrical'])
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
vnffg_dict['status'] = constants.ERROR
|
||||||
|
super(NfvoPlugin, self)._update_vnffg_post(context, vnffg_id,
|
||||||
|
constants.ERROR)
|
||||||
|
super(NfvoPlugin, self)._update_vnffg_post(context, vnffg_id,
|
||||||
|
constants.ACTIVE, new_vnffg)
|
||||||
|
# update chain
|
||||||
|
super(NfvoPlugin, self)._update_sfc_post(context, sfc['id'],
|
||||||
|
constants.ACTIVE, sfc)
|
||||||
|
# update classifier - this is just updating status until functional
|
||||||
|
# updates are supported to classifier
|
||||||
|
super(NfvoPlugin, self)._update_classifier_post(context, fc['id'],
|
||||||
|
constants.ACTIVE)
|
||||||
|
return vnffg_dict
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def delete_vnffg(self, context, vnffg_id):
|
||||||
|
vnffg_dict = super(NfvoPlugin, self)._delete_vnffg_pre(context,
|
||||||
|
vnffg_id)
|
||||||
|
nfp = super(NfvoPlugin, self).get_nfp(context,
|
||||||
|
vnffg_dict['forwarding_paths'])
|
||||||
|
sfc = super(NfvoPlugin, self).get_sfc(context, nfp['chain_id'])
|
||||||
|
|
||||||
|
fc = super(NfvoPlugin, self).get_classifier(context,
|
||||||
|
nfp['classifier_id'])
|
||||||
|
vim_auth = self._get_vim_from_vnf(context,
|
||||||
|
vnffg_dict['vnf_mapping'].values()[0]
|
||||||
|
)
|
||||||
|
driver_type = vim_auth['type']
|
||||||
|
try:
|
||||||
|
if sfc['instance_id'] is not None:
|
||||||
|
self._vim_drivers.invoke(driver_type, 'delete_chain',
|
||||||
|
chain_id=sfc['instance_id'],
|
||||||
|
auth_attr=vim_auth)
|
||||||
|
if fc['instance_id'] is not None:
|
||||||
|
self._vim_drivers.invoke(driver_type,
|
||||||
|
'delete_flow_classifier',
|
||||||
|
fc_id=fc['instance_id'],
|
||||||
|
auth_attr=vim_auth)
|
||||||
|
except Exception:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
vnffg_dict['status'] = constants.ERROR
|
||||||
|
super(NfvoPlugin, self)._delete_vnffg_post(context, vnffg_id,
|
||||||
|
True)
|
||||||
|
super(NfvoPlugin, self)._delete_vnffg_post(context, vnffg_id, False)
|
||||||
|
return vnffg_dict
|
||||||
|
|
||||||
|
def _get_vim_from_vnf(self, context, vnf_id):
|
||||||
|
"""Figures out VIM based on a VNF
|
||||||
|
|
||||||
|
:param context: SQL Session Context
|
||||||
|
:param vnf_id: VNF ID
|
||||||
|
:return: VIM or VIM properties if fields are provided
|
||||||
|
"""
|
||||||
|
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
|
||||||
|
vim_id = vnfm_plugin.get_vnf(context, vnf_id, fields=['vim_id'])
|
||||||
|
vim_obj = self.get_vim(context, vim_id['vim_id'])
|
||||||
|
if vim_obj is None:
|
||||||
|
raise nfvo.VimFromVnfNotFoundException(vnf_id=vnf_id)
|
||||||
|
return vim_obj
|
||||||
|
|
||||||
|
def _vim_resource_name_to_id(self, context, resource, name, vnf_id):
|
||||||
|
"""Converts a VIM resource name to its ID
|
||||||
|
|
||||||
|
: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
|
||||||
|
"""
|
||||||
|
vim_auth = self._get_vim_from_vnf(context, vnf_id)
|
||||||
|
driver_type = vim_auth['type']
|
||||||
|
return self._vim_drivers.invoke(driver_type,
|
||||||
|
'get_vim_resource_id',
|
||||||
|
vim_auth=vim_auth,
|
||||||
|
resource_type=resource,
|
||||||
|
resource_name=name)
|
||||||
|
@ -31,6 +31,10 @@ ipparams = _get_template('vnf_cirros_param_values_ipaddr.yaml')
|
|||||||
vnfd_userdata_template = _get_template('vnf_cirros_template_user_data.yaml')
|
vnfd_userdata_template = _get_template('vnf_cirros_template_user_data.yaml')
|
||||||
userdata_params = _get_template('vnf_cirros_param_values_user_data.yaml')
|
userdata_params = _get_template('vnf_cirros_param_values_user_data.yaml')
|
||||||
config_data = _get_template('config_data.yaml')
|
config_data = _get_template('config_data.yaml')
|
||||||
|
vnffgd_template = yaml.load(_get_template('vnffgd_template.yaml'))
|
||||||
|
vnffgd_tosca_template = yaml.load(_get_template('tosca_vnffgd_template.yaml'))
|
||||||
|
vnffgd_invalid_tosca_template = yaml.load(_get_template(
|
||||||
|
'tosca_invalid_vnffgd_template.yaml'))
|
||||||
|
|
||||||
|
|
||||||
def get_dummy_vnfd_obj():
|
def get_dummy_vnfd_obj():
|
||||||
@ -165,3 +169,31 @@ def get_vim_auth_obj():
|
|||||||
'auth_url': 'http://localhost:5000/v3',
|
'auth_url': 'http://localhost:5000/v3',
|
||||||
'user_domain_name': 'default',
|
'user_domain_name': 'default',
|
||||||
'project_domain_name': 'default'}
|
'project_domain_name': 'default'}
|
||||||
|
|
||||||
|
|
||||||
|
def get_dummy_vnffgd_obj():
|
||||||
|
return {u'vnffgd': {'name': 'dummy_vnfd',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
u'template': {u'vnffgd': vnffgd_tosca_template},
|
||||||
|
'description': 'dummy_vnfd_description'}}
|
||||||
|
|
||||||
|
|
||||||
|
def get_dummy_vnffg_obj():
|
||||||
|
return {'vnffg': {'description': 'dummy_vnf_description',
|
||||||
|
'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_vnffg',
|
||||||
|
'vnf_mapping': {},
|
||||||
|
'symmetrical': False}}
|
||||||
|
|
||||||
|
|
||||||
|
def get_dummy_vnffg_obj_vnf_mapping():
|
||||||
|
return {'vnffg': {'description': 'dummy_vnf_description',
|
||||||
|
'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_vnffg',
|
||||||
|
'vnf_mapping': {
|
||||||
|
'VNF1': '91e32c20-6d1f-47a4-9ba7-08f5e5effe07',
|
||||||
|
'VNF3': '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
||||||
|
},
|
||||||
|
'symmetrical': False}}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||||
|
|
||||||
|
description: example template
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
description: Example VNFFG template
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
|
||||||
|
Forwarding_path1:
|
||||||
|
type: tosca.nodes.nfv.FP.Tacker
|
||||||
|
description: creates path (CP11->CP12->CP32)
|
||||||
|
properties:
|
||||||
|
id: 51
|
||||||
|
policy:
|
||||||
|
type: ACL
|
||||||
|
criteria:
|
||||||
|
- blah: tenant1_net
|
||||||
|
- destination_port_range: 80-1024
|
||||||
|
- ip_proto: 6
|
||||||
|
- ip_dst_prefix: 192.168.1.2/24
|
||||||
|
path:
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP11
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP12
|
||||||
|
- forwarder: VNF3
|
||||||
|
capability: CP32
|
||||||
|
|
||||||
|
groups:
|
||||||
|
VNFFG1:
|
||||||
|
type: tosca.groups.nfv.VNFFG
|
||||||
|
description: HTTP to Corporate Net
|
||||||
|
properties:
|
||||||
|
vendor: tacker
|
||||||
|
version: 1.0
|
||||||
|
number_of_endpoints: 5
|
||||||
|
dependent_virtual_link: [VL1,VL2,VL3]
|
||||||
|
connection_point: [CP11,CP12,CP32]
|
||||||
|
constituent_vnfs: [VNF1,VNF3]
|
||||||
|
members: [Forwarding_path1]
|
@ -0,0 +1,41 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||||
|
|
||||||
|
description: example template
|
||||||
|
|
||||||
|
topology_template:
|
||||||
|
description: Example VNFFG template
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
|
||||||
|
Forwarding_path1:
|
||||||
|
type: tosca.nodes.nfv.FP.Tacker
|
||||||
|
description: creates path (CP11->CP12->CP32)
|
||||||
|
properties:
|
||||||
|
id: 51
|
||||||
|
policy:
|
||||||
|
type: ACL
|
||||||
|
criteria:
|
||||||
|
- network_name: tenant1_net
|
||||||
|
- destination_port_range: 80-1024
|
||||||
|
- ip_proto: 6
|
||||||
|
- ip_dst_prefix: 192.168.1.2/24
|
||||||
|
path:
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP11
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP12
|
||||||
|
- forwarder: VNF3
|
||||||
|
capability: CP32
|
||||||
|
|
||||||
|
groups:
|
||||||
|
VNFFG1:
|
||||||
|
type: tosca.groups.nfv.VNFFG
|
||||||
|
description: HTTP to Corporate Net
|
||||||
|
properties:
|
||||||
|
vendor: tacker
|
||||||
|
version: 1.0
|
||||||
|
number_of_endpoints: 5
|
||||||
|
dependent_virtual_link: [VL1,VL2,VL3]
|
||||||
|
connection_point: [CP11,CP12,CP32]
|
||||||
|
constituent_vnfs: [VNF1,VNF3]
|
||||||
|
members: [Forwarding_path1]
|
@ -0,0 +1,32 @@
|
|||||||
|
Forwarding_path1:
|
||||||
|
type: tosca.nodes.nfv.FP
|
||||||
|
id: 51
|
||||||
|
description: creates path (CP11->CP12->CP32)
|
||||||
|
properties:
|
||||||
|
policy:
|
||||||
|
type: ACL
|
||||||
|
criteria:
|
||||||
|
- network_name: tenant1_net
|
||||||
|
- destination_port_range: 80-1024
|
||||||
|
- ip_proto: 6
|
||||||
|
- ip_dst_prefix: 192.168.1.2/24
|
||||||
|
requirements:
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP11
|
||||||
|
- forwarder: VNF1
|
||||||
|
capability: CP12
|
||||||
|
- forwarder: VNF3
|
||||||
|
capability: CP32
|
||||||
|
|
||||||
|
groups:
|
||||||
|
VNFFG1:
|
||||||
|
type: tosca.groups.nfv.VNFFG
|
||||||
|
description: HTTP to Corporate Net
|
||||||
|
properties:
|
||||||
|
vendor: tacker
|
||||||
|
version: 1.0
|
||||||
|
number_of_endpoints: 5
|
||||||
|
dependent_virtual_link: [VL1,VL2,VL3]
|
||||||
|
connection_point: [CP11,CP12,CP32]
|
||||||
|
constituent_vnfs: [VNF1,VNF3]
|
||||||
|
members: [Forwarding_path1]
|
@ -13,26 +13,120 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import mock
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import mock
|
|
||||||
|
|
||||||
|
from mock import patch
|
||||||
from tacker import context
|
from tacker import context
|
||||||
from tacker.db.common_services import common_services_db
|
from tacker.db.common_services import common_services_db
|
||||||
from tacker.db.nfvo import nfvo_db
|
from tacker.db.nfvo import nfvo_db
|
||||||
|
from tacker.db.nfvo import vnffg_db
|
||||||
|
from tacker.extensions import nfvo
|
||||||
|
from tacker.manager import TackerManager
|
||||||
from tacker.nfvo import nfvo_plugin
|
from tacker.nfvo import nfvo_plugin
|
||||||
from tacker.plugins.common import constants
|
from tacker.plugins.common import constants
|
||||||
from tacker.tests.unit.db import base as db_base
|
from tacker.tests.unit.db import base as db_base
|
||||||
|
from tacker.tests.unit.db import utils
|
||||||
|
|
||||||
SECRET_PASSWORD = '***'
|
SECRET_PASSWORD = '***'
|
||||||
|
|
||||||
|
|
||||||
|
def dummy_get_vim(*args, **kwargs):
|
||||||
|
vim_auth = utils.get_vim_auth_obj()
|
||||||
|
vim_auth['type'] = 'openstack'
|
||||||
|
return vim_auth
|
||||||
|
|
||||||
|
|
||||||
class FakeDriverManager(mock.Mock):
|
class FakeDriverManager(mock.Mock):
|
||||||
def invoke(self, *args, **kwargs):
|
def invoke(self, *args, **kwargs):
|
||||||
if 'create' in args:
|
if any(x in ['create', 'create_chain', 'create_flow_classifier'] for
|
||||||
|
x in args):
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
|
class FakeVNFMPlugin(mock.Mock):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(FakeVNFMPlugin, self).__init__()
|
||||||
|
self.vnf1_vnfd_id = 'eb094833-995e-49f0-a047-dfb56aaf7c4e'
|
||||||
|
self.vnf1_vnf_id = '91e32c20-6d1f-47a4-9ba7-08f5e5effe07'
|
||||||
|
self.vnf3_vnfd_id = 'e4015e9f-1ef2-49fb-adb6-070791ad3c45'
|
||||||
|
self.vnf3_vnf_id = '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
|
||||||
|
self.vnf3_update_vnf_id = '10f66bc5-b2f1-45b7-a7cd-6dd6ad0017f5'
|
||||||
|
|
||||||
|
self.cp11_id = 'd18c8bae-898a-4932-bff8-d5eac981a9c9'
|
||||||
|
self.cp12_id = 'c8906342-3e30-4b2a-9401-a251a7a9b5dd'
|
||||||
|
self.cp32_id = '3d1bd2a2-bf0e-44d1-87af-a2c6b2cad3ed'
|
||||||
|
self.cp32_update_id = '064c0d99-5a61-4711-9597-2a44dc5da14b'
|
||||||
|
|
||||||
|
def get_vnfds(self, *args, **kwargs):
|
||||||
|
if {'name': ['VNF1']} in args:
|
||||||
|
return [{'id': self.vnf1_vnfd_id}]
|
||||||
|
elif {'name': ['VNF3']} in args:
|
||||||
|
return [{'id': self.vnf3_vnfd_id}]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_vnfs(self, *args, **kwargs):
|
||||||
|
if {'vnfd_id': [self.vnf1_vnfd_id]} in args:
|
||||||
|
return [{'id': self.vnf1_vnf_id}]
|
||||||
|
elif {'vnfd_id': [self.vnf3_vnfd_id]} in args:
|
||||||
|
return [{'id': self.vnf3_vnf_id}]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_vnf(self, *args, **kwargs):
|
||||||
|
if self.vnf1_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf1()
|
||||||
|
elif self.vnf3_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf3()
|
||||||
|
elif self.vnf3_update_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf3_update()
|
||||||
|
|
||||||
|
def get_vnf_resources(self, *args, **kwargs):
|
||||||
|
if self.vnf1_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf1_details()
|
||||||
|
elif self.vnf3_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf3_details()
|
||||||
|
elif self.vnf3_update_vnf_id in args:
|
||||||
|
return self.get_dummy_vnf3_update_details()
|
||||||
|
|
||||||
|
def get_dummy_vnf1_details(self):
|
||||||
|
return [{'name': 'CP11', 'id': self.cp11_id},
|
||||||
|
{'name': 'CP12', 'id': self.cp12_id}]
|
||||||
|
|
||||||
|
def get_dummy_vnf3_details(self):
|
||||||
|
return [{'name': 'CP32', 'id': self.cp32_id}]
|
||||||
|
|
||||||
|
def get_dummy_vnf3_update_details(self):
|
||||||
|
return [{'name': 'CP32', 'id': self.cp32_update_id}]
|
||||||
|
|
||||||
|
def get_dummy_vnf1(self):
|
||||||
|
return {'description': 'dummy_vnf_description',
|
||||||
|
'vnfd_id': self.vnf1_vnfd_id,
|
||||||
|
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_vnf1',
|
||||||
|
'attributes': {}}
|
||||||
|
|
||||||
|
def get_dummy_vnf3(self):
|
||||||
|
return {'description': 'dummy_vnf_description',
|
||||||
|
'vnfd_id': self.vnf3_vnfd_id,
|
||||||
|
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_vnf2',
|
||||||
|
'attributes': {}}
|
||||||
|
|
||||||
|
def get_dummy_vnf3_update(self):
|
||||||
|
return {'description': 'dummy_vnf_description',
|
||||||
|
'vnfd_id': self.vnf3_vnfd_id,
|
||||||
|
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_vnf_update',
|
||||||
|
'attributes': {}}
|
||||||
|
|
||||||
|
|
||||||
class TestNfvoPlugin(db_base.SqlTestCase):
|
class TestNfvoPlugin(db_base.SqlTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestNfvoPlugin, self).setUp()
|
super(TestNfvoPlugin, self).setUp()
|
||||||
@ -40,6 +134,8 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
|||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
self._mock_driver_manager()
|
self._mock_driver_manager()
|
||||||
mock.patch('tacker.nfvo.nfvo_plugin.NfvoPlugin.__run__').start()
|
mock.patch('tacker.nfvo.nfvo_plugin.NfvoPlugin.__run__').start()
|
||||||
|
mock.patch('tacker.nfvo.nfvo_plugin.NfvoPlugin._get_vim_from_vnf',
|
||||||
|
side_effect=dummy_get_vim).start()
|
||||||
self.nfvo_plugin = nfvo_plugin.NfvoPlugin()
|
self.nfvo_plugin = nfvo_plugin.NfvoPlugin()
|
||||||
mock.patch('tacker.db.common_services.common_services_db.'
|
mock.patch('tacker.db.common_services.common_services_db.'
|
||||||
'CommonServicesPluginDb.create_event'
|
'CommonServicesPluginDb.create_event'
|
||||||
@ -139,3 +235,189 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
|||||||
self.context, evt_type=constants.RES_EVT_UPDATE, res_id=mock.ANY,
|
self.context, evt_type=constants.RES_EVT_UPDATE, res_id=mock.ANY,
|
||||||
res_state=mock.ANY, res_type=constants.RES_TYPE_VIM,
|
res_state=mock.ANY, res_type=constants.RES_TYPE_VIM,
|
||||||
tstamp=mock.ANY)
|
tstamp=mock.ANY)
|
||||||
|
|
||||||
|
def _insert_dummy_vnffg_template(self):
|
||||||
|
session = self.context.session
|
||||||
|
vnffg_template = vnffg_db.VnffgTemplate(
|
||||||
|
id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
name='fake_template',
|
||||||
|
description='fake_template_description',
|
||||||
|
template={u'vnffgd': utils.vnffgd_tosca_template})
|
||||||
|
session.add(vnffg_template)
|
||||||
|
session.flush()
|
||||||
|
return vnffg_template
|
||||||
|
|
||||||
|
def _insert_dummy_vnffg(self):
|
||||||
|
session = self.context.session
|
||||||
|
vnffg = vnffg_db.Vnffg(
|
||||||
|
id='ffc1a59b-65bb-4874-94d3-84f639e63c74',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
name='dummy_vnffg',
|
||||||
|
description="fake vnffg",
|
||||||
|
vnffgd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
|
status='ACTIVE',
|
||||||
|
vnf_mapping={'VNF1': '91e32c20-6d1f-47a4-9ba7-08f5e5effe07',
|
||||||
|
'VNF3': '7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'})
|
||||||
|
session.add(vnffg)
|
||||||
|
nfp = vnffg_db.VnffgNfp(
|
||||||
|
id='768f76a7-9025-4acd-b51c-0da609759983',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
status="ACTIVE",
|
||||||
|
name='Forwarding_path1',
|
||||||
|
vnffg_id='ffc1a59b-65bb-4874-94d3-84f639e63c74',
|
||||||
|
path_id=51,
|
||||||
|
symmetrical=False)
|
||||||
|
session.add(nfp)
|
||||||
|
sfc = vnffg_db.VnffgChain(
|
||||||
|
id='f28e33bc-1061-4762-b942-76060bbd59c4',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
symmetrical=False,
|
||||||
|
chain=[{'connection_points': [
|
||||||
|
'd18c8bae-898a-4932-bff8-d5eac981a9c9',
|
||||||
|
'c8906342-3e30-4b2a-9401-a251a7a9b5dd'],
|
||||||
|
'name': 'dummy_vnf1'},
|
||||||
|
{'connection_points': ['3d1bd2a2-bf0e-44d1-87af-a2c6b2cad3ed'],
|
||||||
|
'name': 'dummy_vnf2'}],
|
||||||
|
path_id=51,
|
||||||
|
status='ACTIVE',
|
||||||
|
nfp_id='768f76a7-9025-4acd-b51c-0da609759983',
|
||||||
|
instance_id='bcfb295e-578e-405b-a349-39f06b25598c')
|
||||||
|
session.add(sfc)
|
||||||
|
fc = vnffg_db.VnffgClassifier(
|
||||||
|
id='a85f21b5-f446-43f0-86f4-d83bdc5590ab',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
status='ACTIVE',
|
||||||
|
instance_id='3007dc2d-30dc-4651-9184-f1e6273cc0b6',
|
||||||
|
chain_id='f28e33bc-1061-4762-b942-76060bbd59c4',
|
||||||
|
nfp_id='768f76a7-9025-4acd-b51c-0da609759983')
|
||||||
|
session.add(fc)
|
||||||
|
match = vnffg_db.ACLMatchCriteria(
|
||||||
|
id='bdb0f2db-d4c2-42a2-a1df-426079ecc443',
|
||||||
|
vnffgc_id='a85f21b5-f446-43f0-86f4-d83bdc5590ab',
|
||||||
|
eth_src=None, eth_dst=None, eth_type=None, vlan_id=None,
|
||||||
|
vlan_pcp=None, mpls_label=None, mpls_tc=None, ip_dscp=None,
|
||||||
|
ip_ecn=None, ip_src_prefix=None, ip_dst_prefix='192.168.1.2/24',
|
||||||
|
source_port_min=None, source_port_max=None,
|
||||||
|
destination_port_min=80, destination_port_max=1024, ip_proto=6,
|
||||||
|
network_id=None, network_src_port_id=None,
|
||||||
|
network_dst_port_id=None, tenant_id=None, icmpv4_type=None,
|
||||||
|
icmpv4_code=None, arp_op=None, arp_spa=None, arp_tpa=None,
|
||||||
|
arp_sha=None, arp_tha=None, ipv6_src=None, ipv6_dst=None,
|
||||||
|
ipv6_flabel=None, icmpv6_type=None, icmpv6_code=None,
|
||||||
|
ipv6_nd_target=None, ipv6_nd_sll=None, ipv6_nd_tll=None)
|
||||||
|
session.add(match)
|
||||||
|
session.flush()
|
||||||
|
return vnffg
|
||||||
|
|
||||||
|
def test_validate_tosca(self):
|
||||||
|
template = utils.vnffgd_tosca_template
|
||||||
|
self.nfvo_plugin.validate_tosca(template)
|
||||||
|
|
||||||
|
def test_validate_tosca_missing_tosca_ver(self):
|
||||||
|
template = utils.vnffgd_template
|
||||||
|
self.assertRaises(nfvo.ToscaParserFailed,
|
||||||
|
self.nfvo_plugin.validate_tosca,
|
||||||
|
template)
|
||||||
|
|
||||||
|
def test_validate_tosca_invalid(self):
|
||||||
|
template = utils.vnffgd_invalid_tosca_template
|
||||||
|
self.assertRaises(nfvo.ToscaParserFailed,
|
||||||
|
self.nfvo_plugin.validate_tosca,
|
||||||
|
template)
|
||||||
|
|
||||||
|
def test_create_vnffgd(self):
|
||||||
|
vnffgd_obj = utils.get_dummy_vnffgd_obj()
|
||||||
|
result = self.nfvo_plugin.create_vnffgd(self.context, vnffgd_obj)
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
self.assertIn('id', result)
|
||||||
|
self.assertIn('template', result)
|
||||||
|
|
||||||
|
def test_create_vnffg_abstract_types(self):
|
||||||
|
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()
|
||||||
|
self._insert_dummy_vnffg_template()
|
||||||
|
vnffg_obj = utils.get_dummy_vnffg_obj()
|
||||||
|
result = self.nfvo_plugin.create_vnffg(self.context, vnffg_obj)
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
self.assertIn('id', result)
|
||||||
|
self.assertIn('status', result)
|
||||||
|
self.assertEqual('PENDING_CREATE', result['status'])
|
||||||
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
vnfs=mock.ANY,
|
||||||
|
fc_id=mock.ANY,
|
||||||
|
auth_attr=mock.ANY,
|
||||||
|
symmetrical=mock.ANY
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_create_vnffg_vnf_mapping(self):
|
||||||
|
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()
|
||||||
|
self._insert_dummy_vnffg_template()
|
||||||
|
vnffg_obj = utils.get_dummy_vnffg_obj_vnf_mapping()
|
||||||
|
result = self.nfvo_plugin.create_vnffg(self.context, vnffg_obj)
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
self.assertIn('id', result)
|
||||||
|
self.assertIn('status', result)
|
||||||
|
self.assertEqual('PENDING_CREATE', result['status'])
|
||||||
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
vnfs=mock.ANY,
|
||||||
|
fc_id=mock.ANY,
|
||||||
|
auth_attr=mock.ANY,
|
||||||
|
symmetrical=mock.ANY
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_update_vnffg_nonexistent_vnf(self):
|
||||||
|
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()
|
||||||
|
self._insert_dummy_vnffg_template()
|
||||||
|
vnffg = self._insert_dummy_vnffg()
|
||||||
|
updated_vnffg = utils.get_dummy_vnffg_obj_vnf_mapping()
|
||||||
|
updated_vnffg['vnffg']['symmetrical'] = True
|
||||||
|
updated_vnf_mapping = \
|
||||||
|
{'VNF1': '91e32c20-6d1f-47a4-9ba7-08f5e5effe07',
|
||||||
|
'VNF3': '5c7f5631-9e74-46e8-b3d2-397c0eda9d0b'}
|
||||||
|
updated_vnffg['vnffg']['vnf_mapping'] = updated_vnf_mapping
|
||||||
|
self.assertRaises(nfvo.VnffgInvalidMappingException,
|
||||||
|
self.nfvo_plugin.update_vnffg,
|
||||||
|
self.context, vnffg['id'], updated_vnffg)
|
||||||
|
|
||||||
|
def test_update_vnffg(self):
|
||||||
|
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()
|
||||||
|
self._insert_dummy_vnffg_template()
|
||||||
|
vnffg = self._insert_dummy_vnffg()
|
||||||
|
updated_vnffg = utils.get_dummy_vnffg_obj_vnf_mapping()
|
||||||
|
updated_vnffg['vnffg']['symmetrical'] = True
|
||||||
|
updated_vnf_mapping = \
|
||||||
|
{'VNF1': '91e32c20-6d1f-47a4-9ba7-08f5e5effe07',
|
||||||
|
'VNF3': '10f66bc5-b2f1-45b7-a7cd-6dd6ad0017f5'}
|
||||||
|
updated_vnffg['vnffg']['vnf_mapping'] = updated_vnf_mapping
|
||||||
|
self.nfvo_plugin.update_vnffg(self.context, vnffg['id'],
|
||||||
|
updated_vnffg)
|
||||||
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
vnfs=mock.ANY,
|
||||||
|
fc_ids=mock.ANY,
|
||||||
|
chain_id=mock.ANY,
|
||||||
|
auth_attr=mock.ANY,
|
||||||
|
symmetrical=True)
|
||||||
|
|
||||||
|
def test_delete_vnffg(self):
|
||||||
|
self._insert_dummy_vnffg_template()
|
||||||
|
vnffg = self._insert_dummy_vnffg()
|
||||||
|
self.nfvo_plugin.delete_vnffg(self.context, vnffg['id'])
|
||||||
|
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
|
||||||
|
fc_id=mock.ANY,
|
||||||
|
auth_attr=mock.ANY)
|
||||||
|
@ -1,3 +1,157 @@
|
|||||||
|
data_types:
|
||||||
|
tosca.nfv.datatypes.pathType:
|
||||||
|
properties:
|
||||||
|
forwarder:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
capability:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
|
||||||
|
tosca.nfv.datatypes.aclType:
|
||||||
|
properties:
|
||||||
|
eth_type:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
eth_src:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
eth_dst:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
vlan_id:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 1, 4094 ]
|
||||||
|
required: false
|
||||||
|
vlan_pcp:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 7 ]
|
||||||
|
required: false
|
||||||
|
mpls_label:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 16, 1048575]
|
||||||
|
required: false
|
||||||
|
mpls_tc:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 7 ]
|
||||||
|
required: false
|
||||||
|
ip_dscp:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 63 ]
|
||||||
|
required: false
|
||||||
|
ip_ecn:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 3 ]
|
||||||
|
required: false
|
||||||
|
ip_src_prefix:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ip_dst_prefix:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ip_proto:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 1, 254 ]
|
||||||
|
required: false
|
||||||
|
destination_port_range:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
source_port_range:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
network_src_port_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
network_dst_port_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
network_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
network_name:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
tenant_id:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
icmpv4_type:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 254 ]
|
||||||
|
required: false
|
||||||
|
icmpv4_code:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 15 ]
|
||||||
|
required: false
|
||||||
|
arp_op:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 1, 25 ]
|
||||||
|
required: false
|
||||||
|
arp_spa:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
arp_tpa:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
arp_sha:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
arp_tha:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ipv6_src:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ipv6_dst:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ipv6_flabel:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 1048575]
|
||||||
|
required: false
|
||||||
|
icmpv6_type:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 255]
|
||||||
|
required: false
|
||||||
|
icmpv6_code:
|
||||||
|
type: integer
|
||||||
|
constraints:
|
||||||
|
- in_range: [ 0, 7]
|
||||||
|
required: false
|
||||||
|
ipv6_nd_target:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ipv6_nd_sll:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
ipv6_nd_tll:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
|
||||||
|
tosca.nfv.datatypes.policyType:
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ ACL ]
|
||||||
|
criteria:
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
entry_schema:
|
||||||
|
type: tosca.nfv.datatypes.aclType
|
||||||
|
|
||||||
node_types:
|
node_types:
|
||||||
tosca.nodes.nfv.VDU.Tacker:
|
tosca.nodes.nfv.VDU.Tacker:
|
||||||
derived_from: tosca.nodes.nfv.VDU
|
derived_from: tosca.nodes.nfv.VDU
|
||||||
@ -75,3 +229,19 @@ node_types:
|
|||||||
required: false
|
required: false
|
||||||
constraints:
|
constraints:
|
||||||
- valid_values: [ sriov, vnic ]
|
- valid_values: [ sriov, vnic ]
|
||||||
|
|
||||||
|
tosca.nodes.nfv.FP.Tacker:
|
||||||
|
derived_from: tosca.nodes.Root
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
required: false
|
||||||
|
policy:
|
||||||
|
type: tosca.nfv.datatypes.policyType
|
||||||
|
required: true
|
||||||
|
description: policy to use to match traffic for this FP
|
||||||
|
path:
|
||||||
|
type: list
|
||||||
|
required: true
|
||||||
|
entry_schema:
|
||||||
|
type: tosca.nfv.datatypes.pathType
|
||||||
|
Loading…
x
Reference in New Issue
Block a user