synchronize upstream about tosca-parser 09/37609/1
authorshangxdy <shang.xiaodong@zte.com.cn>
Mon, 17 Jul 2017 08:29:36 +0000 (16:29 +0800)
committershangxdy <shang.xiaodong@zte.com.cn>
Mon, 17 Jul 2017 08:29:36 +0000 (16:29 +0800)
synchronize tosca-parser with version of 0.81

JIRA: PARSER-128

Change-Id: Ib00e391bd6c1f4c69776796eb7b28062f14b92fc
Signed-off-by: shangxdy <shang.xiaodong@zte.com.cn>
25 files changed:
tosca2heat/tosca-parser/CONTRIBUTING.rst [new file with mode: 0644]
tosca2heat/tosca-parser/HACKING.rst [new file with mode: 0644]
tosca2heat/tosca-parser/README.rst [moved from tosca2heat/tosca-parser/README.md with 100% similarity]
tosca2heat/tosca-parser/requirements.txt
tosca2heat/tosca-parser/setup.cfg
tosca2heat/tosca-parser/setup.py
tosca2heat/tosca-parser/test-requirements.txt
tosca2heat/tosca-parser/toscaparser/elements/TOSCA_definition_1_0.yaml
tosca2heat/tosca-parser/toscaparser/elements/nodetype.py
tosca2heat/tosca-parser/toscaparser/elements/policytype.py
tosca2heat/tosca-parser/toscaparser/functions.py
tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/node_with_cap.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/policies/tosca_policy_template.yaml
tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml [new file with mode: 0644]
tosca2heat/tosca-parser/toscaparser/tests/data/topology_template/definitions.yaml
tosca2heat/tosca-parser/toscaparser/tests/test_functions.py
tosca2heat/tosca-parser/toscaparser/tests/test_topology_template.py
tosca2heat/tosca-parser/toscaparser/tests/test_toscatplvalidation.py
tosca2heat/tosca-parser/toscaparser/triggers.py
tosca2heat/tosca-parser/tox.ini

diff --git a/tosca2heat/tosca-parser/CONTRIBUTING.rst b/tosca2heat/tosca-parser/CONTRIBUTING.rst
new file mode 100644 (file)
index 0000000..7366825
--- /dev/null
@@ -0,0 +1,16 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps in this page:
+
+   http://docs.openstack.org/infra/manual/developers.html
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+   http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+   https://bugs.launchpad.net/tosca-parser
\ No newline at end of file
diff --git a/tosca2heat/tosca-parser/HACKING.rst b/tosca2heat/tosca-parser/HACKING.rst
new file mode 100644 (file)
index 0000000..60505f1
--- /dev/null
@@ -0,0 +1,4 @@
+tosca-parser Style Commandments
+===============================================
+
+Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
\ No newline at end of file
index 45a19f0..807d749 100644 (file)
@@ -1,10 +1,10 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr>=1.8 # Apache-2.0
-Babel>=2.3.4 # BSD
-cliff>=2.3.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+Babel!=2.4.0,>=2.3.4 # BSD
+cliff>=2.6.0 # Apache-2.0
 PyYAML>=3.10.0 # MIT
 python-dateutil>=2.4.2 # BSD
 six>=1.9.0 # MIT
-requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
+requests>=2.14.2 # Apache-2.0
index 45a5ae7..77e1b2e 100644 (file)
@@ -3,7 +3,7 @@ name = tosca-parser
 url = https://launchpad.net/tosca-parser
 summary = Parser for TOSCA Simple Profile in YAML.
 description-file =
-    README.md
+    README.rst
 author = OpenStack
 author-email = openstack-dev@lists.openstack.org
 home-page = http://docs.openstack.org/developer/tosca-parser/
@@ -28,6 +28,14 @@ packages =
 console_scripts =
     tosca-parser = toscaparser.shell:main
 
+[build_sphinx]
+source-dir = doc/source
+build-dir = doc/build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = doc/build/html
+
 [compile_catalog]
 directory = toscaparser/locale
 domain = toscaparser
index 782bb21..566d844 100644 (file)
@@ -25,5 +25,5 @@ except ImportError:
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.8'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
index d4b59c3..da69d4f 100644 (file)
@@ -1,13 +1,13 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
-coverage>=4.0 # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+coverage!=4.4,>=4.0 # Apache-2.0
 fixtures>=3.0.0 # Apache-2.0/BSD
 oslotest>=1.10.0 # Apache-2.0
 oslosphinx>=4.7.0 # Apache-2.0
 python-subunit>=0.0.18 # Apache-2.0/BSD
