Add support for Simple NFV Profile

* This patchset implements an extensions capability to allow the
    parser to easily support additional TOSCA profiles

  * The first supported extension is the TOSCA NFV profile

Change-Id: I00606c8f0075c205da4bc221c3274e0921b630a8
Partially implements: blueprint tosca-nfv-support
This commit is contained in:
Bob HADDLETON 2015-12-04 14:32:58 -06:00 committed by Bob.Haddleton
parent 3359884aaf
commit 16a300e3e7
12 changed files with 445 additions and 4 deletions

View File

@ -105,6 +105,18 @@ class URLException(TOSCAException):
msg_fmt = _('%(what)s') msg_fmt = _('%(what)s')
class ToscaExtImportError(TOSCAException):
msg_fmt = _('Unable to import extension "%(ext_name)s". '
'Check to see that it exists and has no '
'language definition errors.')
class ToscaExtAttributeError(TOSCAException):
msg_fmt = _('Missing attribute in extension "%(ext_name)s". '
'Check to see that it has required attributes '
'"%(attrs)s" defined.')
class ExceptionCollector(object): class ExceptionCollector(object):
exceptions = [] exceptions = []

View File

@ -12,6 +12,7 @@
import logging import logging
import os import os
from toscaparser.extensions.exttools import ExtTools
import toscaparser.utils.yamlparser import toscaparser.utils.yamlparser
log = logging.getLogger('tosca') log = logging.getLogger('tosca')
@ -113,3 +114,12 @@ class EntityType(object):
inherited.update(value) inherited.update(value)
value.update(inherited) value.update(inherited)
return value return value
def update_definitions(version):
exttools = ExtTools()
extension_defs_file = exttools.get_defs_file(version)
loader = toscaparser.utils.yamlparser.load_yaml
EntityType.TOSCA_DEF.update(loader(extension_defs_file))

View File

View File

@ -0,0 +1,88 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import importlib
import logging
import os
from toscaparser.common.exception import ToscaExtAttributeError
from toscaparser.common.exception import ToscaExtImportError
log = logging.getLogger("tosca.model")
REQUIRED_ATTRIBUTES = ['VERSION', 'DEFS_FILE']
class ExtTools(object):
def __init__(self):
self.EXTENSION_INFO = self._load_extensions()
def _load_extensions(self):
'''Dynamically load all the extensions .'''
extensions = {}
# Use the absolute path of the class path
abs_path = os.path.dirname(os.path.abspath(__file__))
extdirs = [e for e in os.listdir(abs_path) if
not e.startswith('tests') and
not e.endswith('.pyc') and not e.endswith('.py')]
for e in extdirs:
log.info(e)
extpath = abs_path + '/' + e
# Grab all the extension files in the given path
ext_files = [f for f in os.listdir(extpath) if f.endswith('.py')
and not f.startswith('__init__')]
# For each module, pick out the target translation class
for f in ext_files:
log.info(f)
ext_name = 'toscaparser/extensions/' + e + '/' + f.strip('.py')
ext_name = ext_name.replace('/', '.')
try:
extinfo = importlib.import_module(ext_name)
version = getattr(extinfo, 'VERSION')
defs_file = extpath + '/' + getattr(extinfo, 'DEFS_FILE')
# Sections is an optional attribute
sections = getattr(extinfo, 'SECTIONS', ())
extensions[version] = {'sections': sections,
'defs_file': defs_file}
except ImportError:
raise ToscaExtImportError(ext_name=ext_name)
except AttributeError:
attrs = ', '.join(REQUIRED_ATTRIBUTES)
raise ToscaExtAttributeError(ext_name=ext_name,
attrs=attrs)
return extensions
def get_versions(self):
return self.EXTENSION_INFO.keys()
def get_sections(self):
sections = {}
for version in self.EXTENSION_INFO.keys():
sections[version] = self.EXTENSION_INFO[version]['sections']
return sections
def get_defs_file(self, version):
versiondata = self.EXTENSION_INFO.get(version)
if versiondata:
return versiondata.get('defs_file')
else:
return None

