Support VNF create from inline template
Accept transient VNFD template as part of VNF creation without need for VNFD onboarding into Tacker VNFD catalog. Change-Id: I3c8bbe139dec27adbfc943d3ac9f909db8097f89 Implements: blueprint vnf-inline-template Depends-On: I719237dd04dd7fe13fb7e7964402d7074679b2d6
This commit is contained in:
@@ -709,7 +709,19 @@ vnfd_id:
|
|||||||
description: |
|
description: |
|
||||||
The UUID of the VNFD.
|
The UUID of the VNFD.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: false
|
||||||
|
type: string
|
||||||
|
vnfd_template:
|
||||||
|
description: |
|
||||||
|
Template to create VNF.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
|
type: object
|
||||||
|
vnfd_template_source:
|
||||||
|
description: |
|
||||||
|
Source of VNFD.
|
||||||
|
in: body
|
||||||
|
required: false
|
||||||
type: string
|
type: string
|
||||||
vnfds:
|
vnfds:
|
||||||
description: |
|
description: |
|
||||||
|
@@ -15,7 +15,8 @@
|
|||||||
"vnfd": "description: Demo example\nmetadata: {template_name: sample-tosca-vnfd}\ntopology_template:\n node_templates:\n CP1:\n properties: {anti_spoofing_protection: false, management: true, order: 0}\n requirements:\n - virtualLink: {node: VL1}\n - virtualBinding: {node: VDU1}\n type: tosca.nodes.nfv.CP.Tacker\n VDU1:\n capabilities:\n nfv_compute:\n properties: {disk_size: 1 GB, mem_size: 512 MB, num_cpus: 1}\n properties: {image: cirros-0.3.4-x86_64-uec}\n type: tosca.nodes.nfv.VDU.Tacker\n VL1:\n properties: {network_name: net_mgmt, vendor: Tacker}\n type: tosca.nodes.nfv.VL\ntosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\n"
|
"vnfd": "description: Demo example\nmetadata: {template_name: sample-tosca-vnfd}\ntopology_template:\n node_templates:\n CP1:\n properties: {anti_spoofing_protection: false, management: true, order: 0}\n requirements:\n - virtualLink: {node: VL1}\n - virtualBinding: {node: VDU1}\n type: tosca.nodes.nfv.CP.Tacker\n VDU1:\n capabilities:\n nfv_compute:\n properties: {disk_size: 1 GB, mem_size: 512 MB, num_cpus: 1}\n properties: {image: cirros-0.3.4-x86_64-uec}\n type: tosca.nodes.nfv.VDU.Tacker\n VL1:\n properties: {network_name: net_mgmt, vendor: Tacker}\n type: tosca.nodes.nfv.VL\ntosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\n"
|
||||||
},
|
},
|
||||||
"id": "0fb827e7-32b0-4e5b-b300-e1b1dce8a831",
|
"id": "0fb827e7-32b0-4e5b-b300-e1b1dce8a831",
|
||||||
"name": "vnfd-sample"
|
"name": "vnfd-sample",
|
||||||
|
"template_source": "onboarded or inline"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
"vnfd": "description: Demo example\nmetadata: {template_name: sample-tosca-vnfd}\ntopology_template:\n node_templates:\n CP1:\n properties: {anti_spoofing_protection: false, management: true, order: 0}\n requirements:\n - virtualLink: {node: VL1}\n - virtualBinding: {node: VDU1}\n type: tosca.nodes.nfv.CP.Tacker\n VDU1:\n capabilities:\n nfv_compute:\n properties: {disk_size: 1 GB, mem_size: 512 MB, num_cpus: 1}\n properties: {image: cirros-0.3.4-x86_64-uec}\n type: tosca.nodes.nfv.VDU.Tacker\n VL1:\n properties: {network_name: net_mgmt, vendor: Tacker}\n type: tosca.nodes.nfv.VL\ntosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\n"
|
"vnfd": "description: Demo example\nmetadata: {template_name: sample-tosca-vnfd}\ntopology_template:\n node_templates:\n CP1:\n properties: {anti_spoofing_protection: false, management: true, order: 0}\n requirements:\n - virtualLink: {node: VL1}\n - virtualBinding: {node: VDU1}\n type: tosca.nodes.nfv.CP.Tacker\n VDU1:\n capabilities:\n nfv_compute:\n properties: {disk_size: 1 GB, mem_size: 512 MB, num_cpus: 1}\n properties: {image: cirros-0.3.4-x86_64-uec}\n type: tosca.nodes.nfv.VDU.Tacker\n VL1:\n properties: {network_name: net_mgmt, vendor: Tacker}\n type: tosca.nodes.nfv.VL\ntosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0\n"
|
||||||
},
|
},
|
||||||
"id": "0fb827e7-32b0-4e5b-b300-e1b1dce8a831",
|
"id": "0fb827e7-32b0-4e5b-b300-e1b1dce8a831",
|
||||||
"name": "vnfd-sample"
|
"name": "vnfd-sample",
|
||||||
|
"template_source": "onboarded or inline"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,66 @@
|
|||||||
|
{
|
||||||
|
"vnf": {
|
||||||
|
"tenant_id": "6673e4d4e13340acb0b847f9ecde613b",
|
||||||
|
"vim_id": "f6bd6f24-7a0e-4111-8994-e108c5ee2ff2",
|
||||||
|
"name": "OpenWRT",
|
||||||
|
"description": "OpenWRT VNF",
|
||||||
|
"attributes": {
|
||||||
|
"config": {
|
||||||
|
"vdus": {
|
||||||
|
"vdu1": {
|
||||||
|
"config": {
|
||||||
|
"firewall": "package firewall\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"param_values": {
|
||||||
|
"vdus": {
|
||||||
|
"vdu1": {
|
||||||
|
"param": {
|
||||||
|
"vdu-name": "openwrt_vdu1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"placement_attr": {
|
||||||
|
"region_name": "RegionOne"
|
||||||
|
},
|
||||||
|
"vnfd_template": {
|
||||||
|
"tosca_definitions_version": "tosca_simple_profile_for_nfv_1_0_0",
|
||||||
|
"description": "Demo example",
|
||||||
|
"metadata": {
|
||||||
|
"template_name": "sample-tosca-vnfd"},
|
||||||
|
"topology_template": {
|
||||||
|
"node_templates": {
|
||||||
|
"VDU1": {
|
||||||
|
"type": "tosca.nodes.nfv.VDU.Tacker",
|
||||||
|
"capabilities": {
|
||||||
|
"nfv_compute": {
|
||||||
|
"properties": {
|
||||||
|
"num_cpus": 1,
|
||||||
|
"mem_size": "512 MB",
|
||||||
|
"disk_size": "1 GB"}}},
|
||||||
|
"properties": {"image": "cirros-0.3.4-x86_64-uec"}},
|
||||||
|
"CP1": {
|
||||||
|
"type": "tosca.nodes.nfv.CP.Tacker",
|
||||||
|
"properties": {
|
||||||
|
"order": 0,
|
||||||
|
"management": true,
|
||||||
|
"anti_spoofing_protection": false},
|
||||||
|
"requirements": [
|
||||||
|
{"virtualLink": {
|
||||||
|
"node": "VL1"}},
|
||||||
|
{"virtualBinding": {
|
||||||
|
"node": "VDU1"}}]},
|
||||||
|
"VL1": {
|
||||||
|
"type": "tosca.nodes.nfv.VL",
|
||||||
|
"properties": {
|
||||||
|
"vendor": "Tacker",
|
||||||
|
"network_name": "net_mgmt"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -102,6 +102,7 @@ Response Parameters
|
|||||||
- attributes: vnfd_attributes
|
- attributes: vnfd_attributes
|
||||||
- id: vnfd_id
|
- id: vnfd_id
|
||||||
- name: name
|
- name: name
|
||||||
|
- template_source: vnfd_template_source
|
||||||
|
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
@@ -151,6 +152,7 @@ Response Parameters
|
|||||||
- attributes: vnfd_attributes
|
- attributes: vnfd_attributes
|
||||||
- id: vnfd_id
|
- id: vnfd_id
|
||||||
- name: name
|
- name: name
|
||||||
|
- template_source: vnfd_template_source
|
||||||
|
|
||||||
Response Example
|
Response Example
|
||||||
----------------
|
----------------
|
||||||
|
@@ -44,6 +44,7 @@ Request Parameters
|
|||||||
- config: vnf_config_opt
|
- config: vnf_config_opt
|
||||||
- param_values: vnf_param_values_opt
|
- param_values: vnf_param_values_opt
|
||||||
- placement_attr: vnf_placement_attr_opt
|
- placement_attr: vnf_placement_attr_opt
|
||||||
|
- vnfd_template: vnfd_template
|
||||||
|
|
||||||
Request Example
|
Request Example
|
||||||
---------------
|
---------------
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Support VNF create with direct VNFD template input via CLI/API without
|
||||||
|
onboarding VNFD.
|
@@ -563,6 +563,11 @@ class Controller(object):
|
|||||||
if 'validate' not in attr_vals:
|
if 'validate' not in attr_vals:
|
||||||
continue
|
continue
|
||||||
for rule in attr_vals['validate']:
|
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:
|
||||||
|
continue
|
||||||
res = attributes.validators[rule](res_dict[attr],
|
res = attributes.validators[rule](res_dict[attr],
|
||||||
attr_vals['validate'][rule])
|
attr_vals['validate'][rule])
|
||||||
if res:
|
if res:
|
||||||
|
@@ -21,8 +21,10 @@
|
|||||||
import functools
|
import functools
|
||||||
import logging as std_logging
|
import logging as std_logging
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
|
import string
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from eventlet.green import subprocess
|
from eventlet.green import subprocess
|
||||||
@@ -306,3 +308,11 @@ def deprecate_warning(what, as_of, in_favor_of=None, remove_in=1):
|
|||||||
versionutils.deprecation_warning(as_of=as_of, what=what,
|
versionutils.deprecation_warning(as_of=as_of, what=what,
|
||||||
in_favor_of=in_favor_of,
|
in_favor_of=in_favor_of,
|
||||||
remove_in=remove_in)
|
remove_in=remove_in)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_resource_name(resource, prefix='tmpl'):
|
||||||
|
return prefix + '-' \
|
||||||
|
+ ''.join(random.SystemRandom().choice(
|
||||||
|
string.ascii_lowercase + string.digits)
|
||||||
|
for _ in range(16)) \
|
||||||
|
+ '-' + resource
|
||||||
|
@@ -0,0 +1,33 @@
|
|||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
"""add template_source column
|
||||||
|
|
||||||
|
Revision ID: 000632983ada
|
||||||
|
Revises: 0ae5b1ce3024
|
||||||
|
Create Date: 2016-12-22 20:30:03.931290
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '000632983ada'
|
||||||
|
down_revision = '0ad3bbce1c19'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade(active_plugins=None, options=None):
|
||||||
|
op.add_column('vnfd', sa.Column('template_source', sa.String(length=255)))
|
@@ -1 +1 @@
|
|||||||
0ad3bbce1c19
|
000632983ada
|
||||||
|
@@ -67,6 +67,9 @@ class VNFD(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
|
|||||||
attributes = orm.relationship('VNFDAttribute',
|
attributes = orm.relationship('VNFDAttribute',
|
||||||
backref='vnfd')
|
backref='vnfd')
|
||||||
|
|
||||||
|
# vnfd template source - inline or onboarded
|
||||||
|
template_source = sa.Column(sa.String(255))
|
||||||
|
|
||||||
|
|
||||||
class ServiceType(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
|
class ServiceType(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
|
||||||
"""Represents service type which hosting vnf provides.
|
"""Represents service type which hosting vnf provides.
|
||||||
@@ -184,7 +187,8 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
vnfd.service_types)
|
vnfd.service_types)
|
||||||
}
|
}
|
||||||
key_list = ('id', 'tenant_id', 'name', 'description',
|
key_list = ('id', 'tenant_id', 'name', 'description',
|
||||||
'mgmt_driver', 'created_at', 'updated_at')
|
'mgmt_driver', 'created_at', 'updated_at',
|
||||||
|
'template_source')
|
||||||
res.update((key, vnfd[key]) for key in key_list)
|
res.update((key, vnfd[key]) for key in key_list)
|
||||||
return self._fields(res, fields)
|
return self._fields(res, fields)
|
||||||
|
|
||||||
@@ -219,6 +223,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
tenant_id = self._get_tenant_id_for_create(context, vnfd)
|
tenant_id = self._get_tenant_id_for_create(context, vnfd)
|
||||||
service_types = vnfd.get('service_types')
|
service_types = vnfd.get('service_types')
|
||||||
mgmt_driver = vnfd.get('mgmt_driver')
|
mgmt_driver = vnfd.get('mgmt_driver')
|
||||||
|
template_source = vnfd.get("template_source")
|
||||||
|
|
||||||
if (not attributes.is_attr_set(service_types)):
|
if (not attributes.is_attr_set(service_types)):
|
||||||
LOG.debug(_('service types unspecified'))
|
LOG.debug(_('service types unspecified'))
|
||||||
@@ -231,7 +236,8 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
name=vnfd.get('name'),
|
name=vnfd.get('name'),
|
||||||
description=vnfd.get('description'),
|
description=vnfd.get('description'),
|
||||||
mgmt_driver=mgmt_driver)
|
mgmt_driver=mgmt_driver,
|
||||||
|
template_source=template_source)
|
||||||
context.session.add(vnfd_db)
|
context.session.add(vnfd_db)
|
||||||
for (key, value) in vnfd.get('attributes', {}).items():
|
for (key, value) in vnfd.get('attributes', {}).items():
|
||||||
attribute_db = VNFDAttribute(
|
attribute_db = VNFDAttribute(
|
||||||
@@ -288,9 +294,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
vnfs_db = context.session.query(VNF).filter_by(
|
vnfs_db = context.session.query(VNF).filter_by(
|
||||||
vnfd_id=vnfd_id).first()
|
vnfd_id=vnfd_id).first()
|
||||||
if vnfs_db is not None and vnfs_db.deleted_at is None:
|
if vnfs_db is not None and vnfs_db.deleted_at is None:
|
||||||
raise vnfm.VNFDInUse(
|
raise vnfm.VNFDInUse(vnfd_id=vnfd_id)
|
||||||
vnfd_id=vnfd_id)
|
|
||||||
|
|
||||||
vnfd_db = self._get_resource(context, VNFD,
|
vnfd_db = self._get_resource(context, VNFD,
|
||||||
vnfd_id)
|
vnfd_id)
|
||||||
if soft_delete:
|
if soft_delete:
|
||||||
@@ -313,6 +317,9 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
return self._make_vnfd_dict(vnfd_db)
|
return self._make_vnfd_dict(vnfd_db)
|
||||||
|
|
||||||
def get_vnfds(self, context, filters, fields=None):
|
def get_vnfds(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, VNFD,
|
return self._get_collection(context, VNFD,
|
||||||
self._make_vnfd_dict,
|
self._make_vnfd_dict,
|
||||||
filters=filters, fields=fields)
|
filters=filters, fields=fields)
|
||||||
@@ -521,7 +528,8 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
tstamp=timeutils.utcnow(), details="VNF delete initiated")
|
tstamp=timeutils.utcnow(), details="VNF delete initiated")
|
||||||
return deleted_vnf_db
|
return deleted_vnf_db
|
||||||
|
|
||||||
def _delete_vnf_post(self, context, vnf_id, error, soft_delete=True):
|
def _delete_vnf_post(self, context, vnf_dict, error, soft_delete=True):
|
||||||
|
vnf_id = vnf_dict['id']
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
query = (
|
query = (
|
||||||
self._model_query(context, VNF).
|
self._model_query(context, VNF).
|
||||||
@@ -552,6 +560,10 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
filter(VNFAttribute.vnf_id == vnf_id).delete())
|
filter(VNFAttribute.vnf_id == vnf_id).delete())
|
||||||
query.delete()
|
query.delete()
|
||||||
|
|
||||||
|
# Delete corresponding vnfd
|
||||||
|
if vnf_dict['vnfd']['template_source'] == "inline":
|
||||||
|
self.delete_vnfd(context, vnf_dict["vnfd_id"])
|
||||||
|
|
||||||
# reference implementation. needs to be overrided by subclass
|
# reference implementation. needs to be overrided by subclass
|
||||||
def create_vnf(self, context, vnf):
|
def create_vnf(self, context, vnf):
|
||||||
vnf_dict = self._create_vnf_pre(context, vnf)
|
vnf_dict = self._create_vnf_pre(context, vnf)
|
||||||
@@ -577,12 +589,12 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
|
|
||||||
# reference implementation. needs to be overrided by subclass
|
# reference implementation. needs to be overrided by subclass
|
||||||
def delete_vnf(self, context, vnf_id, soft_delete=True):
|
def delete_vnf(self, context, vnf_id, soft_delete=True):
|
||||||
self._delete_vnf_pre(context, vnf_id)
|
vnf_dict = self._delete_vnf_pre(context, vnf_id)
|
||||||
# start actual deletion of hosting vnf.
|
# start actual deletion of hosting vnf.
|
||||||
# Waiting for completion of deletion should be done backgroundly
|
# Waiting for completion of deletion should be done backgroundly
|
||||||
# by another thread if it takes a while.
|
# by another thread if it takes a while.
|
||||||
self._delete_vnf_post(context,
|
self._delete_vnf_post(context,
|
||||||
vnf_id,
|
vnf_dict,
|
||||||
False,
|
False,
|
||||||
soft_delete=soft_delete)
|
soft_delete=soft_delete)
|
||||||
|
|
||||||
|
@@ -236,6 +236,12 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'allow_put': False,
|
'allow_put': False,
|
||||||
'is_visible': True,
|
'is_visible': True,
|
||||||
},
|
},
|
||||||
|
'template_source': {
|
||||||
|
'allow_post': False,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
'default': 'onboarded'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
'vnfs': {
|
'vnfs': {
|
||||||
@@ -258,6 +264,7 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'allow_put': False,
|
'allow_put': False,
|
||||||
'validate': {'type:uuid': None},
|
'validate': {'type:uuid': None},
|
||||||
'is_visible': True,
|
'is_visible': True,
|
||||||
|
'default': None
|
||||||
},
|
},
|
||||||
'vim_id': {
|
'vim_id': {
|
||||||
'allow_post': True,
|
'allow_post': True,
|
||||||
@@ -325,6 +332,13 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
'allow_put': False,
|
'allow_put': False,
|
||||||
'is_visible': True,
|
'is_visible': True,
|
||||||
},
|
},
|
||||||
|
'vnfd_template': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:dict_or_none': None},
|
||||||
|
'is_visible': True,
|
||||||
|
'default': None,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -28,7 +28,8 @@ VNF_CIRROS_CREATE_TIMEOUT = 120
|
|||||||
|
|
||||||
|
|
||||||
class VnfTestToscaCreate(base.BaseTackerTest):
|
class VnfTestToscaCreate(base.BaseTackerTest):
|
||||||
def _test_create_vnf(self, vnfd_file, vnf_name):
|
def _test_create_vnf(self, vnfd_file, vnf_name,
|
||||||
|
template_source="onboarded"):
|
||||||
data = dict()
|
data = dict()
|
||||||
values_str = read_file(vnfd_file)
|
values_str = read_file(vnfd_file)
|
||||||
data['tosca'] = values_str
|
data['tosca'] = values_str
|
||||||
@@ -36,16 +37,23 @@ class VnfTestToscaCreate(base.BaseTackerTest):
|
|||||||
tosca_arg = {'vnfd': {'name': vnf_name,
|
tosca_arg = {'vnfd': {'name': vnf_name,
|
||||||
'attributes': {'vnfd': toscal}}}
|
'attributes': {'vnfd': toscal}}}
|
||||||
|
|
||||||
# Create vnfd with tosca template
|
if template_source == "onboarded":
|
||||||
vnfd_instance = self.client.create_vnfd(body=tosca_arg)
|
# Create vnfd with tosca template
|
||||||
self.assertIsNotNone(vnfd_instance)
|
vnfd_instance = self.client.create_vnfd(body=tosca_arg)
|
||||||
|
self.assertIsNotNone(vnfd_instance)
|
||||||
|
|
||||||
# Create vnf with vnfd_id
|
# Create vnf with vnfd_id
|
||||||
vnfd_id = vnfd_instance['vnfd']['id']
|
vnfd_id = vnfd_instance['vnfd']['id']
|
||||||
vnf_arg = {'vnf': {'vnfd_id': vnfd_id, 'name': vnf_name}}
|
vnf_arg = {'vnf': {'vnfd_id': vnfd_id, 'name': vnf_name}}
|
||||||
vnf_instance = self.client.create_vnf(body=vnf_arg)
|
vnf_instance = self.client.create_vnf(body=vnf_arg)
|
||||||
|
self.validate_vnf_instance(vnfd_instance, vnf_instance)
|
||||||
|
|
||||||
self.validate_vnf_instance(vnfd_instance, vnf_instance)
|
if template_source == 'inline':
|
||||||
|
# create vnf directly from template
|
||||||
|
template = yaml.load(values_str)
|
||||||
|
vnf_arg = {'vnf': {'vnfd_template': template, 'name': vnf_name}}
|
||||||
|
vnf_instance = self.client.create_vnf(body=vnf_arg)
|
||||||
|
vnfd_id = vnf_instance['vnf']['vnfd_id']
|
||||||
|
|
||||||
vnf_id = vnf_instance['vnf']['id']
|
vnf_id = vnf_instance['vnf']['id']
|
||||||
self.wait_until_vnf_active(
|
self.wait_until_vnf_active(
|
||||||
@@ -100,10 +108,10 @@ class VnfTestToscaCreate(base.BaseTackerTest):
|
|||||||
self.addCleanup(self.wait_until_vnf_delete, vnf_id,
|
self.addCleanup(self.wait_until_vnf_delete, vnf_id,
|
||||||
constants.VNF_CIRROS_DELETE_TIMEOUT)
|
constants.VNF_CIRROS_DELETE_TIMEOUT)
|
||||||
|
|
||||||
def test_create_delete_vnf_tosca(self):
|
def _test_create_delete_vnf_tosca(self, vnfd_file, vnf_name,
|
||||||
vnfd_id, vnf_id = self._test_create_vnf(
|
template_source):
|
||||||
'sample-tosca-vnfd.yaml',
|
vnfd_id, vnf_id = self._test_create_vnf(vnfd_file, vnf_name,
|
||||||
'test_tosca_vnf_with_cirros')
|
template_source)
|
||||||
servers = self.novaclient().servers.list()
|
servers = self.novaclient().servers.list()
|
||||||
vdus = []
|
vdus = []
|
||||||
for server in servers:
|
for server in servers:
|
||||||
@@ -116,7 +124,18 @@ class VnfTestToscaCreate(base.BaseTackerTest):
|
|||||||
vdu_ports.append(port['name'])
|
vdu_ports.append(port['name'])
|
||||||
self.assertIn('test-cp', vdu_ports)
|
self.assertIn('test-cp', vdu_ports)
|
||||||
self._test_delete_vnf(vnf_id)
|
self._test_delete_vnf(vnf_id)
|
||||||
self._test_cleanup_vnfd(vnfd_id, vnf_id)
|
if template_source == "onboarded":
|
||||||
|
self._test_cleanup_vnfd(vnfd_id, vnf_id)
|
||||||
|
|
||||||
|
def test_create_delete_vnf_tosca_from_vnfd(self):
|
||||||
|
self._test_create_delete_vnf_tosca('sample-tosca-vnfd.yaml',
|
||||||
|
'test_tosca_vnf_with_cirros',
|
||||||
|
'onboarded')
|
||||||
|
|
||||||
|
def test_create_delete_vnf_from_template(self):
|
||||||
|
self._test_create_delete_vnf_tosca('sample-tosca-vnfd.yaml',
|
||||||
|
'test_tosca_vnf_with_cirros_inline',
|
||||||
|
'inline')
|
||||||
|
|
||||||
def test_create_delete_vnf_static_ip(self):
|
def test_create_delete_vnf_static_ip(self):
|
||||||
vnfd_id, vnf_id = self._test_create_vnf(
|
vnfd_id, vnf_id = self._test_create_vnf(
|
||||||
|
@@ -46,9 +46,31 @@ def get_dummy_vnfd_obj():
|
|||||||
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
u'attributes': {u'vnfd': yaml.safe_load(
|
u'attributes': {u'vnfd': yaml.safe_load(
|
||||||
tosca_vnfd_openwrt)},
|
tosca_vnfd_openwrt)},
|
||||||
'description': 'dummy_vnfd_description'},
|
'description': 'dummy_vnfd_description',
|
||||||
|
'template_source': 'onboarded',
|
||||||
u'auth': {u'tenantName': u'admin', u'passwordCredentials': {
|
u'auth': {u'tenantName': u'admin', u'passwordCredentials': {
|
||||||
u'username': u'admin', u'password': u'devstack'}}}
|
u'username': u'admin', u'password': u'devstack'}}}}
|
||||||
|
|
||||||
|
|
||||||
|
def get_dummy_vnfd_obj_inline():
|
||||||
|
return {u'vnfd': {u'service_types': [{u'service_type': u'vnfd'}],
|
||||||
|
'name': 'tmpl-koeak4tqgoqo8cr4-dummy_inline_vnf',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
u'attributes': {u'vnfd': yaml.safe_load(
|
||||||
|
tosca_vnfd_openwrt)},
|
||||||
|
'template_source': 'inline',
|
||||||
|
u'auth': {u'tenantName': u'admin', u'passwordCredentials': {
|
||||||
|
u'username': u'admin', u'password': u'devstack'}}}}
|
||||||
|
|
||||||
|
|
||||||
|
def get_dummy_inline_vnf_obj():
|
||||||
|
return {'vnf': {'description': 'dummy_inline_vnf_description',
|
||||||
|
'vnfd_template': yaml.safe_load(tosca_vnfd_openwrt),
|
||||||
|
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||||
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
'name': 'dummy_inline_vnf',
|
||||||
|
'attributes': {},
|
||||||
|
'vnfd_id': None}}
|
||||||
|
|
||||||
|
|
||||||
def get_dummy_vnf_obj():
|
def get_dummy_vnf_obj():
|
||||||
@@ -57,7 +79,8 @@ def get_dummy_vnf_obj():
|
|||||||
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
'vim_id': u'6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||||
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
'tenant_id': u'ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
'name': 'dummy_vnf',
|
'name': 'dummy_vnf',
|
||||||
'attributes': {}}}
|
'attributes': {},
|
||||||
|
'vnfd_template': None}}
|
||||||
|
|
||||||
|
|
||||||
def get_dummy_vnf_config_obj():
|
def get_dummy_vnf_config_obj():
|
||||||
|
@@ -123,7 +123,20 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
|||||||
id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||||
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
name='fake_template',
|
name='fake_template',
|
||||||
description='fake_template_description')
|
description='fake_template_description',
|
||||||
|
template_source='onboarded')
|
||||||
|
session.add(device_template)
|
||||||
|
session.flush()
|
||||||
|
return device_template
|
||||||
|
|
||||||
|
def _insert_dummy_device_template_inline(self):
|
||||||
|
session = self.context.session
|
||||||
|
device_template = vnfm_db.VNFD(
|
||||||
|
id='d58bcc4e-d0cf-11e6-bf26-cec0c932ce01',
|
||||||
|
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||||
|
name='tmpl-koeak4tqgoqo8cr4-dummy_inline_vnf',
|
||||||
|
description='inline_fake_template_description',
|
||||||
|
template_source='inline')
|
||||||
session.add(device_template)
|
session.add(device_template)
|
||||||
session.flush()
|
session.flush()
|
||||||
return device_template
|
return device_template
|
||||||
@@ -219,6 +232,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
|||||||
self.assertIn('attributes', result)
|
self.assertIn('attributes', result)
|
||||||
self.assertIn('created_at', result)
|
self.assertIn('created_at', result)
|
||||||
self.assertIn('updated_at', result)
|
self.assertIn('updated_at', result)
|
||||||
|
self.assertIn('template_source', result)
|
||||||
yaml_dict = yaml.safe_load(utils.tosca_vnfd_openwrt)
|
yaml_dict = yaml.safe_load(utils.tosca_vnfd_openwrt)
|
||||||
mock_tosca_template.assert_called_once_with(
|
mock_tosca_template.assert_called_once_with(
|
||||||
a_file=False, yaml_dict_tpl=yaml_dict)
|
a_file=False, yaml_dict_tpl=yaml_dict)
|
||||||
@@ -244,7 +258,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
|||||||
self.vnfm_plugin.create_vnfd,
|
self.vnfm_plugin.create_vnfd,
|
||||||
self.context, vnfd_obj)
|
self.context, vnfd_obj)
|
||||||
|
|
||||||
def test_create_vnf(self):
|
def test_create_vnf_with_vnfd(self):
|
||||||
self._insert_dummy_device_template()
|
self._insert_dummy_device_template()
|
||||||
vnf_obj = utils.get_dummy_vnf_obj()
|
vnf_obj = utils.get_dummy_vnf_obj()
|
||||||
result = self.vnfm_plugin.create_vnf(self.context, vnf_obj)
|
result = self.vnfm_plugin.create_vnf(self.context, vnf_obj)
|
||||||
@@ -268,6 +282,35 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
|||||||
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
||||||
tstamp=mock.ANY, details=mock.ANY)
|
tstamp=mock.ANY, details=mock.ANY)
|
||||||
|
|
||||||
|
@mock.patch('tacker.vnfm.plugin.VNFMPlugin.create_vnfd')
|
||||||
|
def test_create_vnf_from_template(self, mock_create_vnfd):
|
||||||
|
self._insert_dummy_device_template_inline()
|
||||||
|
mock_create_vnfd.return_value = {'id':
|
||||||
|
'd58bcc4e-d0cf-11e6-bf26-cec0c932ce01'}
|
||||||
|
vnf_obj = utils.get_dummy_inline_vnf_obj()
|
||||||
|
result = self.vnfm_plugin.create_vnf(self.context, vnf_obj)
|
||||||
|
self.assertIsNotNone(result)
|
||||||
|
self.assertIn('id', result)
|
||||||
|
self.assertIn('instance_id', result)
|
||||||
|
self.assertIn('status', result)
|
||||||
|
self.assertIn('attributes', result)
|
||||||
|
self.assertIn('mgmt_url', result)
|
||||||
|
self.assertIn('created_at', result)
|
||||||
|
self.assertIn('updated_at', result)
|
||||||
|
mock_create_vnfd.assert_called_once_with(mock.ANY, mock.ANY)
|
||||||
|
self._device_manager.invoke.assert_called_with('test_vim',
|
||||||
|
'create',
|
||||||
|
plugin=mock.ANY,
|
||||||
|
context=mock.ANY,
|
||||||
|
vnf=mock.ANY,
|
||||||
|
auth_attr=mock.ANY)
|
||||||
|
self._pool.spawn_n.assert_called_once_with(mock.ANY)
|
||||||
|
self._cos_db_plugin.create_event.assert_called_with(
|
||||||
|
self.context, evt_type=constants.RES_EVT_CREATE,
|
||||||
|
res_id=mock.ANY,
|
||||||
|
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
||||||
|
tstamp=mock.ANY, details=mock.ANY)
|
||||||
|
|
||||||
def test_show_vnf_details_vnf_inactive(self):
|
def test_show_vnf_details_vnf_inactive(self):
|
||||||
self._insert_dummy_device_template()
|
self._insert_dummy_device_template()
|
||||||
vnf_obj = utils.get_dummy_vnf_obj()
|
vnf_obj = utils.get_dummy_vnf_obj()
|
||||||
|
@@ -163,6 +163,11 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
# framework doesn't know what services are valid for now.
|
# framework doesn't know what services are valid for now.
|
||||||
# so doesn't check it here yet.
|
# so doesn't check it here yet.
|
||||||
pass
|
pass
|
||||||
|
if 'template_source' in vnfd_data:
|
||||||
|
template_source = vnfd_data.get('template_source')
|
||||||
|
else:
|
||||||
|
template_source = 'onboarded'
|
||||||
|
vnfd['vnfd']['template_source'] = template_source
|
||||||
|
|
||||||
self._parse_template_input(vnfd)
|
self._parse_template_input(vnfd)
|
||||||
return super(VNFMPlugin, self).create_vnfd(
|
return super(VNFMPlugin, self).create_vnfd(
|
||||||
@@ -334,6 +339,17 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
name = vnf_info['name']
|
name = vnf_info['name']
|
||||||
if self._get_by_name(context, vnfm_db.VNF, name):
|
if self._get_by_name(context, vnfm_db.VNF, name):
|
||||||
raise exceptions.DuplicateResourceName(resource='VNF', name=name)
|
raise exceptions.DuplicateResourceName(resource='VNF', name=name)
|
||||||
|
|
||||||
|
# if vnfd_template specified, create vnfd from template
|
||||||
|
# create template dictionary structure same as needed in create_vnfd()
|
||||||
|
if vnf_info.get('vnfd_template'):
|
||||||
|
vnfd_name = utils.generate_resource_name(name, 'inline')
|
||||||
|
vnfd = {'vnfd': {'attributes': {'vnfd': vnf_info['vnfd_template']},
|
||||||
|
'name': vnfd_name,
|
||||||
|
'template_source': 'inline',
|
||||||
|
'service_types': [{'service_type': 'vnfd'}]}}
|
||||||
|
vnf_info['vnfd_id'] = self.create_vnfd(context, vnfd).get('id')
|
||||||
|
|
||||||
vnf_attributes = vnf_info['attributes']
|
vnf_attributes = vnf_info['attributes']
|
||||||
if vnf_attributes.get('param_values'):
|
if vnf_attributes.get('param_values'):
|
||||||
param = vnf_attributes['param_values']
|
param = vnf_attributes['param_values']
|
||||||
@@ -464,8 +480,7 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
LOG.exception(_('_delete_vnf_wait'))
|
LOG.exception(_('_delete_vnf_wait'))
|
||||||
|
|
||||||
self.mgmt_delete_post(context, vnf_dict)
|
self.mgmt_delete_post(context, vnf_dict)
|
||||||
vnf_id = vnf_dict['id']
|
self._delete_vnf_post(context, vnf_dict, e)
|
||||||
self._delete_vnf_post(context, vnf_id, e)
|
|
||||||
|
|
||||||
def delete_vnf(self, context, vnf_id):
|
def delete_vnf(self, context, vnf_id):
|
||||||
vnf_dict = self._delete_vnf_pre(context, vnf_id)
|
vnf_dict = self._delete_vnf_pre(context, vnf_id)
|
||||||
@@ -497,7 +512,7 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
vnf_dict['status'] = constants.ERROR
|
vnf_dict['status'] = constants.ERROR
|
||||||
vnf_dict['error_reason'] = six.text_type(e)
|
vnf_dict['error_reason'] = six.text_type(e)
|
||||||
self.mgmt_delete_post(context, vnf_dict)
|
self.mgmt_delete_post(context, vnf_dict)
|
||||||
self._delete_vnf_post(context, vnf_id, e)
|
self._delete_vnf_post(context, vnf_dict, e)
|
||||||
|
|
||||||
self.spawn_n(self._delete_vnf_wait, context, vnf_dict, vim_auth,
|
self.spawn_n(self._delete_vnf_wait, context, vnf_dict, vim_auth,
|
||||||
driver_name)
|
driver_name)
|
||||||
|
Reference in New Issue
Block a user