Update creating directly VNFFG and NS from descriptor template.

Currently, Tacker only support creating VNFFG and NS from VNFFG descriptor
and NS descriptor. This patch will support creating directy VNFFG and NS
from template file without initiating VNFFG and NS descriptors before.

Change-Id: I4e8ec8405a291300aa6749cf814ad582786c285d
Closes-bug: #1681032
This commit is contained in:
Cong Phuoc Hoang 2017-07-10 02:01:17 -07:00
parent a214de6629
commit d62364cbff
17 changed files with 764 additions and 78 deletions

View File

@ -0,0 +1,81 @@
{
"ns": {
"name": "ns_test",
"description": "sample NS",
"tenant_id": "058079ca392749bfa58831e0467a5439",
"attributes": {
"param_values": {
"nsd": {
"vl2_name": "net0",
"vl1_name": "net_mgmt"
}
}
},
"nsd_template": {
"imports": [
"VNFD1",
"VNFD2"
],
"tosca_definitions_version": "tosca_simple_profile_for_nfv_1_0_0",
"topology_template": {
"inputs": {
"vl2_name": {
"default": "net0",
"type": "string",
"description": "name of VL2 virtuallink"
},
"vl1_name": {
"default": "net_mgmt",
"type": "string",
"description": "name of VL1 virtuallink"
}
},
"node_templates": {
"VNF1": {
"type": "tosca.nodes.nfv.VNF1",
"requirements": [
{
"virtualLink1": "VL1"
},
{
"virtualLink2": "VL2"
}
]
},
"VNF2": {
"type": "tosca.nodes.nfv.VNF2"
},
"VL1": {
"type": "tosca.nodes.nfv.VL",
"properties": {
"network_name": {
"get_input": "vl1_name"
},
"vendor": "tacker"
}
},
"VNF1": {
"type": "tosca.nodes.nfv.VNF1",
"requirements": [
{
"virtualLink1": "VL1"
},
{
"virtualLink2": "VL2"
}
]
},
"VL2": {
"type": "tosca.nodes.nfv.VL",
"properties": {
"network_name": {
"get_input": "vl2_name"
},
"vendor": "tacker"
}
}
}
}
}
}
}

View File

@ -0,0 +1,16 @@
{
"ns": {
"status": "PENDING_CREATE",
"created_at": "2017-07-14 17:03:33.596454",
"description": "sample NS",
"tenant_id": "058079ca392749bfa58831e0467a5439",
"vim_id": "0e70ec23-6f32-420a-a039-2cdb2c20c329",
"updated_at": null,
"mgmt_urls": null,
"vnf_ids": null,
"error_reason": null,
"nsd_id": "be18005d-5656-4d81-b499-6af4d4d8437f",
"id": "ff35e3f0-0a11-4071-bce6-279fdf1c8bf9",
"name": "ns_test"
}
}

View File

@ -0,0 +1,74 @@
{
"vnffg": {
"tenant_id": "058079ca392749bfa58831e0467a5439",
"name": "vnffg1",
"vnf_mapping": {
"VNFD2": "e40f29eb-b3e5-4c47-ab05-567be3a63fdb",
"VNFD1": "e759aa4c-ae09-403a-8c04-90c77abfae56"
},
"symmetrical": false,
"vnffgd_template": {
"tosca_definitions_version": "tosca_simple_profile_for_nfv_1_0_0",
"description": "Sample VNFFG template",
"topology_template": {
"description": "Sample VNFFG template",
"node_templates": {
"Forwarding_path1": {
"type": "tosca.nodes.nfv.FP.Tacker",
"description": "creates path (CP12->CP22)",
"properties": {
"id": 51,
"policy": {
"type": "ACL",
"criteria": [
{
"network_src_port_id": "92011ca7-565d-408d-af67-09f21cdf9107"
},
{
"ip_proto": 1
}
]
},
"path": [
{
"forwarder": "VNFD1",
"capability": "CP12"
},
{
"forwarder": "VNFD2",
"capability": "CP22"
}
]
}
}
},
"groups": {
"VNFFG1": {
"type": "tosca.groups.nfv.VNFFG",
"description": "HTTP to Corporate Net",
"properties": {
"vendor": "tacker",
"version": 1.0,
"number_of_endpoints": 2,
"dependent_virtual_link": [
"VL12",
"VL22"
],
"connection_point": [
"CP12",
"CP22"
],
"constituent_vnfs": [
"VNFD1",
"VNFD2"
]
},
"members": [
"Forwarding_path1"
]
}
}
}
}
}
}

View File

