Add output validation for substitution mappings 35/25535/5
authorshangxdy <shang.xiaodong@zte.com.cn>
Mon, 5 Dec 2016 06:23:25 +0000 (14:23 +0800)
committershangxdy <shang.xiaodong@zte.com.cn>
Fri, 9 Dec 2016 15:40:40 +0000 (23:40 +0800)
1. The outputs defined by the topology template have to match the
attributes of the node type or the substituted node template,
2.The observable attributes of the substituted node template have to be
defined as attributes of the node type or outputs in the topology
template.

The patch will be submitted to openstack  too.

JIRA: PARSER-115
Change-Id: Ifa62be9d5c1be79ceacfa1ae6e3835b2de446f88
Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
tosca2heat/tosca-parser/toscaparser/common/exception.py
tosca2heat/tosca-parser/toscaparser/substitution_mappings.py
tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/databasesubsystem.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/queuingsubsystem.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/transactionsubsystem.yaml
tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py

index 724844b..13ccabd 100644 (file)
@@ -114,6 +114,10 @@ class UnknownInputError(TOSCAException):
     msg_fmt = _('Unknown input "%(input_name)s".')
 
 
+class UnknownOutputError(TOSCAException):
+    msg_fmt = _('Unknown output "%(output_name)s" in %(where)s.')
+
+
 class MissingRequiredInputError(TOSCAException):
     msg_fmt = _('%(what)s is missing required input definition '
                 'of input "%(input_name)s".')
@@ -129,6 +133,11 @@ class MissingDefaultValueError(TOSCAException):
                 'of input "%(input_name)s".')
 
 
+class MissingRequiredOutputError(TOSCAException):
+    msg_fmt = _('%(what)s is missing required output definition '
+                'of output "%(output_name)s".')
+
+
 class InvalidPropertyValueError(TOSCAException):
     msg_fmt = _('Value of property "%(what)s" is invalid.')
 
index d4653c3..dea5de7 100644 (file)
@@ -17,7 +17,9 @@ 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 MissingRequiredOutputError
 from toscaparser.common.exception import UnknownFieldError
+from toscaparser.common.exception import UnknownOutputError
 from toscaparser.elements.nodetype import NodeType
 from toscaparser.utils.gettextutils import _
 
@@ -34,6 +36,8 @@ class SubstitutionMappings(object):
     SECTIONS = (NODE_TYPE, REQUIREMENTS, CAPABILITIES) = \
                ('node_type', 'requirements', 'capabilities')
 
+    OPTIONAL_OUTPUTS = ['tosca_id', 'tosca_name', 'state']
+
     def __init__(self, sub_mapping_def, nodetemplates, inputs, outputs,
                  sub_mapped_node_template, custom_defs):
         self.nodetemplates = nodetemplates
@@ -110,9 +114,9 @@ class SubstitutionMappings(object):
         """validate the inputs of substitution mappings.
 
         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.
+        properties of the node type or the substituted node template. If
+        there are more inputs than the substituted node has properties,
+        default values must be defined for those inputs.
         """
 
         all_inputs = set([input.name for input in self.inputs])
@@ -136,9 +140,7 @@ class SubstitutionMappings(object):
         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()])
+        all_properties = set(self.node_definition.get_properties_def())
         for parameter in customized_parameters - all_inputs:
             if parameter in all_properties:
                 ExceptionCollector.appendException(
@@ -191,14 +193,36 @@ class SubstitutionMappings(object):
                 #                      field=req))
 
     def _validate_outputs(self):
-        """validate the outputs of substitution mappings."""
-        pass
-        # The outputs in service template which defines substutition mappings
-        # must be in atrributes of node template wchich be mapped.
-        # outputs_names = self.sub_mapped_node_template.get_properties().
-        #     keys() if self.sub_mapped_node_template else None
-        # for name in outputs_names:
-        #    if name not in [output.name for input in self.outputs]:
-        #        ExceptionCollector.appendException(
-        #            UnknownFieldError(what='SubstitutionMappings',
-        #                              field=name))
+        """validate the outputs of substitution mappings.
+
+        The outputs defined by the topology template have to match the
+        attributes of the node type or the substituted node template,
+        and the observable attributes of the substituted node template
+        have to be defined as attributes of the node type or outputs in
+        the topology template.
+        """
+
+        # The outputs defined by the topology template have to match the
+        # attributes of the node type according to the specification, but
+        # it's reasonable that there are more inputs than the node type
+        # has properties, the specification will be amended?
+        for output in self.outputs:
+            if output.name not in self.node_definition.get_attributes_def():
+                ExceptionCollector.appendException(
+                    UnknownOutputError(
+                        where=_('SubstitutionMappings with node_type ')
+                        + self.node_type,
+                        output_name=output.name))
+
+        # The observable attributes of the substituted node template
+        # have to be defined as attributes of the node type or outputs in
+        # the topology template, the attributes in tosca.node.root are
+        # optional.
+        for attribute in self.node_definition.get_attributes_def():
+            if attribute not in [output.name for output in self.outputs] \
+               and attribute not in self.OPTIONAL_OUTPUTS:
+                ExceptionCollector.appendException(
+                    MissingRequiredOutputError(
+                        what=_('SubstitutionMappings with node_type ')
+                        + self.node_type,
+                        output_name=attribute))
index ebf1856..6990679 100644 (file)
@@ -75,10 +75,10 @@ topology_template:
             distribution: Ubuntu
             version: 14.04
 
-  outputs:
-    receiver_ip:
-      description: private IP address of the database application
-      value: { get_attribute: [ server, private_address ] }
+#  outputs:
+#    receiver_ip:
+#      description: private IP address of the database application
+#      value: { get_attribute: [ server, private_address ] }
 # It seems current _process_intrisic_function can not handle more than 2 arguments, save it for later
 #    receiver_port:
 #      description: Port of the message receiver endpoint
index 76fa7e2..8c4cc76 100644 (file)
@@ -61,9 +61,18 @@ topology_template:
             version: 14.04
 
   outputs:
-    receiver_ip:
-      description: private IP address of the message receiver application
-      value: { get_attribute: [ server, private_address ] }
+#    receiver_ip:
+#      description: private IP address of the message receiver application
+#      value: { get_attribute: [ server, private_address ] }
+
+    server_ip:
+      description: server_ip of the message receiver application
+      value: { get_input: server_ip }
+
+    server_port:
+      description: server_port of the message receiver application
+      value: { get_input: server_port }
+
 # It seems current _process_intrisic_function can not handle more than 2 arguments, save it for later
 #    receiver_port:
 #      description: Port of the message receiver endpoint
index 0f145a3..7b839d7 100644 (file)
@@ -77,6 +77,12 @@ topology_template:
     receiver_ip:
       description: private IP address of the message receiver application
       value: { get_attribute: [ server, private_address ] }
+
+    receiver_port:
+      description: receiver_port of the message receiver application
+      value: { get_input: receiver_port }
+
+
 # It seems current _process_intrisic_function can not handle more than 2 arguments, save it for later
 #    receiver_port:
 #      description: Port of the message receiver endpoint
index 6974d52..eb8d589 100644 (file)
@@ -154,7 +154,7 @@ class TopologyTemplateTest(TestCase):
 
     def test_outputs(self):
         self.assertEqual(
-            ['receiver_ip'],
+            sorted(['receiver_ip', 'receiver_port']),
             sorted([output.name for output in self.topo.outputs]))
 
     def test_groups(self):