Enhanced tosca validation
Using new exception class instead of built-in exception types. Added validation for tosca template field names. Partially implements: blueprint tosca-validation Implements: blueprint check-key-definition Change-Id: Ib6aadffc8922154f5b86da29ea29c9b603f4cfd8
This commit is contained in:
0
translator/toscalib/common/__init__.py
Normal file
0
translator/toscalib/common/__init__.py
Normal file
74
translator/toscalib/common/exception.py
Normal file
74
translator/toscalib/common/exception.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
'''
|
||||||
|
TOSCA exception classes
|
||||||
|
'''
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from translator.toscalib.utils.gettextutils import _
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TOSCAException(Exception):
|
||||||
|
'''Base exception class for TOSCA
|
||||||
|
|
||||||
|
To correctly use this class, inherit from it and define
|
||||||
|
a 'msg_fmt' property.
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
_FATAL_EXCEPTION_FORMAT_ERRORS = False
|
||||||
|
|
||||||
|
message = _('An unknown exception occurred.')
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
try:
|
||||||
|
self.message = self.msg_fmt % kwargs
|
||||||
|
except KeyError:
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
log.exception(_('Exception in string format operation: %s')
|
||||||
|
% exc_info[1])
|
||||||
|
|
||||||
|
if TOSCAException._FATAL_EXCEPTION_FORMAT_ERRORS:
|
||||||
|
raise exc_info[0]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_fatal_format_exception(flag):
|
||||||
|
if isinstance(flag, bool):
|
||||||
|
TOSCAException._FATAL_EXCEPTION_FORMAT_ERRORS = flag
|
||||||
|
|
||||||
|
|
||||||
|
class MissingRequiredFieldError(TOSCAException):
|
||||||
|
msg_fmt = _('%(what)s is missing required field: "%(required)s".')
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownFieldError(TOSCAException):
|
||||||
|
msg_fmt = _('%(what)s contain(s) unknown field: "%(field)s", '
|
||||||
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
|
|
||||||
|
|
||||||
|
class TypeMismatchError(TOSCAException):
|
||||||
|
msg_fmt = _('%(what)s must be of type: "%(type)s".')
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidNodeTypeError(TOSCAException):
|
||||||
|
msg_fmt = _('Node type "%(what)s" is not a valid type.')
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
||||||
from translator.toscalib.functions import get_function
|
from translator.toscalib.functions import get_function
|
||||||
|
|
||||||
@@ -20,6 +21,8 @@ SECTIONS = (LIFECYCLE, CONFIGURE) = \
|
|||||||
('tosca.interfaces.node.Lifecycle',
|
('tosca.interfaces.node.Lifecycle',
|
||||||
'tosca.interfaces.relationship.Configure')
|
'tosca.interfaces.relationship.Configure')
|
||||||
|
|
||||||
|
INTERFACEVALUE = (IMPLEMENTATION, INPUT) = ('implementation', 'input')
|
||||||
|
|
||||||
|
|
||||||
class InterfacesDef(StatefulEntityType):
|
class InterfacesDef(StatefulEntityType):
|
||||||
'''TOSCA built-in interfaces type.'''
|
'''TOSCA built-in interfaces type.'''
|
||||||
@@ -41,8 +44,12 @@ class InterfacesDef(StatefulEntityType):
|
|||||||
for i, j in self.value.items():
|
for i, j in self.value.items():
|
||||||
if i == 'implementation':
|
if i == 'implementation':
|
||||||
self.implementation = j
|
self.implementation = j
|
||||||
if i == 'input':
|
elif i == 'input':
|
||||||
self.input = self._create_input_functions(j)
|
self.input = self._create_input_functions(j)
|
||||||
|
else:
|
||||||
|
what = ('Interfaces of node template %s' %
|
||||||
|
self.node_template.name)
|
||||||
|
raise UnknownFieldError(what=what, field=i)
|
||||||
else:
|
else:
|
||||||
self.implementation = value
|
self.implementation = value
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import InvalidNodeTypeError
|
||||||
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
||||||
from translator.toscalib.elements.interfaces import InterfacesDef
|
from translator.toscalib.elements.interfaces import InterfacesDef
|
||||||
from translator.toscalib.elements.property_definition import PropertyDef
|
from translator.toscalib.elements.property_definition import PropertyDef
|
||||||
from translator.toscalib.elements.relationshiptype import RelationshipType
|
from translator.toscalib.elements.relationshiptype import RelationshipType
|
||||||
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
from translator.toscalib.elements.statefulentitytype import StatefulEntityType
|
||||||
from translator.toscalib.utils.gettextutils import _
|
|
||||||
|
|
||||||
|
|
||||||
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
||||||
@@ -37,8 +37,7 @@ class NodeType(StatefulEntityType):
|
|||||||
elif custom_def and ntype in list(custom_def.keys()):
|
elif custom_def and ntype in list(custom_def.keys()):
|
||||||
self.defs = custom_def[ntype]
|
self.defs = custom_def[ntype]
|
||||||
else:
|
else:
|
||||||
raise ValueError(_('Node type %(ntype)s is not a valid type.')
|
raise InvalidNodeTypeError(what=ntype)
|
||||||
% {'ntype': ntype})
|
|
||||||
self.type = ntype
|
self.type = ntype
|
||||||
self.related = {}
|
self.related = {}
|
||||||
|
|
||||||
@@ -69,16 +68,7 @@ class NodeType(StatefulEntityType):
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
relationship = {}
|
relationship = {}
|
||||||
requires = self.requirements
|
requires = self.get_all_requirements()
|
||||||
parent_node = self.parent_type
|
|
||||||
if requires is None:
|
|
||||||
requires = self.get_value(REQUIREMENTS, None, True)
|
|
||||||
parent_node = parent_node.parent_type
|
|
||||||
if parent_node:
|
|
||||||
while parent_node.type != 'tosca.nodes.Root':
|
|
||||||
req = parent_node.get_value(REQUIREMENTS, None, True)
|
|
||||||
requires.extend(req)
|
|
||||||
parent_node = parent_node.parent_type
|
|
||||||
if requires:
|
if requires:
|
||||||
for req in requires:
|
for req in requires:
|
||||||
for key, value in req.items():
|
for key, value in req.items():
|
||||||
@@ -128,6 +118,19 @@ class NodeType(StatefulEntityType):
|
|||||||
def requirements(self):
|
def requirements(self):
|
||||||
return self.get_value(REQUIREMENTS)
|
return self.get_value(REQUIREMENTS)
|
||||||
|
|
||||||
|
def get_all_requirements(self):
|
||||||
|
requires = self.requirements
|
||||||
|
parent_node = self.parent_type
|
||||||
|
if requires is None:
|
||||||
|
requires = self.get_value(REQUIREMENTS, None, True)
|
||||||
|
parent_node = parent_node.parent_type
|
||||||
|
if parent_node:
|
||||||
|
while parent_node.type != 'tosca.nodes.Root':
|
||||||
|
req = parent_node.get_value(REQUIREMENTS, None, True)
|
||||||
|
requires.extend(req)
|
||||||
|
parent_node = parent_node.parent_type
|
||||||
|
return requires
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def interfaces(self):
|
def interfaces(self):
|
||||||
return self.get_value(INTERFACES)
|
return self.get_value(INTERFACES)
|
||||||
|
|||||||
@@ -16,16 +16,20 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import TypeMismatchError
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
from translator.toscalib.elements.capabilitytype import CapabilityTypeDef
|
||||||
from translator.toscalib.elements.interfaces import InterfacesDef
|
from translator.toscalib.elements.interfaces import InterfacesDef
|
||||||
|
from translator.toscalib.elements.interfaces import LIFECYCLE, CONFIGURE
|
||||||
from translator.toscalib.elements.nodetype import NodeType
|
from translator.toscalib.elements.nodetype import NodeType
|
||||||
from translator.toscalib.properties import Property
|
from translator.toscalib.properties import Property
|
||||||
from translator.toscalib.utils.gettextutils import _
|
|
||||||
|
|
||||||
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
SECTIONS = (DERIVED_FROM, PROPERTIES, REQUIREMENTS,
|
||||||
INTERFACES, CAPABILITIES) = \
|
INTERFACES, CAPABILITIES, TYPE) = \
|
||||||
('derived_from', 'properties', 'requirements', 'interfaces',
|
('derived_from', 'properties', 'requirements', 'interfaces',
|
||||||
'capabilities')
|
'capabilities', 'type')
|
||||||
|
|
||||||
log = logging.getLogger('tosca')
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
@@ -34,10 +38,11 @@ class NodeTemplate(object):
|
|||||||
'''Node template from a Tosca profile.'''
|
'''Node template from a Tosca profile.'''
|
||||||
def __init__(self, name, node_templates, custom_def=None):
|
def __init__(self, name, node_templates, custom_def=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.node_type = NodeType(node_templates[name]['type'], custom_def)
|
|
||||||
self.node_templates = node_templates
|
self.node_templates = node_templates
|
||||||
|
self._validate_field()
|
||||||
self.node_template = node_templates[self.name]
|
self.node_template = node_templates[self.name]
|
||||||
self.type = self.node_template['type']
|
self.type = self.node_template[TYPE]
|
||||||
|
self.node_type = NodeType(self.type, custom_def)
|
||||||
self.related = {}
|
self.related = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -95,30 +100,12 @@ class NodeTemplate(object):
|
|||||||
def properties(self):
|
def properties(self):
|
||||||
props = []
|
props = []
|
||||||
properties = self.node_type.get_value(PROPERTIES, self.node_template)
|
properties = self.node_type.get_value(PROPERTIES, self.node_template)
|
||||||
requiredprop = []
|
|
||||||
for p in self.node_type.properties_def:
|
|
||||||
if p.required:
|
|
||||||
requiredprop.append(p.name)
|
|
||||||
if properties:
|
if properties:
|
||||||
#make sure it's not missing any property required by a node type
|
|
||||||
missingprop = []
|
|
||||||
for r in requiredprop:
|
|
||||||
if r not in properties.keys():
|
|
||||||
missingprop.append(r)
|
|
||||||
if missingprop:
|
|
||||||
raise ValueError(_("Node template %(tpl)s is missing "
|
|
||||||
"one or more required properties %(prop)s")
|
|
||||||
% {'tpl': self.name, 'prop': missingprop})
|
|
||||||
for name, value in properties.items():
|
for name, value in properties.items():
|
||||||
for p in self.node_type.properties_def:
|
for p in self.node_type.properties_def:
|
||||||
if p.name == name:
|
if p.name == name:
|
||||||
prop = Property(name, value, p.schema)
|
prop = Property(name, value, p.schema)
|
||||||
props.append(prop)
|
props.append(prop)
|
||||||
else:
|
|
||||||
if requiredprop:
|
|
||||||
raise ValueError(_("Node template %(tpl)s is missing"
|
|
||||||
"one or more required properties %(prop)s")
|
|
||||||
% {'tpl': self.name, 'prop': requiredprop})
|
|
||||||
return props
|
return props
|
||||||
|
|
||||||
def _add_next(self, nodetpl, relationship):
|
def _add_next(self, nodetpl, relationship):
|
||||||
@@ -151,5 +138,103 @@ class NodeTemplate(object):
|
|||||||
return c.property_value
|
return c.property_value
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self._validate_capabilities()
|
||||||
|
self._validate_requirments()
|
||||||
|
self._validate_properties()
|
||||||
|
self._validate_interfaces()
|
||||||
for prop in self.properties:
|
for prop in self.properties:
|
||||||
prop.validate()
|
prop.validate()
|
||||||
|
|
||||||
|
def _validate_capabilities(self):
|
||||||
|
type_capabilities = self.node_type.capabilities
|
||||||
|
allowed_caps = []
|
||||||
|
if type_capabilities:
|
||||||
|
for tcap in type_capabilities:
|
||||||
|
allowed_caps.append(tcap.name)
|
||||||
|
capabilities = self.node_type.get_value(CAPABILITIES,
|
||||||
|
self.node_template)
|
||||||
|
if capabilities:
|
||||||
|
self._common_validate_field(capabilities, allowed_caps,
|
||||||
|
'Capabilities')
|
||||||
|
|
||||||
|
def _validate_requirments(self):
|
||||||
|
type_requires = self.node_type.get_all_requirements()
|
||||||
|
allowed_reqs = []
|
||||||
|
if type_requires:
|
||||||
|
for treq in type_requires:
|
||||||
|
for key in treq:
|
||||||
|
allowed_reqs.append(key)
|
||||||
|
requires = self.node_type.get_value(REQUIREMENTS, self.node_template)
|
||||||
|
if requires:
|
||||||
|
if not isinstance(requires, list):
|
||||||
|
raise TypeMismatchError(
|
||||||
|
what='Requirements of node template %s' % self.name,
|
||||||
|
type='list')
|
||||||
|
for req in requires:
|
||||||
|
self._common_validate_field(req, allowed_reqs, 'Requirements')
|
||||||
|
|
||||||
|
def _validate_interfaces(self):
|
||||||
|
ifaces = self.node_type.get_value(INTERFACES, self.node_template)
|
||||||
|
if ifaces:
|
||||||
|
for i in ifaces:
|
||||||
|
for name, value in ifaces.items():
|
||||||
|
if name == LIFECYCLE:
|
||||||
|
self._common_validate_field(
|
||||||
|
value, InterfacesDef.
|
||||||
|
interfaces_node_lifecycle_operations,
|
||||||
|
'Interfaces')
|
||||||
|
elif name == CONFIGURE:
|
||||||
|
self._common_validate_field(
|
||||||
|
value, InterfacesDef.
|
||||||
|
interfaces_relationship_confiure_operations,
|
||||||
|
'Interfaces')
|
||||||
|
else:
|
||||||
|
raise UnknownFieldError(
|
||||||
|
what='Interfaces of node template %s' % self.name,
|
||||||
|
field=name)
|
||||||
|
|
||||||
|
def _validate_properties(self):
|
||||||
|
properties = self.node_type.get_value(PROPERTIES, self.node_template)
|
||||||
|
allowed_props = []
|
||||||
|
required_props = []
|
||||||
|
for p in self.node_type.properties_def:
|
||||||
|
allowed_props.append(p.name)
|
||||||
|
if p.required:
|
||||||
|
required_props.append(p.name)
|
||||||
|
if properties:
|
||||||
|
self._common_validate_field(properties, allowed_props,
|
||||||
|
'Properties')
|
||||||
|
#make sure it's not missing any property required by a node type
|
||||||
|
missingprop = []
|
||||||
|
for r in required_props:
|
||||||
|
if r not in properties.keys():
|
||||||
|
missingprop.append(r)
|
||||||
|
if missingprop:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Properties of node template %s' % self.name,
|
||||||
|
required=missingprop)
|
||||||
|
else:
|
||||||
|
if required_props:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Properties of node template %s' % self.name,
|
||||||
|
required=missingprop)
|
||||||
|
|
||||||
|
def _validate_field(self):
|
||||||
|
if not isinstance(self.node_templates[self.name], dict):
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Node template %s' % self.name, required=TYPE)
|
||||||
|
try:
|
||||||
|
self.node_templates[self.name][TYPE]
|
||||||
|
except KeyError:
|
||||||
|
raise MissingRequiredFieldError(
|
||||||
|
what='Node template %s' % self.name, required=TYPE)
|
||||||
|
self._common_validate_field(self.node_templates[self.name], SECTIONS,
|
||||||
|
'Second level')
|
||||||
|
|
||||||
|
def _common_validate_field(self, schema, allowedlist, section):
|
||||||
|
for name in schema:
|
||||||
|
if name not in allowedlist:
|
||||||
|
raise UnknownFieldError(
|
||||||
|
what='%(section)s of node template %(nodename)s'
|
||||||
|
% {'section': section, 'nodename': self.name},
|
||||||
|
field=name)
|
||||||
|
|||||||
@@ -16,41 +16,63 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.elements.constraints import Constraint
|
from translator.toscalib.elements.constraints import Constraint
|
||||||
from translator.toscalib.properties import Property
|
from translator.toscalib.properties import Property
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('tosca')
|
log = logging.getLogger('tosca')
|
||||||
|
|
||||||
|
|
||||||
class Input(object):
|
class Input(object):
|
||||||
|
|
||||||
|
INPUTFIELD = (TYPE, DESCRIPTION, DEFAULT, CONSTRAINTS) = \
|
||||||
|
('type', 'description', 'default', 'constraints')
|
||||||
|
|
||||||
def __init__(self, name, schema):
|
def __init__(self, name, schema):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.schema = schema
|
self.schema = schema
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return self.schema['type']
|
return self.schema[self.TYPE]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
if Property.DESCRIPTION in self.schema:
|
if Property.DESCRIPTION in self.schema:
|
||||||
return self.schema['description']
|
return self.schema[self.DESCRIPTION]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default(self):
|
def default(self):
|
||||||
if self.Property.DEFAULT in self.schema:
|
if self.Property.DEFAULT in self.schema:
|
||||||
return self.schema['default']
|
return self.schema[self.DEFAULT]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def constraints(self):
|
def constraints(self):
|
||||||
if Property.CONSTRAINTS in self.schema:
|
if Property.CONSTRAINTS in self.schema:
|
||||||
return self.schema['constraints']
|
return self.schema[self.CONSTRAINTS]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self._validate_field()
|
||||||
self.validate_type(self.type)
|
self.validate_type(self.type)
|
||||||
if self.constraints:
|
if self.constraints:
|
||||||
self.validate_constraints(self.constraints)
|
self.validate_constraints(self.constraints)
|
||||||
|
|
||||||
|
def _validate_field(self):
|
||||||
|
if not isinstance(self.schema, dict):
|
||||||
|
raise MissingRequiredFieldError(what='Input %s' % self.name,
|
||||||
|
required=self.TYPE)
|
||||||
|
try:
|
||||||
|
self.type
|
||||||
|
except KeyError:
|
||||||
|
raise MissingRequiredFieldError(what='Input %s' % self.name,
|
||||||
|
required=self.TYPE)
|
||||||
|
for name in self.schema:
|
||||||
|
if name not in self.INPUTFIELD:
|
||||||
|
raise UnknownFieldError(what='Input %s' % self.name,
|
||||||
|
field=name)
|
||||||
|
|
||||||
def validate_type(self, input_type):
|
def validate_type(self, input_type):
|
||||||
if input_type not in Property.PROPERTIY_TYPES:
|
if input_type not in Property.PROPERTIY_TYPES:
|
||||||
raise ValueError(_('Invalid type %s') % type)
|
raise ValueError(_('Invalid type %s') % type)
|
||||||
@@ -63,14 +85,34 @@ class Input(object):
|
|||||||
|
|
||||||
|
|
||||||
class Output(object):
|
class Output(object):
|
||||||
|
|
||||||
|
OUTPUTFIELD = (DESCRIPTION, VALUE) = ('description', 'value')
|
||||||
|
|
||||||
def __init__(self, name, attrs):
|
def __init__(self, name, attrs):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.attrs = attrs
|
self.attrs = attrs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
return self.attrs['description']
|
return self.attrs[self.DESCRIPTION]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def value(self):
|
def value(self):
|
||||||
return self.attrs['value']
|
return self.attrs[self.VALUE]
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
self._validate_field()
|
||||||
|
|
||||||
|
def _validate_field(self):
|
||||||
|
if not isinstance(self.attrs, dict):
|
||||||
|
raise MissingRequiredFieldError(what='Output %s' % self.name,
|
||||||
|
required=self.VALUE)
|
||||||
|
try:
|
||||||
|
self.value
|
||||||
|
except KeyError:
|
||||||
|
raise MissingRequiredFieldError(what='Output %s' % self.name,
|
||||||
|
required=self.VALUE)
|
||||||
|
for name in self.attrs:
|
||||||
|
if name not in self.OUTPUTFIELD:
|
||||||
|
raise UnknownFieldError(what='Output %s' % self.name,
|
||||||
|
field=name)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
description: >
|
||||||
|
TOSCA simple profile missing version section.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
|
||||||
|
node_templates:
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
server_address:
|
||||||
|
description: IP address of server instance.
|
||||||
|
value: { get_property: [server, ip_address] }
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
tosca_definitions_version: tosca_simple_1.0
|
||||||
|
|
||||||
|
description: >
|
||||||
|
TOSCA simple profile with invalid top-level key: 'node_template'.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
|
||||||
|
node_template:
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
server_address:
|
||||||
|
description: IP address of server instance.
|
||||||
|
value: { get_property: [server, ip_address] }
|
||||||
44
translator/toscalib/tests/test_exception.py
Normal file
44
translator/toscalib/tests/test_exception.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import TOSCAException
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
|
from translator.toscalib.tests.base import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestCase, self).setUp()
|
||||||
|
TOSCAException.set_fatal_format_exception(False)
|
||||||
|
|
||||||
|
def test_message(self):
|
||||||
|
ex = MissingRequiredFieldError(what='Template', required='type')
|
||||||
|
self.assertEqual('Template is missing required field: "type".',
|
||||||
|
ex.__str__())
|
||||||
|
|
||||||
|
def test_set_flag(self):
|
||||||
|
TOSCAException.set_fatal_format_exception('True')
|
||||||
|
self.assertFalse(TOSCAException._FATAL_EXCEPTION_FORMAT_ERRORS)
|
||||||
|
|
||||||
|
def test_format_error(self):
|
||||||
|
ex = UnknownFieldError(what='Template')
|
||||||
|
self.assertEqual('An unknown exception occurred.', ex.__str__(),)
|
||||||
|
self.assertRaises(KeyError, self._formate_exception)
|
||||||
|
|
||||||
|
def _formate_exception(self):
|
||||||
|
UnknownFieldError.set_fatal_format_exception(True)
|
||||||
|
raise UnknownFieldError(what='Template')
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import InvalidNodeTypeError
|
||||||
from translator.toscalib.elements.nodetype import NodeType
|
from translator.toscalib.elements.nodetype import NodeType
|
||||||
from translator.toscalib.tests.base import TestCase
|
from translator.toscalib.tests.base import TestCase
|
||||||
compute_type = NodeType('tosca.nodes.Compute')
|
compute_type = NodeType('tosca.nodes.Compute')
|
||||||
@@ -22,7 +23,8 @@ component_type = NodeType('tosca.nodes.SoftwareComponent')
|
|||||||
class ToscaDefTest(TestCase):
|
class ToscaDefTest(TestCase):
|
||||||
def test_type(self):
|
def test_type(self):
|
||||||
self.assertEqual(compute_type.type, "tosca.nodes.Compute")
|
self.assertEqual(compute_type.type, "tosca.nodes.Compute")
|
||||||
self.assertRaises(ValueError, NodeType, 'tosca.nodes.Invalid')
|
self.assertRaises(InvalidNodeTypeError, NodeType,
|
||||||
|
'tosca.nodes.Invalid')
|
||||||
|
|
||||||
def test_parent_type(self):
|
def test_parent_type(self):
|
||||||
self.assertEqual(compute_type.parent_type.type, "tosca.nodes.Root")
|
self.assertEqual(compute_type.parent_type.type, "tosca.nodes.Root")
|
||||||
|
|||||||
430
translator/toscalib/tests/test_toscatplvalidation.py
Normal file
430
translator/toscalib/tests/test_toscatplvalidation.py
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
#
|
||||||
|
# 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 translator.toscalib.common.exception import InvalidNodeTypeError
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import TypeMismatchError
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
|
from translator.toscalib.nodetemplate import NodeTemplate
|
||||||
|
from translator.toscalib.parameters import Input, Output
|
||||||
|
from translator.toscalib.tests.base import TestCase
|
||||||
|
from translator.toscalib.tosca_template import ToscaTemplate
|
||||||
|
import translator.toscalib.utils.yamlparser
|
||||||
|
|
||||||
|
|
||||||
|
class ToscaTemplateValidationTest(TestCase):
|
||||||
|
|
||||||
|
def test_well_defined_template(self):
|
||||||
|
tpl_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/tosca_single_instance_wordpress.yaml")
|
||||||
|
self.assertIsNotNone(ToscaTemplate(tpl_path))
|
||||||
|
|
||||||
|
def test_first_level_sections(self):
|
||||||
|
tpl_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/test_tosca_top_level_error1.yaml")
|
||||||
|
try:
|
||||||
|
ToscaTemplate(tpl_path)
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, MissingRequiredFieldError))
|
||||||
|
self.assertEqual('Template is missing required field: '
|
||||||
|
'"tosca_definitions_version".', err.__str__())
|
||||||
|
|
||||||
|
tpl_path = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/test_tosca_top_level_error2.yaml")
|
||||||
|
try:
|
||||||
|
ToscaTemplate(tpl_path)
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, UnknownFieldError))
|
||||||
|
self.assertEqual('Template contain(s) unknown field: '
|
||||||
|
'"node_template", refer to the TOSCA specs '
|
||||||
|
'to verify valid values.', err.__str__())
|
||||||
|
|
||||||
|
def test_inputs(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraints:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
'''
|
||||||
|
inputs = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet)['inputs'])
|
||||||
|
name, attrs = list(inputs.items())[0]
|
||||||
|
input = Input(name, attrs)
|
||||||
|
try:
|
||||||
|
input.validate()
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, MissingRequiredFieldError))
|
||||||
|
self.assertEqual('Input cpus is missing required field: '
|
||||||
|
'"type".', err.__str__())
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
inputs:
|
||||||
|
cpus:
|
||||||
|
type: integer
|
||||||
|
description: Number of CPUs for the server.
|
||||||
|
constraint:
|
||||||
|
- valid_values: [ 1, 2, 4, 8 ]
|
||||||
|
'''
|
||||||
|
inputs = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet)['inputs'])
|
||||||
|
name, attrs = list(inputs.items())[0]
|
||||||
|
input = Input(name, attrs)
|
||||||
|
try:
|
||||||
|
input.validate()
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, UnknownFieldError))
|
||||||
|
self.assertEqual('Input cpus contain(s) unknown field: '
|
||||||
|
'"constraint", refer to the TOSCA specs to '
|
||||||
|
'verify valid values.', err.__str__())
|
||||||
|
|
||||||
|
def test_outputs(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
outputs:
|
||||||
|
server_address:
|
||||||
|
description: IP address of server instance.
|
||||||
|
values: { get_property: [server, ip_address] }
|
||||||
|
'''
|
||||||
|
outputs = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet)['outputs'])
|
||||||
|
name, attrs = list(outputs.items())[0]
|
||||||
|
output = Output(name, attrs)
|
||||||
|
try:
|
||||||
|
output.validate()
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, MissingRequiredFieldError))
|
||||||
|
self.assertEqual('Output server_address is missing required '
|
||||||
|
'field: "value".', err.__str__())
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
outputs:
|
||||||
|
server_address:
|
||||||
|
descriptions: IP address of server instance.
|
||||||
|
value: { get_property: [server, ip_address] }
|
||||||
|
'''
|
||||||
|
outputs = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet)['outputs'])
|
||||||
|
name, attrs = list(outputs.items())[0]
|
||||||
|
output = Output(name, attrs)
|
||||||
|
try:
|
||||||
|
output.validate()
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, UnknownFieldError))
|
||||||
|
self.assertEqual('Output server_address contain(s) unknown '
|
||||||
|
'field: "descriptions", refer to the TOSCA '
|
||||||
|
'specs to verify valid values.',
|
||||||
|
err.__str__())
|
||||||
|
|
||||||
|
def _custom_types(self):
|
||||||
|
custom_types = {}
|
||||||
|
def_file = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"data/custom_types/wordpress.yaml")
|
||||||
|
custom_type = translator.toscalib.utils.yamlparser.load_yaml(def_file)
|
||||||
|
node_types = custom_type['node_types']
|
||||||
|
for name in node_types:
|
||||||
|
defintion = node_types[name]
|
||||||
|
custom_types[name] = defintion
|
||||||
|
return custom_types
|
||||||
|
|
||||||
|
def _single_node_template_content_test(self, tpl_snippet, expectederror,
|
||||||
|
expectedmessage):
|
||||||
|
nodetemplates = (translator.toscalib.utils.yamlparser.
|
||||||
|
simple_parse(tpl_snippet))['node_templates']
|
||||||
|
name = list(nodetemplates.keys())[0]
|
||||||
|
try:
|
||||||
|
nodetemplate = NodeTemplate(name, nodetemplates,
|
||||||
|
self._custom_types())
|
||||||
|
nodetemplate.validate()
|
||||||
|
nodetemplate.requirements
|
||||||
|
nodetemplate.capabilities
|
||||||
|
nodetemplate.properties
|
||||||
|
nodetemplate.interfaces
|
||||||
|
except Exception as err:
|
||||||
|
self.assertTrue(isinstance(err, expectederror))
|
||||||
|
self.assertEqual(expectedmessage, err.__str__())
|
||||||
|
|
||||||
|
def test_node_templates(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
server:
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Node template server is missing '
|
||||||
|
'required field: "type".')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
MissingRequiredFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
mysql_dbms:
|
||||||
|
type: tosca.nodes.DBMS
|
||||||
|
properties:
|
||||||
|
dbms_root_password: { get_input: db_root_pwd }
|
||||||
|
dbms_port: { get_input: db_port }
|
||||||
|
requirement:
|
||||||
|
- host: server
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
create: mysql_dbms_install.sh
|
||||||
|
start: mysql_dbms_start.sh
|
||||||
|
configure:
|
||||||
|
implementation: mysql_dbms_configure.sh
|
||||||
|
input:
|
||||||
|
db_root_password: { get_property: [ mysql_dbms, \
|
||||||
|
dbms_root_password ] }
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Second level of node template mysql_dbms '
|
||||||
|
'contain(s) unknown field: "requirement", '
|
||||||
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_node_template_type(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
mysql_database:
|
||||||
|
type: tosca.nodes.Databases
|
||||||
|
properties:
|
||||||
|
db_name: { get_input: db_name }
|
||||||
|
db_user: { get_input: db_user }
|
||||||
|
db_password: { get_input: db_pwd }
|
||||||
|
capabilities:
|
||||||
|
database_endpoint:
|
||||||
|
properties:
|
||||||
|
port: { get_input: db_port }
|
||||||
|
requirements:
|
||||||
|
- host: mysql_dbms
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
configure: mysql_database_configure.sh
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Node type "tosca.nodes.Databases" is not '
|
||||||
|
'a valid type.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
InvalidNodeTypeError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_node_template_requirements(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
webserver:
|
||||||
|
type: tosca.nodes.WebServer
|
||||||
|
requirements:
|
||||||
|
host: server
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
create: webserver_install.sh
|
||||||
|
start: webserver_start.sh
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Requirements of node template webserver '
|
||||||
|
'must be of type: "list".')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
TypeMismatchError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
mysql_database:
|
||||||
|
type: tosca.nodes.Database
|
||||||
|
properties:
|
||||||
|
db_name: { get_input: db_name }
|
||||||
|
db_user: { get_input: db_user }
|
||||||
|
db_password: { get_input: db_pwd }
|
||||||
|
capabilities:
|
||||||
|
database_endpoint:
|
||||||
|
properties:
|
||||||
|
port: { get_input: db_port }
|
||||||
|
requirements:
|
||||||
|
- host: mysql_dbms
|
||||||
|
- database_endpoint: mysql_database
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
configure: mysql_database_configure.sh
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Requirements of node template mysql_database '
|
||||||
|
'contain(s) unknown field: "database_endpoint", '
|
||||||
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_node_template_capabilities(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
mysql_database:
|
||||||
|
type: tosca.nodes.Database
|
||||||
|
properties:
|
||||||
|
db_name: { get_input: db_name }
|
||||||
|
db_user: { get_input: db_user }
|
||||||
|
db_password: { get_input: db_pwd }
|
||||||
|
capabilities:
|
||||||
|
http_endpoint:
|
||||||
|
properties:
|
||||||
|
port: { get_input: db_port }
|
||||||
|
requirements:
|
||||||
|
- host: mysql_dbms
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
configure: mysql_database_configure.sh
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Capabilities of node template mysql_database '
|
||||||
|
'contain(s) unknown field: "http_endpoint", '
|
||||||
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_node_template_properties(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Properties of node template server is missing '
|
||||||
|
'required field: "[\'os_type\']".')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
MissingRequiredFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
server:
|
||||||
|
type: tosca.nodes.Compute
|
||||||
|
properties:
|
||||||
|
# compute properties (flavor)
|
||||||
|
disk_size: 10
|
||||||
|
num_cpus: { get_input: cpus }
|
||||||
|
mem_size: 4096
|
||||||
|
# host image properties
|
||||||
|
os_arch: x86_64
|
||||||
|
os_type: Linux
|
||||||
|
os_distribution: Fedora
|
||||||
|
os_version: 18
|
||||||
|
os_image: F18_x86_64
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Properties of node template server contain(s) '
|
||||||
|
'unknown field: "os_image", refer to the TOSCA '
|
||||||
|
'specs to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
def test_node_template_interfaces(self):
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
wordpress:
|
||||||
|
type: tosca.nodes.WebApplication.WordPress
|
||||||
|
requirements:
|
||||||
|
- host: webserver
|
||||||
|
- database_endpoint: mysql_database
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycles:
|
||||||
|
create: wordpress_install.sh
|
||||||
|
configure:
|
||||||
|
implementation: wordpress_configure.sh
|
||||||
|
input:
|
||||||
|
wp_db_name: { get_property: [ mysql_database, db_name ] }
|
||||||
|
wp_db_user: { get_property: [ mysql_database, db_user ] }
|
||||||
|
wp_db_password: { get_property: [ mysql_database, \
|
||||||
|
db_password ] }
|
||||||
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
|
database_endpoint, port ] }
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Interfaces of node template wordpress '
|
||||||
|
'contain(s) unknown field: '
|
||||||
|
'"tosca.interfaces.node.Lifecycles", '
|
||||||
|
'refer to the TOSCA specs to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
wordpress:
|
||||||
|
type: tosca.nodes.WebApplication.WordPress
|
||||||
|
requirements:
|
||||||
|
- host: webserver
|
||||||
|
- database_endpoint: mysql_database
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
create: wordpress_install.sh
|
||||||
|
config:
|
||||||
|
implementation: wordpress_configure.sh
|
||||||
|
input:
|
||||||
|
wp_db_name: { get_property: [ mysql_database, db_name ] }
|
||||||
|
wp_db_user: { get_property: [ mysql_database, db_user ] }
|
||||||
|
wp_db_password: { get_property: [ mysql_database, \
|
||||||
|
db_password ] }
|
||||||
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
|
database_endpoint, port ] }
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Interfaces of node template wordpress contain(s) '
|
||||||
|
'unknown field: "config", refer to the TOSCA specs'
|
||||||
|
' to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
|
|
||||||
|
tpl_snippet = '''
|
||||||
|
node_templates:
|
||||||
|
wordpress:
|
||||||
|
type: tosca.nodes.WebApplication.WordPress
|
||||||
|
requirements:
|
||||||
|
- host: webserver
|
||||||
|
- database_endpoint: mysql_database
|
||||||
|
interfaces:
|
||||||
|
tosca.interfaces.node.Lifecycle:
|
||||||
|
create: wordpress_install.sh
|
||||||
|
configure:
|
||||||
|
implementation: wordpress_configure.sh
|
||||||
|
inputs:
|
||||||
|
wp_db_name: { get_property: [ mysql_database, db_name ] }
|
||||||
|
wp_db_user: { get_property: [ mysql_database, db_user ] }
|
||||||
|
wp_db_password: { get_property: [ mysql_database, \
|
||||||
|
db_password ] }
|
||||||
|
wp_db_port: { get_ref_property: [ database_endpoint, \
|
||||||
|
database_endpoint, port ] }
|
||||||
|
'''
|
||||||
|
expectedmessage = ('Interfaces of node template wordpress contain(s) '
|
||||||
|
'unknown field: "inputs", refer to the TOSCA specs'
|
||||||
|
' to verify valid values.')
|
||||||
|
self._single_node_template_content_test(tpl_snippet,
|
||||||
|
UnknownFieldError,
|
||||||
|
expectedmessage)
|
||||||
@@ -16,13 +16,17 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from translator.toscalib.common.exception import MissingRequiredFieldError
|
||||||
|
from translator.toscalib.common.exception import UnknownFieldError
|
||||||
from translator.toscalib.nodetemplate import NodeTemplate
|
from translator.toscalib.nodetemplate import NodeTemplate
|
||||||
from translator.toscalib.parameters import Input, Output
|
from translator.toscalib.parameters import Input, Output
|
||||||
from translator.toscalib.tpl_relationship_graph import ToscaGraph
|
from translator.toscalib.tpl_relationship_graph import ToscaGraph
|
||||||
from translator.toscalib.utils.gettextutils import _
|
|
||||||
import translator.toscalib.utils.yamlparser
|
import translator.toscalib.utils.yamlparser
|
||||||
|
|
||||||
|
|
||||||
|
#Simple YAML definition A.4.1 Keynames
|
||||||
SECTIONS = (VERSION, DESCRIPTION, IMPORTS, INPUTS,
|
SECTIONS = (VERSION, DESCRIPTION, IMPORTS, INPUTS,
|
||||||
NODE_TEMPLATES, OUTPUTS) = \
|
NODE_TEMPLATES, OUTPUTS) = \
|
||||||
('tosca_definitions_version', 'description', 'imports', 'inputs',
|
('tosca_definitions_version', 'description', 'imports', 'inputs',
|
||||||
@@ -38,6 +42,7 @@ class ToscaTemplate(object):
|
|||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.tpl = YAML_LOADER(path)
|
self.tpl = YAML_LOADER(path)
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self._validate_field()
|
||||||
self.version = self._tpl_version()
|
self.version = self._tpl_version()
|
||||||
self.description = self._tpl_description()
|
self.description = self._tpl_description()
|
||||||
self.inputs = self._inputs()
|
self.inputs = self._inputs()
|
||||||
@@ -49,9 +54,6 @@ class ToscaTemplate(object):
|
|||||||
inputs = []
|
inputs = []
|
||||||
for name, attrs in self._tpl_inputs().items():
|
for name, attrs in self._tpl_inputs().items():
|
||||||
input = Input(name, attrs)
|
input = Input(name, attrs)
|
||||||
if not isinstance(input.schema, dict):
|
|
||||||
raise ValueError(_("The input %(input)s has no attributes.")
|
|
||||||
% {'input': input})
|
|
||||||
input.validate()
|
input.validate()
|
||||||
inputs.append(input)
|
inputs.append(input)
|
||||||
return inputs
|
return inputs
|
||||||
@@ -73,7 +75,7 @@ class ToscaTemplate(object):
|
|||||||
custom_types[name] = defintion
|
custom_types[name] = defintion
|
||||||
nodetemplates = []
|
nodetemplates = []
|
||||||
tpls = self._tpl_nodetemplates()
|
tpls = self._tpl_nodetemplates()
|
||||||
for name, value in tpls.items():
|
for name in tpls:
|
||||||
tpl = NodeTemplate(name, tpls, custom_types)
|
tpl = NodeTemplate(name, tpls, custom_types)
|
||||||
tpl.validate()
|
tpl.validate()
|
||||||
nodetemplates.append(tpl)
|
nodetemplates.append(tpl)
|
||||||
@@ -82,7 +84,9 @@ class ToscaTemplate(object):
|
|||||||
def _outputs(self):
|
def _outputs(self):
|
||||||
outputs = []
|
outputs = []
|
||||||
for name, attrs in self._tpl_outputs().items():
|
for name, attrs in self._tpl_outputs().items():
|
||||||
outputs.append(Output(name, attrs))
|
output = Output(name, attrs)
|
||||||
|
output.validate()
|
||||||
|
outputs.append(output)
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
def _tpl_version(self):
|
def _tpl_version(self):
|
||||||
@@ -103,3 +107,12 @@ class ToscaTemplate(object):
|
|||||||
|
|
||||||
def _tpl_outputs(self):
|
def _tpl_outputs(self):
|
||||||
return self.tpl[OUTPUTS]
|
return self.tpl[OUTPUTS]
|
||||||
|
|
||||||
|
def _validate_field(self):
|
||||||
|
try:
|
||||||
|
self._tpl_version()
|
||||||
|
except KeyError:
|
||||||
|
raise MissingRequiredFieldError(what='Template', required=VERSION)
|
||||||
|
for name in self.tpl:
|
||||||
|
if name not in SECTIONS:
|
||||||
|
raise UnknownFieldError(what='Template', field=name)
|
||||||
|
|||||||
@@ -24,3 +24,14 @@ else:
|
|||||||
def load_yaml(path):
|
def load_yaml(path):
|
||||||
with open(path) as f:
|
with open(path) as f:
|
||||||
return yaml.load(f.read(), Loader=yaml_loader)
|
return yaml.load(f.read(), Loader=yaml_loader)
|
||||||
|
|
||||||
|
|
||||||
|
def simple_parse(tmpl_str):
|
||||||
|
try:
|
||||||
|
tpl = yaml.load(tmpl_str, Loader=yaml_loader)
|
||||||
|
except yaml.YAMLError as yea:
|
||||||
|
raise ValueError(yea)
|
||||||
|
else:
|
||||||
|
if tpl is None:
|
||||||
|
tpl = {}
|
||||||
|
return tpl
|
||||||
|
|||||||
Reference in New Issue
Block a user