@ -0,0 +1,84 @@
{
"vnffg": {
"forwarding_paths": "a2d2d1dd-433f-47b7-89f8-be3cd6b2086a",
"status": "PENDING_CREATE",
"description": "",
"tenant_id": "058079ca392749bfa58831e0467a5439",
"vnffgd_id": "5263b036-8d11-410c-9417-6e0300456160",
"vnf_mapping": {
"VNFD2": "af76238b-75db-4f70-9ad7-bd286bb0f88a",
"VNFD1": "dcda3482-1e75-4b37-8bdd-77c018a59cd7"
},
"attributes": {
"vnffgd": {
"imports": [
"/opt/stack/tacker/tacker/tosca/lib/tacker_defs.yaml",
"/opt/stack/tacker/tacker/tosca/lib/tacker_nfv_defs.yaml"
],
"description": "Sample VNFFG template",
"topology_template": {
"node_templates": {
"Forwarding_path1": {
"type": "tosca.nodes.nfv.FP.Tacker",
"description": "creates path (CP12->CP22)",
"properties": {
"policy": {
"type": "ACL",
"criteria": [
{
"network_src_port_id": "9ec6bef0-5da9-4b04-bdc7-c57bc26bcdea"
},
{
"ip_proto": 1
}
]
},
"path": [
{
"capability": "CP12",
"forwarder": "VNFD1"
},
{
"capability": "CP22",
"forwarder": "VNFD2"
}
],
"id": 51
}
}
},
"description": "Sample VNFFG template",
"groups": {
"VNFFG1": {
"type": "tosca.groups.nfv.VNFFG",
"description": "HTTP to Corporate Net",
"members": [
"Forwarding_path1"
],
"properties": {
"vendor": "tacker",
"connection_point": [
"CP12",
"CP22"
],
"version": 1,
"constituent_vnfs": [
"VNFD1",
"VNFD2"
],
"number_of_endpoints": 2,
"dependent_virtual_link": [
"VL12",
"VL22"
]
}
}
}
},
"tosca_definitions_version": "tosca_simple_profile_for_nfv_1_0_0"
}
},
"id": "d86b2e7c-6e88-4d6e-ac1a-9a9ad56b48f5",
"name": "vnffg1"
}
}

View File

@ -36,6 +36,7 @@ deploy a sample VNFD templates using vnf1.yaml and vnf2.yaml as mentioned in
reference section.
::
tacker vnfd-create --vnfd-file vnfd1.yaml VNFD1
tacker vnfd-create --vnfd-file vnfd2.yaml VNFD2
@ -71,7 +72,9 @@ The following code represents sample NSD which instantiates the above VNFs
In above NSD template VL1 and VL2 are substituting the virtuallinks of VNF1.
To onboard the above NSD:
**tacker nsd-create --nsd-file <nsd file> <nsd name>**
::
tacker nsd-create --nsd-file <nsd-file> <nsd-name>
Creating the NS
~~~~~~~~~~~~~~~~
@ -81,7 +84,16 @@ VNFDS(which NS is substituting)
Tacker provides the following CLI to create NS:
**tacker ns-create --nsd-id <nsd-id> <ns-name>**
::
tacker ns-create --nsd-id <nsd-id> <ns-name>
Or you can create directly a NS without creating onboarded NSD before by
following CLI command:
::
tacker ns-create --nsd-template <nsd-file> <ns-name>
Reference
~~~~~~~~~

View File