-sphinx>=1.5.1 # BSD
+sphinx>=1.6.2 # BSD
 testrepository>=0.0.18 # Apache-2.0/BSD
 testscenarios>=0.4 # Apache-2.0/BSD
 testtools>=1.4.0 # MIT
index 9f3369e..57c9bf9 100644 (file)
@@ -339,6 +339,26 @@ node_types:
           relationship: tosca.relationships.network.LinksTo
           node: tosca.nodes.network.Network
 
+  tosca.nodes.network.FloatingIP:
+    derived_from: tosca.nodes.Root
+    description: >
+      The TOSCA FloatingIP node represents a floating IP that can associate to a Port.
+    properties:
+      floating_network:
+        type: string
+        required: true
+      floating_ip_address:
+        type: string
+        required: false
+      port_id:
+        type: string
+        required: false
+    requirements:
+      - link:
+          capability: tosca.capabilities.network.Linkable
+          relationship: tosca.relationships.network.LinksTo
+          node: tosca.nodes.network.Port
+
   tosca.nodes.ObjectStorage:
     derived_from: tosca.nodes.Root
     description: >
@@ -928,6 +948,11 @@ policy_types:
     description: The TOSCA Policy Type definition that is used to govern
       scaling of TOSCA nodes or groups of nodes.
 
+  tosca.policies.Monitoring:
+    derived_from: tosca.policies.Root
+    description: The TOSCA Policy Type definition that is used to govern
+      monitoring of TOSCA nodes or groups of nodes.
+
   tosca.policies.Update:
     derived_from: tosca.policies.Root
     description: The TOSCA Policy Type definition that is used to govern
index 7f3da2d..4889ed8 100644 (file)
@@ -83,8 +83,6 @@ class NodeType(StatefulEntityType):
                                 captype = value['capability']
                                 value = (self.
                                          _get_node_type_by_cap(captype))
-                            #            _get_node_type_by_cap(key, captype))
-                            # relation = self._get_relation(key, value)
                             keyword = key
                             node_type = value
                 rtype = RelationshipType(relation, keyword, self.custom_def)
index a922d26..82aed0a 100644 (file)
@@ -113,7 +113,7 @@ class PolicyType(StatefulEntityType):
 
         for entry_schema, entry_schema_type in meta_data.items():
             if isinstance(entry_schema_type, dict) and not \