View File

@ -0,0 +1,242 @@
# 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.
##########################################################################
# The content of this file reflects TOSCA NFV Profile in YAML version
# 1.0.0. It describes the definition for TOSCA NFV types including Node Type,
# Relationship Type, Capability Type and Interfaces.
##########################################################################
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
##########################################################################
# Node Type.
# A Node Type is a reusable entity that defines the type of one or more
# Node Templates.
##########################################################################
tosca.nodes.nfv.VNF:
derived_from: tosca.nodes.Root # Or should this be its own top - level type?
properties:
id:
type: string
description: ID of this VNF
vendor:
type: string
description: name of the vendor who generate this VNF
version:
type: version
description: version of the software for this VNF
requirements:
- virtualLink:
capability: tosca.capabilities.nfv.VirtualLinkable
tosca.nodes.nfv.VDU:
derived_from: tosca.nodes.Compute
capabilities:
high_availability:
type: tosca.capabilities.nfv.HA
Virtualbinding:
type: tosca.capabilities.nfv.VirtualBindable
monitoring_parameter:
type: tosca.capabilities.nfv.Metric
requirements:
- high_availability:
capability: tosca.capabilities.nfv.HA
relationship: tosca.relationships.nfv.HA
occurrences: [ 0, 1 ]
tosca.nodes.nfv.CP:
derived_from: tosca.nodes.network.Port
properties:
type:
type: string
required: false
requirements:
- virtualLink:
capability: tosca.capabilities.VirtualLinkable
- virtualBinding:
capability: tosca.capabilities.nfv.VirtualBindable
attributes:
IP_address:
type: string
required: false
tosca.nodes.nfv.VL:
derived_from: tosca.nodes.network.Network
properties:
vendor:
type: string
required: true
description: name of the vendor who generate this VL
capabilities:
virtual_linkable:
type: tosca.capabilities.nfv.VirtualLinkable
tosca.nodes.nfv.VL.ELine:
derived_from: tosca.nodes.nfv.VL
capabilities:
virtual_linkable:
occurrences: 2
tosca.nodes.nfv.VL.ELAN:
derived_from: tosca.nodes.nfv.VL
tosca.nodes.nfv.VL.ETree:
derived_from: tosca.nodes.nfv.VL
tosca.nodes.nfv.FP:
derived_from: tosca.nodes.Root
properties:
policy:
type: string
required: false
description: name of the vendor who generate this VL
requirements:
forwarder:
-capability: tosca.capabilities.nfv.Forwarder
##########################################################################
# Relationship Type.
# A Relationship Type is a reusable entity that defines the type of one
# or more relationships between Node Types or Node Templates.
##########################################################################
tosca.relationships.nfv.VirtualLinksTo:
derived_from: tosca.relationships.ConnectsTo
valid_target_types: [ tosca.capabilities.nfv.VirtualLinkable ]
tosca.relationships.nfv.VirtualBindsTo:
derived_from: tosca.relationships.ConnectsTo
valid_target_types: [ tosca.capabilities.nfv.VirtualBindable ]
tosca.relationships.nfv.HA:
derived_from: tosca.relationships.Root
valid_target_types: [ tosca.capabilities.nfv.HA ]
tosca.relationships.nfv.Monitor:
derived_from: tosca.relationships.ConnectsTo
valid_target_types: [ tosca.capabilities.nfv.Metric ]
tosca.relationships.nfv. ForwardsTo:
derived_from: tosca.relationships.root
valid_target_types: [ tosca.capabilities.nfv.Forwarder]
##########################################################################
# Capability Type.
# A Capability Type is a reusable entity that describes a kind of
# capability that a Node Type can declare to expose.
##########################################################################
tosca.capabilities.nfv.VirtualLinkable:
derived_from: tosca.capabilities.Root
tosca.capabilities.nfv.VirtualBindable:
derived_from: tosca.capabilities.Root
tosca.capabilities.nfv.HA:
derived_from: tosca.capabilities.Root
valid_source_types: [ tosca.nodes.nfv.VDU ]
tosca.capabilities.nfv.HA.ActiveActive:
derived_from: tosca.capabilities.nfv.HA
tosca.capabilities.nfv.HA.ActivePassive:
derived_from: tosca.capabilities.nfv.HA
tosca.capabilities.nfv.Metric:
derived_from: tosca.capabilities.Root
tosca.capabilities.nfv.Forwarder:
derived_from: tosca.capabilities.Root
##########################################################################
# Interfaces Type.
# The Interfaces element describes a list of one or more interface
# definitions for a modelable entity (e.g., a Node or Relationship Type)
# as defined within the TOSCA Simple Profile specification.
##########################################################################
##########################################################################
# Data Type.
# A Datatype is a complex data type declaration which contains other
# complex or simple data types.
##########################################################################
##########################################################################
# Artifact Type.
# An Artifact Type is a reusable entity that defines the type of one or more
# files which Node Types or Node Templates can have dependent relationships
# and used during operations such as during installation or deployment.
##########################################################################
##########################################################################
# Policy Type.
# TOSCA Policy Types represent logical grouping of TOSCA nodes that have
# an implied relationship and need to be orchestrated or managed together
# to achieve some result.
##########################################################################
##########################################################################
# Group Type
#
##########################################################################
tosca.groups.nfv.VNFFG:
derived_from: tosca.groups.Root
properties:
vendor:
type: string
required: true
description: name of the vendor who generate this VNFFG
version:
type: string
required: true
description: version of this VNFFG
number_of_endpoints:
type: integer
required: true
description: count of the external endpoints included in this VNFFG
dependent_virtual_link:
type: list
entry_schema:
type: string
required: true
description: Reference to a VLD used in this Forwarding Graph
connection_point:
type: list
entry_schema:
type: string
required: true
description: Reference to Connection Points forming the VNFFG
constituent_vnfs:
type: list
entry_schema:
type: string
required: true
description: Reference to a list of VNFD used in this VNF Forwarding Graph
targets:
type: list
entry_schema:
type: string
required: false
description: list of Network Forwarding Path within the VNFFG
requirements:
forwarder:
-capability: tosca.capabilities.nfv.Forwarder