@ -29,9 +29,9 @@ Forwarding Graph Descriptors (VNFFGD). Please see the `devref guide
/vnffgd_template_description.rst>`_ on VNFFGD to learn more about
how a VNFFGD is defined.
After creating a VNFFGD, a VNFFG is instantiated by a separate Tacker
command. This action will build the chain and classifier necessary to
realize the VNFFG.
VNFFG can be instantiated from VNFFGD or directly from VNFFGD template by
separate Tacker commands. This action will build the chain and classifier
necessary to realize the VNFFG.
Prerequisites
~~~~~~~~~~~~~
@ -43,6 +43,22 @@ in order to use Tacker VNFFG. Networking-sfc also requires at least OVS 2.5
.0, so also ensure that is installed. See the full `Networking-sfc guide
<https://wiki.openstack.org/wiki/Neutron/ServiceInsertionAndChaining>`_.
A simple example of a service chain would be one that forces all traffice
from HTTP client to HTTP server to go through VNFs that was created by
VNFFG.
Firstly, HTTP client and HTTP server must be launched.
.. code-block:: console
net_id=$(openstack network list | grep net0 | awk '{print $2}')
openstack server create --flavor m1.tiny --image cirros-0.3.5-x86_64-disk \
--nic net-id=$net_id http_client
openstack server create --flavor m1.tiny --image cirros-0.3.5-x86_64-disk \
--nic net-id=$net_id http_server
Creating the VNFFGD
~~~~~~~~~~~~~~~~~~~
@ -62,6 +78,22 @@ tosca-vnffgd-sample.yaml>`_.
type: ACL
criteria:
- network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1
- destination_port_range: 80-1024
- ip_proto: 6
- ip_dst_prefix: 192.168.1.2/24
You can get network_src_port_id and IP destination address through
OpenStack commands like bellow:
.. code-block:: console
client_ip=$(openstack server list | grep http_client | \
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
network_source_port_id=$(openstack port list | grep $client_ip | awk '{print $2}')
ip_dst=$(openstack server list | grep http_server | \
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+')
This is required due to a limitation of Neutron networking-sfc and only
@ -71,7 +103,7 @@ Tacker provides the following CLI to create a VNFFGD:
.. code-block:: console
tacker vnffgd-create --vnffgd-file <vnffgd file> <vnffgd name>
tacker vnffgd-create --vnffgd-file <vnffgd-file> <vnffgd-name>
Creating the VNFFG
@ -82,24 +114,41 @@ VNFD types listed in the VNFFGD. Failure to do so will result in error when
trying to create a VNFFG. Note, the VNFD you define **must** include the
same Connection Point definitions as the ones you declared in your VNFFGD.
Refer the 'Getting Started' link below on how to create a VNFD and deploy a
VNF:
.. code-block:: console
tacker vnfd-create --vnfd-file tosca-vnffg-vnfd1.yaml VNFD1
tacker vnf-create --vnfd-name VNFD1 VNF1
tacker vnfd-create --vnfd-file tosca-vnffg-vnfd2.yaml VNFD2
tacker vnf-create --vnfd-name VNFD2 VNF2
Refer the 'Getting Started' link below on how to create a VNFD and deploy
2 VNFs: `VNF1`_ and `VNF2`_.
http://docs.openstack.org/developer/tacker/install/getting_started.html
Tacker provides the following CLI to create VNFFG:
Tacker provides the following CLI to create VNFFG from VNFFGD:
.. code-block:: console
tacker vnffg-create --vnffgd-name <vnffgd name> \
--vnf-mapping <vnf mapping> --symmetrical <boolean>
tacker vnffg-create --vnffgd-name <vnffgd-name> \
--vnf-mapping <vnf-mapping> --symmetrical <boolean> <vnffg-name>
or you can create directly VNFFG from vnffgd template without initiating
VNFFGD.
.. code-block:: console
tacker vnffg-create --vnffgd-template <vnffgd-template> \
--vnf-mapping <vnf-mapping> --symmetrical <boolean> <vnffg-name>
If you use a parameterized vnffg template:
.. code-block:: console
tacker vnffg-create --vnffgd-name <vnffgd name> \
--param-file <param file> --vnf-mapping <vnf mapping> \
--symmetrical <boolean>
tacker vnffg-create --vnffgd-name <vnffgd-name> \
--param-file <param-file> --vnf-mapping <vnf-mapping> \
--symmetrical <boolean> <vnffg-name>
Here,
@ -109,18 +158,23 @@ Here,
* symmetrical - True/False
VNF Mapping is used to declare which exact VNF instance to be used for
each VNF in the Forwarding Path. For example, imagine a Forwarding Path
'path' which includes VNF1 and VNF2 VNFDs. Two VNF instances already exist
(one from each VNFD): '91e32c20-6d1f-47a4-9ba7-08f5e5effe07',
'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'. The following command would then
map each VNFD defined in the VNFFGD Forwarding Path to the desired VNF
instance:
each VNF in the Forwarding Path. The following command would list VNFs
in Tacker and then map each VNFD defined in the VNFFGD Forwarding Path
to the desired VNF instance:
.. code-block:: console
tacker vnffg-create --vnffgd-name myvnffgd \
--vnf-mapping VNF1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07', \
VNF2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'
tacker vnf-list
+--------------------------------------+------+---------------------------+--------+--------------------------------------+--------------------------------------+
| id | name | mgmt_url | status | vim_id | vnfd_id |
+--------------------------------------+------+---------------------------+--------+--------------------------------------+--------------------------------------+
| 7168062e-9fa1-4203-8cb7-f5c99ff3ee1b | VNF2 | {"VDU1": "192.168.1.5"} | ACTIVE | 0e70ec23-6f32-420a-a039-2cdb2c20c329 | ea842879-5a7a-4f29-a8b0-528b2ad3b027 |
| 91e32c20-6d1f-47a4-9ba7-08f5e5effe07 | VNF1 | {"VDU1": "192.168.1.7"} | ACTIVE | 0e70ec23-6f32-420a-a039-2cdb2c20c329 | 27795330-62a7-406d-9443-2daad76e674b |
+--------------------------------------+------+---------------------------+--------+--------------------------------------+--------------------------------------+
tacker vnffg-create --vnffgd-name myvnffgd --vnf-mapping \
VNFD1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07',VNF2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b' myvnffg
Alternatively, if no vnf-mapping is provided then Tacker VNFFG will attempt
to search for VNF instances derived from the given VNFDs in the VNFFGD. If
@ -147,7 +201,7 @@ The value of a parameterized attribute can be specified like *{get_input foo}*
in the TOSCA VNFFGD template. The corresponding param-file in the following
YAML format can be provided in the vnffg-create command,
::
.. code-block:: console
{
foo: bar
@ -155,10 +209,11 @@ YAML format can be provided in the vnffg-create command,
VNFFG command with parameter file:
**tacker vnffg-create --vnffgd-name myvnffgd**
**--vnf-mapping VNF1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07',**
**VNF2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b'**
**--param-file cust-site-x-param.yaml**
.. code-block:: console
tacker vnffg-create --vnffgd-name vnffgd-param --vnf-mapping VNFD1:'91e32c20-6d1f-47a4-9ba7-08f5e5effe07',\
VNFD2:'7168062e-9fa1-4203-8cb7-f5c99ff3ee1b' --param-file vnffg-param-file.yaml myvnffg
See `VNFFGD template samples with parameter support <https://github.com/
@ -192,3 +247,6 @@ Known Issues and Limitations
'network_name'
- NSH attributes not yet supported
- Symmetrical is not supported by driver yet
.. _VNF1: https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd1.yaml
.. _VNF2: https://github.com/openstack/tacker/blob/master/samples/tosca-templates/vnffgd/tosca-vnffg-vnfd2.yaml