-                entry_schema_type.get('type') == 'string':
+                    entry_schema_type.get('type') == 'string':
                 ExceptionCollector.appendException(
                     InvalidTypeError(what='"%s" defined in policy for '
                                      'metadata "%s"'
index d498229..b64f1bc 100644 (file)
@@ -158,6 +158,8 @@ class GetAttribute(Function):
                 # then check the req or caps
                 attr = self._find_req_or_cap_attribute(self.args[1],
                                                        self.args[2])
+                if not attr:
+                    return
 
             value_type = attr.schema['type']
             if len(self.args) > index:
@@ -236,8 +238,8 @@ class GetAttribute(Function):
                     target_node = self._find_node_template(target_name)
                     target_type = target_node.type_definition
                     for capability in target_type.get_capabilities_objects():
-                        if capability.type in \
-                                hosted_on_rel['valid_target_types']:
+                        if capability.inherits_from(
+                                hosted_on_rel['valid_target_types']):
                             if self._attribute_exists_in_type(target_type):
                                 return target_node
                             return self._find_host_containing_attribute(
@@ -412,6 +414,8 @@ class GetProperty(Function):
 
     def _find_req_or_cap_property(self, req_or_cap, property_name):
         node_tpl = self._find_node_template(self.args[0])
+        if node_tpl is None:
+            return None
         # Find property in node template's requirements
         for r in node_tpl.requirements:
             for req, node_name in r.items():
@@ -429,7 +433,8 @@ class GetProperty(Function):
     def _get_capability_property(self,
                                  node_template,
                                  capability_name,
-                                 property_name):
+                                 property_name,
+                                 throw_errors=True):
         """Gets a node template capability property."""
         caps = node_template.get_capabilities()
         if caps and capability_name in caps.keys():
@@ -438,7 +443,7 @@ class GetProperty(Function):
             props = cap.get_properties()
             if props and property_name in props.keys():
                 property = props[property_name].value
-            if not property:
+            if property is None and throw_errors:
                 ExceptionCollector.appendException(
                     KeyError(_('Property "%(prop)s" was not found in '
                                'capability "%(cap)s" of node template '
@@ -448,12 +453,15 @@ class GetProperty(Function):
                                                   'ntpl1': node_template.name,
                                                   'ntpl2': self.context.name}))
             return property
-        msg = _('Requirement/Capability "{0}" referenced from node template '
-                '"{1}" was not found in node template "{2}".').format(
-                    capability_name,
-                    self.context.name,
-                    node_template.name)
-        ExceptionCollector.appendException(KeyError(msg))
+        if throw_errors:
+            msg = _('Requirement/Capability "{0}" referenced from '
+                    'node template "{1}" was not found in node template'
+                    ' "{2}".').format(capability_name,
+                                      self.context.name,
+                                      node_template.name)
+            ExceptionCollector.appendException(KeyError(msg))
+        else:
+            return None
 
     def _find_property(self, property_name):
         node_tpl = self._find_node_template(self.args[0])
@@ -475,7 +483,18 @@ class GetProperty(Function):
             return self.context
         # enable the HOST value in the function
         if node_template_name == HOST:
-            return self._find_host_containing_property()
+            node = self._find_host_containing_property()
+            if node is None:
+                ExceptionCollector.appendException(
+                    KeyError(_(
+                        "Property '{0}' not found in capability/requirement"
+                        " '{1}' referenced from node template {2}").
+                        format(self.args[2],
+                               self.args[1],
+                               self.context.name)))
+                return None
+            else:
+                return node
         if node_template_name == TARGET:
             if not isinstance(self.context.type_definition, RelationshipType):
                 ExceptionCollector.appendException(
@@ -498,7 +517,9 @@ class GetProperty(Function):
         ExceptionCollector.appendException(
             KeyError(_(
                 'Node template "{0}" was not found.'
-                ).format(node_template_name)))
+                ' referenced from node template {1}'
+                ).format(node_template_name,
+                         self.context.name)))
 
     def _get_index_value(self, value, index):
         if isinstance(value, list):
@@ -555,9 +576,19 @@ class GetProperty(Function):
                 target_node = self._find_node_template(target_name)
                 target_type = target_node.type_definition
                 for capability in target_type.get_capabilities_objects():
-                    if capability.type in hosted_on_rel['valid_target_types']:
+                    if capability.inherits_from(
+                            hosted_on_rel['valid_target_types']):
                         if self._property_exists_in_type(target_type):
                             return target_node
+                        # If requirement was not found, look in node
+                        # template's capabilities
+                        if (len(self.args) > 2 and
+                                self._get_capability_property(target_node,
+                                                              self.args[1],
+                                                              self.args[2],
+                                                              False)
+                                is not None):
+                            return target_node
                         return self._find_host_containing_property(
                             target_name)
         return None
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/container_cap_child.yaml
new file mode 100644 (file)
index 0000000..1df09dd
--- /dev/null
@@ -0,0 +1,33 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+  Define a capability class that inherits from tosca.capabilities.Container
+
+capability_types:
+
+  tosca.capabilities.ContainerChild:
+    derived_from: tosca.capabilities.Container
+
+node_types:
+
+  tosca.nodes.SomeNode:
+    derived_from: tosca.nodes.Root
+    properties:
+      some_prop:
+        type: string
+    requirements:
+      - host_child:
+          capability: tosca.capabilities.ContainerChild
+          node: tosca.nodes.SomeNode2
+          relationship: tosca.relationships.HostedOn
+
+  tosca.nodes.SomeNode2:
+    derived_from: tosca.nodes.Root
+    capabilities:
+        host_child:
+          type: tosca.capabilities.ContainerChild
+    requirements:
+      - host:
+          capability: tosca.capabilities.Container
+          node: tosca.nodes.Compute
+          relationship: tosca.relationships.HostedOn
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/custom_types/custom_cap.yaml
new file mode 100644 (file)
index 0000000..018bcf6
--- /dev/null
@@ -0,0 +1,22 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+capability_types:
+
+  tosca.capabilities.SomeCap:
+    derived_from: tosca.capabilities.Container
+
+node_types:
+
+  tosca.nodes.NodeWithReq:
+    derived_from: tosca.nodes.SoftwareComponent
+    requirements:
+      - host:
+          capability: tosca.capabilities.SomeCap
+          relationship: tosca.relationships.HostedOn
+          occurrences: [1, 1]
+
+  tosca.nodes.NodeWithCap:
+    derived_from: tosca.nodes.SoftwareComponent
+    capabilities:
+      host:
+        type: tosca.capabilities.SomeCap
index b17513f..332f830 100644 (file)
@@ -6,7 +6,7 @@ description: >
 capability_types:
 
   tosca.capabilities.SomeCap:
-    derived_from: tosca.capabilities.Root
+    derived_from: tosca.capabilities.Container
     properties:
       type:
         type: string
@@ -19,6 +19,11 @@ node_types:
 
   tosca.nodes.SomeNode:
     derived_from: tosca.nodes.Root
+    properties:
+      some_prop:
+        type: string
+        required: false
+        default: some
     requirements:
       - some_req:
           capability: tosca.capabilities.SomeCap
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_container_cap_child.yaml
new file mode 100644 (file)
index 0000000..84118c8
--- /dev/null
@@ -0,0 +1,28 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+  TOSCA simple profile to test the get attribute function with HOST parameter
+  using a node that has as capability a child class of Container
+
+imports:
+  - ../custom_types/container_cap_child.yaml
+
+topology_template:
+
+  node_templates:
+
+    test_node:
+      type: tosca.nodes.SomeNode
+      properties:
+        some_prop: { get_attribute: [ HOST, public_address ] }
+      requirements:
+        - host_child: test_node2
+
+    test_node2:
+      type: tosca.nodes.SomeNode2
+      requirements:
+        - host: server
+
+    server:
+      type: tosca.nodes.Compute
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_bool.yaml
new file mode 100644 (file)
index 0000000..d9c4c1c
--- /dev/null
@@ -0,0 +1,37 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA test for boolean properties
+
+node_types:
+
+  tosca.nodes.SoftwareComponentTest:
+    derived_from: tosca.nodes.SoftwareComponent
+    properties:
+      some_prop:
+        type: boolean
+
+  tosca.nodes.ComputeTest:
+    derived_from: tosca.nodes.Compute
+    capabilities:
+      endpoint:
+        type: tosca.capabilities.Endpoint
+
+topology_template:
+
+  node_templates:
+
+    software:
+      type: tosca.nodes.SoftwareComponentTest
+      properties:
+        some_prop: { get_property: [ HOST, endpoint, secure ] }
+      requirements:
+        - host: server
+
+    server:
+      type: tosca.nodes.ComputeTest
+      capabilities:
+        endpoint:
+          properties:
+            network_name: PUBLIC
+            secure: false
+
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/functions/test_get_prop_cap_host.yaml
new file mode 100644 (file)
index 0000000..7fcb4a7
--- /dev/null
@@ -0,0 +1,25 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+description: TOSCA test for the IM
+
+imports:
+ - test: ../custom_types/node_with_cap.yaml
+
+topology_template:
+
+  node_templates:
+
+    some_node:
+      type: tosca.nodes.SomeNode
+      properties:
+        some_prop: { get_property: [ HOST, some_req, type ] }
+      requirements:
+        - some_req: server
+
+    server:
+      type: tosca.nodes.NodeWithCap
+      capabilities:
+        some_req:
+          properties:
+            type: someval
+
index 47f7870..4c18d9d 100644 (file)
@@ -75,6 +75,32 @@ topology_template:
                 inputs:
                  strategy: LEAST_USED
                  implementation: Senlin.webhook()
+           high_cpu_usage:
+             description: trigger
+             meter_name: cpu_util
+             condition:
+               constraint: utilization greater_than 60%
+               threshold: 60
+               period: 600
+               evaluations: 1
+               method: average
+               comparison_operator: gt
+             metadata: SG1
+             action: [SP1]
+
+           low_cpu_usage:
+             description: trigger
+             meter_name: cpu_util
+             condition:
+               constraint: utilization less_than 20%
+               threshold: 20
+               period: 600
+               evaluations: 1
+               method: average
+               comparison_operator: gt
+             metadata: SG1
+             action: [SP1]
+
     - my_groups_placement:
         type: mycompany.mytypes.myScalingPolicy
         targets: [ webserver_group ]