View File

View File

@ -0,0 +1,17 @@
# 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.
# VERSION and DEFS_FILE are required for all extensions
VERSION = 'tosca_simple_profile_for_nfv_1_0_0'
DEFS_FILE = "TOSCA_nfv_definition_1_0.yaml"

View File

@ -0,0 +1,28 @@
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
description: Template for deploying a single server with predefined properties.
topology_template:
node_templates:
VNF1:
type: tosca.nodes.nfv.VNF
properties:
id: vnf1
vendor: acmetelco
version: 1.0
VDU1:
type: tosca.nodes.nfv.VDU
CP1:
type: tosca.nodes.nfv.CP
properties:
type: vPort
requirements:
- virtualLink: PrivateNetwork
- virtualBinding: VDU1
PrivateNetwork:
type: tosca.nodes.nfv.VL
properties:
vendor: ACME Networks

View File

@ -0,0 +1,29 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from toscaparser.tests.base import TestCase
from toscaparser.tosca_template import ToscaTemplate
class ToscaNFVTemplateTest(TestCase):
'''TOSCA NFV template.'''
tosca_tpl = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"data/tosca_helloworld_nfv.yaml")
tosca = ToscaTemplate(tosca_tpl)
def test_version(self):
self.assertEqual(self.tosca.version,
"tosca_simple_profile_for_nfv_1_0_0")

View File

