http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py index 9bafeec..e411104 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py @@ -88,7 +88,7 @@ def get_assigned_and_defined_parameter_values(context, presentation, field_name) definition = definitions[name] values[name] = coerce_parameter_value(context, value, definition, value.value) else: - context.validation.report('assignment to undefined {0} "{1}" in "{2}"' + context.validation.report(u'assignment to undefined {0} "{1}" in "{2}"' .format(field_name, name, presentation._fullname), locator=value._locator, level=Issue.BETWEEN_TYPES) @@ -99,7 +99,7 @@ def get_assigned_and_defined_parameter_values(context, presentation, field_name) if (name not in values) and \ (('default' in definition._raw) or (field_name == 'attribute')): values[name] = coerce_parameter_value(context, presentation, definition, - definition.default) + definition.default, 'default') validate_required_values(context, presentation, values, definitions) @@ -131,7 +131,8 @@ def get_parameter_values(context, presentation, field_name): parameter.value) else: default = parameter.default if hasattr(parameter, 'default') else None - values[name] = coerce_parameter_value(context, presentation, parameter, default) + values[name] = coerce_parameter_value(context, presentation, parameter, default, + 'default') return values @@ -147,11 +148,21 @@ def validate_required_values(context, presentation, values, definitions): if not definitions: return + + def has_value(name): + if values is None: + return False + value = values.get(name) + if value is None: + return False + if isinstance(value, Value) and (value.value is None): + return False + return True + for name, definition in definitions.iteritems(): - if getattr(definition, 'required', False) and \ - ((values is None) or (values.get(name) is None)): - context.validation.report('required property "%s" is not assigned a value in "%s"' - % (name, presentation._fullname), + if getattr(definition, 'required', False) and not has_value(name): + context.validation.report(u'required property "{0}" is not assigned a value in "{1}"' + .format(name, presentation._fullname), locator=presentation._get_child_locator('properties'), level=Issue.BETWEEN_TYPES) @@ -166,14 +177,14 @@ def merge_raw_parameter_definition(context, presentation, raw_property_definitio our_property_definition._reset_method_cache() type2 = our_property_definition._get_type(context) - if type1 != type2: - if not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2): - context.validation.report( - 'property definition type "{0}" is not a descendant of overridden ' - 'property definition type "{1}"' \ - .format(type1_name, type2._name), - locator=presentation._get_child_locator(field_name, property_name), - level=Issue.BETWEEN_TYPES) + if (type1 is not type2) and \ + (not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2)): + context.validation.report( + u'property definition type "{0}" is not a descendant of overridden ' + u'property definition type "{1}"' \ + .format(our_property_definition.type, type1_name), + locator=presentation._get_child_locator(field_name, property_name), + level=Issue.BETWEEN_TYPES) merge(raw_property_definition, our_property_definition._raw) @@ -225,6 +236,6 @@ def coerce_parameter_value(context, presentation, definition, value, aspect=None def convert_parameter_definitions_to_values(context, definitions): values = OrderedDict() for name, definition in definitions.iteritems(): - default = definition.default - values[name] = coerce_parameter_value(context, definition, definition, default) + values[name] = coerce_parameter_value(context, definition, definition, definition.default, + 'default') return values
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py index 6bdb5b1..79958a1 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py @@ -82,7 +82,6 @@ def get_template_requirements(context, presentation): for requirement_name, requirement_definition in requirement_definitions: # Allowed occurrences allowed_occurrences = requirement_definition.occurrences - allowed_occurrences = allowed_occurrences if allowed_occurrences is not None else None # Count actual occurrences actual_occurrences = 0 @@ -103,28 +102,30 @@ def get_template_requirements(context, presentation): None, presentation) validate_requirement_assignment(context, presentation, requirement_assignment, relationship_property_definitions, - relationship_interface_definitions) + relationship_interface_definitions, + requirement_definition) requirement_assignments.append((requirement_name, requirement_assignment)) elif actual_occurrences > 1: context.validation.report( - 'requirement "%s" is allowed only one occurrence in "%s": %d' - % (requirement_name, presentation._fullname, actual_occurrences), + u'requirement "{0}" is allowed only one occurrence in "{1}": {2:d}' + .format(requirement_name, presentation._fullname, actual_occurrences), locator=presentation._locator, level=Issue.BETWEEN_TYPES) else: if not allowed_occurrences.is_in(actual_occurrences): if allowed_occurrences.value[1] == 'UNBOUNDED': context.validation.report( - 'requirement "%s" does not have at least %d occurrences in "%s": has %d' - % (requirement_name, allowed_occurrences.value[0], - presentation._fullname, actual_occurrences), + u'requirement "{0}" does not have at least {1:d} occurrences in "{3}":' + u' has {4:d}' + .format(requirement_name, allowed_occurrences.value[0], + presentation._fullname, actual_occurrences), locator=presentation._locator, level=Issue.BETWEEN_TYPES) else: context.validation.report( - 'requirement "%s" is allowed between %d and %d occurrences in "%s":' - ' has %d' - % (requirement_name, allowed_occurrences.value[0], - allowed_occurrences.value[1], presentation._fullname, - actual_occurrences), + u'requirement "{0}" is allowed between {1:d} and {2:d} occurrences in' + u' "{3}": has {4:d}' + .format(requirement_name, allowed_occurrences.value[0], + allowed_occurrences.value[1], presentation._fullname, + actual_occurrences), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return requirement_assignments @@ -134,7 +135,7 @@ def get_template_requirements(context, presentation): # Utils # -def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches +def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches our_requirement_assignment, container): from ..assignments import RequirementAssignment @@ -174,9 +175,9 @@ def convert_requirement_from_definition_to_assignment(context, requirement_defin # Make sure the type is derived if not definition_relationship_type._is_descendant(context, relationship_type): context.validation.report( - 'assigned relationship type "%s" is not a descendant of declared relationship type' - ' "%s"' \ - % (relationship_type._name, definition_relationship_type._name), + u'assigned relationship type "{0}" is not a descendant of declared relationship ' + u' type "{1}"' + .format(relationship_type._name, definition_relationship_type._name), locator=container._locator, level=Issue.BETWEEN_TYPES) if relationship_type is not None: @@ -253,12 +254,13 @@ def add_requirement_assignments(context, presentation, requirement_assignments, or our_requirement_assignment, requirement_assignment, relationship_property_definitions, - relationship_interface_definitions) + relationship_interface_definitions, + requirement_definition) requirement_assignments.append((requirement_name, requirement_assignment)) else: - context.validation.report('requirement "%s" not declared at node type "%s" in "%s"' - % (requirement_name, presentation.type, - presentation._fullname), + context.validation.report(u'requirement "{0}" not declared at node type "{1}" in "{2}"' + .format(requirement_name, presentation.type, + presentation._fullname), locator=our_requirement_assignment._locator, level=Issue.BETWEEN_TYPES) @@ -278,7 +280,7 @@ def merge_requirement_assignment(context, relationship_property_definitions, requirement._raw['node_filter'] = deepcopy_with_locators(our_node_filter._raw) our_relationship = our_requirement.relationship # RelationshipAssignment - if (our_relationship is not None) and (our_relationship.type is None): + if our_relationship is not None: # Make sure we have a dict if 'relationship' not in requirement._raw: requirement._raw['relationship'] = OrderedDict() @@ -291,7 +293,8 @@ def merge_requirement_assignment(context, relationship_property_definitions, def merge_requirement_assignment_relationship(context, presentation, property_definitions, interface_definitions, requirement, our_relationship): - our_relationship_properties = our_relationship._raw.get('properties') + our_relationship_properties = our_relationship._raw.get('properties') \ + if isinstance(our_relationship._raw, dict) else None if our_relationship_properties: # Make sure we have a dict if 'properties' not in requirement._raw['relationship']: @@ -305,10 +308,10 @@ def merge_requirement_assignment_relationship(context, presentation, property_de coerce_parameter_value(context, presentation, definition, prop) else: context.validation.report( - 'relationship property "%s" not declared at definition of requirement "%s"' - ' in "%s"' - % (property_name, requirement._fullname, - presentation._container._container._fullname), + u'relationship property "{0}" not declared at definition of requirement "{1}"' + u' in "{2}"' + .format(property_name, requirement._fullname, + presentation._container._container._fullname), locator=our_relationship._get_child_locator('properties', property_name), level=Issue.BETWEEN_TYPES) @@ -330,28 +333,58 @@ def merge_requirement_assignment_relationship(context, presentation, property_de interface_definition, interface_name) else: context.validation.report( - 'relationship interface "%s" not declared at definition of requirement "%s"' - ' in "%s"' - % (interface_name, requirement._fullname, - presentation._container._container._fullname), + u'relationship interface "{0}" not declared at definition of requirement "{1}"' + u' in "{2}"' + .format(interface_name, requirement._fullname, + presentation._container._container._fullname), locator=our_relationship._locator, level=Issue.BETWEEN_TYPES) def validate_requirement_assignment(context, presentation, requirement_assignment, relationship_property_definitions, - relationship_interface_definitions): + relationship_interface_definitions, requirement_definition): + # Validate node + definition_node_type = requirement_definition._get_node_type(context) + assignment_node_type, node_variant = requirement_assignment._get_node(context) + if node_variant == 'node_template': + assignment_node_type = assignment_node_type._get_type(context) + if (assignment_node_type is not None) and (definition_node_type is not None) and \ + (assignment_node_type is not definition_node_type) and \ + (not definition_node_type._is_descendant(context, assignment_node_type)): + context.validation.report( + u'requirement assignment node "{0}" is not derived from node type "{1}" of requirement ' + u'definition in {2}' + .format(requirement_assignment.node, requirement_definition.node, + presentation._container._fullname), + locator=presentation._locator, level=Issue.BETWEEN_TYPES) + + # Validate capability + definition_capability_type = requirement_definition._get_capability_type(context) + assignment_capability_type, capability_variant = requirement_assignment._get_capability(context) + if capability_variant == 'capability_assignment': + assignment_capability_type = assignment_capability_type._get_type(context) + if (assignment_capability_type is not None) and (definition_capability_type is not None) and \ + (assignment_capability_type is not definition_capability_type) and \ + (not definition_capability_type._is_descendant(context, assignment_capability_type)): + context.validation.report( + u'requirement assignment capability "{0}" is not derived from capability type "{1}" of ' + u'requirement definition in {2}' + .format(requirement_assignment.capability, requirement_definition.capability, + presentation._container._fullname), + locator=presentation._locator, level=Issue.BETWEEN_TYPES) + relationship = requirement_assignment.relationship - if relationship is None: - return - validate_required_values(context, presentation, relationship.properties, - relationship_property_definitions) + values = OrderedDict((name, prop.value) + for name, prop in relationship.properties.iteritems()) \ + if (relationship and relationship.properties) else {} + validate_required_values(context, presentation, values, relationship_property_definitions) if relationship_interface_definitions: for interface_name, relationship_interface_definition \ in relationship_interface_definitions.iteritems(): interface_assignment = relationship.interfaces.get(interface_name) \ - if relationship.interfaces is not None else None + if (relationship and relationship.interfaces) else None validate_required_inputs(context, presentation, interface_assignment, relationship_interface_definition, None, interface_name) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py index e2af4b8..ae2f924 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py @@ -18,8 +18,7 @@ from aria.parser.validation import Issue def validate_substitution_mappings_requirement(context, presentation): - - # validate that the requirement in substitution_mapping is defined in the substitution node type + # Validate that the requirement in substitution_mapping is defined in the substitution node type substitution_node_type = presentation._container._get_type(context) if substitution_node_type is None: return @@ -29,7 +28,7 @@ def validate_substitution_mappings_requirement(context, presentation): break else: context.validation.report( - 'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format( + u'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format( presentation._name, substitution_node_type._name), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return @@ -38,7 +37,7 @@ def validate_substitution_mappings_requirement(context, presentation): _report_invalid_mapping_format(context, presentation, field='requirement') return - # validate that the mapped requirement is defined in the corresponding node template + # Validate that the mapped requirement is defined in the corresponding node template node_template = _get_node_template(context, presentation) if node_template is None: _report_missing_node_template(context, presentation, field='requirement') @@ -50,24 +49,24 @@ def validate_substitution_mappings_requirement(context, presentation): break else: context.validation.report( - 'substitution mapping requirement "{0}" refers to an unknown requirement of node ' - 'template "{1}": {mapped_requirement_name}'.format( + u'substitution mapping requirement "{0}" refers to an unknown requirement of node ' + u'template "{1}": {mapped_requirement_name}'.format( presentation._name, node_template._name, mapped_requirement_name=safe_repr(mapped_requirement_name)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return - # validate that the requirement's capability type in substitution_mapping is derived from the + # Validate that the requirement's capability type in substitution_mapping is derived from the # requirement's capability type in the corresponding node template substitution_type_requirement_capability_type = \ substitution_type_requirement._get_capability_type(context) node_template_requirement_capability_type = \ node_template_requirement._get_capability(context)[0] - if not node_template_requirement_capability_type._is_descendant( - context, substitution_type_requirement_capability_type): + if not substitution_type_requirement_capability_type._is_descendant( + context, node_template_requirement_capability_type): context.validation.report( - 'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant ' - 'of the mapped node template capability type "{2}"'.format( + u'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant ' + u'of the mapped node template capability type "{2}"'.format( presentation._name, substitution_type_requirement_capability_type._name, node_template_requirement_capability_type._name), @@ -75,8 +74,7 @@ def validate_substitution_mappings_requirement(context, presentation): def validate_substitution_mappings_capability(context, presentation): - - # validate that the capability in substitution_mapping is defined in the substitution node type + # Validate that the capability in substitution_mapping is defined in the substitution node type substitution_node_type = presentation._container._get_type(context) if substitution_node_type is None: return @@ -84,8 +82,8 @@ def validate_substitution_mappings_capability(context, presentation): substitution_type_capability = substitution_type_capabilities.get(presentation._name) if substitution_type_capability is None: context.validation.report( - 'substitution mapping capability "{0}" ' - 'is not declared in node type "{substitution_type}"'.format( + u'substitution mapping capability "{0}" ' + u'is not declared in node type "{substitution_type}"'.format( presentation._name, substitution_type=substitution_node_type._name), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return @@ -94,7 +92,7 @@ def validate_substitution_mappings_capability(context, presentation): _report_invalid_mapping_format(context, presentation, field='capability') return - # validate that the capability in substitution_mapping is declared in the corresponding + # Validate that the capability in substitution_mapping is declared in the corresponding # node template node_template = _get_node_template(context, presentation) if node_template is None: @@ -105,22 +103,21 @@ def validate_substitution_mappings_capability(context, presentation): if node_template_capability is None: context.validation.report( - 'substitution mapping capability "{0}" refers to an unknown ' - 'capability of node template "{1}": {mapped_capability_name}'.format( + u'substitution mapping capability "{0}" refers to an unknown ' + u'capability of node template "{1}": {mapped_capability_name}'.format( presentation._name, node_template._name, mapped_capability_name=safe_repr(mapped_capability_name)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return - # validate that the capability type in substitution_mapping is derived from the capability type + # Validate that the capability type in substitution_mapping is derived from the capability type # in the corresponding node template substitution_type_capability_type = substitution_type_capability._get_type(context) node_template_capability_type = node_template_capability._get_type(context) - if not substitution_type_capability_type._is_descendant(context, node_template_capability_type): context.validation.report( - 'node template capability type "{0}" is not a descendant of substitution mapping ' - 'capability "{1}" of type "{2}"'.format( + u'node template capability type "{0}" is not a descendant of substitution mapping ' + u'capability "{1}" of type "{2}"'.format( node_template_capability_type._name, presentation._name, substitution_type_capability_type._name), @@ -132,7 +129,9 @@ def validate_substitution_mappings_capability(context, presentation): # def _validate_mapping_format(presentation): - """Validate that the mapping is a list of 2 strings""" + """ + Validate that the mapping is a list of 2 strings. + """ if not isinstance(presentation._raw, list) or \ len(presentation._raw) != 2 or \ not isinstance(presentation._raw[0], basestring) or \ @@ -150,8 +149,8 @@ def _get_node_template(context, presentation): def _report_missing_node_template(context, presentation, field): context.validation.report( - 'substitution mappings {field} "{node_template_mapping}" ' - 'refers to an unknown node template: {node_template_name}'.format( + u'substitution mappings {field} "{node_template_mapping}" ' + u'refers to an unknown node template: {node_template_name}'.format( field=field, node_template_mapping=presentation._name, node_template_name=safe_repr(presentation._raw[0])), @@ -160,7 +159,7 @@ def _report_missing_node_template(context, presentation, field): def _report_invalid_mapping_format(context, presentation, field): context.validation.report( - 'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format( + u'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format( field=field, field_name=presentation._name, value=safe_repr(presentation._raw)), http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py index 0e3c94d..9fa056d 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py @@ -30,4 +30,4 @@ class ExtensiblePresentation(Presentation): @cachedmethod def _get_extension(self, name, default=None): extensions = self._extensions - return extensions.get(name, default) if extensions is not None else None # pylint: disable=no-member + return extensions.get(name, default) if extensions is not None else None # pylint: disable=no-member http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py index f14164a..9cd58e2 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py @@ -14,10 +14,12 @@ # limitations under the License. from aria.utils.formatting import safe_repr +from aria.utils.type import full_type_name from aria.parser.exceptions import InvalidValueError +from aria.parser.presentation import NULL -def data_type_class_getter(cls): +def data_type_class_getter(cls, allow_null=False): """ Wraps the field value in a specialized data type class. @@ -26,12 +28,14 @@ def data_type_class_getter(cls): def getter(field, presentation, context=None): raw = field.default_get(presentation, context) - if raw is not None: - try: - return cls(None, None, raw, None) - except ValueError as e: - raise InvalidValueError( - '%s is not a valid "%s" in "%s": %s' - % (field.full_name, field.full_cls_name, presentation._name, safe_repr(raw)), - cause=e, locator=field.get_locator(raw)) + if (raw is None) or (allow_null and (raw is NULL)): + return raw + try: + return cls(None, None, raw, None) + except ValueError as e: + raise InvalidValueError( + u'{0} is not a valid "{1}" in "{2}": {3}' + .format(field.full_name, full_type_name(cls), presentation._name, + safe_repr(raw)), + cause=e, locator=field.get_locator(raw)) return getter http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py index e5853d8..2ff5143 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py @@ -52,8 +52,8 @@ def copy_validator(template_type_name, templates_dict_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)), + u'"copy" field refers to a {0} that itself is a copy in "{1}": {2}' + .format(template_type_name, presentation._fullname, safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return validator_fn @@ -84,8 +84,8 @@ def data_type_validator(type_name='data type'): 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)), + u'type of property "{0}" creates a circular value hierarchy: {1}' + .format(presentation._fullname, safe_repr(value)), locator=presentation._get_child_locator('type'), level=Issue.BETWEEN_TYPES) # Can be a complex data type @@ -135,14 +135,14 @@ def entry_schema_validator(field, presentation, context): 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), + u'"entry_schema" does not have a value as required by data type "{0}" in "{1}"' + .format(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), + u'"entry_schema" has a value but it is not used by data type "{0}" in "{1}"' + .format(get_data_type_name(the_type), presentation._container._fullname), locator=presentation._locator, level=Issue.BETWEEN_TYPES) @@ -201,8 +201,8 @@ def data_type_constraints_validator(field, presentation, context): 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, + u'data type "{0}" defines constraints but does not have a primitive ancestor' + .format(presentation._fullname), locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES) @@ -220,8 +220,8 @@ def data_type_properties_validator(field, presentation, context): 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, + u'data type "{0}" defines properties even though it has a primitive ancestor' + .format(presentation._fullname), locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES) @@ -272,17 +272,18 @@ def constraint_clause_in_range_validator(field, presentation, context): # 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 + # Second "in_range" value must be greater or equal 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)), + u'upper bound of "in_range" constraint is not greater than the lower bound' + u' in "{0}": {1} <= {2}' + .format(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), + u'constraint "{0}" is not a list of exactly 2 elements in "{1}": {2}' + .format(field.name, presentation._fullname, safe_repr(values)), locator=presentation._get_child_locator(field.name), level=Issue.FIELD) @@ -325,8 +326,8 @@ def constraint_clause_pattern_validator(field, presentation, context): 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), + u'constraint "{0}" is not a valid regular expression in "{1}": {2}' + .format(field.name, presentation._fullname, safe_repr(value)), locator=presentation._get_child_locator(field.name), level=Issue.FIELD, exception=e) @@ -379,21 +380,21 @@ def capability_definition_or_type_validator(field, presentation, context): 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), + u'"{0}" refers to a capability type even though "node" has a value in "{1}"' + .format(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)), + u'requirement "{0}" refers to an unknown capability definition name or capability' + u' type in "{1}": {2}' + .format(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)), + u'requirement "{0}" refers to an unknown capability type in "{1}": {2}' + .format(presentation._name, presentation._container._fullname, safe_repr(value)), locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES) @@ -412,9 +413,9 @@ def node_filter_validator(field, presentation, context): _, 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), + u'requirement "{0}" has a node filter even though "node" does not refer to a node' + u' type in "{1}"' + .format(presentation._fullname, presentation._container._fullname), locator=presentation._locator, level=Issue.BETWEEN_FIELDS) @@ -468,6 +469,54 @@ def list_node_type_or_group_type_validator(field, presentation, context): # +# GroupTemplate +# + +def group_members_validator(field, presentation, context): + """ + Makes sure that the field's elements refer to node templates and that they match the node types + declared in the group type. + + Used with the :func:`field_validator` decorator for the ``targets`` field in + :class:`GroupTemplate`. + """ + + field.default_validate(presentation, context) + + values = getattr(presentation, field.name) + if values is not None: + node_templates = \ + context.presentation.get('service_template', 'topology_template', 'node_templates') \ + or {} + for value in values: + if value not in node_templates: + report_issue_for_unknown_type(context, presentation, 'node template', field.name, + value) + + group_type = presentation._get_type(context) + if group_type is None: + break + + node_types = group_type._get_members(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 + + if not is_valid: + context.validation.report( + u'group definition target does not match a node type' + u' declared in the group type in "{0}": {1}' + .format(presentation._name, safe_repr(value)), + locator=presentation._locator, level=Issue.BETWEEN_TYPES) + + +# # PolicyTemplate # @@ -484,13 +533,12 @@ def policy_targets_validator(field, 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') \ + node_templates = \ + context.presentation.get('service_template', 'topology_template', 'node_templates') \ or {} + groups = context.presentation.get('service_template', 'topology_template', 'groups') \ + or {} + for value in values: 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) @@ -519,9 +567,9 @@ def policy_targets_validator(field, presentation, context): 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)), + u'policy definition target does not match either a node type or a group type' + u' declared in the policy type in "{0}": {1}' + .format(presentation._name, safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) @@ -547,8 +595,8 @@ def node_filter_properties_validator(field, presentation, 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), + u'node filter refers to an unknown property definition in "{0}": {1}' + .format(node_type._name, name), locator=presentation._locator, level=Issue.BETWEEN_TYPES) @@ -564,7 +612,7 @@ def node_filter_capabilities_validator(field, presentation, context): field.default_validate(presentation, context) values = getattr(presentation, field.name) - if values is not None: # pylint: disable=too-many-nested-blocks + 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) @@ -577,12 +625,12 @@ def node_filter_capabilities_validator(field, presentation, context): 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), + u'node filter refers to an unknown capability definition' + u' property in "{0}": {1}' + .format(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), + u'node filter refers to an unknown capability definition in "{0}": {1}' + .format(node_type._name, name), locator=presentation._locator, level=Issue.BETWEEN_TYPES) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py index 5f9750e..f31b6c9 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py @@ -14,7 +14,7 @@ # limitations under the License. -def convert_name_to_full_type_name(context, name, types_dict): # pylint: disable=unused-argument +def convert_name_to_full_type_name(context, name, types_dict): # pylint: disable=unused-argument """ Converts a type name to its full type name, or else returns it unchanged. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/presenter.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_v1_0/presenter.py index 28c9f7b..e84decc 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/presenter.py +++ b/extensions/aria_extension_tosca/simple_v1_0/presenter.py @@ -23,7 +23,7 @@ from .modeling.functions import (Concat, Token, GetInput, GetProperty, GetAttrib from .templates import ServiceTemplate -class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstract-method +class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstract-method """ ARIA presenter for the `TOSCA Simple Profile v1.0 cos01 <http://docs.oasis-open.org/tosca /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html>`__. @@ -70,7 +70,7 @@ class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstrac @cachedmethod def _get_import_locations(self, context): import_locations = [] - if context.presentation.import_profile: + if context.presentation.configuration.get('tosca.import_profile', True): import_locations.append(self.SIMPLE_PROFILE_LOCATION) imports = self._get('service_template', 'imports') if imports: @@ -78,5 +78,5 @@ class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstrac return FrozenList(import_locations) if import_locations else EMPTY_READ_ONLY_LIST @cachedmethod - def _get_model(self, context): # pylint: disable=no-self-use + def _get_model(self, context): # pylint: disable=no-self-use return create_service_template_model(context) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/templates.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/templates.py b/extensions/aria_extension_tosca/simple_v1_0/templates.py index 3c36bb8..d4d012e 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/templates.py +++ b/extensions/aria_extension_tosca/simple_v1_0/templates.py @@ -19,11 +19,11 @@ from aria.parser import implements_specification from aria.parser.presentation import (has_fields, primitive_field, primitive_list_field, object_field, object_list_field, object_dict_field, object_sequenced_list_field, field_validator, - type_validator, list_type_validator) + type_validator) from .assignments import (PropertyAssignment, AttributeAssignment, RequirementAssignment, CapabilityAssignment, InterfaceAssignment, ArtifactAssignment) -from .definitions import ParameterDefinition +from .definitions import (InputDefinition, OutputDefinition) from .filters import NodeFilter from .misc import (Description, MetaData, Repository, Import, SubstitutionMappings) from .modeling.parameters import (get_assigned_and_defined_parameter_values, get_parameter_values) @@ -34,7 +34,8 @@ from .modeling.artifacts import get_inherited_artifact_definitions from .modeling.policies import get_policy_targets from .modeling.copy import get_default_raw_from_copy from .presentation.extensible import ExtensiblePresentation -from .presentation.field_validators import copy_validator, policy_targets_validator +from .presentation.field_validators import (copy_validator, group_members_validator, + policy_targets_validator) from .presentation.types import (convert_name_to_full_type_name, get_type_by_name) from .types import (ArtifactType, DataType, CapabilityType, InterfaceType, RelationshipType, NodeType, GroupType, PolicyType) @@ -182,6 +183,7 @@ class NodeTemplate(ExtensiblePresentation): def _validate(self, context): super(NodeTemplate, self)._validate(context) self._get_property_values(context) + self._get_attribute_default_values(context) self._get_requirements(context) self._get_capabilities(context) self._get_interfaces(context) @@ -284,12 +286,17 @@ class RelationshipTemplate(ExtensiblePresentation): return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'property')) @cachedmethod + def _get_attribute_default_values(self, context): + return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'attribute')) + + @cachedmethod def _get_interfaces(self, context): return FrozenDict(get_template_interfaces(context, self, 'relationship template')) def _validate(self, context): super(RelationshipTemplate, self)._validate(context) self._get_property_values(context) + self._get_attribute_default_values(context) self._get_interfaces(context) def _dump(self, context): @@ -340,7 +347,7 @@ class GroupTemplate(ExtensiblePresentation): :type: {:obj:`basestring`: :class:`PropertyAssignment`} """ - @field_validator(list_type_validator('node template', 'topology_template', 'node_templates')) + @field_validator(group_members_validator) @primitive_list_field(str) def members(self): """ @@ -464,13 +471,13 @@ class TopologyTemplate(ExtensiblePresentation): :type: :class:`Description` """ - @object_dict_field(ParameterDefinition) + @object_dict_field(InputDefinition) def inputs(self): """ An optional list of input parameters (i.e., as parameter definitions) for the Topology Template. - :type: {:obj:`basestring`: :class:`ParameterDefinition`} + :type: {:obj:`basestring`: :class:`InputDefinition`} """ @object_dict_field(NodeTemplate) @@ -506,13 +513,13 @@ class TopologyTemplate(ExtensiblePresentation): :type: {:obj:`basestring`: :class:`PolicyTemplate`} """ - @object_dict_field(ParameterDefinition) + @object_dict_field(OutputDefinition) def outputs(self): """ An optional list of output parameters (i.e., as parameter definitions) for the Topology Template. - :type: {:obj:`basestring`: :class:`ParameterDefinition`} + :type: {:obj:`basestring`: :class:`OutputDefinition`} """ @object_field(SubstitutionMappings) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/extensions/aria_extension_tosca/simple_v1_0/types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/types.py b/extensions/aria_extension_tosca/simple_v1_0/types.py index 43af44b..8ab7b07 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/types.py @@ -34,6 +34,7 @@ from .modeling.capabilities import (get_inherited_valid_source_types, from .modeling.data_types import (get_data_type, get_inherited_constraints, coerce_data_type_value, validate_data_type_name) from .modeling.interfaces import (get_inherited_interface_definitions, get_inherited_operations) +from .modeling.groups import get_inherited_members from .modeling.policies import get_inherited_targets from .modeling.parameters import get_inherited_parameter_definitions from .modeling.requirements import get_inherited_requirement_definitions @@ -70,7 +71,7 @@ class ArtifactType(ExtensiblePresentation): """ @field_getter(data_type_class_getter(Version)) - @primitive_field() + @primitive_field(str) def version(self): """ An optional version for the Artifact Type definition. @@ -153,7 +154,8 @@ class DataType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Data Type definition. @@ -210,7 +212,7 @@ class DataType(ExtensiblePresentation): if not isinstance(parent, DataType): return parent else: - return parent._get_primitive_ancestor(context) # pylint: disable=no-member + return parent._get_primitive_ancestor(context) # pylint: disable=no-member return None @cachedmethod @@ -261,7 +263,8 @@ class CapabilityType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Capability Type definition. @@ -312,7 +315,9 @@ class CapabilityType(ExtensiblePresentation): @cachedmethod def _is_descendant(self, context, other_type): - """returns True iff `other_type` is a descendant of the represented capability type""" + """ + Checks if ``other_type`` is our descendant (or equal to us). + """ if other_type is None: return False elif other_type._name == self._name: @@ -324,12 +329,17 @@ class CapabilityType(ExtensiblePresentation): return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties')) @cachedmethod + def _get_attributes(self, context): + return FrozenDict(get_inherited_parameter_definitions(context, self, 'attributes')) + + @cachedmethod def _get_valid_source_types(self, context): return get_inherited_valid_source_types(context, self) def _validate(self, context): super(CapabilityType, self)._validate(context) self._get_properties(context) + self._get_attributes(context) def _dump(self, context): self._dump_content(context, ( @@ -363,7 +373,8 @@ class InterfaceType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Interface Type definition. @@ -417,8 +428,7 @@ class InterfaceType(ExtensiblePresentation): def _validate(self, context): super(InterfaceType, self)._validate(context) self._get_inputs(context) - for operation in self.operations.itervalues(): # pylint: disable=no-member - operation._validate(context) + self._get_operations(context) def _dump(self, context): self._dump_content(context, ( @@ -450,7 +460,8 @@ class RelationshipType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Relationship Type definition. @@ -508,6 +519,9 @@ class RelationshipType(ExtensiblePresentation): @cachedmethod def _is_descendant(self, context, the_type): + """ + Checks if ``other_type`` is our descendant (or equal to us). + """ if the_type is None: return False elif the_type._name == self._name: @@ -565,7 +579,8 @@ class NodeType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Node Type definition. @@ -639,6 +654,9 @@ class NodeType(ExtensiblePresentation): @cachedmethod def _is_descendant(self, context, the_type): + """ + Checks if ``other_type`` is our descendant (or equal to us). + """ if the_type is None: return False elif the_type._name == self._name: @@ -721,7 +739,8 @@ class GroupType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Group Type definition. @@ -775,6 +794,9 @@ class GroupType(ExtensiblePresentation): @cachedmethod def _is_descendant(self, context, the_type): + """ + Checks if ``other_type`` is our descendant (or equal to us). + """ if the_type is None: return False elif the_type._name == self._name: @@ -786,6 +808,11 @@ class GroupType(ExtensiblePresentation): return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties')) @cachedmethod + def _get_members(self, context): + node_types = get_inherited_members(context, self) + return FrozenList(node_types) + + @cachedmethod def _get_interfaces(self, context): return FrozenDict(get_inherited_interface_definitions(context, self, 'group type')) @@ -827,7 +854,8 @@ class PolicyType(ExtensiblePresentation): :type: :obj:`basestring` """ - @object_field(Version) + @field_getter(data_type_class_getter(Version)) + @primitive_field(str) def version(self): """ An optional version for the Policy Type definition. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/requirements.in ---------------------------------------------------------------------- diff --git a/requirements.in b/requirements.in index c5bfc78..f71a1ee 100644 --- a/requirements.in +++ b/requirements.in @@ -17,7 +17,7 @@ backports.shutil_get_terminal_size>=1, <2 blinker>=1.4, <1.5 bottle>=0.12, <0.13 CacheControl[filecache]>=0.11.0, <0.13 -click>=6, < 7 +click>=6, <7 click_didyoumean>=0.0.3, <0.1 colorama>=0.3, <=0.4 Jinja2>=2.9, <3.0 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/requirements.txt ---------------------------------------------------------------------- diff --git a/requirements.txt b/requirements.txt index 2af8fe3..bbbe97b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,15 +7,15 @@ backports.shutil-get-terminal-size==1.0.0 blinker==1.4 bottle==0.12.13 -cachecontrol[filecache]==0.12.1 +cachecontrol[filecache]==0.12.3 click-didyoumean==0.0.3 click==6.7 colorama==0.3.9 decorator==4.1.2 # via networkx jinja2==2.9.6 -jsonpickle==0.9.4 +jsonpickle==0.9.5 lockfile==0.12.2 # via cachecontrol -logutils==0.3.4.1 +logutils==0.3.5 markupsafe==1.0 # via jinja2 msgpack-python==0.4.8 # via cachecontrol networkx==2.0 @@ -23,12 +23,12 @@ prettytable==0.7.2 psutil==5.4.0 requests==2.13.0 retrying==1.3.3 -ruamel.ordereddict==0.4.9 # via ruamel.yaml +ruamel.ordereddict==0.4.13 # via ruamel.yaml ruamel.yaml==0.15.34 shortuuid==0.5.0 -six==1.10.0 # via retrying -sqlalchemy==1.1.6 -wagon==0.6.0 +six==1.11.0 # via retrying +sqlalchemy==1.1.14 +wagon==0.6.1 wheel==0.29.0 # via wagon # The following packages are considered to be unsafe in a requirements file: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/setup.py ---------------------------------------------------------------------- diff --git a/setup.py b/setup.py index 8483c5b..c833da8 100644 --- a/setup.py +++ b/setup.py @@ -60,7 +60,7 @@ extras_require = { with open(os.path.join(root_dir, 'requirements.in')) as requirements: for requirement in requirements.readlines(): - requirement = requirement.split('#')[0].strip() # Get rid of comments or trailing comments + requirement = requirement.split('#', 1)[0].strip() # Remove comments if not requirement: continue # Skip empty and comment lines @@ -70,7 +70,7 @@ with open(os.path.join(root_dir, 'requirements.in')) as requirements: # https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies # https://hynek.me/articles/conditional-python-dependencies/ if ';' in requirement: - package, condition = requirement.split(';') + package, condition = requirement.split(';', 1) cond_name = ':{0}'.format(condition.strip()) extras_require.setdefault(cond_name, []) extras_require[cond_name].append(package.strip()) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/end2end/test_hello_world.py ---------------------------------------------------------------------- diff --git a/tests/end2end/test_hello_world.py b/tests/end2end/test_hello_world.py index 094ffc3..eeb1781 100644 --- a/tests/end2end/test_hello_world.py +++ b/tests/end2end/test_hello_world.py @@ -55,7 +55,7 @@ def _verify_deployed_service_in_storage(service_name, model_storage): service = service_templates[0].services[service_name] assert service.name == service_name assert len(service.executions) == 1 - assert len(service.nodes) == 2 + assert len(service.nodes) == 1 assert service.outputs['port'].value == 9090 assert all(node.state == node.STARTED for node in service.nodes.itervalues()) assert len(service.executions[0].logs) > 0 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/__init__.py b/tests/extensions/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/__init__.py b/tests/extensions/aria_extension_tosca/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py new file mode 100644 index 0000000..14a2e8a --- /dev/null +++ b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py @@ -0,0 +1,22 @@ +# 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. + + +def test_profile(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +imports: + - aria-1.0 +""", import_profile=True, validate_normative=True).assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/conftest.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/conftest.py b/tests/extensions/aria_extension_tosca/conftest.py new file mode 100644 index 0000000..a2020b7 --- /dev/null +++ b/tests/extensions/aria_extension_tosca/conftest.py @@ -0,0 +1,45 @@ +# 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. + +""" +PyTest configuration module. + +Add support for a "--tosca-parser" CLI option. +""" + +import pytest + +from ...mechanisms.parsing.aria import AriaParser + + +def pytest_addoption(parser): + parser.addoption('--tosca-parser', action='store', default='aria', help='TOSCA parser') + + +def pytest_report_header(config): + tosca_parser = config.getoption('--tosca-parser') + return 'tosca-parser: {0}'.format(tosca_parser) + + +@pytest.fixture(scope='session') +def parser(request): + tosca_parser = request.config.getoption('--tosca-parser') + verbose = request.config.getoption('verbose') > 0 + if tosca_parser == 'aria': + with AriaParser() as p: + p.verbose = verbose + yield p + else: + pytest.fail('configured tosca-parser not supported: {0}'.format(tosca_parser)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py new file mode 100644 index 0000000..fb756bc --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py @@ -0,0 +1,20 @@ +# 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. + + +def test_profile(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 +""", import_profile=True, validate_normative=True).assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_v1_0/data.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/data.py b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py new file mode 100644 index 0000000..104e6bb --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +# 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. + + +# Keywords + +TYPE_NAME_PLURAL = { + 'artifact': 'artifacts', + 'data': 'datatypes', + 'capability': 'capabilities', + 'interface': 'interfaces', + 'relationship': 'relationships', + 'node': 'nodes', + 'group': 'groups', + 'policy': 'policies' +} +TEMPLATE_NAME_SECTIONS = { + 'node': 'node_templates', + 'group': 'groups', + 'relationship': 'relationship_templates', + 'policy': 'policies' +} +PRIMITIVE_TYPE_NAMES = ('string', 'integer', 'float', 'boolean') +PARAMETER_TYPE_NAMES = PRIMITIVE_TYPE_NAMES + ('MyType',) +CONSTRAINTS_WITH_VALUE = ('equal', 'greater_than', 'greater_or_equal', 'less_than', 'less_or_equal') +CONSTRAINTS_WITH_VALUE_LIST = ('valid_values',) +CONSTRAINTS_WITH_VALUE_RANGE = ('in_range',) +CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT = ('length', 'min_length', 'max_length') + + +# Values + +PRIMITIVE_VALUES = ('null', 'true', 'a string', '123', '0.123', '[]', '{}') +NOT_A_DICT = ('null', 'true', 'a string', '123', '0.123', '[]') +NOT_A_DICT_WITH_ONE_KEY = NOT_A_DICT + ('{}', '{k1: v1, k2: v2}',) +NOT_A_DICT_OR_STRING = ('null', 'true', '123', '0.123', '[]') +NOT_A_LIST = ('null', 'true', 'a string', '123', '0.123', '{}') +NOT_A_LIST_OF_TWO = NOT_A_LIST + ('[]', '[a]', '[a, b, c]') +NOT_A_STRING = ('null', 'true', '123', '0.123', '[]', '{}') +NOT_A_BOOL = ('null', 'a string', '123', '0.123', '[]', '{}') +NOT_A_RANGE = NOT_A_LIST + ( + '[]', '[ 1 ]', '[ 1, 2, 3 ]', + '[ 1, 1 ]', '[ 2, 1 ]', + '[ 1, a string ]', '[ a string, 1 ]', + '[ 1.5, 2 ]', '[ 1, 2.5 ]' +) +OCCURRENCES = ('[ 0, 1 ]', '[ 10, UNBOUNDED ]') +BAD_OCCURRENCES = NOT_A_RANGE + ('[ -1, 1 ]', '[ 0, unbounded ]') +GOOD_VERSIONS = ("'6.1'", '2.0.1', '3.1.0.beta', "'1.0.0.alpha-10'") +BAD_VERSIONS = ('a_string', '1.2.3.4.5', '1.2.beta', '1.0.0.alpha-x') +STATUSES = ('supported', 'unsupported', 'experimental', 'deprecated') +PARAMETER_VALUES = ( + ('string', 'a string'), + ('integer', '1'), + ('float', '1.1'), + ('MyType', '{my_field: a string}') +) +ENTRY_SCHEMA_VALUES = ( + ('string', 'a string', 'another string'), + ('integer', '1', '2'), + ('float', '1.1', '2.2'), + ('MyType', '{my_field: a string}', '{}') +) +ENTRY_SCHEMA_VALUES_BAD = ( + ('string', 'a string', '1'), + ('integer', '1', 'a string'), + ('float', '1.1', 'a string'), + ('MyType', '{my_field: a string}', 'a string') +) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py @@ -0,0 +1,14 @@ +# 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. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py new file mode 100644 index 0000000..23a274a --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py @@ -0,0 +1,102 @@ + # -*- coding: utf-8 -*- +# 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. + + +def test_functions_concat_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: + properties: + my_parameter: + type: string +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { concat: [] } +""").assert_success() + + +def test_functions_concat_strings(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: + properties: + my_parameter: + type: string +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { concat: [ a, b, c ] } +""").assert_success() + + +def test_functions_concat_mixed(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: + properties: + my_parameter: + type: string +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { concat: [ a, 1, 1.1, null, [], {} ] } +""").assert_success() + + +def test_functions_concat_nested(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: + properties: + my_parameter: + type: string +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { concat: [ a, { concat: [ b, c ] } ] } +""").assert_success() + + +# Unicode + +def test_functions_concat_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + é¡å: + properties: + åæ¸: + type: string +topology_template: + node_templates: + 模æ¿: + type: é¡å + properties: + åæ¸: { concat: [ ä¸, äº, ä¸ ] } +""").assert_success() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/89b9f130/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py ---------------------------------------------------------------------- diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py new file mode 100644 index 0000000..6fa1a3c --- /dev/null +++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py @@ -0,0 +1,156 @@ +# -*- coding: utf-8 -*- +# 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 pytest + + +# Syntax + +def test_functions_get_artifact_syntax_empty(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +node_types: + MyType: + properties: + my_parameter: + type: string +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { get_artifact: [] } # needs at least two args +""").assert_failure() + + +# Arguments + +def test_functions_get_artifact_2_arguments(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +artifact_types: + MyType: {} +node_types: + MyType: + properties: + my_parameter: + type: string + artifacts: + my_artifact: + type: MyType + file: filename +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { get_artifact: [ my_node, my_artifact ] } +""").assert_success() + + +@pytest.mark.xfail(reason='not yet implemented') +def test_functions_get_artifact_unknown(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +artifact_types: + MyType: {} +node_types: + MyType: + properties: + my_parameter: + type: string + artifacts: + my_artifact: + type: MyType + file: filename +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { get_artifact: [ unknown, my_artifact ] } +""").assert_failure() + + +def test_functions_get_artifact_3_arguments(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +artifact_types: + MyType: {} +node_types: + MyType: + properties: + my_parameter: + type: string + artifacts: + my_artifact: + type: MyType + file: filename +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { get_artifact: [ my_node, my_artifact, path ] } +""").assert_success() + + +def test_functions_get_artifact_4_arguments(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +artifact_types: + MyType: {} +node_types: + MyType: + properties: + my_parameter: + type: string + artifacts: + my_artifact: + type: MyType + file: filename +topology_template: + node_templates: + my_node: + type: MyType + properties: + my_parameter: { get_artifact: [ my_node, my_artifact, path, true ] } +""").assert_success() + + +# Unicode + +def test_functions_get_artifact_unicode(parser): + parser.parse_literal(""" +tosca_definitions_version: tosca_simple_yaml_1_0 +artifact_types: + é¡å: {} +node_types: + é¡å: + properties: + åæ¸: + type: string + artifacts: + ç¥å¨: + type: é¡å + file: æ件å +topology_template: + node_templates: + 模æ¿: + type: é¡å + properties: + åæ¸: { get_artifact: [ 模æ¿, ç¥å¨, è·¯å¾, true ] } +""").assert_success()