Implement VNFFG support for NS
This patch will add VNFFG support for NS. In NSD, users can describe VNFDs and nested VNFFGDs inside. When NS is created, VNFs and VNFFGs are also created too. Work items of this patch: 1. Add sample nsd templates 2. Update NFVO plugin to adapt VNFFGD template extraction from NSD and create VNFFGs using that template 3. Modify Mistral workflow to create/delete VNFFGs using NSD User guide, defref will be added in following patches. Co-Authored-By: Cong Phuoc Hoang <hoangphuocbk2.07@gmail.com> Change-Id: Id5827e66aec0231bf27e87fc96d8fc6bc5cb9c26 Partially-implements: blueprint vnffg-ns
This commit is contained in:
parent
22c33bda5a
commit
422055b9ba
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
This patch will add VNFFG support for NS. In NSD, users can describe VNFDs
|
||||
and nested VNFFGD inside. When NS is created, VNFs and VNFFG are also
|
||||
created.
|
5
samples/tosca-templates/vnffg-nsd/ns_param.yaml
Normal file
5
samples/tosca-templates/vnffg-nsd/ns_param.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
nsd:
|
||||
vl1_name: net_mgmt
|
||||
vl2_name: net0
|
||||
net_src_port_id: c0a40f9c-d229-40b7-871f-55131cf17783
|
||||
ip_dest_prefix: 10.10.0.11/24
|
109
samples/tosca-templates/vnffg-nsd/tosca-multiple-vnffg-nsd.yaml
Normal file
109
samples/tosca-templates/vnffg-nsd/tosca-multiple-vnffg-nsd.yaml
Normal file
@ -0,0 +1,109 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
|
||||
description: Import VNFDs(already on-boarded) with input parameters
|
||||
imports:
|
||||
- sample-vnfd1
|
||||
- sample-vnfd2
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
vl1_name:
|
||||
type: string
|
||||
description: name of VL1 virtuallink
|
||||
default: net_mgmt
|
||||
vl2_name:
|
||||
type: string
|
||||
description: name of VL2 virtuallink
|
||||
default: net0
|
||||
net_src_port_id:
|
||||
type: string
|
||||
description: neutron port id of source port
|
||||
ip_dest_prefix:
|
||||
type: string
|
||||
description: IP prefix of destination port
|
||||
|
||||
node_templates:
|
||||
VNF1:
|
||||
type: tosca.nodes.nfv.VNF1
|
||||
requirements:
|
||||
- virtualLink1: VL1
|
||||
|
||||
VNF2:
|
||||
type: tosca.nodes.nfv.VNF2
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl1_name}
|
||||
vendor: tacker
|
||||
|
||||
VL2:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl2_name}
|
||||
vendor: tacker
|
||||
|
||||
Forwarding_path1:
|
||||
type: tosca.nodes.nfv.FP.TackerV2
|
||||
description: creates path inside ns (src_port->CP12->CP22->dst_port)
|
||||
properties:
|
||||
id: 51
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: {get_input: net_src_port_id}
|
||||
destination_port_range: 80-1024
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: {get_input: ip_dest_prefix}
|
||||
path:
|
||||
- forwarder: sample-vnfd1
|
||||
capability: CP12
|
||||
- forwarder: sample-vnfd2
|
||||
capability: CP22
|
||||
|
||||
Forwarding_path2:
|
||||
type: tosca.nodes.nfv.FP.TackerV2
|
||||
description: creates path inside ns (src_port->CP12->dst_port)
|
||||
properties:
|
||||
id: 52
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: {get_input: net_src_port_id}
|
||||
destination_port_range: 8080-8080
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: {get_input: ip_dest_prefix}
|
||||
path:
|
||||
- forwarder: sample-vnfd1
|
||||
capability: CP12
|
||||
|
||||
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: [VL1, VL2]
|
||||
connection_point: [CP12, CP22]
|
||||
constituent_vnfs: [sample-vnfd1, sample-vnfd2]
|
||||
members: [Forwarding_path1]
|
||||
|
||||
VNFFG2:
|
||||
type: tosca.groups.nfv.VNFFG
|
||||
description: HTTP to Corporate Net
|
||||
properties:
|
||||
vendor: tacker
|
||||
version: 1.0
|
||||
number_of_endpoints: 1
|
||||
dependent_virtual_link: [VL1]
|
||||
connection_point: [CP12]
|
||||
constituent_vnfs: [sample-vnfd1]
|
||||
members: [Forwarding_path2]
|
||||
|
@ -0,0 +1,78 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
|
||||
description: Import VNFDs(already on-boarded) with input parameters
|
||||
imports:
|
||||
- sample-vnfd1
|
||||
- sample-vnfd2
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
vl1_name:
|
||||
type: string
|
||||
description: name of VL1 virtuallink
|
||||
default: net_mgmt
|
||||
vl2_name:
|
||||
type: string
|
||||
description: name of VL2 virtuallink
|
||||
default: net0
|
||||
net_src_port_id:
|
||||
type: string
|
||||
description: neutron port id of source port
|
||||
ip_dest_prefix:
|
||||
type: string
|
||||
description: IP prefix of destination port
|
||||
|
||||
node_templates:
|
||||
VNF1:
|
||||
type: tosca.nodes.nfv.VNF1
|
||||
requirements:
|
||||
- virtualLink1: VL1
|
||||
|
||||
VNF2:
|
||||
type: tosca.nodes.nfv.VNF2
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl1_name}
|
||||
vendor: tacker
|
||||
|
||||
VL2:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: {get_input: vl2_name}
|
||||
vendor: tacker
|
||||
|
||||
Forwarding_path1:
|
||||
type: tosca.nodes.nfv.FP.TackerV2
|
||||
description: creates path inside ns (src_port->CP12->CP22->dst_port)
|
||||
properties:
|
||||
id: 51
|
||||
policy:
|
||||
type: ACL
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: {get_input: net_src_port_id}
|
||||
destination_port_range: 80-1024
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: {get_input: ip_dest_prefix}
|
||||
path:
|
||||
- forwarder: sample-vnfd1
|
||||
capability: CP12
|
||||
- forwarder: sample-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: [VL1, VL2]
|
||||
connection_point: [CP12, CP22]
|
||||
constituent_vnfs: [sample-vnfd1, sample-vnfd2]
|
||||
members: [Forwarding_path1]
|
67
samples/tosca-templates/vnffg-nsd/tosca-vnfd1-sample.yaml
Normal file
67
samples/tosca-templates/vnffg-nsd/tosca-vnfd1-sample.yaml
Normal file
@ -0,0 +1,67 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
|
||||
description: VirtualLinks of CP11 and CP22 will be provided by NS descriptor
|
||||
node_types:
|
||||
tosca.nodes.nfv.VNF1:
|
||||
requirements:
|
||||
- virtualLink1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
required: true
|
||||
capabilities:
|
||||
forwader1:
|
||||
type: tosca.capabilities.nfv.Forwarder
|
||||
|
||||
topology_template:
|
||||
substitution_mappings:
|
||||
node_type: tosca.nodes.nfv.VNF1
|
||||
requirements:
|
||||
virtualLink1: [CP11, virtualLink]
|
||||
capabilities:
|
||||
forwarder1: [CP11, forwarder]
|
||||
|
||||
node_templates:
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.VDU.Tacker
|
||||
properties:
|
||||
image: cirros-0.3.5-x86_64-disk
|
||||
flavor: m1.tiny
|
||||
availability_zone: nova
|
||||
mgmt_driver: noop
|
||||
config: |
|
||||
param0: key1
|
||||
param1: key2
|
||||
user_data_format: RAW
|
||||
user_data: |
|
||||
#!/bin/sh
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
CP11:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
management: true
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
CP12:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL2
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net_mgmt
|
||||
vendor: Tacker
|
||||
|
||||
VL2:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net0
|
||||
vendor: Tacker
|
61
samples/tosca-templates/vnffg-nsd/tosca-vnfd2-sample.yaml
Normal file
61
samples/tosca-templates/vnffg-nsd/tosca-vnfd2-sample.yaml
Normal file
@ -0,0 +1,61 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
|
||||
description: Demo example
|
||||
|
||||
node_types:
|
||||
tosca.nodes.nfv.VNF2:
|
||||
capabilities:
|
||||
forwarder1:
|
||||
type: tosca.capabilities.nfv.Forwarder
|
||||
topology_template:
|
||||
substitution_mappings:
|
||||
node_type: tosca.nodes.nfv.VNF2
|
||||
capabilities:
|
||||
forwarder1: [CP21, forwarder]
|
||||
node_templates:
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.VDU.Tacker
|
||||
properties:
|
||||
image: cirros-0.3.5-x86_64-disk
|
||||
flavor: m1.tiny
|
||||
availability_zone: nova
|
||||
mgmt_driver: noop
|
||||
config: |
|
||||
param0: key1
|
||||
param1: key2
|
||||
user_data_format: RAW
|
||||
user_data: |
|
||||
#!/bin/sh
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
|
||||
CP21:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
management: true
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL1
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
CP22:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL2
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net_mgmt
|
||||
vendor: Tacker
|
||||
|
||||
VL2:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net0
|
||||
vendor: Tacker
|
||||
|
@ -16,10 +16,10 @@ topology_template:
|
||||
criteria:
|
||||
- name: block_tcp
|
||||
classifier:
|
||||
network_src_port_id: 640dfd77-c92b-45a3-b8fc-22712de480e1
|
||||
network_src_port_id: 14ad4f29-629f-4b97-8bc8-86e96cb49974
|
||||
destination_port_range: 80-1024
|
||||
ip_proto: 6
|
||||
ip_dst_prefix: 192.168.1.2/24
|
||||
ip_dst_prefix: 10.10.0.5/24
|
||||
path:
|
||||
- forwarder: VNFD1
|
||||
capability: CP12
|
||||
|
@ -0,0 +1,41 @@
|
||||
# Copyright 2018 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 support vnffg to ns database
|
||||
|
||||
Revision ID: 4747cc26b9c6
|
||||
Revises: 5d490546290c
|
||||
Create Date: 2018-06-27 03:18:12.227673
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4747cc26b9c6'
|
||||
down_revision = '5d490546290c'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from tacker.db import types
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.add_column('ns', sa.Column(
|
||||
'vnffg_ids', sa.TEXT(length=65535), nullable=True))
|
||||
op.add_column('vnffgs', sa.Column(
|
||||
'ns_id', types.Uuid(length=36), nullable=True))
|
||||
op.create_foreign_key('vnffg_foreign_key',
|
||||
'vnffgs', 'ns',
|
||||
['ns_id'], ['id'])
|
@ -1 +1 @@
|
||||
5d490546290c
|
||||
4747cc26b9c6
|
@ -98,6 +98,9 @@ class NS(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
|
||||
# Dict of VNF details that network service launches
|
||||
vnf_ids = sa.Column(sa.TEXT(65535), nullable=True)
|
||||
|
||||
# VNFFG ids
|
||||
vnffg_ids = sa.Column(sa.TEXT(65535), nullable=True)
|
||||
|
||||
# Dict of mgmt urls that network servic launches
|
||||
mgmt_urls = sa.Column(sa.TEXT(65535), nullable=True)
|
||||
|
||||
@ -161,8 +164,8 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
LOG.debug('ns_db %s', ns_db)
|
||||
res = {}
|
||||
key_list = ('id', 'tenant_id', 'nsd_id', 'name', 'description',
|
||||
'vnf_ids', 'status', 'mgmt_urls', 'error_reason',
|
||||
'vim_id', 'created_at', 'updated_at')
|
||||
'vnf_ids', 'vnffg_ids', 'status', 'mgmt_urls',
|
||||
'error_reason', 'vim_id', 'created_at', 'updated_at')
|
||||
res.update((key, ns_db[key]) for key in key_list)
|
||||
return self._fields(res, fields)
|
||||
|
||||
@ -209,18 +212,14 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
tstamp=nsd_dict[constants.RES_EVT_CREATED_FLD])
|
||||
return nsd_dict
|
||||
|
||||
def delete_nsd(self,
|
||||
context,
|
||||
nsd_id,
|
||||
soft_delete=True):
|
||||
def delete_nsd(self, context, nsd_id, soft_delete=True):
|
||||
with context.session.begin(subtransactions=True):
|
||||
nss_db = context.session.query(NS).filter_by(
|
||||
nsd_id=nsd_id).first()
|
||||
if nss_db is not None and nss_db.deleted_at is None:
|
||||
raise nfvo.NSDInUse(nsd_id=nsd_id)
|
||||
|
||||
nsd_db = self._get_resource(context, NSD,
|
||||
nsd_id)
|
||||
nsd_db = self._get_resource(context, NSD, nsd_id)
|
||||
if soft_delete:
|
||||
nsd_db.update({'deleted_at': timeutils.utcnow()})
|
||||
self._cos_db_plg.create_event(
|
||||
@ -254,7 +253,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 = ns['ns_id']
|
||||
description = None
|
||||
if 'description' in ns:
|
||||
description = ns.get('description')
|
||||
@ -269,6 +268,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
name=name,
|
||||
description=description,
|
||||
vnf_ids=None,
|
||||
vnffg_ids=None,
|
||||
status=constants.PENDING_CREATE,
|
||||
mgmt_urls=None,
|
||||
nsd_id=nsd_id,
|
||||
@ -291,11 +291,12 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
return self._make_ns_dict(ns_db)
|
||||
|
||||
def create_ns_post(self, context, ns_id, mistral_obj,
|
||||
vnfd_dict, error_reason):
|
||||
vnfd_dict, vnffgd_templates, error_reason):
|
||||
LOG.debug('ns ID %s', ns_id)
|
||||
output = ast.literal_eval(mistral_obj.output)
|
||||
mgmt_urls = dict()
|
||||
vnf_ids = dict()
|
||||
vnffg_ids = dict()
|
||||
if len(output) > 0:
|
||||
for vnfd_name, vnfd_val in iteritems(vnfd_dict):
|
||||
for instance in vnfd_val['instances']:
|
||||
@ -305,28 +306,37 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
vnf_ids[instance] = output['vnf_id_' + instance]
|
||||
vnf_ids = str(vnf_ids)
|
||||
mgmt_urls = str(mgmt_urls)
|
||||
if vnffgd_templates:
|
||||
for vnffg_name in vnffgd_templates:
|
||||
vnffg_output = 'vnffg_id_%s' % vnffg_name
|
||||
vnffg_ids[vnffg_name] = output[vnffg_output]
|
||||
vnffg_ids = str(vnffg_ids)
|
||||
|
||||
if not vnf_ids:
|
||||
vnf_ids = None
|
||||
if not mgmt_urls:
|
||||
mgmt_urls = None
|
||||
if not vnffg_ids:
|
||||
vnffg_ids = None
|
||||
status = constants.ACTIVE if mistral_obj.state == 'SUCCESS' \
|
||||
else constants.ERROR
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
ns_db = self._get_resource(context, NS,
|
||||
ns_id)
|
||||
ns_db = self._get_resource(context, NS, ns_id)
|
||||
ns_db.update({'vnf_ids': vnf_ids})
|
||||
ns_db.update({'vnffg_ids': vnffg_ids})
|
||||
ns_db.update({'mgmt_urls': mgmt_urls})
|
||||
ns_db.update({'status': status})
|
||||
ns_db.update({'error_reason': error_reason})
|
||||
ns_db.update({'updated_at': timeutils.utcnow()})
|
||||
ns_dict = self._make_ns_dict(ns_db)
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=ns_dict['id'],
|
||||
res_type=constants.RES_TYPE_NS,
|
||||
res_state=constants.RES_EVT_NA_STATE,
|
||||
evt_type=constants.RES_EVT_UPDATE,
|
||||
tstamp=ns_dict[constants.RES_EVT_UPDATED_FLD])
|
||||
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=ns_dict['id'],
|
||||
res_type=constants.RES_TYPE_NS,
|
||||
res_state=constants.RES_EVT_NA_STATE,
|
||||
evt_type=constants.RES_EVT_UPDATE,
|
||||
tstamp=ns_dict[constants.RES_EVT_UPDATED_FLD])
|
||||
return ns_dict
|
||||
|
||||
# reference implementation. needs to be overrided by subclass
|
||||
|
@ -24,6 +24,7 @@ from sqlalchemy.orm import exc as orm_exc
|
||||
from tacker.db import db_base
|
||||
from tacker.db import model_base
|
||||
from tacker.db import models_v1
|
||||
from tacker.db.nfvo.ns_db import NS
|
||||
from tacker.db import types
|
||||
from tacker.extensions import nfvo
|
||||
from tacker.extensions.nfvo_plugins import vnffg
|
||||
@ -97,6 +98,9 @@ class Vnffg(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
|
||||
|
||||
attributes = sa.Column(types.Json)
|
||||
|
||||
# Associated Network Service
|
||||
ns_id = sa.Column(types.Uuid, sa.ForeignKey('ns.id'), nullable=True)
|
||||
|
||||
|
||||
class VnffgNfp(model_base.BASE, models_v1.HasTenant, models_v1.HasId):
|
||||
"""Network Forwarding Path Data Model"""
|
||||
@ -332,7 +336,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
|
||||
param_value=param_vattrs_dict)
|
||||
for param_key in param_vattrs_dict.keys():
|
||||
if param_matched.get(param_key) is None:
|
||||
raise nfvo.VnffgParamValueNotUsed(param_key=param_key)
|
||||
LOG.warning("Param input %s not used.", param_key)
|
||||
|
||||
def _parametrize_topology_template(self, vnffg, template_db):
|
||||
if vnffg.get('attributes') and \
|
||||
@ -353,6 +357,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
|
||||
name = vnffg.get('name')
|
||||
vnffg_id = vnffg.get('id') or uuidutils.generate_uuid()
|
||||
template_id = vnffg['vnffgd_id']
|
||||
ns_id = vnffg.get('ns_id', None)
|
||||
symmetrical = vnffg['symmetrical']
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
@ -377,6 +382,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
|
||||
description=template_db.description,
|
||||
vnf_mapping=vnf_mapping,
|
||||
vnffgd_id=template_id,
|
||||
ns_id=ns_id,
|
||||
attributes=template_db.get('template'),
|
||||
status=constants.PENDING_CREATE)
|
||||
context.session.add(vnffg_db)
|
||||
@ -835,7 +841,7 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
|
||||
res = {
|
||||
'forwarding_paths': vnffg_db.forwarding_paths[0]['id']
|
||||
}
|
||||
key_list = ('id', 'tenant_id', 'name', 'description',
|
||||
key_list = ('id', 'tenant_id', 'name', 'description', 'ns_id',
|
||||
'vnf_mapping', 'status', 'vnffgd_id', 'attributes')
|
||||
res.update((key, vnffg_db[key]) for key in key_list)
|
||||
return self._fields(res, fields)
|
||||
@ -1240,6 +1246,14 @@ class VnffgPluginDbMixin(vnffg.VNFFGPluginBase, db_base.CommonDbMixin):
|
||||
|
||||
def _delete_vnffg_pre(self, context, vnffg_id):
|
||||
vnffg = self.get_vnffg(context, vnffg_id)
|
||||
ns_id = vnffg.get('ns_id')
|
||||
if ns_id:
|
||||
ns_db = self._get_resource(context, NS, ns_id)
|
||||
# If network service is not in pending_delete status,
|
||||
# raise error when delete vnffg.
|
||||
if ns_db['status'] != constants.PENDING_DELETE:
|
||||
raise nfvo.VnffgInUseNS(vnffg_id=vnffg_id,
|
||||
ns_id=vnffg.get('ns_id'))
|
||||
nfp = self.get_nfp(context, vnffg['forwarding_paths'])
|
||||
chain = self.get_sfc(context, nfp['chain_id'])
|
||||
classifiers = [self.get_classifier(context, classifier_id)
|
||||
|
@ -153,10 +153,6 @@ class VnffgTemplateParamParsingException(exceptions.TackerException):
|
||||
"missing input param %(get_input)s.")
|
||||
|
||||
|
||||
class VnffgParamValueNotUsed(exceptions.TackerException):
|
||||
message = _("Param input %(param_key)s not used.")
|
||||
|
||||
|
||||
class VnffgPropertyNotFoundException(exceptions.NotFound):
|
||||
message = _('VNFFG Property %(vnffg_property)s could not be found')
|
||||
|
||||
@ -183,6 +179,11 @@ class VnffgDeleteFailed(exceptions.TackerException):
|
||||
message = _('Deleting VNFFG %(vnffg_id)s failed')
|
||||
|
||||
|
||||
class VnffgInUseNS(exceptions.TackerException):
|
||||
message = _('VNFFG %(vnffg_id)s belongs to active network service '
|
||||
'%(ns_id)s')
|
||||
|
||||
|
||||
class NfpAttributeNotFoundException(exceptions.NotFound):
|
||||
message = _('NFP attribute %(attribute)s could not be found')
|
||||
|
||||
@ -485,6 +486,12 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'is_visible': True,
|
||||
'default': None,
|
||||
},
|
||||
'ns_id': {
|
||||
'allow_post': True,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
'default': None,
|
||||
},
|
||||
},
|
||||
|
||||
'nfps': {
|
||||
@ -745,6 +752,13 @@ RESOURCE_ATTRIBUTE_MAP = {
|
||||
'is_visible': True,
|
||||
'default': '',
|
||||
},
|
||||
'vnffg_ids': {
|
||||
'allow_post': True,
|
||||
'allow_put': False,
|
||||
'validate': {'type:string': None},
|
||||
'is_visible': True,
|
||||
'default': '',
|
||||
},
|
||||
'nsd_id': {
|
||||
'allow_post': True,
|
||||
'allow_put': False,
|
||||
|
@ -11,13 +11,13 @@
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.mistral import workflow_generator
|
||||
|
||||
|
||||
OUTPUT = {
|
||||
'create_vnf': ['vnf_id', 'vim_id', 'mgmt_url', 'status']
|
||||
'create_vnf': ['vnf_id', 'vim_id', 'mgmt_url', 'status'],
|
||||
'create_vnffg': ['vnffg_id'],
|
||||
}
|
||||
|
||||
|
||||
@ -31,9 +31,9 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
for node in nodes:
|
||||
task = self.wf_name + '_' + node
|
||||
task_dict[task] = {
|
||||
'action': 'tacker.create_vnf body=<% $.vnf.{0} '
|
||||
'action': 'tacker.create_vnf body=<% $.ns.{0} '
|
||||
'%>'.format(node),
|
||||
'input': {'body': '<% $.vnf.{0} %>'.format(node)},
|
||||
'input': {'body': '<% $.ns.{0} %>'.format(node)},
|
||||
'publish': {
|
||||
'vnf_id_' + node: '<% task({0}).result.vnf.id '
|
||||
'%>'.format(task),
|
||||
@ -79,9 +79,20 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
'"ERROR" %>'.format(node)}
|
||||
]
|
||||
}
|
||||
vnffgd_templates = ns.get('vnffgd_templates')
|
||||
if vnffgd_templates:
|
||||
for vnffg_name in vnffgd_templates:
|
||||
vnffg_group = vnffgd_templates[vnffg_name][
|
||||
'topology_template']['groups'][vnffg_name]
|
||||
constituent_vnfs = vnffg_group[
|
||||
'properties']['constituent_vnfs']
|
||||
|
||||
if vnfd_name in constituent_vnfs:
|
||||
task_dict[task]['on-success'].append(
|
||||
'create_vnffg_%s' % vnffg_name)
|
||||
return task_dict
|
||||
|
||||
def _add_delete_vnf_tasks(self, ns):
|
||||
def _add_delete_vnf_tasks(self, ns, vnffg_ids=None):
|
||||
vnfds = ns['vnfd_details']
|
||||
task_dict = dict()
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
@ -92,6 +103,55 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_{0}'
|
||||
'%>'.format(node),
|
||||
}
|
||||
if vnffg_ids and len(vnffg_ids):
|
||||
task_dict[task].update({'join': 'all'})
|
||||
return task_dict
|
||||
|
||||
def _add_create_vnffg_task(self, vnffgd_templates):
|
||||
task_dict = dict()
|
||||
previous_task = None
|
||||
for vnffg_name in vnffgd_templates:
|
||||
task = 'create_vnffg_%s' % vnffg_name
|
||||
vnffg_output = 'vnffg_id_%s' % vnffg_name
|
||||
task_dict[task] = {
|
||||
'join': 'all',
|
||||
'action': 'tacker.create_vnffg body=<% $.ns.{0} '
|
||||
'%>'.format(vnffg_name),
|
||||
'input': {'body': '<% $.ns.{0} %>'.format(vnffg_name)},
|
||||
'publish': {
|
||||
vnffg_output: '<% task({0}).result.'
|
||||
'vnffg.id %>'.format(task)}
|
||||
}
|
||||
if previous_task:
|
||||
task_dict[previous_task].update({'on-success': [task]})
|
||||
previous_task = task
|
||||
return task_dict
|
||||
|
||||
def _add_delete_vnffg_task(self, ns):
|
||||
task_dict = dict()
|
||||
vnfds = ns['vnfd_details']
|
||||
vnffg_ids = ns['vnffg_details']
|
||||
delayed_tasks = list()
|
||||
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
nodes = vnfd_info['instances']
|
||||
for node in nodes:
|
||||
wait_task = 'delete_vnf_%s' % node
|
||||
delayed_tasks.append(wait_task)
|
||||
|
||||
previous_task = None
|
||||
for vnffg_name in vnffg_ids.keys():
|
||||
task = 'delete_vnffg_%s' % vnffg_name
|
||||
if previous_task:
|
||||
wait_tasks = delayed_tasks + [previous_task]
|
||||
else:
|
||||
wait_tasks = delayed_tasks
|
||||
previous_task = task
|
||||
task_dict[task] = {
|
||||
'action': 'tacker.delete_vnffg vnffg=<% $.{0} '
|
||||
'%>'.format(vnffg_name),
|
||||
'on-success': wait_tasks
|
||||
}
|
||||
return task_dict
|
||||
|
||||
def _build_output_dict(self, ns):
|
||||
@ -100,9 +160,16 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
nodes = vnfd_info['instances']
|
||||
for node in nodes:
|
||||
for op_name in OUTPUT[self.wf_name]:
|
||||
for op_name in OUTPUT['create_vnf']:
|
||||
task_dict[op_name + '_' + node] = \
|
||||
'<% $.{0}_{1} %>'.format(op_name, node)
|
||||
vnffgd_templates = ns.get('vnffgd_templates')
|
||||
if vnffgd_templates:
|
||||
for vnffg_name in vnffgd_templates:
|
||||
for op_name in OUTPUT['create_vnffg']:
|
||||
vnffg_output = '%s_%s' % (op_name, vnffg_name)
|
||||
task_dict[vnffg_output] = \
|
||||
'<% $.{0}_{1} %>'.format(op_name, vnffg_name)
|
||||
return task_dict
|
||||
|
||||
def get_input_dict(self):
|
||||
@ -110,24 +177,60 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
|
||||
def build_input(self, ns, params):
|
||||
vnfds = ns['vnfd_details']
|
||||
id = uuidutils.generate_uuid()
|
||||
self.input_dict = {'vnf': {}}
|
||||
ns_id = ns['ns'].get('ns_id')
|
||||
ns_name = ns['ns'].get('name')
|
||||
self.input_dict = {'ns': {}}
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
nodes = vnfd_info['instances']
|
||||
for node in nodes:
|
||||
self.input_dict['vnf'][node] = dict()
|
||||
self.input_dict['vnf'][node]['vnf'] = {
|
||||
vnf_name = '%s_VNF_%s' % (ns_name, vnfd_info['id'])
|
||||
self.input_dict['ns'][node] = dict()
|
||||
self.input_dict['ns'][node]['vnf'] = {
|
||||
'attributes': {},
|
||||
'vim_id': ns['ns'].get('vim_id', ''),
|
||||
'vnfd_id': vnfd_info['id'],
|
||||
'name': 'create_vnf_%s_%s' % (vnfd_info['id'], id)
|
||||
'name': vnf_name
|
||||
}
|
||||
if params.get(vnfd_name):
|
||||
self.input_dict['vnf'][node]['vnf']['attributes'] = {
|
||||
self.input_dict['ns'][node]['vnf']['attributes'] = {
|
||||
'param_values': params.get(vnfd_name)
|
||||
}
|
||||
if ns.get('vnffgd_templates'):
|
||||
vnffg_input = self.build_vnffg_input(ns, params, ns_id)
|
||||
self.input_dict['ns'].update(vnffg_input)
|
||||
|
||||
def create_vnf(self, **kwargs):
|
||||
def build_vnffg_input(self, ns, params, ns_id):
|
||||
vnffgd_templates = ns.get('vnffgd_templates')
|
||||
vnffg_input = dict()
|
||||
for vnffg_name in vnffgd_templates:
|
||||
vnffg_group = vnffgd_templates[vnffg_name][
|
||||
'topology_template']['groups'][vnffg_name]
|
||||
constituent_vnfs = vnffg_group[
|
||||
'properties']['constituent_vnfs']
|
||||
vnf_mapping = self.get_vnf_mapping(ns, constituent_vnfs)
|
||||
vnffgd_body = dict()
|
||||
vnffgd_body['vnffg'] = {
|
||||
'name': '%s_%s_%s' % (ns['ns'].get('name'), vnffg_name, ns_id),
|
||||
'vnffgd_template': vnffgd_templates[vnffg_name],
|
||||
'vnf_mapping': vnf_mapping,
|
||||
'attributes': {
|
||||
'param_values': params.get('nsd')},
|
||||
'ns_id': ns_id
|
||||
}
|
||||
vnffg_input[vnffg_name] = vnffgd_body
|
||||
return vnffg_input
|
||||
|
||||
def get_vnf_mapping(self, ns, constituent_vnfs):
|
||||
vnfds = ns['vnfd_details']
|
||||
vnf_mapping = dict()
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
if vnfd_name in constituent_vnfs:
|
||||
vnf_name = '%s_VNF_%s' % (ns['ns'].get('name'),
|
||||
vnfd_info['id'])
|
||||
vnf_mapping[vnfd_name] = vnf_name
|
||||
return vnf_mapping
|
||||
|
||||
def create_ns(self, **kwargs):
|
||||
ns = kwargs.get('ns')
|
||||
params = kwargs.get('params')
|
||||
# TODO(anyone): Keep this statements in a loop and
|
||||
@ -142,8 +245,12 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
self.definition[self.wf_identifier]['output'] = \
|
||||
self._build_output_dict(ns)
|
||||
self.build_input(ns, params)
|
||||
vnffgd_templates = ns.get('vnffgd_templates')
|
||||
if vnffgd_templates:
|
||||
self.definition[self.wf_identifier]['tasks'].update(
|
||||
self._add_create_vnffg_task(vnffgd_templates))
|
||||
|
||||
def delete_vnf(self, ns):
|
||||
def delete_ns(self, ns):
|
||||
ns_dict = {'vnfd_details': {}}
|
||||
vnf_ids = ast.literal_eval(ns['vnf_ids'])
|
||||
self.definition[self.wf_identifier]['input'] = []
|
||||
@ -153,5 +260,14 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
self.input_dict[vnf_key] = vnf_ids[vnf]
|
||||
ns_dict['vnfd_details'][vnf] = {'instances': [vnf]}
|
||||
self.definition[self.wf_identifier]['tasks'] = dict()
|
||||
|
||||
vnffg_ids = ast.literal_eval(ns.get('vnffg_ids'))
|
||||
if len(vnffg_ids):
|
||||
for vnffg_name in vnffg_ids.keys():
|
||||
self.definition[self.wf_identifier]['input'].append(vnffg_name)
|
||||
self.input_dict[vnffg_name] = vnffg_ids[vnffg_name]
|
||||
ns_dict['vnffg_details'] = vnffg_ids
|
||||
self.definition[self.wf_identifier]['tasks'].update(
|
||||
self._add_delete_vnffg_task(ns_dict))
|
||||
self.definition[self.wf_identifier]['tasks'].update(
|
||||
self._add_delete_vnf_tasks(ns_dict))
|
||||
self._add_delete_vnf_tasks(ns_dict, vnffg_ids))
|
||||
|
@ -663,16 +663,41 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
if vnfd_name == vnfd['name']:
|
||||
return vnfd['id']
|
||||
|
||||
@log.log
|
||||
def _get_vnffgds_from_nsd(self, nsd_dict):
|
||||
ns_topo = nsd_dict.get('topology_template')
|
||||
vnffgd_templates = dict()
|
||||
if ns_topo and ns_topo.get('groups'):
|
||||
for vnffg_name in ns_topo.get('groups'):
|
||||
vnffgd_template = dict()
|
||||
# TODO(phuoc): add checking in case vnffg_name exists
|
||||
# more than one time.
|
||||
# Constructing vnffgd from nsd, remove imports section
|
||||
vnffgd_template['tosca_definitions_version'] = \
|
||||
nsd_dict.get('tosca_definitions_version')
|
||||
vnffgd_template['description'] = nsd_dict.get('description')
|
||||
vnffgd_template['topology_template'] = dict()
|
||||
vnffgd_template['topology_template']['groups'] = dict()
|
||||
vnffgd_template['topology_template']['groups'][vnffg_name] = \
|
||||
ns_topo['groups'].get(vnffg_name)
|
||||
vnffgd_template['topology_template']['node_templates'] = dict()
|
||||
for fp_name in ns_topo['groups'][vnffg_name]['members']:
|
||||
vnffgd_template['topology_template']['node_templates'][
|
||||
fp_name] = ns_topo['node_templates'].get(fp_name)
|
||||
vnffgd_templates[vnffg_name] = vnffgd_template
|
||||
return vnffgd_templates
|
||||
|
||||
@log.log
|
||||
def create_ns(self, context, ns):
|
||||
"""Create NS and corresponding VNFs.
|
||||
"""Create NS, corresponding VNFs, VNFFGs.
|
||||
|
||||
:param ns: ns dict which contains nsd_id and attributes
|
||||
This method has 3 steps:
|
||||
step-1: substitute all get_input params to its corresponding values
|
||||
step-2: Build params dict for substitution mappings case through which
|
||||
VNFs will actually substitute their requirements.
|
||||
step-3: Create mistral workflow and execute the workflow
|
||||
step-3: Create mistral workflow to create VNFs, VNFFG and execute the
|
||||
workflow
|
||||
"""
|
||||
ns_info = ns['ns']
|
||||
name = ns_info['name']
|
||||
@ -699,6 +724,13 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
if not ns['ns']['vim_id']:
|
||||
ns['ns']['vim_id'] = vim_res['vim_id']
|
||||
|
||||
# TODO(phuoc): currently, create_ns function does not have
|
||||
# create_ns_pre function, that pre-defines information of a network
|
||||
# service. Creating ns_uuid keeps ns_id for consistency, it should be
|
||||
# provided as return value of create_ns_pre function in ns db.
|
||||
# Generate ns_uuid
|
||||
ns['ns']['ns_id'] = uuidutils.generate_uuid()
|
||||
|
||||
# Step-1
|
||||
param_values = ns['ns']['attributes'].get('param_values', {})
|
||||
if 'get_input' in str(nsd_dict):
|
||||
@ -740,6 +772,11 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
param_values[vnfd_name]['substitution_mappings'][
|
||||
'requirements'] = req_dict
|
||||
ns['vnfd_details'] = vnfd_dict
|
||||
|
||||
vnffgd_templates = self._get_vnffgds_from_nsd(nsd_dict)
|
||||
LOG.debug('vnffgd_templates: %s', vnffgd_templates)
|
||||
ns['vnffgd_templates'] = vnffgd_templates
|
||||
|
||||
# Step-3
|
||||
kwargs = {'ns': ns, 'params': param_values}
|
||||
|
||||
@ -747,7 +784,7 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
workflow = self._vim_drivers.invoke(
|
||||
driver_type,
|
||||
'prepare_and_create_workflow',
|
||||
resource='vnf',
|
||||
resource='ns',
|
||||
action='create',
|
||||
auth_dict=self.get_auth_dict(context),
|
||||
kwargs=kwargs)
|
||||
@ -780,6 +817,8 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
if exec_state == 'SUCCESS' or exec_state == 'ERROR':
|
||||
break
|
||||
mistral_retries = mistral_retries - 1
|
||||
# TODO(phuoc): add more information about error reason in case
|
||||
# of exec_state is 'ERROR'
|
||||
error_reason = None
|
||||
if mistral_retries == 0 and exec_state == 'RUNNING':
|
||||
error_reason = _(
|
||||
@ -801,8 +840,9 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
'delete_workflow',
|
||||
workflow_id=workflow['id'],
|
||||
auth_dict=self.get_auth_dict(context))
|
||||
super(NfvoPlugin, self).create_ns_post(context, ns_id, exec_obj,
|
||||
vnfd_dict, error_reason)
|
||||
super(NfvoPlugin, self).create_ns_post(
|
||||
context, ns_id, exec_obj, vnfd_dict,
|
||||
vnffgd_templates, error_reason)
|
||||
|
||||
self.spawn_n(_create_ns_wait, self, ns_dict['id'],
|
||||
mistral_execution.id)
|
||||
@ -837,6 +877,7 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
@log.log
|
||||
def delete_ns(self, context, ns_id):
|
||||
ns = super(NfvoPlugin, self).get_ns(context, ns_id)
|
||||
LOG.debug("Deleting ns: %s", ns)
|
||||
vim_res = self.vim_client.get_vim(context, ns['vim_id'])
|
||||
super(NfvoPlugin, self).delete_ns_pre(context, ns_id)
|
||||
driver_type = vim_res['vim_type']
|
||||
@ -846,11 +887,10 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
workflow = self._vim_drivers.invoke(
|
||||
driver_type,
|
||||
'prepare_and_create_workflow',
|
||||
resource='vnf',
|
||||
resource='ns',
|
||||
action='delete',
|
||||
auth_dict=self.get_auth_dict(context),
|
||||
kwargs={
|
||||
'ns': ns})
|
||||
kwargs={'ns': ns})
|
||||
except nfvo.NoTasksException:
|
||||
LOG.warning("No VNF deletion task(s).")
|
||||
if workflow:
|
||||
@ -884,6 +924,8 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
if exec_state == 'SUCCESS' or exec_state == 'ERROR':
|
||||
break
|
||||
mistral_retries -= 1
|
||||
# TODO(phuoc): add more information about error reason in case
|
||||
# of exec_state is 'ERROR'
|
||||
error_reason = None
|
||||
if mistral_retries == 0 and exec_state == 'RUNNING':
|
||||
error_reason = _(
|
||||
|
@ -31,6 +31,71 @@ def get_dummy_ns():
|
||||
'placement_attr': {}}
|
||||
|
||||
|
||||
def get_dummy_vnffg_ns():
|
||||
return {
|
||||
u'ns': {
|
||||
'description': '',
|
||||
'vim_id': u'96025dd5-ca16-49f3-9823-958eb04260c4',
|
||||
'vnf_ids': '', u'attributes': {},
|
||||
u'nsd_id': u'b8587afb-6099-4f56-abce-572c62e3d61d',
|
||||
u'name': u'test_create_ns'},
|
||||
'vnfd_details': {
|
||||
u'vnf1': {'instances': ['VNF1'],
|
||||
'id': u'dec09ed4-f355-4ec8-a00b-8548f6575a80'},
|
||||
u'vnf2': {'instances': ['VNF2'],
|
||||
'id': u'9f8f2af7-6407-4f79-a6fe-302c56172231'}},
|
||||
'placement_attr': {},
|
||||
'vnffgd_templates': {
|
||||
'VNFFG1': {
|
||||
'tosca_definitions_version':
|
||||
'tosca_simple_profile_for_nfv_1_0_0',
|
||||
'description': 'VNFFG1 descriptor',
|
||||
'topology_template': {
|
||||
'node_templates': {
|
||||
'Forwarding_path1': {
|
||||
'type': 'tosca.nodes.nfv.FP.TackerV2',
|
||||
'description': 'creates path inside ns - test',
|
||||
'properties': {
|
||||
'policy': {
|
||||
'type': 'ACL',
|
||||
'criteria': [{
|
||||
'classifier': {
|
||||
'ip_proto': 6,
|
||||
'network_src_port_id': {
|
||||
'get_input': 'net_src_port_id'
|
||||
},
|
||||
'ip_dst_prefix': {
|
||||
'get_input': 'ip_dest_prefix'
|
||||
},
|
||||
'destination_port_range': '80-1024'
|
||||
},
|
||||
'name': 'block_tcp'}]},
|
||||
'path': [
|
||||
{'capability': 'CP12',
|
||||
'forwarder': 'vnf1'},
|
||||
{'capability': 'CP22',
|
||||
'forwarder': 'vnf2'}],
|
||||
'id': 51}}},
|
||||
'groups': {
|
||||
'VNFFG1': {
|
||||
'type': 'tosca.groups.nfv.VNFFG',
|
||||
'description': 'HTTP to Corporate Net',
|
||||
'members': ['Forwarding_path1'],
|
||||
'properties': {
|
||||
'version': 1.0,
|
||||
'vendor': 'tacker',
|
||||
'constituent_vnfs': ['vnf1', 'vnf2'],
|
||||
'connection_point': ['CP12', 'CP22'],
|
||||
'number_of_endpoints': 2,
|
||||
'dependent_virtual_link': ['VL1', 'VL2']}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_dummy_param():
|
||||
return {u'vnf1': {'substitution_mappings': {u'VL1b8587afb-60': {
|
||||
'type': 'tosca.nodes.nfv.VL', 'properties': {
|
||||
@ -45,7 +110,7 @@ def get_dummy_param():
|
||||
|
||||
|
||||
def get_dummy_create_workflow():
|
||||
return {'std.create_vnf_dummy': {'input': ['vnf'],
|
||||
return {'std.create_ns_dummy': {'input': ['ns'],
|
||||
'tasks': {
|
||||
'wait_vnf_active_VNF2': {
|
||||
'action': 'tacker.show_vnf vnf=<% $.vnf_id_VNF2 %>',
|
||||
@ -61,30 +126,30 @@ def get_dummy_create_workflow():
|
||||
'on-success': [{
|
||||
'delete_vnf_VNF2': '<% $.status_VNF2='
|
||||
'"ERROR" %>'}]},
|
||||
'create_vnf_VNF2': {
|
||||
'action': 'tacker.create_vnf body=<% $.vnf.VNF2 %>',
|
||||
'input': {'body': '<% $.vnf.VNF2 %>'},
|
||||
'create_ns_VNF2': {
|
||||
'action': 'tacker.create_vnf body=<% $.ns.VNF2 %>',
|
||||
'input': {'body': '<% $.ns.VNF2 %>'},
|
||||
'publish': {
|
||||
'status_VNF2': '<% task(create_vnf_VNF2).'
|
||||
'status_VNF2': '<% task(create_ns_VNF2).'
|
||||
'result.vnf.status %>',
|
||||
'vim_id_VNF2': '<% task(create_vnf_VNF2).'
|
||||
'vim_id_VNF2': '<% task(create_ns_VNF2).'
|
||||
'result.vnf.vim_id %>',
|
||||
'mgmt_url_VNF2': '<% task(create_vnf_VNF2).'
|
||||
'mgmt_url_VNF2': '<% task(create_ns_VNF2).'
|
||||
'result.vnf.mgmt_url %>',
|
||||
'vnf_id_VNF2': '<% task(create_vnf_VNF2)'
|
||||
'vnf_id_VNF2': '<% task(create_ns_VNF2)'
|
||||
'.result.vnf.id %>'},
|
||||
'on-success': ['wait_vnf_active_VNF2']},
|
||||
'create_vnf_VNF1': {
|
||||
'action': 'tacker.create_vnf body=<% $.vnf.VNF1 %>',
|
||||
'input': {'body': '<% $.vnf.VNF1 %>'},
|
||||
'create_ns_VNF1': {
|
||||
'action': 'tacker.create_vnf body=<% $.ns.VNF1 %>',
|
||||
'input': {'body': '<% $.ns.VNF1 %>'},
|
||||
'publish': {
|
||||
'status_VNF1': '<% task(create_vnf_VNF1).'
|
||||
'status_VNF1': '<% task(create_ns_VNF1).'
|
||||
'result.vnf.status %>',
|
||||
'vnf_id_VNF1': '<% task(create_vnf_VNF1).'
|
||||
'vnf_id_VNF1': '<% task(create_ns_VNF1).'
|
||||
'result.vnf.id %>',
|
||||
'mgmt_url_VNF1': '<% task(create_vnf_VNF1).'
|
||||
'mgmt_url_VNF1': '<% task(create_ns_VNF1).'
|
||||
'result.vnf.mgmt_url %>',
|
||||
'vim_id_VNF1': '<% task(create_vnf_VNF1).'
|
||||
'vim_id_VNF1': '<% task(create_ns_VNF1).'
|
||||
'result.vnf.vim_id %>'},
|
||||
'on-success': ['wait_vnf_active_VNF1']},
|
||||
'wait_vnf_active_VNF1': {
|
||||
@ -100,10 +165,10 @@ def get_dummy_create_workflow():
|
||||
'result.vnf.mgmt_url %>'},
|
||||
'on-success': [{'delete_vnf_VNF1': '<% $.status_VNF1='
|
||||
'"ERROR" %>'}]},
|
||||
'delete_vnf_VNF1': {'action': 'tacker.delete_vnf vnf=<% '
|
||||
'$.vnf_id_VNF1%>'},
|
||||
'delete_vnf_VNF2': {'action': 'tacker.delete_vnf vnf=<% '
|
||||
'$.vnf_id_VNF2%>'}},
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'delete_vnf_VNF2': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>'}},
|
||||
'type': 'direct', 'output': {
|
||||
'status_VNF1': '<% $.status_VNF1 %>',
|
||||
'status_VNF2': '<% $.status_VNF2 %>',
|
||||
@ -116,15 +181,132 @@ def get_dummy_create_workflow():
|
||||
'version': '2.0'}
|
||||
|
||||
|
||||
def get_dummy_create_vnffg_ns_workflow():
|
||||
return {
|
||||
'std.create_ns_dummy': {
|
||||
'input': ['ns'],
|
||||
'tasks': {
|
||||
'wait_vnf_active_VNF2': {
|
||||
'action': 'tacker.show_vnf vnf=<% $.vnf_id_VNF2 %>',
|
||||
'retry': {
|
||||
'count': 10,
|
||||
'delay': 10,
|
||||
'continue-on':
|
||||
'<% $.status_VNF2 = "PENDING_CREATE" %>',
|
||||
'break-on':
|
||||
'<% $.status_VNF2 = "ERROR" %>'},
|
||||
'publish': {
|
||||
'status_VNF2':
|
||||
'<% task(wait_vnf_active_VNF2).result.'
|
||||
'vnf.status %>',
|
||||
'mgmt_url_VNF2':
|
||||
' <% task(wait_vnf_active_VNF2).result.'
|
||||
'vnf.mgmt_url %>'},
|
||||
'on-success': [
|
||||
{'delete_vnf_VNF2': '<% $.status_VNF2="ERROR" %>'},
|
||||
'create_vnffg_VNFFG1']},
|
||||
'create_vnffg_VNFFG1': {
|
||||
'action': 'tacker.create_vnffg body=<% $.ns.VNFFG1 %>',
|
||||
'input': {'body': '<% $.ns.VNFFG1 %>'},
|
||||
'join': 'all',
|
||||
'publish': {
|
||||
'vnffg_id_VNFFG1': '<% task(create_vnffg_VNFFG1).'
|
||||
'result.vnffg.id %>'}},
|
||||
'wait_vnf_active_VNF1': {
|
||||
'action': 'tacker.show_vnf vnf=<% $.vnf_id_VNF1 %>',
|
||||
'retry': {
|
||||
'count': 10,
|
||||
'delay': 10,
|
||||
'continue-on':
|
||||
'<% $.status_VNF1 = "PENDING_CREATE" %>',
|
||||
'break-on':
|
||||
'<% $.status_VNF1 = "ERROR" %>'},
|
||||
'publish': {
|
||||
'status_VNF1':
|
||||
'<% task(wait_vnf_active_VNF1).result.'
|
||||
'vnf.status %>',
|
||||
'mgmt_url_VNF1':
|
||||
' <% task(wait_vnf_active_VNF1).result.'
|
||||
'vnf.mgmt_url %>'},
|
||||
'on-success': [
|
||||
{'delete_vnf_VNF1': '<% $.status_VNF1="ERROR" %>'},
|
||||
'create_vnffg_VNFFG1']},
|
||||
'create_ns_VNF1': {
|
||||
'action': 'tacker.create_vnf body=<% $.ns.VNF1 %>',
|
||||
'input': {'body': '<% $.ns.VNF1 %>'},
|
||||
'publish': {
|
||||
'status_VNF1':
|
||||
'<% task(create_ns_VNF1).result.vnf.status %>',
|
||||
'vnf_id_VNF1':
|
||||
'<% task(create_ns_VNF1).result.vnf.id %>',
|
||||
'mgmt_url_VNF1':
|
||||
'<% task(create_ns_VNF1).result.vnf.mgmt_url %>',
|
||||
'vim_id_VNF1':
|
||||
'<% task(create_ns_VNF1).result.vnf.vim_id %>'},
|
||||
'on-success': ['wait_vnf_active_VNF1']},
|
||||
'create_ns_VNF2': {
|
||||
'action': 'tacker.create_vnf body=<% $.ns.VNF2 %>',
|
||||
'input': {'body': '<% $.ns.VNF2 %>'},
|
||||
'publish': {
|
||||
'status_VNF2':
|
||||
'<% task(create_ns_VNF2).result.vnf.status %>',
|
||||
'vim_id_VNF2':
|
||||
'<% task(create_ns_VNF2).result.vnf.vim_id %>',
|
||||
'mgmt_url_VNF2':
|
||||
'<% task(create_ns_VNF2).result.vnf.mgmt_url %>',
|
||||
'vnf_id_VNF2':
|
||||
'<% task(create_ns_VNF2).result.vnf.id %>'},
|
||||
'on-success': ['wait_vnf_active_VNF2']},
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'delete_vnf_VNF2': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>'}},
|
||||
'type': 'direct',
|
||||
'output': {
|
||||
'status_VNF1': '<% $.status_VNF1 %>',
|
||||
'status_VNF2': '<% $.status_VNF2 %>',
|
||||
'mgmt_url_VNF2': '<% $.mgmt_url_VNF2 %>',
|
||||
'mgmt_url_VNF1': '<% $.mgmt_url_VNF1 %>',
|
||||
'vnffg_id_VNFFG1': '<% $.vnffg_id_VNFFG1 %>',
|
||||
'vim_id_VNF2': '<% $.vim_id_VNF2 %>',
|
||||
'vnf_id_VNF1': '<% $.vnf_id_VNF1 %>',
|
||||
'vnf_id_VNF2': '<% $.vnf_id_VNF2 %>',
|
||||
'vim_id_VNF1': '<% $.vim_id_VNF1 %>'}},
|
||||
'version': '2.0'}
|
||||
|
||||
|
||||
def dummy_delete_ns_obj():
|
||||
return {'vnf_ids': u"{'VNF1': '5de5eca6-3e21-4bbd-a9d7-86458de75f0c'}"}
|
||||
return {'vnf_ids': u"{'VNF1': '5de5eca6-3e21-4bbd-a9d7-86458de75f0c'}",
|
||||
'vnffg_ids': u"{}"}
|
||||
|
||||
|
||||
def dummy_delete_vnffg_ns_obj():
|
||||
return {'vnf_ids': u"{'VNF1': '5de5eca6-3e21-4bbd-a9d7-86458de75f0c'}",
|
||||
'vnffg_ids': u"{'VNFFG1': '99066f25-3124-44f1-bc5d-bc0bf236b012'}"}
|
||||
|
||||
|
||||
def get_dummy_delete_workflow():
|
||||
return {'version': '2.0',
|
||||
'std.delete_vnf_dummy': {'input': ['vnf_id_VNF1'],
|
||||
'tasks': {'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'}},
|
||||
'std.delete_ns_dummy': {
|
||||
'input': ['vnf_id_VNF1'],
|
||||
'tasks': {
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'}},
|
||||
'type': 'direct'}}
|
||||
|
||||
|
||||
def get_dummy_delete_vnffg_ns_workflow():
|
||||
return {'version': '2.0',
|
||||
'std.delete_ns_dummy': {
|
||||
'input': ['vnf_id_VNF1', 'VNFFG1'],
|
||||
'tasks': {
|
||||
'delete_vnf_VNF1': {
|
||||
'join': 'all',
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'delete_vnffg_VNFFG1': {
|
||||
'action': 'tacker.delete_vnffg vnffg='
|
||||
'<% $.VNFFG1 %>',
|
||||
'on-success': ['delete_vnf_VNF1']}},
|
||||
'type': 'direct'}}
|
||||
|
||||
|
||||
@ -151,22 +333,49 @@ class TestWorkflowGenerator(base.TestCase):
|
||||
|
||||
def test_prepare_workflow_create(self):
|
||||
fPlugin = FakeNFVOPlugin(context, self.mistral_client,
|
||||
resource='vnf', action='create')
|
||||
resource='ns', action='create')
|
||||
fPlugin.prepare_workflow(ns=get_dummy_ns(), params=get_dummy_param())
|
||||
wf_def_values = [fPlugin.wg.definition[k] for
|
||||
k in fPlugin.wg.definition]
|
||||
self.assertIn(get_dummy_create_workflow()['std.create_vnf_dummy'],
|
||||
self.assertIn(get_dummy_create_workflow()['std.create_ns_dummy'],
|
||||
wf_def_values)
|
||||
self.assertEqual(get_dummy_create_workflow()['version'],
|
||||
fPlugin.wg.definition['version'])
|
||||
|
||||
def test_prepare_vnffg_ns_workflow_create(self):
|
||||
fPlugin = FakeNFVOPlugin(context, self.mistral_client,
|
||||
resource='ns', action='create')
|
||||
fPlugin.prepare_workflow(ns=get_dummy_vnffg_ns(),
|
||||
params=get_dummy_param())
|
||||
wf_def_values = [fPlugin.wg.definition[k] for
|
||||
k in fPlugin.wg.definition]
|
||||
self.assertIn(
|
||||
get_dummy_create_vnffg_ns_workflow()['std.create_ns_dummy'],
|
||||
wf_def_values)
|
||||
self.assertEqual(
|
||||
get_dummy_create_vnffg_ns_workflow()['version'],
|
||||
fPlugin.wg.definition['version'])
|
||||
|
||||
def test_prepare_workflow_delete(self):
|
||||
fPlugin = FakeNFVOPlugin(context, self.mistral_client,
|
||||
resource='vnf', action='delete')
|
||||
resource='ns', action='delete')
|
||||
fPlugin.prepare_workflow(ns=dummy_delete_ns_obj())
|
||||
wf_def_values = [fPlugin.wg.definition[k] for
|
||||
k in fPlugin.wg.definition]
|
||||
self.assertIn(get_dummy_delete_workflow()['std.delete_vnf_dummy'],
|
||||
self.assertIn(get_dummy_delete_workflow()['std.delete_ns_dummy'],
|
||||
wf_def_values)
|
||||
self.assertEqual(get_dummy_delete_workflow()['version'],
|
||||
fPlugin.wg.definition['version'])
|
||||
|
||||
def test_prepare_vnffg_ns_workflow_delete(self):
|
||||
fPlugin = FakeNFVOPlugin(context, self.mistral_client,
|
||||
resource='ns', action='delete')
|
||||
fPlugin.prepare_workflow(ns=dummy_delete_vnffg_ns_obj())
|
||||
wf_def_values = [fPlugin.wg.definition[k] for
|
||||
k in fPlugin.wg.definition]
|
||||
self.assertIn(
|
||||
get_dummy_delete_vnffg_ns_workflow()['std.delete_ns_dummy'],
|
||||
wf_def_values)
|
||||
self.assertEqual(
|
||||
get_dummy_delete_vnffg_ns_workflow()['version'],
|
||||
fPlugin.wg.definition['version'])
|
||||
|
@ -621,6 +621,7 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
'name': vnffg.get('name'),
|
||||
'description': 'fake_template_description',
|
||||
'vnffgd_id': vnffg.get('vnffgd_id'),
|
||||
'ns_id': None,
|
||||
'attributes': template_db.get('template'),
|
||||
'status': constants.PENDING_CREATE,
|
||||
'vnf_mapping': vnf_mapping}
|
||||
@ -771,16 +772,6 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
self.nfvo_plugin.create_vnffg,
|
||||
self.context, vnffg_obj)
|
||||
|
||||
def test_create_vnffg_param_value_not_use(self):
|
||||
with patch.object(TackerManager, 'get_service_plugins') as \
|
||||
mock_plugins:
|
||||
mock_plugins.return_value = {'VNFM': FakeVNFMPlugin()}
|
||||
self._insert_dummy_vnffg_param_template()
|
||||
vnffg_obj = utils.get_dummy_vnffg_multi_param_obj()
|
||||
self.assertRaises(nfvo.VnffgParamValueNotUsed,
|
||||
self.nfvo_plugin.create_vnffg,
|
||||
self.context, vnffg_obj)
|
||||
|
||||
def test_create_vnffg_vnf_mapping(self):
|
||||
with patch.object(TackerManager, 'get_service_plugins') as \
|
||||
mock_plugins:
|
||||
|
Loading…
x
Reference in New Issue
Block a user