View File

@ -0,0 +1,5 @@
---
features:
- |
Support to create directly VNFFG and NS from its descriptor template
without creating VNFFGD and NSD.

View File

@ -566,8 +566,16 @@ class Controller(object):
for rule in attr_vals['validate']:
# skip validating vnfd_id when vnfd_template is specified to
# create vnf
if resource == 'vnf' and 'vnfd_template' in body['vnf'] and \
attr == "vnfd_id" and is_create:
if (resource == 'vnf') and ('vnfd_template' in body['vnf'])\
and (attr == "vnfd_id") and is_create:
continue
# skip validating vnffgd_id when vnffgd_template is provided
if (resource == 'vnffg') and ('vnffgd_template' in body['vnffg'])\
and (attr == 'vnffgd_id') and is_create:
continue
# skip validating nsd_id when nsd_template is provided
if (resource == 'ns') and ('nsd_template' in body['ns'])\
and (attr == 'nsd_id') and is_create:
continue
res = attributes.validators[rule](res_dict[attr],
attr_vals['validate'][rule])

View File

@ -1 +1 @@
f5c1c3b0f6b4
e9a1e47fb0b5

View File

@ -0,0 +1,45 @@
# Copyright 2017 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.
#
"""add onboarded status for vnffgd and nsd
Revision ID: e9a1e47fb0b5
Revises: f5c1c3b0f6b4
Create Date: 2017-07-17 10:02:37.572587
"""
# revision identifiers, used by Alembic.
revision = 'e9a1e47fb0b5'
down_revision = 'f5c1c3b0f6b4'
from alembic import op
import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
op.add_column('vnffgtemplates',
sa.Column('template_source',
sa.String(length=255),
server_default='onboarded'))
op.execute("UPDATE vnffgtemplates set template_source='onboarded'"
" WHERE template_source is NULL")
op.add_column('nsd',
sa.Column('template_source',
sa.String(length=255),
server_default='onboarded'))
op.execute("UPDATE nsd set template_source='onboarded'"
" WHERE template_source is NULL")

View File