diff --git a/tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml b/tosca2heat/tosca-parser/toscaparser/tests/data/test_custom_capabilty.yaml
new file mode 100644 (file)
index 0000000..03a8a07
--- /dev/null
@@ -0,0 +1,23 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: TOSCA simple profile to test a custom defined capability
+
+imports:
+  - custom_types/custom_cap.yaml
+
+topology_template:
+
+  node_templates:
+
+    node_req:
+      type: tosca.nodes.NodeWithReq
+      requirements:
+        - host: node_cap
+
+    node_cap:
+      type: tosca.nodes.NodeWithCap
+      requirements:
+        - host: server
+
+    server:
+      type: tosca.nodes.Compute
index ba5eac1..300bb8a 100644 (file)
@@ -49,7 +49,7 @@ node_types:
     derived_from: tosca.nodes.Database
 
   example.SomeApp:
-    derived_from: tosca.nodes.SoftwareComponent
+    derived_from: tosca.nodes.WebApplication
     properties:
       admin_user:
         type: string
index fa60140..2e1d71e 100644 (file)
@@ -188,6 +188,26 @@ class IntrinsicFunctionsTest(TestCase):
         self.assertIsInstance(source_port, functions.GetProperty)
         self.assertEqual(3306, source_port.result())
 
+    def test_get_prop_cap_host(self):
+        tosca_tpl = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            "data/functions/test_get_prop_cap_host.yaml")
+        some_node = self._get_node('some_node',
+                                   ToscaTemplate(tosca_tpl))
+        some_prop = some_node.get_properties()['some_prop']
+        self.assertIsInstance(some_prop.value, functions.GetProperty)
+        self.assertEqual('someval', some_prop.value.result())
+
+    def test_get_prop_cap_bool(self):
+        tosca_tpl = os.path.join(
+            os.path.dirname(os.path.abspath(__file__)),
+            "data/functions/test_get_prop_cap_bool.yaml")
+        some_node = self._get_node('software',
+                                   ToscaTemplate(tosca_tpl))
+        some_prop = some_node.get_properties()['some_prop']
+        self.assertIsInstance(some_prop.value, functions.GetProperty)
+        self.assertEqual(False, some_prop.value.result())
+
 
 class GetAttributeTest(TestCase):
 
@@ -318,6 +338,10 @@ class GetAttributeTest(TestCase):
         self.assertIsNotNone(self._load_template(
             'functions/test_get_implicit_attribute.yaml'))
 
+    def test_get_attribute_capability_inheritance(self):
+        self.assertIsNotNone(self._load_template(
+            'functions/test_container_cap_child.yaml'))
+
 
 class ConcatTest(TestCase):
 
index 3aabc9b..a0d6dc3 100644 (file)
@@ -91,7 +91,7 @@ class TopologyTemplateTest(TestCase):
         tpl_name = "app"
         expected_type = "example.SomeApp"
         expected_properties = ['admin_user', 'pool_size']
-        expected_capabilities = ['feature', 'message_receiver']
+        expected_capabilities = ['app_endpoint', 'feature', 'message_receiver']
         expected_requirements = [{'host': {'node': 'websrv'}}]
         expected_relationshp = ['tosca.relationships.HostedOn']
         expected_host = ['websrv']
index 911867f..f7c22ab 100644 (file)
@@ -1581,7 +1581,7 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
             lambda: Policy(name, policies[name], None, None))
         self.assertEqual(expectedmessage, err.__str__())
 
-    def test_policy_trigger_valid_keyname(self):
+    def test_policy_trigger_valid_keyname_senlin_resources(self):
         tpl_snippet = '''
         triggers:
          - resize_compute:
@@ -1610,7 +1610,28 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
         name = list(triggers.keys())[0]
         Triggers(name, triggers[name])
 
-    def test_policy_trigger_invalid_keyname(self):
+    def test_policy_trigger_valid_keyname_heat_resources(self):
+        tpl_snippet = '''
+        triggers:
+         - high_cpu_usage:
+             description: trigger
+             meter_name: cpu_util
+             condition:
+               constraint: utilization greater_than 60%
+               threshold: 60
+               period: 600
+               evaluations: 1
+               method: average
+               comparison_operator: gt
+             metadata: SG1
+             action: [SP1]
+        '''
+        triggers = (toscaparser.utils.yamlparser.
+                    simple_parse(tpl_snippet))['triggers'][0]
+        name = list(triggers.keys())[0]
+        Triggers(name, triggers[name])
+
+    def test_policy_trigger_invalid_keyname_senlin_resources(self):
         tpl_snippet = '''
         triggers:
          - resize_compute:
@@ -1646,6 +1667,34 @@ heat-translator/master/translator/tests/data/custom_types/wordpress.yaml
             lambda: Triggers(name, triggers[name]))
         self.assertEqual(expectedmessage, err.__str__())
 
+    def test_policy_trigger_invalid_keyname_heat_resources(self):
+        tpl_snippet = '''
+        triggers:
+         - high_cpu_usage:
+             description: trigger
+             meter_name: cpu_util
+             condition:
+               constraint: utilization greater_than 60%
+               threshold: 60
+               period: 600
+               evaluations: 1
+               method: average
+               comparison_operator: gt
+             metadata1: SG1
+             action: [SP1]
+        '''
+        triggers = (toscaparser.utils.yamlparser.
+                    simple_parse(tpl_snippet))['triggers'][0]
+        name = list(triggers.keys())[0]
+        expectedmessage = _(
+            'Triggers "high_cpu_usage" contains unknown field '
+            '"metadata1". Refer to the definition '
+            'to verify valid values.')
+        err = self.assertRaises(
+            exception.UnknownFieldError,
+            lambda: Triggers(name, triggers[name]))
+        self.assertEqual(expectedmessage, err.__str__())
+
     def test_policy_missing_required_keyname(self):
         tpl_snippet = '''
         policies:
index 9edeef4..8e47eee 100644 (file)
@@ -16,12 +16,16 @@ import logging
 from toscaparser.common.exception import ExceptionCollector
 from toscaparser.common.exception import UnknownFieldError
 from toscaparser.entity_template import EntityTemplate
-
-SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, TARGET_FILTER, CONDITION, ACTION) = \
-           ('description', 'event_type', 'schedule',
-            'target_filter', 'condition', 'action')
-CONDITION_KEYNAMES = (CONTRAINT, PERIOD, EVALUATIONS, METHOD) = \
-                     ('constraint', 'period', 'evaluations', 'method')
+from toscaparser.utils import validateutils
+
+SECTIONS = (DESCRIPTION, EVENT, SCHEDULE, METER_NAME, METADATA,
+            TARGET_FILTER, CONDITION, ACTION) = \
+           ('description', 'event_type', 'schedule', 'meter_name',
+            'metadata', 'target_filter', 'condition', 'action')
+CONDITION_KEYNAMES = (CONSTRAINT, PERIOD, EVALUATIONS, METHOD,
+                      THRESHOLD, COMPARISON_OPERATOR) = \
+                     ('constraint', 'period', 'evaluations',
+                      'method', 'threshold', 'comparison_operator')
 log = logging.getLogger('tosca')
 
 
@@ -34,6 +38,7 @@ class Triggers(EntityTemplate):
         self.trigger_tpl = trigger_tpl
         self._validate_keys()
         self._validate_condition()
+        self._validate_input()
 
     def get_description(self):
         return self.trigger_tpl['description']
@@ -66,3 +71,12 @@ class Triggers(EntityTemplate):
                 ExceptionCollector.appendException(
                     UnknownFieldError(what='Triggers "%s"' % self.name,
                                       field=key))
+
+    def _validate_input(self):
+        for key, value in self.get_condition().items():
+            if key in [PERIOD, EVALUATIONS]:
+                validateutils.validate_integer(value)
+            elif key == THRESHOLD:
+                validateutils.validate_numeric(value)
+            elif key in [METER_NAME, METHOD]:
+                validateutils.validate_string(value)
index 8fc2a88..3603a18 100644 (file)
@@ -1,5 +1,5 @@
 [tox]
-minversion = 1.6
+minversion = 2.0
 envlist = py27,pep8
 skipsdist = True
 
@@ -28,9 +28,6 @@ commands = python setup.py build_sphinx
 commands = oslo_debug_helper -t toscaparser/tests {posargs}
 
 [flake8]
-# H803 skipped on purpose per list discussion.
-# E123, E125 skipped as they are invalid PEP-8.
-
 show-source = True
 ignore = E123,E125,H803
 builtins = _