summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py588
1 files changed, 588 insertions, 0 deletions
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
index 0000000..e5853d8
--- /dev/null
+++ b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
@@ -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)