@ -12,11 +12,12 @@
import ast
from datetime import datetime
import uuid
from oslo_db.exception import DBDuplicateEntry
from oslo_log import log as logging
from oslo_utils import timeutils
from oslo_utils import uuidutils
from six import iteritems
import sqlalchemy as sa
from sqlalchemy import orm
@ -54,6 +55,9 @@ class NSD(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
description = sa.Column(sa.Text)
vnfds = sa.Column(types.Json, nullable=True)
# Nsd template source - onboarded
template_source = sa.Column(sa.String(255), server_default='onboarded')
# (key, value) pair to spin up
attributes = orm.relationship('NSDAttribute',
backref='nsd')
@ -146,7 +150,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
'attributes': self._make_attributes_dict(nsd['attributes']),
}
key_list = ('id', 'tenant_id', 'name', 'description',
'created_at', 'updated_at', 'vnfds')
'created_at', 'updated_at', 'vnfds', 'template_source')
res.update((key, nsd[key]) for key in key_list)
return self._fields(res, fields)
@ -167,21 +171,23 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
nsd = nsd['nsd']
LOG.debug(_('nsd %s'), nsd)
tenant_id = self._get_tenant_id_for_create(context, nsd)
template_source = nsd.get('template_source')
try:
with context.session.begin(subtransactions=True):
nsd_id = uuidutils.generate_uuid()
nsd_id = str(uuid.uuid4())
nsd_db = NSD(
id=nsd_id,
tenant_id=tenant_id,
name=nsd.get('name'),
vnfds=vnfds,
description=nsd.get('description'),
deleted_at=datetime.min)
deleted_at=datetime.min,
template_source=template_source)
context.session.add(nsd_db)
for (key, value) in nsd.get('attributes', {}).items():
attribute_db = NSDAttribute(
id=uuidutils.generate_uuid(),
id=str(uuid.uuid4()),
nsd_id=nsd_id,
key=key,
value=value)
@ -233,6 +239,9 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
return self._make_nsd_dict(nsd_db)
def get_nsds(self, context, filters, fields=None):
if ('template_source' in filters) and \
(filters['template_source'][0] == 'all'):
filters.pop('template_source')
return self._get_collection(context, NSD,
self._make_nsd_dict,
filters=filters, fields=fields)
@ -245,7 +254,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
nsd_id = ns['nsd_id']
vim_id = ns['vim_id']
name = ns.get('name')
ns_id = uuidutils.generate_uuid()
ns_id = str(uuid.uuid4())
try:
with context.session.begin(subtransactions=True):
nsd_db = self._get_resource(context, NSD,
@ -283,7 +292,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
mgmt_urls = dict()
vnf_ids = dict()
if len(output) > 0:
for vnfd_name, vnfd_val in (vnfd_dict).items():
for vnfd_name, vnfd_val in iteritems(vnfd_dict):
for instance in vnfd_val['instances']:
if 'mgmt_url_' + instance in output:
mgmt_urls[instance] = ast.literal_eval(
@ -332,6 +341,8 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
def delete_ns_post(self, context, ns_id, mistral_obj,
error_reason, soft_delete=True):
ns = self.get_ns(context, ns_id)
nsd_id = ns.get('nsd_id')
with context.session.begin(subtransactions=True):
query = (
self._model_query(context, NS).
@ -359,6 +370,9 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
details="ns Delete Complete")
else:
query.delete()
template_db = self._get_resource(context, NSD, nsd_id)
if template_db.get('template_source') == 'inline':
self.delete_nsd(context, nsd_id)
def get_ns(self, context, ns_id, fields=None):
ns_db = self._get_resource(context, NS, ns_id)

View File

@ -15,9 +15,10 @@
import random
import sqlalchemy as sa
import uuid
from oslo_log import log as logging
from oslo_utils import uuidutils
from six import iteritems
from sqlalchemy import orm
from sqlalchemy.orm import exc as orm_exc
from tacker._i18n import _
@ -73,6 +74,9 @@ class VnffgTemplate(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
# Vnffg template
template = sa.Column(types.Json)
# Vnffgd template source - onboarded
template_source = sa.Column(sa.String(255), server_default='onboarded')
class Vnffg(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
"""VNF Forwarding Graph Data Model"""
@ -192,8 +196,8 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
def create_vnffg(self, context, vnffg):
vnffg_dict = self._create_vnffg_pre(context, vnffg)
sfc_instance = uuidutils.generate_uuid()
fc_instance = uuidutils.generate_uuid()
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)
@ -220,15 +224,17 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
template = vnffgd['vnffgd']
LOG.debug(_('template %s'), template)
tenant_id = self._get_tenant_id_for_create(context, template)
template_source = template.get('template_source')
with context.session.begin(subtransactions=True):
template_id = uuidutils.generate_uuid()
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'))
template=template.get('template'),
template_source=template_source)
context.session.add(template_db)
LOG.debug(_('template_db %(template_db)s'),
@ -241,6 +247,9 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
return self._make_template_dict(template_db, fields)
def get_vnffgds(self, context, filters=None, fields=None):
if ('template_source' in filters) and \
(filters['template_source'][0] == 'all'):
filters.pop('template_source')
return self._get_collection(context, VnffgTemplate,
self._make_template_dict,
filters=filters, fields=fields)
@ -288,7 +297,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
if 'get_input' not in str(original):
return
if isinstance(original, dict):
for key_, value in (original).items():
for key_, value in iteritems(original):
if isinstance(value, dict) and 'get_input' in value:
if value['get_input'] in paramvalues:
original[key_] = paramvalues[value['get_input']]
@ -324,7 +333,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
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 uuidutils.generate_uuid()
vnffg_id = vnffg.get('id') or str(uuid.uuid4())
template_id = vnffg['vnffgd_id']
symmetrical = vnffg['symmetrical']
@ -362,9 +371,9 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
status=constants.PENDING_CREATE)
context.session.add(vnffg_db)
nfp_id = uuidutils.generate_uuid()
sfc_id = uuidutils.generate_uuid()
classifier_id = uuidutils.generate_uuid()
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,
@ -400,7 +409,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
LOG.debug(_('acl_match %s'), match)
match_db_table = ACLMatchCriteria(
id=uuidutils.generate_uuid(),
id=str(uuid.uuid4()),
vnffgc_id=classifier_id,
**match)
@ -663,7 +672,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
"""
# this should be overridden with driver call to find ID given name
# for resource
return uuidutils.generate_uuid()
return str(uuid.uuid4())
# called internally, not by REST API
# instance_id = None means error on creation
@ -904,9 +913,17 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
nfp_query.delete()
vnffg_query.delete()
vnffgd_id = vnffg.get('vnffgd_id')
template_db = self._get_resource(context, VnffgTemplate,
vnffgd_id)
if template_db.get('template_source') == 'inline':
self.delete_vnffgd(context, vnffgd_id)
def _make_template_dict(self, template, fields=None):
res = {}
key_list = ('id', 'tenant_id', 'name', 'description', 'template')
key_list = ('id', 'tenant_id', 'name', 'description', 'template',
'template_source')
res.update((key, template[key]) for key in key_list)
return self._fields(res, fields)

View File

@ -367,6 +367,12 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True,
'default': None,
},
'template_source': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
'default': 'onboarded'
}
},
'vnffgs': {
@ -389,6 +395,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'default': None
},
'name': {
'allow_post': True,
@ -436,6 +443,13 @@ RESOURCE_ATTRIBUTE_MAP = {
'allow_put': False,
'is_visible': True,
},
'vnffgd_template': {
'allow_post': True,
'allow_put': False,
'validate': {'type:dict_or_nodata': None},
'is_visible': True,
'default': None,
},
},
'nfps': {
@ -636,6 +650,12 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True,
'default': None,
},
'template_source': {
'allow_post': False,
'allow_put': False,
'is_visible': True,
'default': 'onboarded'
},
},
@ -689,6 +709,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'default': None,
},
'vim_id': {
'allow_post': True,
@ -722,6 +743,13 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:dict_or_nodata': None},
'is_visible': True,
},
'nsd_template': {
'allow_post': True,
'allow_put': False,
'validate': {'type:dict_or_nodata': None},
'is_visible': True,
'default': None,
},
},
}

View File

@ -266,6 +266,12 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
def create_vnffgd(self, context, vnffgd):
template = vnffgd['vnffgd']
if 'template_source' in template:
template_source = template.get('template_source')
else:
template_source = 'onboarded'
vnffgd['vnffgd']['template_source'] = template_source
if 'vnffgd' not in template.get('template'):
raise nfvo.VnffgdInvalidTemplate(template=template.get('template'))
else:
@ -279,6 +285,20 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
@log.log
def create_vnffg(self, context, vnffg):
vnffg_info = vnffg['vnffg']
name = vnffg_info['name']
if vnffg_info.get('vnffgd_template'):
vnffgd_name = utils.generate_resource_name(name, 'inline')
vnffgd = {'vnffgd': {'tenant_id': vnffg_info['tenant_id'],
'name': vnffgd_name,
'template': {
'vnffgd': vnffg_info['vnffgd_template']},
'template_source': 'inline',
'description': vnffg_info['description']}}
vnffg_info['vnffgd_id'] = \
self.create_vnffgd(context, vnffgd).get('id')
vnffg_dict = super(NfvoPlugin, self)._create_vnffg_pre(context, vnffg)
nfp = super(NfvoPlugin, self).get_nfp(context,
vnffg_dict['forwarding_paths'])
@ -502,6 +522,12 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
template)
LOG.debug(_('nsd %s'), nsd_data)
if 'template_source' in nsd_data:
template_source = nsd_data.get('template_source')
else:
template_source = "onboarded"
nsd['nsd']['template_source'] = template_source
self._parse_template_input(context, nsd)
return super(NfvoPlugin, self).create_nsd(
context, nsd)
@ -576,6 +602,19 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
VNFs will actually substitute their requirements.
step-3: Create mistral workflow and execute the workflow
"""
ns_info = ns['ns']
name = ns_info['name']
if ns_info.get('nsd_template'):
nsd_name = utils.generate_resource_name(name, 'inline')
nsd = {'nsd': {
'attributes': {'nsd': ns_info['nsd_template']},
'description': ns_info['description'],
'name': nsd_name,
'template_source': 'inline',
'tenant_id': ns_info['tenant_id']}}
ns_info['nsd_id'] = self.create_nsd(context, nsd).get('id')
nsd = self.get_nsd(context, ns['ns']['nsd_id'])
nsd_dict = yaml.safe_load(nsd['attributes']['nsd'])
vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']

View File

@ -101,33 +101,56 @@ class NsdTestCreate(base.BaseTackerTest):
int(time.time()) - start_time) > timeout):
raise Exception("Failed with status: %s" % status)
def _test_create_delete_ns(self, nsd_file, ns_name):
def _test_create_delete_ns(self, nsd_file, ns_name,
template_source='onboarded'):
vnfd1_id = self._test_create_tosca_vnfd(
'test-ns-vnfd1.yaml',
'test-ns-vnfd1')
vnfd2_id = self._test_create_tosca_vnfd(
'test-ns-vnfd2.yaml',
'test-ns-vnfd2')
nsd_id = self._test_create_nsd(
nsd_file,
'test-ns-nsd')
ns_arg = {'ns': {'nsd_id': nsd_id, 'name': ns_name,
'attributes': {"param_values": {"nsd":
{"vl2_name": "net0",
"vl1_name": "net_mgmt"}}}}}
ns_instance = self.client.create_ns(body=ns_arg)
ns_id = ns_instance['ns']['id']
if template_source == 'onboarded':
nsd_id = self._test_create_nsd(
nsd_file,
'test-ns-nsd')
ns_arg = {'ns': {
'nsd_id': nsd_id,
'name': ns_name,
'attributes': {"param_values": {
"nsd": {
"vl2_name": "net0",
"vl1_name": "net_mgmt"}}}}}
ns_instance = self.client.create_ns(body=ns_arg)
ns_id = ns_instance['ns']['id']
if template_source == 'inline':
input_yaml = read_file(nsd_file)
template = yaml.safe_load(input_yaml)
ns_arg = {'ns': {
'name': ns_name,
'attributes': {"param_values": {
"nsd": {
"vl2_name": "net0",
"vl1_name": "net_mgmt"}}},
'nsd_template': template}}
ns_instance = self.client.create_ns(body=ns_arg)
ns_id = ns_instance['ns']['id']
self._wait_until_ns_status(ns_id, 'ACTIVE',
constants.NS_CREATE_TIMEOUT,
constants.ACTIVE_SLEEP_TIME)
ns_show_out = self.client.show_ns(ns_id)['ns']
self.assertIsNotNone(ns_show_out['mgmt_urls'])
try:
self.client.delete_ns(ns_id)
except Exception:
except Exception as e:
print("Exception:", e)
assert False, "ns Delete failed"
self._wait_until_ns_delete(ns_id, constants.NS_DELETE_TIMEOUT)
self._test_delete_nsd(nsd_id)
if template_source == 'onboarded':
self._wait_until_ns_delete(ns_id, constants.NS_DELETE_TIMEOUT)
self._test_delete_nsd(nsd_id)
self._test_delete_vnfd(vnfd1_id)
self._test_delete_vnfd(vnfd2_id)
@ -147,4 +170,9 @@ class NsdTestCreate(base.BaseTackerTest):
def test_create_delete_network_service(self):
self._test_create_delete_ns('test-ns-nsd.yaml',
'test-ns')
'test-ns-onboarded',
template_source='onboarded')
time.sleep(1)
self._test_create_delete_ns('test-ns-nsd.yaml',
'test-ns-inline',
template_source='inline')

