summaryrefslogtreecommitdiffstats
path: root/tosca2heat/tosca-parser/toscaparser
diff options
context:
space:
mode:
Diffstat (limited to 'tosca2heat/tosca-parser/toscaparser')
-rw-r--r--tosca2heat/tosca-parser/toscaparser/common/exception.py9
-rw-r--r--tosca2heat/tosca-parser/toscaparser/substitution_mappings.py77
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/queuingsubsystem_invalid_input.yaml79
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/system_invalid_input.yaml24
-rw-r--r--tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py57
5 files changed, 211 insertions, 35 deletions
diff --git a/tosca2heat/tosca-parser/toscaparser/common/exception.py b/tosca2heat/tosca-parser/toscaparser/common/exception.py
index f67a277..724844b 100644
--- a/tosca2heat/tosca-parser/toscaparser/common/exception.py
+++ b/tosca2heat/tosca-parser/toscaparser/common/exception.py
@@ -116,14 +116,19 @@ class UnknownInputError(TOSCAException):
class MissingRequiredInputError(TOSCAException):
msg_fmt = _('%(what)s is missing required input definition '
- ' with name: "%(input_name)s".')
+ 'of input "%(input_name)s".')
class MissingRequiredParameterError(TOSCAException):
- msg_fmt = _('%(what)s is missing required parameter for input: '
+ msg_fmt = _('%(what)s is missing required parameter for input '
'"%(input_name)s".')
+class MissingDefaultValueError(TOSCAException):
+ msg_fmt = _('%(what)s is missing required default value '
+ 'of input "%(input_name)s".')
+
+
class InvalidPropertyValueError(TOSCAException):
msg_fmt = _('Value of property "%(what)s" is invalid.')
diff --git a/tosca2heat/tosca-parser/toscaparser/substitution_mappings.py b/tosca2heat/tosca-parser/toscaparser/substitution_mappings.py
index 35ffdf3..d4653c3 100644
--- a/tosca2heat/tosca-parser/toscaparser/substitution_mappings.py
+++ b/tosca2heat/tosca-parser/toscaparser/substitution_mappings.py
@@ -14,9 +14,9 @@ import logging
from toscaparser.common.exception import ExceptionCollector
from toscaparser.common.exception import InvalidNodeTypeError
+from toscaparser.common.exception import MissingDefaultValueError
from toscaparser.common.exception import MissingRequiredFieldError
from toscaparser.common.exception import MissingRequiredInputError
-from toscaparser.common.exception import MissingRequiredParameterError
from toscaparser.common.exception import UnknownFieldError
from toscaparser.elements.nodetype import NodeType
from toscaparser.utils.gettextutils import _
@@ -27,7 +27,7 @@ log = logging.getLogger('tosca')
class SubstitutionMappings(object):
'''SubstitutionMappings class declaration
- Substitution_mappings exports the topology template as an
+ SubstitutionMappings exports the topology template as an
implementation of a Node type.
'''
@@ -74,7 +74,7 @@ class SubstitutionMappings(object):
return NodeType(self.node_type, self.custom_defs)
def _validate(self):
- # basic valiation
+ # Basic validation
self._validate_keys()
self._validate_type()
@@ -104,48 +104,59 @@ class SubstitutionMappings(object):
node_type_def = self.custom_defs.get(node_type)
if not node_type_def:
ExceptionCollector.appendException(
- InvalidNodeTypeError(what=node_type_def))
+ InvalidNodeTypeError(what=node_type))
def _validate_inputs(self):
"""validate the inputs of substitution mappings.
- The inputs in service template which provides substutition mappings
- must be in properties of node template which is mapped or provide
- defualt value. Currently the input.name is not restrict to be the
- same as property name in specification, but they should be equal
- for current implementation.
+ The inputs defined by the topology template have to match the
+ properties of the node type or the substituted node. If there are
+ more inputs than the substituted node has properties, default values
+ must be defined for those inputs.
"""
- # Must provide parameters for required properties of node_type
- # This checking is internal(inside SubstitutionMappings)
- for propery in self.node_definition.get_properties_def_objects():
+ all_inputs = set([input.name for input in self.inputs])
+ required_properties = set([p.name for p in
+ self.node_definition.
+ get_properties_def_objects()
+ if p.required and p.default is None])
+ # Must provide inputs for required properties of node type.
+ for property in required_properties:
# Check property which is 'required' and has no 'default' value
- if propery.required and propery.default is None and \
- propery.name not in [input.name for input in self.inputs]:
+ if property not in all_inputs:
ExceptionCollector.appendException(
MissingRequiredInputError(
- what=_('SubstitutionMappings with node_type:')
+ what=_('SubstitutionMappings with node_type ')
+ self.node_type,
- input_name=propery.name))
-
- # Get property names from substituted node tempalte
- property_names = list(self.sub_mapped_node_template
- .get_properties().keys()
- if self.sub_mapped_node_template else [])
- # Sub_mapped_node_template is None(deploy standaolone), will check
- # according to node_type
- if 0 == len(property_names):
- property_names = list(self.node_definition
- .get_properties_def().keys())
- # Provide default value for parameter which is not property of
- # node with the type node_type, this may not be mandatory for
- # current implematation, but the specification express it mandatory.
- # This checking is external(outside SubstitutionMappings)
+ input_name=property))
+
+ # If the optional properties of node type need to be customized by
+ # substituted node, it also is necessary to define inputs for them,
+ # otherwise they are not mandatory to be defined.
+ customized_parameters = set(self.sub_mapped_node_template
+ .get_properties().keys()
+ if self.sub_mapped_node_template else [])
+ all_properties = set([p.name for p in
+ self.node_definition.
+ get_properties_def_objects()])
+ for parameter in customized_parameters - all_inputs:
+ if parameter in all_properties:
+ ExceptionCollector.appendException(
+ MissingRequiredInputError(
+ what=_('SubstitutionMappings with node_type ')
+ + self.node_type,
+ input_name=parameter))
+
+ # Additional inputs are not in the properties of node type must
+ # provide default values. Currently the scenario may not happen
+ # because of parameters validation in nodetemplate, here is a
+ # guarantee.
for input in self.inputs:
- if input.name not in property_names and input.default is None:
+ if input.name in all_inputs - all_properties \
+ and input.default is None:
ExceptionCollector.appendException(
- MissingRequiredParameterError(
- what=_('SubstitutionMappings with node_type:')
+ MissingDefaultValueError(
+ what=_('SubstitutionMappings with node_type ')
+ self.node_type,
input_name=input.name))
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/queuingsubsystem_invalid_input.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/queuingsubsystem_invalid_input.yaml
new file mode 100644
index 0000000..c54c12c
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/queuingsubsystem_invalid_input.yaml
@@ -0,0 +1,79 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+ This template is a test template which contains invalid input needed for substitution mappings.
+ The required properties without default value in substituted node template which be mapped must be
+ as inputs of nested service template which defines substutition mappings, and the inputs of nested
+ service template which are not in the properties of the substituted node template must provide
+ default values.
+ This template provides an additional input of server_port1/my_cpus/my_input which are not defined
+ in example.QueuingSubsystem, and the default value are 8080/2/123, all of these are right. But the
+ required property of server_port defined in example.QueuingSubsystem is not appeared in inputs
+ definiton, so will raise excepton of "MissingRequiredInputError".
+
+imports:
+ - ../definitions.yaml
+
+topology_template:
+ description: Template of a database including its hosting stack.
+
+ inputs:
+ server_ip:
+ type: string
+ description: IP address of the message queuing server to receive messages from.
+ default: 127.0.0.1
+ server_port1:
+ type: integer
+ description: Port to be used for receiving messages.
+ default: 8080
+ my_cpus:
+ type: integer
+ description: Number of CPUs for the server.
+ default: 2
+ constraints:
+ - valid_values: [ 1, 2, 4, 8 ]
+ my_input:
+ type: integer
+ description: test for input validation.
+ default: 123
+
+ substitution_mappings:
+ node_type: example.QueuingSubsystem
+ requirements:
+ receiver1: [ tran_app, receiver1 ]
+ receiver2: [ tran_app, receiver2 ]
+
+ node_templates:
+ tran_app:
+ type: example.QueuingSubsystem
+ properties:
+ server_ip: { get_input: server_ip }
+ server_port: { get_input: server_port1 }
+ requirements:
+ - host:
+ node: server
+
+ server:
+ type: tosca.nodes.Compute
+ capabilities:
+ host:
+ properties:
+ disk_size: 10 GB
+ num_cpus: { get_input: my_cpus }
+ mem_size: 4096 MB
+ os:
+ properties:
+ architecture: x86_64
+ type: Linux
+ distribution: Ubuntu
+ version: 14.04
+
+ outputs:
+ receiver_ip:
+ description: private IP address of the message receiver application
+ value: { get_attribute: [ server, private_address ] }
+
+ groups:
+ tran_server_group:
+ members: [ tran_app, server ]
+ type: tosca.groups.Root
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/system_invalid_input.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/system_invalid_input.yaml
new file mode 100644
index 0000000..e3cdd71
--- /dev/null
+++ b/tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/validate/system_invalid_input.yaml
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+imports:
+ - queuingsubsystem_invalid_input.yaml
+
+topology_template:
+ description: Test template with invalid input.
+
+ inputs:
+ mq_server_ip:
+ type: string
+ default: 127.0.0.1
+ description: IP address of the message queuing server to receive messages from.
+ mq_server_port:
+ type: integer
+ default: 8080
+ description: Port to be used for receiving messages.
+
+ node_templates:
+ mq:
+ type: example.QueuingSubsystem
+ properties:
+ server_ip: { get_input: mq_server_ip }
+ server_port: { get_input: mq_server_port }
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py b/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
index edb834b..6974d52 100644
--- a/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
+++ b/tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
@@ -55,6 +55,18 @@ class TopologyTemplateTest(TestCase):
custom_defs.update(self._get_custom_def('capability_types'))
return custom_defs
+ def _get_custom_types(self):
+ custom_types = {}
+ def_file = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/topology_template/definitions.yaml")
+ custom_type = YAML_LOADER(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 test_description(self):
expected_desc = 'Template of a database including its hosting stack.'
self.assertEqual(expected_desc, self.topo.description)
@@ -165,6 +177,7 @@ class TopologyTemplateTest(TestCase):
self.assertEqual(
len(system_tosca_template.
nested_tosca_templates_with_topology), 4)
+ self.assertTrue(system_tosca_template.has_nested_templates())
def test_invalid_keyname(self):
tpl_snippet = '''
@@ -205,3 +218,47 @@ class TopologyTemplateTest(TestCase):
lambda: SubstitutionMappings(sub_mappings, None, None,
None, None, None))
self.assertEqual(expected_message, err.__str__())
+
+ def test_invalid_nodetype(self):
+ tpl_snippet = '''
+ substitution_mappings:
+ node_type: example.DatabaseSubsystem1
+ capabilities:
+ database_endpoint: [ db_app, database_endpoint ]
+ requirements:
+ receiver1: [ tran_app, receiver1 ]
+ '''
+ sub_mappings = (toscaparser.utils.yamlparser.
+ simple_parse(tpl_snippet))['substitution_mappings']
+ custom_defs = self._get_custom_types()
+ expected_message = _('Node type "example.DatabaseSubsystem1" '
+ 'is not a valid type.')
+ err = self.assertRaises(
+ exception.InvalidNodeTypeError,
+ lambda: SubstitutionMappings(sub_mappings, None, None,
+ None, None, custom_defs))
+ self.assertEqual(expected_message, err.__str__())
+
+ def test_system_with_input_validation(self):
+ tpl_path0 = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/topology_template/validate/system_invalid_input.yaml")
+ tpl_path1 = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ "data/topology_template/validate/"
+ "queuingsubsystem_invalid_input.yaml")
+ errormsg = _('SubstitutionMappings with node_type '
+ 'example.QueuingSubsystem is missing '
+ 'required input definition of input "server_port".')
+
+ # It's invalid in nested template.
+ self.assertRaises(exception.ValidationError,
+ lambda: ToscaTemplate(tpl_path0))
+ exception.ExceptionCollector.assertExceptionMessage(
+ exception.MissingRequiredInputError, errormsg)
+
+ # Subtemplate deploy standaolone is also invalid.
+ self.assertRaises(exception.ValidationError,
+ lambda: ToscaTemplate(tpl_path1))
+ exception.ExceptionCollector.assertExceptionMessage(
+ exception.MissingRequiredInputError, errormsg)