@ -465,8 +465,9 @@ class ToscaTemplateTest(TestCase):
"data/test_multiple_validation_errors.yaml") "data/test_multiple_validation_errors.yaml")
self.assertRaises(exception.ValidationError, ToscaTemplate, tosca_tpl, self.assertRaises(exception.ValidationError, ToscaTemplate, tosca_tpl,
None) None)
err1_msg = _('The template version "tosca_simple_yaml_1" is invalid. ' valid_versions = ', '.join(ToscaTemplate.VALID_TEMPLATE_VERSIONS)
'Valid versions are "tosca_simple_yaml_1_0".') err1_msg = (_('The template version "tosca_simple_yaml_1" is invalid. '
'Valid versions are "%s".') % valid_versions)
exception.ExceptionCollector.assertExceptionMessage( exception.ExceptionCollector.assertExceptionMessage(
exception.InvalidTemplateVersion, err1_msg) exception.InvalidTemplateVersion, err1_msg)

View File

@ -19,6 +19,8 @@ from toscaparser.common.exception import InvalidTemplateVersion
from toscaparser.common.exception import MissingRequiredFieldError from toscaparser.common.exception import MissingRequiredFieldError
from toscaparser.common.exception import UnknownFieldError from toscaparser.common.exception import UnknownFieldError
from toscaparser.common.exception import ValidationError from toscaparser.common.exception import ValidationError
from toscaparser.elements.entity_type import update_definitions
from toscaparser.extensions.exttools import ExtTools
import toscaparser.imports import toscaparser.imports
from toscaparser.prereq.csar import CSAR from toscaparser.prereq.csar import CSAR
from toscaparser.topology_template import TopologyTemplate from toscaparser.topology_template import TopologyTemplate
@ -38,7 +40,8 @@ SECTIONS = (DEFINITION_VERSION, DEFAULT_NAMESPACE, TEMPLATE_NAME,
'template_version', 'description', 'imports', 'dsl_definitions', 'template_version', 'description', 'imports', 'dsl_definitions',
'node_types', 'relationship_types', 'relationship_templates', 'node_types', 'relationship_types', 'relationship_templates',
'capability_types', 'artifact_types', 'datatype_definitions') 'capability_types', 'artifact_types', 'datatype_definitions')
# Special key names
# Sections that are specific to individual template definitions
SPECIAL_SECTIONS = (METADATA) = ('metadata') SPECIAL_SECTIONS = (METADATA) = ('metadata')
log = logging.getLogger("tosca.model") log = logging.getLogger("tosca.model")
@ -47,9 +50,16 @@ YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
class ToscaTemplate(object): class ToscaTemplate(object):
exttools = ExtTools()
VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0'] VALID_TEMPLATE_VERSIONS = ['tosca_simple_yaml_1_0']
VALID_TEMPLATE_VERSIONS.extend(exttools.get_versions())
ADDITIONAL_SECTIONS = {'tosca_simple_yaml_1_0': SPECIAL_SECTIONS}
ADDITIONAL_SECTIONS.update(exttools.get_sections())
'''Load the template data.''' '''Load the template data.'''
def __init__(self, path, parsed_params=None, a_file=True): def __init__(self, path, parsed_params=None, a_file=True):
ExceptionCollector.start() ExceptionCollector.start()
@ -171,7 +181,8 @@ class ToscaTemplate(object):
self.version = version self.version = version
for name in self.tpl: for name in self.tpl:
if name not in SECTIONS and name not in SPECIAL_SECTIONS: if (name not in SECTIONS and
name not in self.ADDITIONAL_SECTIONS.get(version, ())):
ExceptionCollector.appendException( ExceptionCollector.appendException(
UnknownFieldError(what='Template', field=name)) UnknownFieldError(what='Template', field=name))
@ -181,6 +192,9 @@ class ToscaTemplate(object):
InvalidTemplateVersion( InvalidTemplateVersion(
what=version, what=version,
valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS))) valid_versions=', '. join(self.VALID_TEMPLATE_VERSIONS)))
else:
if version != 'tosca_simple_yaml_1_0':
update_definitions(version)
def _get_path(self, path): def _get_path(self, path):
if path.lower().endswith('.yaml'): if path.lower().endswith('.yaml'):