vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / extensions / aria_extension_tosca / simple_v1_0 / presentation / field_validators.py
diff --git a/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
new file mode 100644 (file)
index 0000000..e5853d8
--- /dev/null
@@ -0,0 +1,588 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 re
+
+from aria.utils.formatting import safe_repr
+from aria.parser import implements_specification
+from aria.parser.presentation import (report_issue_for_unknown_type, derived_from_validator)
+from aria.parser.validation import Issue
+
+from ..modeling.data_types import (get_primitive_data_type, get_data_type_name, coerce_value,
+                                   get_container_data_type)
+from .types import (get_type_by_name, convert_name_to_full_type_name)
+
+
+
+#
+# NodeTemplate, RelationshipTemplate
+#
+
+@implements_specification('3.7.3.3', 'tosca-simple-1.0')
+def copy_validator(template_type_name, templates_dict_name):
+    """
+    Makes sure that the field refers to an existing template defined in the root presenter.
+
+    Use with the :func:`field_validator` decorator for the ``copy`` field in
+    :class:`NodeTemplate` and :class:`RelationshipTemplate`.
+    """
+
+    def validator_fn(field, presentation, context):
+        field.default_validate(presentation, context)
+
+        # Make sure type exists
+        value = getattr(presentation, field.name)
+        if value is not None:
+            copy = context.presentation.get_from_dict('service_template', 'topology_template',
+                                                      templates_dict_name, value)
+            if copy is None:
+                report_issue_for_unknown_type(context, presentation, template_type_name, field.name)
+            else:
+                if copy.copy is not None:
+                    context.validation.report(
+                        '"copy" field refers to a %s that itself is a copy in "%s": %s'
+                        % (template_type_name, presentation._fullname, safe_repr(value)),
+                        locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+    return validator_fn
+
+
+#
+# PropertyDefinition, AttributeDefinition, ParameterDefinition, EntrySchema
+#
+
+def data_type_validator(type_name='data type'):
+    """
+    Makes sure that the field refers to a valid data type, whether complex or primitive.
+
+    Used with the :func:`field_validator` decorator for the ``type`` fields in
+    :class:`PropertyDefinition`, :class:`AttributeDefinition`, :class:`ParameterDefinition`,
+    and :class:`EntrySchema`.
+
+    Extra behavior beyond validation: generated function returns true if field is a complex data
+    type.
+    """
+
+    def validator(field, presentation, context):
+        field.default_validate(presentation, context)
+
+        value = getattr(presentation, field.name)
+        if value is not None:
+            # Test for circular definitions
+            container_data_type = get_container_data_type(presentation)
+            if (container_data_type is not None) and (container_data_type._name == value):
+                context.validation.report(
+                    'type of property "%s" creates a circular value hierarchy: %s'
+                    % (presentation._fullname, safe_repr(value)),
+                    locator=presentation._get_child_locator('type'), level=Issue.BETWEEN_TYPES)
+
+            # Can be a complex data type
+            if get_type_by_name(context, value, 'data_types') is not None:
+                return True
+
+            # Can be a primitive data type
+            if get_primitive_data_type(value) is None:
+                report_issue_for_unknown_type(context, presentation, type_name, field.name)
+
+        return False
+
+    return validator
+
+
+#
+# PropertyDefinition, AttributeDefinition
+#
+
+def entry_schema_validator(field, presentation, context):
+    """
+    According to whether the data type supports ``entry_schema`` (e.g., it is or inherits from
+    list or map), make sure that we either have or don't have a valid data type value.
+
+    Used with the :func:`field_validator` decorator for the ``entry_schema`` field in
+    :class:`PropertyDefinition` and :class:`AttributeDefinition`.
+    """
+
+    field.default_validate(presentation, context)
+
+    def type_uses_entry_schema(the_type):
+        use_entry_schema = the_type._get_extension('use_entry_schema', False) \
+            if hasattr(the_type, '_get_extension') else False
+        if use_entry_schema:
+            return True
+        parent = the_type._get_parent(context) if hasattr(the_type, '_get_parent') else None
+        if parent is None:
+            return False
+        return type_uses_entry_schema(parent)
+
+    value = getattr(presentation, field.name)
+    the_type = presentation._get_type(context)
+    if the_type is None:
+        return
+    use_entry_schema = type_uses_entry_schema(the_type)
+
+    if use_entry_schema:
+        if value is None:
+            context.validation.report(
+                '"entry_schema" does not have a value as required by data type "%s" in "%s"'
+                % (get_data_type_name(the_type), presentation._container._fullname),
+                locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+    else:
+        if value is not None:
+            context.validation.report(
+                '"entry_schema" has a value but it is not used by data type "%s" in "%s"'
+                % (get_data_type_name(the_type), presentation._container._fullname),
+                locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+
+def data_value_validator(field, presentation, context):
+    """
+    Makes sure that the field contains a valid value according to data type and constraints.
+
+    Used with the :func:`field_validator` decorator for the ``default`` field in
+    :class:`PropertyDefinition` and :class:`AttributeDefinition`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        the_type = presentation._get_type(context)
+        entry_schema = presentation.entry_schema
+        # AttributeDefinition does not have this:
+        constraints = presentation._get_constraints(context) \
+            if hasattr(presentation, '_get_constraints') else None
+        coerce_value(context, presentation, the_type, entry_schema, constraints, value, field.name)
+
+
+#
+# DataType
+#
+
+_data_type_validator = data_type_validator()
+_data_type_derived_from_validator = derived_from_validator(convert_name_to_full_type_name,
+                                                           'data_types')
+
+
+def data_type_derived_from_validator(field, presentation, context):
+    """
+    Makes sure that the field refers to a valid parent data type (complex or primitive).
+
+    Used with the :func:`field_validator` decorator for the ``derived_from`` field in
+    :class:`DataType`.
+    """
+
+    if _data_type_validator(field, presentation, context):
+        # Validate derivation only if a complex data type (primitive types have no derivation
+        # hierarchy)
+        _data_type_derived_from_validator(field, presentation, context)
+
+
+def data_type_constraints_validator(field, presentation, context):
+    """
+    Makes sure that we do not have constraints if we are a complex type (with no primitive
+    ancestor).
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        if presentation._get_primitive_ancestor(context) is None:
+            context.validation.report(
+                'data type "%s" defines constraints but does not have a primitive ancestor'
+                % presentation._fullname,
+                locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
+
+
+def data_type_properties_validator(field, presentation, context):
+    """
+    Makes sure that we do not have properties if we have a primitive ancestor.
+
+    Used with the :func:`field_validator` decorator for the ``properties`` field in
+    :class:`DataType`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None:
+        if presentation._get_primitive_ancestor(context) is not None:
+            context.validation.report(
+                'data type "%s" defines properties even though it has a primitive ancestor'
+                % presentation._fullname,
+                locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
+
+
+#
+# ConstraintClause
+#
+
+def constraint_clause_field_validator(field, presentation, context):
+    """
+    Makes sure that field contains a valid value for the container type.
+
+    Used with the :func:`field_validator` decorator for various field in :class:`ConstraintClause`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        the_type = presentation._get_type(context)
+        constraints = the_type._get_constraints(context) \
+            if hasattr(the_type, '_get_constraints') else None
+        coerce_value(context, presentation, the_type, None, constraints, value, field.name)
+
+
+def constraint_clause_in_range_validator(field, presentation, context):
+    """
+    Makes sure that the value is a list with exactly two elements, that both lower bound contains a
+    valid value for the container type, and that the upper bound is either "UNBOUNDED" or a valid
+    value for the container type.
+
+    Used with the :func:`field_validator` decorator for the ``in_range`` field in
+    :class:`ConstraintClause`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if isinstance(values, list):
+        # Make sure list has exactly two elements
+        if len(values) == 2:
+            lower, upper = values
+            the_type = presentation._get_type(context)
+
+            # Lower bound must be coercible
+            lower = coerce_value(context, presentation, the_type, None, None, lower, field.name)
+
+            if upper != 'UNBOUNDED':
+                # Upper bound be coercible
+                upper = coerce_value(context, presentation, the_type, None, None, upper, field.name)
+
+                # Second "in_range" value must be greater than first
+                if (lower is not None) and (upper is not None) and (lower >= upper):
+                    context.validation.report(
+                        'upper bound of "in_range" constraint is not greater than the lower bound'
+                        ' in "%s": %s <= %s'
+                        % (presentation._container._fullname, safe_repr(lower), safe_repr(upper)),
+                        locator=presentation._locator, level=Issue.FIELD)
+        else:
+            context.validation.report(
+                'constraint "%s" is not a list of exactly 2 elements in "%s"'
+                % (field.name, presentation._fullname),
+                locator=presentation._get_child_locator(field.name), level=Issue.FIELD)
+
+
+def constraint_clause_valid_values_validator(field, presentation, context):
+    """
+    Makes sure that the value is a list of valid values for the container type.
+
+    Used with the :func:`field_validator` decorator for the ``valid_values`` field in
+    :class:`ConstraintClause`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if isinstance(values, list):
+        the_type = presentation._get_type(context)
+        for value in values:
+            coerce_value(context, presentation, the_type, None, None, value, field.name)
+
+
+def constraint_clause_pattern_validator(field, presentation, context):
+    """
+    Makes sure that the value is a valid regular expression.
+
+    Used with the :func:`field_validator` decorator for the ``pattern`` field in
+    :class:`ConstraintClause`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        try:
+            # From TOSCA 1.0 3.5.2.1:
+            #
+            # "Note: Future drafts of this specification will detail the use of regular expressions
+            # and reference an appropriate standardized grammar."
+            #
+            # So we will just use Python's.
+            re.compile(value)
+        except re.error as e:
+            context.validation.report(
+                'constraint "%s" is not a valid regular expression in "%s"'
+                % (field.name, presentation._fullname),
+                locator=presentation._get_child_locator(field.name), level=Issue.FIELD, exception=e)
+
+
+#
+# RequirementAssignment
+#
+
+def node_template_or_type_validator(field, presentation, context):
+    """
+    Makes sure that the field refers to either a node template or a node type.
+
+    Used with the :func:`field_validator` decorator for the ``node`` field in
+    :class:`RequirementAssignment`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        node_templates = \
+            context.presentation.get('service_template', 'topology_template', 'node_templates') \
+            or {}
+        if (value not in node_templates) and \
+            (get_type_by_name(context, value, 'node_types') is None):
+            report_issue_for_unknown_type(context, presentation, 'node template or node type',
+                                          field.name)
+
+
+def capability_definition_or_type_validator(field, presentation, context):
+    """
+    Makes sure refers to either a capability assignment name in the node template referred to by the
+    ``node`` field or a general capability type.
+
+    If the value refers to a capability type, make sure the ``node`` field was not assigned.
+
+    Used with the :func:`field_validator` decorator for the ``capability`` field in
+    :class:`RequirementAssignment`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        node, node_variant = presentation._get_node(context)
+        if node_variant == 'node_template':
+            capabilities = node._get_capabilities(context)
+            if value in capabilities:
+                return
+
+        if get_type_by_name(context, value, 'capability_types') is not None:
+            if node is not None:
+                context.validation.report(
+                    '"%s" refers to a capability type even though "node" has a value in "%s"'
+                    % (presentation._name, presentation._container._fullname),
+                    locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_FIELDS)
+            return
+
+        if node_variant == 'node_template':
+            context.validation.report(
+                'requirement "%s" refers to an unknown capability definition name or capability'
+                ' type in "%s": %s'
+                % (presentation._name, presentation._container._fullname, safe_repr(value)),
+                locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
+        else:
+            context.validation.report(
+                'requirement "%s" refers to an unknown capability type in "%s": %s'
+                % (presentation._name, presentation._container._fullname, safe_repr(value)),
+                locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
+
+
+def node_filter_validator(field, presentation, context):
+    """
+    Makes sure that the field has a value only if "node" refers to a node type.
+
+    Used with the :func:`field_validator` decorator for the ``node_filter`` field in
+    :class:`RequirementAssignment`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        _, node_type_variant = presentation._get_node(context)
+        if node_type_variant != 'node_type':
+            context.validation.report(
+                'requirement "%s" has a node filter even though "node" does not refer to a node'
+                ' type in "%s"'
+                % (presentation._fullname, presentation._container._fullname),
+                locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
+
+
+#
+# RelationshipAssignment
+#
+
+def relationship_template_or_type_validator(field, presentation, context):
+    """
+    Makes sure that the field refers to either a relationship template or a relationship type.
+
+    Used with the :func:`field_validator` decorator for the ``type`` field in
+    :class:`RelationshipAssignment`.
+    """
+
+    field.default_validate(presentation, context)
+
+    value = getattr(presentation, field.name)
+    if value is not None:
+        relationship_templates = \
+            context.presentation.get('service_template', 'topology_template',
+                                     'relationship_templates') \
+            or {}
+        if (value not in relationship_templates) and \
+            (get_type_by_name(context, value, 'relationship_types') is None):
+            report_issue_for_unknown_type(context, presentation,
+                                          'relationship template or relationship type', field.name)
+
+
+#
+# PolicyType
+#
+
+def list_node_type_or_group_type_validator(field, presentation, context):
+    """
+    Makes sure that the field's elements refer to either node types or a group types.
+
+    Used with the :func:`field_validator` decorator for the ``targets`` field in
+    :class:`PolicyType`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None:
+        for value in values:
+            if (get_type_by_name(context, value, 'node_types') is None) and \
+                    (get_type_by_name(context, value, 'group_types') is None):
+                report_issue_for_unknown_type(context, presentation, 'node type or group type',
+                                              field.name, value)
+
+
+#
+# PolicyTemplate
+#
+
+def policy_targets_validator(field, presentation, context):
+    """
+    Makes sure that the field's elements refer to either node templates or groups, and that
+    they match the node types and group types declared in the policy type.
+
+    Used with the :func:`field_validator` decorator for the ``targets`` field in
+    :class:`PolicyTemplate`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None:
+        for value in values:
+            node_templates = \
+                context.presentation.get('service_template', 'topology_template',
+                                         'node_templates') \
+                or {}
+            groups = context.presentation.get('service_template', 'topology_template', 'groups') \
+                or {}
+            if (value not in node_templates) and (value not in groups):
+                report_issue_for_unknown_type(context, presentation, 'node template or group',
+                                              field.name, value)
+
+            policy_type = presentation._get_type(context)
+            if policy_type is None:
+                break
+
+            node_types, group_types = policy_type._get_targets(context)
+
+            is_valid = False
+
+            if value in node_templates:
+                our_node_type = node_templates[value]._get_type(context)
+                for node_type in node_types:
+                    if node_type._is_descendant(context, our_node_type):
+                        is_valid = True
+                        break
+
+            elif value in groups:
+                our_group_type = groups[value]._get_type(context)
+                for group_type in group_types:
+                    if group_type._is_descendant(context, our_group_type):
+                        is_valid = True
+                        break
+
+            if not is_valid:
+                context.validation.report(
+                    'policy definition target does not match either a node type or a group type'
+                    ' declared in the policy type in "%s": %s'
+                    % (presentation._name, safe_repr(value)),
+                    locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+
+#
+# NodeFilter
+#
+
+def node_filter_properties_validator(field, presentation, context):
+    """
+    Makes sure that the field's elements refer to defined properties in the target node type.
+
+    Used with the :func:`field_validator` decorator for the ``properties`` field in
+    :class:`NodeFilter`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None:
+        node_type = presentation._get_node_type(context)
+        if node_type is not None:
+            properties = node_type._get_properties(context)
+            for name, _ in values:
+                if name not in properties:
+                    context.validation.report(
+                        'node filter refers to an unknown property definition in "%s": %s'
+                        % (node_type._name, name),
+                        locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+
+def node_filter_capabilities_validator(field, presentation, context):
+    """
+    Makes sure that the field's elements refer to defined capabilities and properties in the target
+    node type.
+
+    Used with the :func:`field_validator` decorator for the ``capabilities`` field in
+    :class:`NodeFilter`.
+    """
+
+    field.default_validate(presentation, context)
+
+    values = getattr(presentation, field.name)
+    if values is not None: # pylint: disable=too-many-nested-blocks
+        node_type = presentation._get_node_type(context)
+        if node_type is not None:
+            capabilities = node_type._get_capabilities(context)
+            for name, value in values:
+                capability = capabilities.get(name)
+                if capability is not None:
+                    properties = value.properties
+                    capability_properties = capability.properties
+                    if (properties is not None) and (capability_properties is not None):
+                        for property_name, _ in properties:
+                            if property_name not in capability_properties:
+                                context.validation.report(
+                                    'node filter refers to an unknown capability definition'
+                                    ' property in "%s": %s'
+                                    % (node_type._name, property_name),
+                                    locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+                else:
+                    context.validation.report(
+                        'node filter refers to an unknown capability definition in "%s": %s'
+                        % (node_type._name, name),
+                        locator=presentation._locator, level=Issue.BETWEEN_TYPES)