1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements. See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 from aria.utils.formatting import safe_repr
19 from aria.parser import implements_specification
20 from aria.parser.presentation import (report_issue_for_unknown_type, derived_from_validator)
21 from aria.parser.validation import Issue
23 from ..modeling.data_types import (get_primitive_data_type, get_data_type_name, coerce_value,
24 get_container_data_type)
25 from .types import (get_type_by_name, convert_name_to_full_type_name)
30 # NodeTemplate, RelationshipTemplate
33 @implements_specification('3.7.3.3', 'tosca-simple-1.0')
34 def copy_validator(template_type_name, templates_dict_name):
36 Makes sure that the field refers to an existing template defined in the root presenter.
38 Use with the :func:`field_validator` decorator for the ``copy`` field in
39 :class:`NodeTemplate` and :class:`RelationshipTemplate`.
42 def validator_fn(field, presentation, context):
43 field.default_validate(presentation, context)
45 # Make sure type exists
46 value = getattr(presentation, field.name)
48 copy = context.presentation.get_from_dict('service_template', 'topology_template',
49 templates_dict_name, value)
51 report_issue_for_unknown_type(context, presentation, template_type_name, field.name)
53 if copy.copy is not None:
54 context.validation.report(
55 '"copy" field refers to a %s that itself is a copy in "%s": %s'
56 % (template_type_name, presentation._fullname, safe_repr(value)),
57 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
63 # PropertyDefinition, AttributeDefinition, ParameterDefinition, EntrySchema
66 def data_type_validator(type_name='data type'):
68 Makes sure that the field refers to a valid data type, whether complex or primitive.
70 Used with the :func:`field_validator` decorator for the ``type`` fields in
71 :class:`PropertyDefinition`, :class:`AttributeDefinition`, :class:`ParameterDefinition`,
72 and :class:`EntrySchema`.
74 Extra behavior beyond validation: generated function returns true if field is a complex data
78 def validator(field, presentation, context):
79 field.default_validate(presentation, context)
81 value = getattr(presentation, field.name)
83 # Test for circular definitions
84 container_data_type = get_container_data_type(presentation)
85 if (container_data_type is not None) and (container_data_type._name == value):
86 context.validation.report(
87 'type of property "%s" creates a circular value hierarchy: %s'
88 % (presentation._fullname, safe_repr(value)),
89 locator=presentation._get_child_locator('type'), level=Issue.BETWEEN_TYPES)
91 # Can be a complex data type
92 if get_type_by_name(context, value, 'data_types') is not None:
95 # Can be a primitive data type
96 if get_primitive_data_type(value) is None:
97 report_issue_for_unknown_type(context, presentation, type_name, field.name)
105 # PropertyDefinition, AttributeDefinition
108 def entry_schema_validator(field, presentation, context):
110 According to whether the data type supports ``entry_schema`` (e.g., it is or inherits from
111 list or map), make sure that we either have or don't have a valid data type value.
113 Used with the :func:`field_validator` decorator for the ``entry_schema`` field in
114 :class:`PropertyDefinition` and :class:`AttributeDefinition`.
117 field.default_validate(presentation, context)
119 def type_uses_entry_schema(the_type):
120 use_entry_schema = the_type._get_extension('use_entry_schema', False) \
121 if hasattr(the_type, '_get_extension') else False
124 parent = the_type._get_parent(context) if hasattr(the_type, '_get_parent') else None
127 return type_uses_entry_schema(parent)
129 value = getattr(presentation, field.name)
130 the_type = presentation._get_type(context)
133 use_entry_schema = type_uses_entry_schema(the_type)
137 context.validation.report(
138 '"entry_schema" does not have a value as required by data type "%s" in "%s"'
139 % (get_data_type_name(the_type), presentation._container._fullname),
140 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
142 if value is not None:
143 context.validation.report(
144 '"entry_schema" has a value but it is not used by data type "%s" in "%s"'
145 % (get_data_type_name(the_type), presentation._container._fullname),
146 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
149 def data_value_validator(field, presentation, context):
151 Makes sure that the field contains a valid value according to data type and constraints.
153 Used with the :func:`field_validator` decorator for the ``default`` field in
154 :class:`PropertyDefinition` and :class:`AttributeDefinition`.
157 field.default_validate(presentation, context)
159 value = getattr(presentation, field.name)
160 if value is not None:
161 the_type = presentation._get_type(context)
162 entry_schema = presentation.entry_schema
163 # AttributeDefinition does not have this:
164 constraints = presentation._get_constraints(context) \
165 if hasattr(presentation, '_get_constraints') else None
166 coerce_value(context, presentation, the_type, entry_schema, constraints, value, field.name)
173 _data_type_validator = data_type_validator()
174 _data_type_derived_from_validator = derived_from_validator(convert_name_to_full_type_name,
178 def data_type_derived_from_validator(field, presentation, context):
180 Makes sure that the field refers to a valid parent data type (complex or primitive).
182 Used with the :func:`field_validator` decorator for the ``derived_from`` field in
186 if _data_type_validator(field, presentation, context):
187 # Validate derivation only if a complex data type (primitive types have no derivation
189 _data_type_derived_from_validator(field, presentation, context)
192 def data_type_constraints_validator(field, presentation, context):
194 Makes sure that we do not have constraints if we are a complex type (with no primitive
198 field.default_validate(presentation, context)
200 value = getattr(presentation, field.name)
201 if value is not None:
202 if presentation._get_primitive_ancestor(context) is None:
203 context.validation.report(
204 'data type "%s" defines constraints but does not have a primitive ancestor'
205 % presentation._fullname,
206 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
209 def data_type_properties_validator(field, presentation, context):
211 Makes sure that we do not have properties if we have a primitive ancestor.
213 Used with the :func:`field_validator` decorator for the ``properties`` field in
217 field.default_validate(presentation, context)
219 values = getattr(presentation, field.name)
220 if values is not None:
221 if presentation._get_primitive_ancestor(context) is not None:
222 context.validation.report(
223 'data type "%s" defines properties even though it has a primitive ancestor'
224 % presentation._fullname,
225 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
232 def constraint_clause_field_validator(field, presentation, context):
234 Makes sure that field contains a valid value for the container type.
236 Used with the :func:`field_validator` decorator for various field in :class:`ConstraintClause`.
239 field.default_validate(presentation, context)
241 value = getattr(presentation, field.name)
242 if value is not None:
243 the_type = presentation._get_type(context)
244 constraints = the_type._get_constraints(context) \
245 if hasattr(the_type, '_get_constraints') else None
246 coerce_value(context, presentation, the_type, None, constraints, value, field.name)
249 def constraint_clause_in_range_validator(field, presentation, context):
251 Makes sure that the value is a list with exactly two elements, that both lower bound contains a
252 valid value for the container type, and that the upper bound is either "UNBOUNDED" or a valid
253 value for the container type.
255 Used with the :func:`field_validator` decorator for the ``in_range`` field in
256 :class:`ConstraintClause`.
259 field.default_validate(presentation, context)
261 values = getattr(presentation, field.name)
262 if isinstance(values, list):
263 # Make sure list has exactly two elements
265 lower, upper = values
266 the_type = presentation._get_type(context)
268 # Lower bound must be coercible
269 lower = coerce_value(context, presentation, the_type, None, None, lower, field.name)
271 if upper != 'UNBOUNDED':
272 # Upper bound be coercible
273 upper = coerce_value(context, presentation, the_type, None, None, upper, field.name)
275 # Second "in_range" value must be greater than first
276 if (lower is not None) and (upper is not None) and (lower >= upper):
277 context.validation.report(
278 'upper bound of "in_range" constraint is not greater than the lower bound'
280 % (presentation._container._fullname, safe_repr(lower), safe_repr(upper)),
281 locator=presentation._locator, level=Issue.FIELD)
283 context.validation.report(
284 'constraint "%s" is not a list of exactly 2 elements in "%s"'
285 % (field.name, presentation._fullname),
286 locator=presentation._get_child_locator(field.name), level=Issue.FIELD)
289 def constraint_clause_valid_values_validator(field, presentation, context):
291 Makes sure that the value is a list of valid values for the container type.
293 Used with the :func:`field_validator` decorator for the ``valid_values`` field in
294 :class:`ConstraintClause`.
297 field.default_validate(presentation, context)
299 values = getattr(presentation, field.name)
300 if isinstance(values, list):
301 the_type = presentation._get_type(context)
303 coerce_value(context, presentation, the_type, None, None, value, field.name)
306 def constraint_clause_pattern_validator(field, presentation, context):
308 Makes sure that the value is a valid regular expression.
310 Used with the :func:`field_validator` decorator for the ``pattern`` field in
311 :class:`ConstraintClause`.
314 field.default_validate(presentation, context)
316 value = getattr(presentation, field.name)
317 if value is not None:
319 # From TOSCA 1.0 3.5.2.1:
321 # "Note: Future drafts of this specification will detail the use of regular expressions
322 # and reference an appropriate standardized grammar."
324 # So we will just use Python's.
326 except re.error as e:
327 context.validation.report(
328 'constraint "%s" is not a valid regular expression in "%s"'
329 % (field.name, presentation._fullname),
330 locator=presentation._get_child_locator(field.name), level=Issue.FIELD, exception=e)
334 # RequirementAssignment
337 def node_template_or_type_validator(field, presentation, context):
339 Makes sure that the field refers to either a node template or a node type.
341 Used with the :func:`field_validator` decorator for the ``node`` field in
342 :class:`RequirementAssignment`.
345 field.default_validate(presentation, context)
347 value = getattr(presentation, field.name)
348 if value is not None:
350 context.presentation.get('service_template', 'topology_template', 'node_templates') \
352 if (value not in node_templates) and \
353 (get_type_by_name(context, value, 'node_types') is None):
354 report_issue_for_unknown_type(context, presentation, 'node template or node type',
358 def capability_definition_or_type_validator(field, presentation, context):
360 Makes sure refers to either a capability assignment name in the node template referred to by the
361 ``node`` field or a general capability type.
363 If the value refers to a capability type, make sure the ``node`` field was not assigned.
365 Used with the :func:`field_validator` decorator for the ``capability`` field in
366 :class:`RequirementAssignment`.
369 field.default_validate(presentation, context)
371 value = getattr(presentation, field.name)
372 if value is not None:
373 node, node_variant = presentation._get_node(context)
374 if node_variant == 'node_template':
375 capabilities = node._get_capabilities(context)
376 if value in capabilities:
379 if get_type_by_name(context, value, 'capability_types') is not None:
381 context.validation.report(
382 '"%s" refers to a capability type even though "node" has a value in "%s"'
383 % (presentation._name, presentation._container._fullname),
384 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_FIELDS)
387 if node_variant == 'node_template':
388 context.validation.report(
389 'requirement "%s" refers to an unknown capability definition name or capability'
391 % (presentation._name, presentation._container._fullname, safe_repr(value)),
392 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
394 context.validation.report(
395 'requirement "%s" refers to an unknown capability type in "%s": %s'
396 % (presentation._name, presentation._container._fullname, safe_repr(value)),
397 locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
400 def node_filter_validator(field, presentation, context):
402 Makes sure that the field has a value only if "node" refers to a node type.
404 Used with the :func:`field_validator` decorator for the ``node_filter`` field in
405 :class:`RequirementAssignment`.
408 field.default_validate(presentation, context)
410 value = getattr(presentation, field.name)
411 if value is not None:
412 _, node_type_variant = presentation._get_node(context)
413 if node_type_variant != 'node_type':
414 context.validation.report(
415 'requirement "%s" has a node filter even though "node" does not refer to a node'
417 % (presentation._fullname, presentation._container._fullname),
418 locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
422 # RelationshipAssignment
425 def relationship_template_or_type_validator(field, presentation, context):
427 Makes sure that the field refers to either a relationship template or a relationship type.
429 Used with the :func:`field_validator` decorator for the ``type`` field in
430 :class:`RelationshipAssignment`.
433 field.default_validate(presentation, context)
435 value = getattr(presentation, field.name)
436 if value is not None:
437 relationship_templates = \
438 context.presentation.get('service_template', 'topology_template',
439 'relationship_templates') \
441 if (value not in relationship_templates) and \
442 (get_type_by_name(context, value, 'relationship_types') is None):
443 report_issue_for_unknown_type(context, presentation,
444 'relationship template or relationship type', field.name)
451 def list_node_type_or_group_type_validator(field, presentation, context):
453 Makes sure that the field's elements refer to either node types or a group types.
455 Used with the :func:`field_validator` decorator for the ``targets`` field in
459 field.default_validate(presentation, context)
461 values = getattr(presentation, field.name)
462 if values is not None:
464 if (get_type_by_name(context, value, 'node_types') is None) and \
465 (get_type_by_name(context, value, 'group_types') is None):
466 report_issue_for_unknown_type(context, presentation, 'node type or group type',
474 def policy_targets_validator(field, presentation, context):
476 Makes sure that the field's elements refer to either node templates or groups, and that
477 they match the node types and group types declared in the policy type.
479 Used with the :func:`field_validator` decorator for the ``targets`` field in
480 :class:`PolicyTemplate`.
483 field.default_validate(presentation, context)
485 values = getattr(presentation, field.name)
486 if values is not None:
489 context.presentation.get('service_template', 'topology_template',
492 groups = context.presentation.get('service_template', 'topology_template', 'groups') \
494 if (value not in node_templates) and (value not in groups):
495 report_issue_for_unknown_type(context, presentation, 'node template or group',
498 policy_type = presentation._get_type(context)
499 if policy_type is None:
502 node_types, group_types = policy_type._get_targets(context)
506 if value in node_templates:
507 our_node_type = node_templates[value]._get_type(context)
508 for node_type in node_types:
509 if node_type._is_descendant(context, our_node_type):
513 elif value in groups:
514 our_group_type = groups[value]._get_type(context)
515 for group_type in group_types:
516 if group_type._is_descendant(context, our_group_type):
521 context.validation.report(
522 'policy definition target does not match either a node type or a group type'
523 ' declared in the policy type in "%s": %s'
524 % (presentation._name, safe_repr(value)),
525 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
532 def node_filter_properties_validator(field, presentation, context):
534 Makes sure that the field's elements refer to defined properties in the target node type.
536 Used with the :func:`field_validator` decorator for the ``properties`` field in
540 field.default_validate(presentation, context)
542 values = getattr(presentation, field.name)
543 if values is not None:
544 node_type = presentation._get_node_type(context)
545 if node_type is not None:
546 properties = node_type._get_properties(context)
547 for name, _ in values:
548 if name not in properties:
549 context.validation.report(
550 'node filter refers to an unknown property definition in "%s": %s'
551 % (node_type._name, name),
552 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
555 def node_filter_capabilities_validator(field, presentation, context):
557 Makes sure that the field's elements refer to defined capabilities and properties in the target
560 Used with the :func:`field_validator` decorator for the ``capabilities`` field in
564 field.default_validate(presentation, context)
566 values = getattr(presentation, field.name)
567 if values is not None: # pylint: disable=too-many-nested-blocks
568 node_type = presentation._get_node_type(context)
569 if node_type is not None:
570 capabilities = node_type._get_capabilities(context)
571 for name, value in values:
572 capability = capabilities.get(name)
573 if capability is not None:
574 properties = value.properties
575 capability_properties = capability.properties
576 if (properties is not None) and (capability_properties is not None):
577 for property_name, _ in properties:
578 if property_name not in capability_properties:
579 context.validation.report(
580 'node filter refers to an unknown capability definition'
581 ' property in "%s": %s'
582 % (node_type._name, property_name),
583 locator=presentation._locator, level=Issue.BETWEEN_TYPES)
585 context.validation.report(
586 'node filter refers to an unknown capability definition in "%s": %s'
587 % (node_type._name, name),
588 locator=presentation._locator, level=Issue.BETWEEN_TYPES)