View File

@ -165,14 +165,23 @@ def get_vim_auth_obj():
def get_dummy_vnffgd_obj():
return {u'vnffgd': {'name': 'dummy_vnfd',
return {u'vnffgd': {'name': 'dummy_vnffgd',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
u'template': {u'vnffgd': vnffgd_tosca_template},
'description': 'dummy_vnfd_description'}}
'description': 'dummy_vnffgd_description',
'template_source': 'onboarded'}}
def get_dummy_vnffgd_obj_inline():
return {u'vnffgd': {'name': 'dummy_vnffgd_inline',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
u'template': {u'vnffgd': vnffgd_tosca_template},
'description': 'dummy_vnffgd_description_inline',
'template_source': 'inline'}}
def get_dummy_vnffg_obj():
return {'vnffg': {'description': 'dummy_vnf_description',
return {'vnffg': {'description': 'dummy_vnffg_description',
'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'name': 'dummy_vnffg',
@ -181,6 +190,16 @@ def get_dummy_vnffg_obj():
'symmetrical': False}}
def get_dummy_vnffg_obj_inline():
return {'vnffg': {'description': 'dummy_vnffg_description_inline',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'name': 'dummy_vnffg_inline',
u'attributes': {u'template': vnffgd_tosca_template},
'vnf_mapping': {},
'symmetrical': False,
'vnffgd_template': vnffgd_tosca_template}}
def get_dummy_vnffg_param_obj():
return {'vnffg': {'description': 'dummy_vnf_description',
'vnffgd_id': u'eb094833-995e-49f0-a047-dfb56aaf7c4e',
@ -234,10 +253,19 @@ def get_dummy_vnffg_obj_vnf_mapping():
def get_dummy_nsd_obj():
return {'nsd': {'description': 'dummy nsd description',
return {'nsd': {'description': 'dummy_nsd_description',
'name': 'dummy_NSD',
'tenant_id': u'8819a1542a5948b68f94d4be0fd50496',
'attributes': {u'nsd': nsd_tosca_template}}}
'attributes': {u'nsd': nsd_tosca_template},
'template_source': 'onboarded'}}
def get_dummy_nsd_obj_inline():
return {'nsd': {'description': 'dummy_nsd_description_inline',
'name': 'dummy_NSD_inline',
'tenant_id': u'8819a1542a5948b68f94d4be0fd50496',
'attributes': {u'nsd': nsd_tosca_template},
'template_source': 'inline'}}
def get_dummy_ns_obj():
@ -252,6 +280,18 @@ def get_dummy_ns_obj():
'vl2_name': 'net0'}}}}}
def get_dummy_ns_obj_inline():
return {'ns': {'description': 'dummy_ns_description_inline',
'id': u'ff35e3f0-0a11-4071-bce6-279fdf1c8bf9',
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
'name': 'dummy_ns_inline',
'attributes': {
'param_values': {'nsd': {'vl1_name': 'net_mgmt',
'vl2_name': 'net0'}}},
'nsd_template': nsd_tosca_template}}
def get_dummy_ns_obj_2():
return {'ns': {'description': 'dummy_ns_description',
'id': u'ba6bf017-f6f7-45f1-a280-57b073bf78ea',

View File

@ -17,10 +17,10 @@ import codecs
from datetime import datetime
import mock
import os
import uuid
from mock import patch
from oslo_utils import uuidutils
from tacker import context
from tacker.db.common_services import common_services_db_plugin
@ -57,7 +57,7 @@ class FakeDriverManager(mock.Mock):
def invoke(self, *args, **kwargs):
if any(x in ['create', 'create_chain', 'create_flow_classifier'] for
x in args):
return uuidutils.generate_uuid()
return str(uuid.uuid4())
elif 'execute_workflow' in args:
mock_execution = mock.Mock()
mock_execution.id.return_value = \
@ -366,7 +366,21 @@ class TestNfvoPlugin(db_base.SqlTestCase):
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
name='fake_template',
description='fake_template_description',
template={u'vnffgd': utils.vnffgd_tosca_template})
template={u'vnffgd': utils.vnffgd_tosca_template},
template_source='onboarded')
session.add(vnffg_template)
session.flush()
return vnffg_template
def _insert_dummy_vnffg_template_inline(self):
session = self.context.session
vnffg_template = vnffg_db.VnffgTemplate(
id='11da9f20-9347-4283-bc68-eb98061ef8f7',
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
name='dummy_vnffgd_inline',
description='dummy_vnffgd_description_inline',
template={u'vnffgd': utils.vnffgd_tosca_template},
template_source='inline')
session.add(vnffg_template)
session.flush()
return vnffg_template
@ -501,6 +515,16 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIsNotNone(result)
self.assertIn('id', result)
self.assertIn('template', result)
self.assertIn('template_source', result)
self.assertEqual('onboarded', result['template_source'])
def test_create_vnffgd_inline(self):
vnffgd_obj = utils.get_dummy_vnffgd_obj_inline()
result = self.nfvo_plugin.create_vnffgd(self.context, vnffgd_obj)
self.assertIsNotNone(result)
self.assertIn('id', result)
self.assertIn('template', result)
self.assertEqual('inline', result['template_source'])
def test_create_vnffg_abstract_types(self):
with patch.object(TackerManager, 'get_service_plugins') as \
@ -523,6 +547,32 @@ class TestNfvoPlugin(db_base.SqlTestCase):
symmetrical=mock.ANY
)
@mock.patch('tacker.nfvo.nfvo_plugin.NfvoPlugin.create_vnffgd')
def test_create_vnffg_abstract_types_inline(self, mock_create_vnffgd):
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()
mock_create_vnffgd.return_value = {'id':
'11da9f20-9347-4283-bc68-eb98061ef8f7'}
self._insert_dummy_vnffg_template_inline()
vnffg_obj = utils.get_dummy_vnffg_obj_inline()
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.assertEqual('dummy_vnffg_inline', result['name'])
mock_create_vnffgd.assert_called_once_with(mock.ANY, mock.ANY)
self._driver_manager.invoke.assert_called_with(mock.ANY, mock.ANY,
name=mock.ANY,
vnfs=mock.ANY,
fc_id=mock.ANY,
auth_attr=mock.ANY,
symmetrical=mock.ANY
)
def test_create_vnffg_param_values(self):
with patch.object(TackerManager, 'get_service_plugins') as \
mock_plugins:
@ -669,11 +719,12 @@ class TestNfvoPlugin(db_base.SqlTestCase):
vnfds={'tosca.nodes.nfv.VNF1': 'vnf1',
'tosca.nodes.nfv.VNF2': 'vnf2'},
description='fake_nsd_template_description',
deleted_at=datetime.min)
deleted_at=datetime.min,
template_source='onboarded')
session.add(nsd_template)
for (key, value) in attributes.items():
attribute_db = ns_db.NSDAttribute(
id=uuidutils.generate_uuid(),
id=str(uuid.uuid4()),
nsd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
key=key,
value=value)
@ -681,16 +732,53 @@ class TestNfvoPlugin(db_base.SqlTestCase):
session.flush()
return nsd_template
def _insert_dummy_ns_template_inline(self):
session = self.context.session
attributes = {
u'nsd': 'imports: [VNF1, VNF2]\ntopology_template:\n inputs:\n '
' vl1_name: {default: net_mgmt, description: name of VL1'
' virtuallink, type: string}\n vl2_name: {default: '
'net0, description: name of VL2 virtuallink, type: string'
'}\n node_templates:\n VL1:\n properties:\n '
' network_name: {get_input: vl1_name}\n vendor: '
'tacker\n type: tosca.nodes.nfv.VL\n VL2:\n '
'properties:\n network_name: {get_input: vl2_name}'
'\n vendor: tacker\n type: tosca.nodes.nfv.VL'
'\n VNF1:\n requirements:\n - {virtualLink1: '
'VL1}\n - {virtualLink2: VL2}\n type: tosca.node'
's.nfv.VNF1\n VNF2: {type: tosca.nodes.nfv.VNF2}\ntosca'
'_definitions_version: tosca_simple_profile_for_nfv_1_0_0'
'\n'}
nsd_template = ns_db.NSD(
id='be18005d-5656-4d81-b499-6af4d4d8437f',
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
name='dummy_NSD',
vnfds={'tosca.nodes.nfv.VNF1': 'vnf1',
'tosca.nodes.nfv.VNF2': 'vnf2'},
description='dummy_nsd_description',
deleted_at=datetime.min,
template_source='inline')
session.add(nsd_template)
for (key, value) in attributes.items():
attribute_db = ns_db.NSDAttribute(
id=str(uuid.uuid4()),
nsd_id='be18005d-5656-4d81-b499-6af4d4d8437f',
key=key,
value=value)
session.add(attribute_db)
session.flush()
return nsd_template
def _insert_dummy_ns(self):
session = self.context.session
ns = ns_db.NS(
id='ba6bf017-f6f7-45f1-a280-57b073bf78ea',
name='fake_ns',
name='dummy_ns',
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
status='ACTIVE',
nsd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
description='fake_ns_description',
description='dummy_ns_description',
deleted_at=datetime.min)
session.add(ns)
session.flush()
@ -720,7 +808,24 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIsNotNone(result)
self.assertEqual('dummy_NSD', result['name'])
self.assertIn('id', result)
self.assertEqual('dummy nsd description', result['description'])
self.assertEqual('dummy_NSD', result['name'])
self.assertEqual('onboarded', result['template_source'])
self.assertEqual('8819a1542a5948b68f94d4be0fd50496',
result['tenant_id'])
self.assertIn('attributes', result)
self.assertIn('created_at', result)
self.assertIn('updated_at', result)
def test_create_nsd_inline(self):
nsd_obj = utils.get_dummy_nsd_obj_inline()
with patch.object(TackerManager, 'get_service_plugins') as \
mock_plugins: