http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/data_types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/data_types.py index 216f1e4..b85caa1 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/data_types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/data_types.py @@ -37,13 +37,13 @@ class Timezone(tzinfo): super(Timezone, self).__init__() self._offset = timedelta(hours=hours, minutes=minutes) - def utcoffset(self, dt): # pylint: disable=unused-argument + def utcoffset(self, dt): # pylint: disable=unused-argument return self._offset - def tzname(self, dt): # pylint: disable=unused-argument - return str(self._offset) + def tzname(self, dt): # pylint: disable=unused-argument + return unicode(self._offset) - def dst(self, dt): # pylint: disable=unused-argument + def dst(self, dt): # pylint: disable=unused-argument return Timezone._ZERO _ZERO = timedelta(0) @@ -74,8 +74,8 @@ class Timestamp(object): r'(([ \t]*)Z|(?P<tzhour>[-+][0-9][0-9])?(:(?P<tzminute>[0-9][0-9])?)?)?$' CANONICAL = '%Y-%m-%dT%H:%M:%S' - def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument - value = str(value) + def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + value = unicode(value) match = re.match(Timestamp.REGULAR_SHORT, value) if match is not None: # Parse short form @@ -116,8 +116,8 @@ class Timestamp(object): Timezone(tzhour, tzminute)) else: raise ValueError( - 'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": %s' - % safe_repr(value)) + u'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": {0}' + .format(safe_repr(value))) @property def as_datetime_utc(self): @@ -129,8 +129,8 @@ class Timestamp(object): def __str__(self): the_datetime = self.as_datetime_utc - return '%s%sZ' \ - % (the_datetime.strftime(Timestamp.CANONICAL), Timestamp._fraction_as_str(the_datetime)) + return u'{0}{1}Z'.format(the_datetime.strftime(Timestamp.CANONICAL), + Timestamp._fraction_as_str(the_datetime)) def __repr__(self): return repr(self.__str__()) @@ -145,7 +145,7 @@ class Timestamp(object): @staticmethod def _fraction_as_str(the_datetime): - return '{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0') + return u'{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0') @total_ordering @@ -165,7 +165,7 @@ class Version(object): REGEX = \ r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<fix>\d+)' + \ - r'((\.(?P<qualifier>\d+))(\-(?P<build>\d+))?)?)?$' + r'((\.(?P<qualifier>\w+))(\-(?P<build>\d+))?)?)?$' @staticmethod def key(version): @@ -174,14 +174,13 @@ class Version(object): """ return (version.major, version.minor, version.fix, version.qualifier, version.build) - def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument - str_value = str(value) - match = re.match(Version.REGEX, str_value) + def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + str_value = unicode(value) + match = re.match(Version.REGEX, str_value, flags=re.UNICODE) if match is None: raise ValueError( - 'version must be formatted as <major_version>.<minor_version>' - '[.<fix_version>[.<qualifier>[-<build_version]]]: %s' - % safe_repr(value)) + u'version must be formatted as <major_version>.<minor_version>' + u'[.<fix_version>[.<qualifier>[-<build_version]]]: {0}'.format(safe_repr(value))) self.value = str_value @@ -193,8 +192,6 @@ class Version(object): if self.fix is not None: self.fix = int(self.fix) self.qualifier = match.group('qualifier') - if self.qualifier is not None: - self.qualifier = int(self.qualifier) self.build = match.group('build') if self.build is not None: self.build = int(self.build) @@ -215,6 +212,7 @@ class Version(object): return (self.major, self.minor, self.fix, self.qualifier, self.build) == \ (version.major, version.minor, version.fix, version.qualifier, version.build) + @implements_specification('3.2.2.1', 'tosca-simple-1.0') def __lt__(self, version): if self.major < version.major: return True @@ -225,9 +223,7 @@ class Version(object): if self.fix < version.fix: return True elif self.fix == version.fix: - if self.qualifier < version.qualifier: - return True - elif self.qualifier == version.qualifier: + if self.qualifier == version.qualifier: if self.build < version.build: return True return False @@ -244,28 +240,29 @@ class Range(object): #TYPE_TOSCA_RANGE>`__ """ - def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument if not isinstance(value, list): - raise ValueError('range value is not a list: %s' % safe_repr(value)) + raise ValueError(u'range value is not a list: {0}'.format(safe_repr(value))) if len(value) != 2: - raise ValueError('range value does not have exactly 2 elements: %s' % safe_repr(value)) + raise ValueError(u'range value does not have exactly 2 elements: {0}' + .format(safe_repr(value))) def is_int(v): return isinstance(v, int) and (not isinstance(v, bool)) # In Python bool is an int if not is_int(value[0]): - raise ValueError('lower bound of range is not a valid integer: %s' - % safe_repr(value[0])) + raise ValueError(u'lower bound of range is not a valid integer: {0}' + .format(safe_repr(value[0]))) if value[1] != 'UNBOUNDED': if not is_int(value[1]): - raise ValueError('upper bound of range is not a valid integer or "UNBOUNDED": %s' - % safe_repr(value[0])) + raise ValueError(u'upper bound of range is not a valid integer or "UNBOUNDED": {0}' + .format(safe_repr(value[0]))) if value[0] >= value[1]: raise ValueError( - 'upper bound of range is not greater than the lower bound: %s >= %s' - % (safe_repr(value[0]), safe_repr(value[1]))) + u'upper bound of range is not greater than the lower bound: {0} >= {1}' + .format(safe_repr(value[0]), safe_repr(value[1]))) self.value = value @@ -294,9 +291,9 @@ class List(list): """ @staticmethod - def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument if not isinstance(value, list): - raise ValueError('"list" data type value is not a list: %s' % safe_repr(value)) + raise ValueError(u'"list" data type value is not a list: {0}'.format(safe_repr(value))) entry_schema_type = entry_schema._get_type(context) entry_schema_constraints = entry_schema.constraints @@ -328,12 +325,12 @@ class Map(StrictDict): """ @staticmethod - def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument if not isinstance(value, dict): - raise ValueError('"map" data type value is not a dict: %s' % safe_repr(value)) + raise ValueError(u'"map" data type value is not a dict: {0}'.format(safe_repr(value))) if entry_schema is None: - raise ValueError('"map" data type does not define "entry_schema"') + raise ValueError(u'"map" data type does not define "entry_schema"') entry_schema_type = entry_schema._get_type(context) entry_schema_constraints = entry_schema.constraints @@ -348,7 +345,7 @@ class Map(StrictDict): return the_map def __init__(self, items=None): - super(Map, self).__init__(items, key_class=str) + super(Map, self).__init__(items, key_class=basestring) # Can't define as property because it's old-style Python class def as_raw(self): @@ -374,29 +371,31 @@ class Scalar(object): """ return scalar.value - def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument - str_value = str(value) - match = re.match(self.REGEX, str_value) # pylint: disable=no-member + def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument + str_value = unicode(value) + match = re.match(self.REGEX, str_value, flags=re.UNICODE) # pylint: disable=no-member if match is None: - raise ValueError('scalar must be formatted as <scalar> <unit>: %s' % safe_repr(value)) + raise ValueError(u'scalar must be formatted as <scalar> <unit>: {0}' + .format(safe_repr(value))) self.factor = float(match.group('scalar')) if self.factor < 0: - raise ValueError('scalar is negative: %s' % safe_repr(self.factor)) + raise ValueError('scalar is negative: {0}'.format(safe_repr(self.factor))) self.unit = match.group('unit') unit_lower = self.unit.lower() unit_size = None - for k, v in self.UNITS.iteritems(): # pylint: disable=no-member + for k, v in self.UNITS.iteritems(): # pylint: disable=no-member if k.lower() == unit_lower: self.unit = k unit_size = v break if unit_size is None: - raise ValueError('scalar specified with unsupported unit: %s' % safe_repr(self.unit)) + raise ValueError(u'scalar specified with unsupported unit: {0}' + .format(safe_repr(self.unit))) - self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member + self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member @property def as_raw(self): @@ -404,10 +403,10 @@ class Scalar(object): ('value', self.value), ('factor', self.factor), ('unit', self.unit), - ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member + ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member def __str__(self): - return '%s %s' % (self.value, self.UNIT) # pylint: disable=no-member + return u'{0} {1}'.format(self.value, self.UNIT) # pylint: disable=no-member def __repr__(self): return repr(self.__str__()) @@ -416,14 +415,14 @@ class Scalar(object): if isinstance(scalar, Scalar): value = scalar.value else: - value = self.TYPE(scalar) # pylint: disable=no-member + value = self.TYPE(scalar) # pylint: disable=no-member return self.value == value def __lt__(self, scalar): if isinstance(scalar, Scalar): value = scalar.value else: - value = self.TYPE(scalar) # pylint: disable=no-member + value = self.TYPE(scalar) # pylint: disable=no-member return self.value < value @@ -509,12 +508,12 @@ class ScalarFrequency(Scalar): # The following are hooked in the YAML as 'coerce_value' extensions # -def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument +def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument return coerce_to_data_type_class(context, presentation, Timestamp, entry_schema, constraints, value, aspect) -def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument +def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument return coerce_to_data_type_class(context, presentation, Version, entry_schema, constraints, value, aspect) @@ -533,23 +532,23 @@ def coerce_range(context, presentation, the_type, entry_schema, constraints, val value, aspect) -def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument +def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument return coerce_to_data_type_class(context, presentation, List, entry_schema, constraints, value, aspect) -def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument +def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument return coerce_to_data_type_class(context, presentation, Map, entry_schema, constraints, value, aspect) -def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument +def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument aspect): return coerce_to_data_type_class(context, presentation, ScalarSize, entry_schema, constraints, value, aspect) -def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument +def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument aspect): return coerce_to_data_type_class(context, presentation, ScalarTime, entry_schema, constraints, value, aspect)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/definitions.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/definitions.py b/extensions/aria_extension_tosca/simple_v1_0/definitions.py index 9158776..a7e2806 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/definitions.py +++ b/extensions/aria_extension_tosca/simple_v1_0/definitions.py @@ -15,7 +15,9 @@ from aria.utils.collections import FrozenDict from aria.utils.caching import cachedmethod +from aria.utils.formatting import safe_repr from aria.parser import implements_specification +from aria.parser.validation import Issue from aria.parser.presentation import (has_fields, short_form_field, allow_unknown_fields, primitive_field, primitive_list_field, object_field, object_list_field, object_dict_field, @@ -192,8 +194,8 @@ class AttributeDefinition(ExtensiblePresentation): @has_fields -@implements_specification('3.5.12', 'tosca-simple-1.0') -class ParameterDefinition(PropertyDefinition): +@implements_specification('3.5.12-1', 'tosca-simple-1.0') +class InputDefinition(PropertyDefinition): """ A parameter definition is essentially a TOSCA property definition; however, it also allows a value to be assigned to it (as for a TOSCA property assignment). In addition, in the case of @@ -205,6 +207,18 @@ class ParameterDefinition(PropertyDefinition): #DEFN_ELEMENT_PARAMETER_DEF>`__ """ + @field_validator(data_value_validator) + @primitive_field() + def value(self): + """ + The type-compatible value to assign to the named parameter. Parameter values may be provided + as the result from the evaluation of an expression or a function. + """ + + +@has_fields +@implements_specification('3.5.12-2', 'tosca-simple-1.0') +class OutputDefinition(InputDefinition): @field_validator(data_type_validator()) @primitive_field(str) def type(self): @@ -214,15 +228,10 @@ class ParameterDefinition(PropertyDefinition): Note: This keyname is required for a TOSCA Property definition, but is not for a TOSCA Parameter definition. - :type: :obj:`basestring` - """ + ARIA NOTE: the spec must be mistaken: inputs should have this field requires, only outputs + have it as optional. - @field_validator(data_value_validator) - @primitive_field() - def value(self): - """ - The type-compatible value to assign to the named parameter. Parameter values may be provided - as the result from the evaluation of an expression or a function. + :type: :obj:`basestring` """ @@ -283,7 +292,7 @@ class InterfaceDefinition(ExtensiblePresentation): @field_validator(type_validator('interface type', convert_name_to_full_type_name, 'interface_types')) - @primitive_field(str) + @primitive_field(str, required=True) def type(self): """ ARIA NOTE: This field is not mentioned in the spec, but is implied. @@ -322,9 +331,8 @@ class InterfaceDefinition(ExtensiblePresentation): def _validate(self, context): super(InterfaceDefinition, self)._validate(context) - if self.operations: - for operation in self.operations.itervalues(): # pylint: disable=no-member - operation._validate(context) + self._get_inputs(context) + self._get_operations(context) @short_form_field('type') @@ -342,6 +350,8 @@ class RelationshipDefinition(ExtensiblePresentation): The optional reserved keyname used to provide the name of the Relationship Type for the requirement definition's relationship keyname. + ARIA NOTE: the spec shows this as a required field. + :type: :obj:`basestring` """ @@ -428,6 +438,16 @@ class RequirementDefinition(ExtensiblePresentation): def _get_node_type(self, context): return context.presentation.get_from_dict('service_template', 'node_types', self.node) + def _validate(self, context): + super(RequirementDefinition, self)._validate(context) + occurrences = self.occurrences + if (occurrences is not None) and ((occurrences.value[0] < 0) or \ + ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))): + context.validation.report( + 'requirements definition "{0}" occurrences range includes negative integers: {1}' + .format(self._name, safe_repr(occurrences)), + locator=self._locator, level=Issue.BETWEEN_TYPES) + @short_form_field('type') @has_fields @@ -516,3 +536,13 @@ class CapabilityDefinition(ExtensiblePresentation): if container_parent is not None else None return container_parent_capabilities.get(self._name) \ if container_parent_capabilities is not None else None + + def _validate(self, context): + super(CapabilityDefinition, self)._validate(context) + occurrences = self.occurrences + if (occurrences is not None) and ((occurrences.value[0] < 0) or \ + ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))): + context.validation.report( + 'capability definition "{0}" occurrences range includes negative integers: {1}' + .format(self._name, safe_repr(occurrences)), + locator=self._locator, level=Issue.BETWEEN_TYPES) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/misc.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py index 221163c..914691e 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/misc.py +++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py @@ -20,14 +20,16 @@ from aria.parser import implements_specification from aria.parser.presentation import (AsIsPresentation, has_fields, allow_unknown_fields, short_form_field, primitive_field, primitive_list_field, primitive_dict_unknown_fields, object_field, - object_list_field, object_dict_field, field_validator, - type_validator) + object_list_field, object_dict_field, field_getter, + field_validator, type_validator, not_negative_validator) +from .data_types import Version from .modeling.data_types import (get_data_type, get_data_type_value, get_property_constraints, apply_constraint_to_value) from .modeling.substitution_mappings import (validate_substitution_mappings_requirement, validate_substitution_mappings_capability) from .presentation.extensible import ExtensiblePresentation +from .presentation.field_getters import data_type_class_getter from .presentation.field_validators import (constraint_clause_field_validator, constraint_clause_in_range_validator, constraint_clause_valid_values_validator, @@ -47,7 +49,7 @@ class Description(AsIsPresentation): #DEFN_ELEMENT_DESCRIPTION>`__ """ - def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument + def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument super(Description, self).__init__(name, raw, container, cls=unicode) def _dump(self, context): @@ -79,6 +81,7 @@ class MetaData(ExtensiblePresentation): as a single-line string value. """ + @field_getter(data_type_class_getter(Version, allow_null=True)) @primitive_field(str) @implements_specification('3.9.3.5', 'tosca-simple-1.0') def template_version(self): @@ -87,7 +90,7 @@ class MetaData(ExtensiblePresentation): service template as a single-line string value. """ - @primitive_dict_unknown_fields() + @primitive_dict_unknown_fields(str) def custom(self): """ :type: dict @@ -135,6 +138,10 @@ class Repository(ExtensiblePresentation): def _get_credential(self, context): return get_data_type_value(context, self, 'credential', 'tosca.datatypes.Credential') + def _validate(self, context): + super(Repository, self)._validate(context) + self._get_credential(context) + @short_form_field('file') @has_fields @@ -255,18 +262,21 @@ class ConstraintClause(ExtensiblePresentation): Constrains a property or parameter to a value that is in the list of declared values. """ + @field_validator(not_negative_validator) @primitive_field(int) def length(self): """ Constrains the property or parameter to a value of a given length. """ + @field_validator(not_negative_validator) @primitive_field(int) def min_length(self): """ Constrains the property or parameter to a value to a minimum length. """ + @field_validator(not_negative_validator) @primitive_field(int) def max_length(self): """ http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py index 0c621a0..17b94fc 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -27,7 +27,7 @@ from datetime import datetime from aria.parser.validation import Issue -from aria.utils.formatting import string_list_as_string +from aria.utils.formatting import (string_list_as_string, safe_repr) from aria.utils.collections import (StrictDict, OrderedDict) from aria.utils.yaml import yaml from aria.orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS @@ -49,7 +49,7 @@ from ..data_types import coerce_value IMPLEMENTATION_PREFIX_REGEX = re.compile(r'(?<!\\)(?:\\\\)*>') -def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches +def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches model = ServiceTemplate(created_at=datetime.now(), main_file_name=os.path.basename(str(context.presentation.location))) @@ -315,6 +315,10 @@ def create_relationship_template_model(context, service_template, relationship): create_parameter_models_from_assignments(model.properties, relationship.properties, model_cls=Property) + # TODO: does not exist in models, but should + #create_parameter_models_from_assignments(model.attributes, + # relationship.attributes, + # model_cls=Attribute) create_interface_template_models(context, service_template, model.interface_templates, relationship.interfaces) @@ -346,6 +350,10 @@ def create_capability_template_model(context, service_template, capability): create_parameter_models_from_assignments(model.properties, capability.properties, model_cls=Property) + # TODO: does not exist in models, but should + #create_parameter_models_from_assignments(model.attributes, + # capability.attributes, + # model_cls=Attribute) return model @@ -399,7 +407,7 @@ def create_operation_template_model(context, service_template, operation): value = yaml.load(value, Loader=yaml.SafeLoader) except yaml.parser.MarkedYAMLError as e: context.validation.report( - 'YAML parser {0} in operation configuration: {1}' + u'YAML parser {0} in operation configuration: {1}' .format(e.problem, value), locator=implementation._locator, level=Issue.FIELD) @@ -518,7 +526,7 @@ def create_workflow_operation_template_model(context, service_template, policy): used_reserved_names = WORKFLOW_DECORATOR_RESERVED_ARGUMENTS.intersection(model.inputs.keys()) if used_reserved_names: - context.validation.report('using reserved arguments in workflow policy "{0}": {1}' + context.validation.report(u'using reserved arguments in workflow policy "{0}": {1}' .format( policy._name, string_list_as_string(used_reserved_names)), @@ -630,7 +638,8 @@ def create_node_filter_constraints(context, node_filter, target_node_template_co for property_name, constraint_clause in properties: constraint = create_constraint(context, node_filter, constraint_clause, property_name, None) - target_node_template_constraints.append(constraint) + if constraint is not None: + target_node_template_constraints.append(constraint) capabilities = node_filter.capabilities if capabilities is not None: @@ -640,24 +649,33 @@ def create_node_filter_constraints(context, node_filter, target_node_template_co for property_name, constraint_clause in properties: constraint = create_constraint(context, node_filter, constraint_clause, property_name, capability_name) - target_node_template_constraints.append(constraint) + if constraint is not None: + target_node_template_constraints.append(constraint) -def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements +def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements + if (not isinstance(constraint_clause._raw, dict)) or (len(constraint_clause._raw) != 1): + context.validation.report( + u'node_filter constraint is not a dict with one key: {0}' + .format(safe_repr(constraint_clause._raw)), + locator=node_filter._locator, + level=Issue.FIELD) + return None + constraint_key = constraint_clause._raw.keys()[0] the_type = constraint_clause._get_type(context) - def coerce_constraint(constraint): + def coerce_constraint(constraint, the_type=the_type): if the_type is not None: return coerce_value(context, node_filter, the_type, None, None, constraint, constraint_key) else: return constraint - def coerce_constraints(constraints): + def coerce_constraints(constraints, the_type=the_type): if the_type is not None: - return tuple(coerce_constraint(constraint) for constraint in constraints) + return tuple(coerce_constraint(constraint, the_type) for constraint in constraints) else: return constraints @@ -684,18 +702,22 @@ def create_constraint(context, node_filter, constraint_clause, property_name, ca coerce_constraints(constraint_clause.valid_values)) elif constraint_key == 'length': return Length(property_name, capability_name, - coerce_constraint(constraint_clause.length)) + coerce_constraint(constraint_clause.length, int)) elif constraint_key == 'min_length': return MinLength(property_name, capability_name, - coerce_constraint(constraint_clause.min_length)) + coerce_constraint(constraint_clause.min_length, int)) elif constraint_key == 'max_length': return MaxLength(property_name, capability_name, - coerce_constraint(constraint_clause.max_length)) + coerce_constraint(constraint_clause.max_length, int)) elif constraint_key == 'pattern': return Pattern(property_name, capability_name, - coerce_constraint(constraint_clause.pattern)) + coerce_constraint(constraint_clause.pattern, unicode)) else: - raise ValueError('malformed node_filter: {0}'.format(constraint_key)) + context.validation.report( + u'unsupported node_filter constraint: {0}'.format(constraint_key), + locator=node_filter._locator, + level=Issue.FIELD) + return None def split_prefix(string): @@ -703,7 +725,7 @@ def split_prefix(string): Splits the prefix on the first non-escaped ">". """ - split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1) + split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1) if string is not None else () if len(split) < 2: return None, None return split[0].strip(), split[1].strip() @@ -740,7 +762,7 @@ def extract_implementation_primary(context, service_template, presentation, mode model.function = postfix if model.plugin_specification is None: context.validation.report( - 'no policy for plugin "{0}" specified in operation implementation: {1}' + u'no policy for plugin "{0}" specified in operation implementation: {1}' .format(prefix, primary), locator=presentation._get_child_locator('properties', 'implementation'), level=Issue.BETWEEN_TYPES) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py index 1b95bec..bf6636b 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py @@ -73,8 +73,8 @@ def get_inherited_capability_definitions(context, presentation, for_presentation if not type1._is_descendant(context, type2): context.validation.report( - 'capability definition type "{0}" is not a descendant of overridden ' - 'capability definition type "{1}"' \ + u'capability definition type "{0}" is not a descendant of overridden ' + u'capability definition type "{1}"' \ .format(type1._name, type2._name), locator=our_capability_definition._locator, level=Issue.BETWEEN_TYPES) @@ -139,9 +139,18 @@ def get_template_capabilities(context, presentation): if values: capability_assignment._raw['properties'] = values capability_assignment._reset_method_cache() + + # Assign attributes + values = get_assigned_and_defined_parameter_values(context, + our_capability_assignment, + 'attribute') + + if values: + capability_assignment._raw['attributes'] = values + capability_assignment._reset_method_cache() else: context.validation.report( - 'capability "{0}" not declared at node type "{1}" in "{2}"' + u'capability "{0}" not declared at node type "{1}" in "{2}"' .format(capability_name, presentation.type, presentation._fullname), locator=our_capability_assignment._locator, level=Issue.BETWEEN_TYPES) @@ -161,29 +170,38 @@ def convert_capability_from_definition_to_assignment(context, presentation, cont if properties is not None: raw['properties'] = convert_parameter_definitions_to_values(context, properties) - # TODO attributes + attributes = presentation.attributes + if attributes is not None: + raw['attributes'] = convert_parameter_definitions_to_values(context, attributes) return CapabilityAssignment(name=presentation._name, raw=raw, container=container) def merge_capability_definition(context, presentation, capability_definition, from_capability_definition): - raw_properties = OrderedDict() - capability_definition._raw['type'] = from_capability_definition.type - # Merge properties from type - from_property_defintions = from_capability_definition.properties - merge_raw_parameter_definitions(context, presentation, raw_properties, from_property_defintions, - 'properties') + raw_properties = OrderedDict() + raw_attributes = OrderedDict() - # Merge our properties + # Merge parameters from type merge_raw_parameter_definitions(context, presentation, raw_properties, capability_definition.properties, 'properties') + merge_raw_parameter_definitions(context, presentation, raw_attributes, + capability_definition.attributes, 'attributes') + + # Merge our parameters + merge_raw_parameter_definitions(context, presentation, raw_properties, + from_capability_definition.properties, 'properties') + merge_raw_parameter_definitions(context, presentation, raw_attributes, + from_capability_definition.attributes, 'attributes') if raw_properties: capability_definition._raw['properties'] = raw_properties capability_definition._reset_method_cache() + if raw_attributes: + capability_definition._raw['attributes'] = raw_attributes + capability_definition._reset_method_cache() # Merge occurrences occurrences = from_capability_definition._raw.get('occurrences') @@ -194,23 +212,31 @@ def merge_capability_definition(context, presentation, capability_definition, def merge_capability_definition_from_type(context, presentation, capability_definition): """ - Merge ``properties`` and ``valid_source_types`` from the node type's capability definition - over those taken from the parent node type. + Merge ``properties``, ``attributes``, and ``valid_source_types`` from the node type's capability + definition over those taken from the parent node type. """ raw_properties = OrderedDict() + raw_attributes = OrderedDict() - # Merge properties from parent + # Merge parameters from parent the_type = capability_definition._get_type(context) type_property_defintions = the_type._get_properties(context) + type_attribute_defintions = the_type._get_attributes(context) merge_raw_parameter_definitions(context, presentation, raw_properties, type_property_defintions, 'properties') + merge_raw_parameter_definitions(context, presentation, raw_attributes, + type_attribute_defintions, 'attributes') - # Merge our properties (might override definitions in parent) + # Merge our parameters (might override definitions in parent) merge_raw_parameter_definitions(context, presentation, raw_properties, capability_definition.properties, 'properties') + merge_raw_parameter_definitions(context, presentation, raw_attributes, + capability_definition.attributes, 'attributes') if raw_properties: capability_definition._raw['properties'] = raw_properties + if raw_attributes: + capability_definition._raw['attributes'] = raw_attributes # Override valid_source_types if capability_definition._raw.get('valid_source_types') is None: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py index bd9037f..b3c2e49 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py @@ -23,7 +23,7 @@ def get_default_raw_from_copy(presentation, field_name): """ copy = presentation._raw.get('copy') - if copy is not None: + if isinstance(copy, basestring): templates = getattr(presentation._container, field_name) if templates is not None: template = templates.get(copy) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py index 13ce9a3..25e53c6 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py @@ -52,7 +52,7 @@ def get_inherited_constraints(context, presentation): return constraints -def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument +def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument aspect): """ Handles the ``_coerce_data()`` hook for complex data types. @@ -90,8 +90,8 @@ def coerce_data_type_value(context, presentation, data_type, entry_schema, const aspect) else: context.validation.report( - 'assignment to undefined property "%s" in type "%s" in "%s"' - % (name, data_type._fullname, presentation._fullname), + u'assignment to undefined property "{0}" in type "{1}" in "{2}"' + .format(name, data_type._fullname, presentation._fullname), locator=get_locator(v, value, presentation), level=Issue.BETWEEN_TYPES) # Fill in defaults from the definitions, and check if required definitions have not been @@ -108,15 +108,15 @@ def coerce_data_type_value(context, presentation, data_type, entry_schema, const if getattr(definition, 'required', False) and (temp.get(name) is None): context.validation.report( - 'required property "%s" in type "%s" is not assigned a value in "%s"' - % (name, data_type._fullname, presentation._fullname), + u'required property "{0}" in type "{1}" is not assigned a value in "{2}"' + .format(name, data_type._fullname, presentation._fullname), locator=presentation._get_child_locator('definitions'), level=Issue.BETWEEN_TYPES) value = temp elif value is not None: - context.validation.report('value of type "%s" is not a dict in "%s"' - % (data_type._fullname, presentation._fullname), + context.validation.report(u'value of type "{0}" is not a dict in "{1}"' + .format(data_type._fullname, presentation._fullname), locator=get_locator(value, presentation), level=Issue.BETWEEN_TYPES) value = None @@ -131,8 +131,8 @@ def validate_data_type_name(context, presentation): name = presentation._name if get_primitive_data_type(name) is not None: - context.validation.report('data type name is that of a built-in type: %s' - % safe_repr(name), + context.validation.report(u'data type name is that of a built-in type: {0}' + .format(safe_repr(name)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) @@ -201,7 +201,7 @@ def get_property_constraints(context, presentation): # ConstraintClause # -def apply_constraint_to_value(context, presentation, constraint_clause, value): # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches +def apply_constraint_to_value(context, presentation, constraint_clause, value): # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches """ Returns false if the value does not conform to the constraint. """ @@ -216,10 +216,10 @@ def apply_constraint_to_value(context, presentation, constraint_clause, value): constraint_key) def report(message, constraint): - context.validation.report('value %s %s per constraint in "%s": %s' - % (message, safe_repr(constraint), - presentation._name or presentation._container._name, - safe_repr(value)), + context.validation.report(u'value {0} {1} per constraint in "{2}": {3}' + .format(message, safe_repr(constraint), + presentation._name or presentation._container._name, + safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_FIELDS) if constraint_key == 'equal': @@ -318,15 +318,15 @@ def apply_constraint_to_value(context, presentation, constraint_clause, value): # def get_data_type_value(context, presentation, field_name, type_name): - the_type = get_type_by_name(context, type_name, 'data_types') - if the_type is not None: - value = getattr(presentation, field_name) - if value is not None: + value = getattr(presentation, field_name) + if value is not None: + the_type = get_type_by_name(context, type_name, 'data_types') + if the_type is not None: return coerce_data_type_value(context, presentation, the_type, None, None, value, None) - else: - context.validation.report('field "%s" in "%s" refers to unknown data type "%s"' - % (field_name, presentation._fullname, type_name), - locator=presentation._locator, level=Issue.BETWEEN_TYPES) + else: + context.validation.report(u'field "{0}" in "{1}" refers to unknown data type "{2}"' + .format(field_name, presentation._fullname, type_name), + locator=presentation._locator, level=Issue.BETWEEN_TYPES) return None @@ -372,7 +372,7 @@ def get_data_type_name(the_type): return the_type._name if hasattr(the_type, '_name') else full_type_name(the_type) -def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None): # pylint: disable=too-many-return-statements +def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None): # pylint: disable=too-many-return-statements """ Returns the value after it's coerced to its type, reporting validation errors if it cannot be coerced. @@ -394,8 +394,8 @@ def coerce_value(context, presentation, the_type, entry_schema, constraints, val if the_type == None.__class__: if value is not None: - context.validation.report('field "%s" is of type "null" but has a non-null value: %s' - % (presentation._name, safe_repr(value)), + context.validation.report(u'field "{0}" is of type "null" but has a non-null value: {1}' + .format(presentation._name, safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_FIELDS) return None @@ -498,17 +498,18 @@ def report_issue_for_bad_format(context, presentation, the_type, value, aspect, if aspect == 'default': aspect = '"default" value' elif aspect is not None: - aspect = '"%s" aspect' % aspect + aspect = u'"{0}" aspect'.format(aspect) if aspect is not None: - context.validation.report('%s for field "%s" is not a valid "%s": %s' - % (aspect, presentation._name or presentation._container._name, - get_data_type_name(the_type), safe_repr(value)), + context.validation.report(u'{0} for field "{1}" is not a valid "{2}": {3}' + .format(aspect, + presentation._name or presentation._container._name, + get_data_type_name(the_type), safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_FIELDS, exception=e) else: - context.validation.report('field "%s" is not a valid "%s": %s' - % (presentation._name or presentation._container._name, - get_data_type_name(the_type), safe_repr(value)), + context.validation.report(u'field "{0}" is not a valid "{1}": {2}' + .format(presentation._name or presentation._container._name, + get_data_type_name(the_type), safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_FIELDS, exception=e) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py index ecbfde9..16c49fa 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py @@ -24,6 +24,7 @@ from aria.parser.exceptions import InvalidValueError from aria.parser.validation import Issue from aria.modeling.exceptions import CannotEvaluateFunctionException from aria.modeling.models import (Node, NodeTemplate, Relationship, RelationshipTemplate) +from aria.modeling.mixins import ParameterMixin from aria.modeling.functions import (Function, Evaluation) @@ -43,7 +44,7 @@ class Concat(Function): if not isinstance(argument, list): raise InvalidValueError( - 'function "concat" argument must be a list of string expressions: {0}' + u'function "concat" argument must be a list of string expressions: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -84,7 +85,7 @@ class Token(Function): self.locator = presentation._locator if (not isinstance(argument, list)) or (len(argument) != 3): - raise InvalidValueError('function "token" argument must be a list of 3 parameters: {0}' + raise InvalidValueError(u'function "token" argument must be a list of 3 parameters: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -95,6 +96,10 @@ class Token(Function): argument[1]) self.substring_index = parse_int(context, presentation, 'token', 2, 'the 0-based index of the token to return', argument[2]) + if self.substring_index < 0: + raise invalid_value('token', 2, 'a non-negative integer', + 'the 0-based index of the token to return', self.substring_index, + presentation._locator) @property def as_raw(self): @@ -143,7 +148,7 @@ class GetInput(Function): 'inputs', self.input_property_name) if the_input is None: raise InvalidValueError( - 'function "get_input" argument is not a valid input name: {0}' + u'function "get_input" argument is not a valid input name: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -162,7 +167,7 @@ class GetInput(Function): return Evaluation(value, False) # We never return final evaluations! raise InvalidValueError( - 'function "get_input" argument is not a valid input name: {0}' + u'function "get_input" argument is not a valid input name: {0}' .format(safe_repr(self.input_property_name)), locator=self.locator) @@ -179,8 +184,8 @@ class GetProperty(Function): if (not isinstance(argument, list)) or (len(argument) < 2): raise InvalidValueError( - 'function "get_property" argument must be a list of at least 2 string expressions: ' - '{0}'.format(safe_repr(argument)), + u'function "get_property" argument must be a list of at least 2 string expressions:' + u' {0}'.format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_modelable_entity_name(context, presentation, @@ -224,14 +229,13 @@ class GetProperty(Function): properties = modelable_entity.properties nested_property_name_or_index = self.nested_property_name_or_index - evaluation = get_modelable_entity_parameter(modelable_entity, properties, - nested_property_name_or_index) + evaluation = get_modelable_entity_parameter(properties, nested_property_name_or_index) if evaluation is not None: return evaluation raise InvalidValueError( - 'function "get_property" could not find "{0}" in modelable entity "{1}"' - .format('.'.join(self.nested_property_name_or_index), self.modelable_entity_name), + u'function "get_property" could not find "{0}" in modelable entity "{1}"' + .format(u'.'.join(self.nested_property_name_or_index), self.modelable_entity_name), locator=self.locator) @@ -251,8 +255,8 @@ class GetAttribute(Function): if (not isinstance(argument, list)) or (len(argument) < 2): raise InvalidValueError( - 'function "get_attribute" argument must be a list of at least 2 string expressions:' - ' {0}'.format(safe_repr(argument)), + u'function "get_attribute" argument must be a list of at least 2 string ' + u'expressions: {0}'.format(safe_repr(argument)), locator=self.locator) self.modelable_entity_name = parse_modelable_entity_name(context, presentation, @@ -270,15 +274,14 @@ class GetAttribute(Function): for modelable_entity in modelable_entities: attributes = modelable_entity.attributes nested_attribute_name_or_index = self.nested_attribute_name_or_index - evaluation = get_modelable_entity_parameter(modelable_entity, attributes, - nested_attribute_name_or_index) + evaluation = get_modelable_entity_parameter(attributes, nested_attribute_name_or_index) if evaluation is not None: evaluation.final = False # We never return final evaluations! return evaluation raise InvalidValueError( - 'function "get_attribute" could not find "{0}" in modelable entity "{1}"' - .format('.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name), + u'function "get_attribute" could not find "{0}" in modelable entity "{1}"' + .format(u'.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name), locator=self.locator) @@ -286,7 +289,7 @@ class GetAttribute(Function): # Operation # -@implements_specification('4.6.1', 'tosca-simple-1.0') # pylint: disable=abstract-method +@implements_specification('4.6.1', 'tosca-simple-1.0') # pylint: disable=abstract-method class GetOperationOutput(Function): """ The ``get_operation_output`` function is used to retrieve the values of variables exposed / @@ -298,7 +301,7 @@ class GetOperationOutput(Function): if (not isinstance(argument, list)) or (len(argument) != 4): raise InvalidValueError( - 'function "get_operation_output" argument must be a list of 4 parameters: {0}' + u'function "get_operation_output" argument must be a list of 4 parameters: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -327,6 +330,9 @@ class GetOperationOutput(Function): return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name, output_variable_name]} + def __evaluate__(self, container): + return Evaluation(None) + # # Navigation @@ -349,7 +355,7 @@ class GetNodesOfType(Function): node_types = context.presentation.get('service_template', 'node_types') if (node_types is None) or (self.node_type_name not in node_types): raise InvalidValueError( - 'function "get_nodes_of_type" argument is not a valid node type name: {0}' + u'function "get_nodes_of_type" argument is not a valid node type name: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -361,14 +367,14 @@ class GetNodesOfType(Function): return {'get_nodes_of_type': node_type_name} def __evaluate__(self, container): - pass + return Evaluation(None) # # Artifact # -@implements_specification('4.8.1', 'tosca-simple-1.0') # pylint: disable=abstract-method +@implements_specification('4.8.1', 'tosca-simple-1.0') # pylint: disable=abstract-method class GetArtifact(Function): """ The ``get_artifact`` function is used to retrieve artifact location between modelable @@ -380,7 +386,7 @@ class GetArtifact(Function): if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4): raise InvalidValueError( - 'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}' + u'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}' .format(safe_repr(argument)), locator=self.locator) @@ -389,10 +395,16 @@ class GetArtifact(Function): argument[0]) self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1, 'the artifact name', argument[1]) - self.location = parse_string_expression(context, presentation, 'get_artifact', 2, - 'the location or "LOCAL_FILE"', argument[2]) - self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag', - argument[3]) + if len(argument) > 2: + self.location = parse_string_expression(context, presentation, 'get_artifact', 2, + 'the location or "LOCAL_FILE"', argument[2]) + else: + self.location = None + if len(argument) > 3: + self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag', + argument[3]) + else: + self.remove = None @property def as_raw(self): @@ -400,9 +412,22 @@ class GetArtifact(Function): if hasattr(artifact_name, 'as_raw'): artifact_name = as_raw(artifact_name) location = self.location - if hasattr(location, 'as_raw'): - location = as_raw(location) - return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]} + if location is not None: + if hasattr(location, 'as_raw'): + location = as_raw(location) + remove = self.remove + if hasattr(remove, 'as_raw'): + remove = as_raw(remove) + if remove is not None: + return {'get_artifact': [self.modelable_entity_name, artifact_name, location, + remove]} + else: + return {'get_artifact': [self.modelable_entity_name, artifact_name, location]} + else: + return {'get_artifact': [self.modelable_entity_name, artifact_name]} + + def __evaluate__(self, container): + return Evaluation(None) # @@ -422,16 +447,16 @@ def get_function(context, presentation, value): return False, None -def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument +def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument is_function, func = get_function(context, presentation, value) if is_function: return func else: - value = str(value) + value = unicode(value) return value -def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument +def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument if not isinstance(value, int): try: value = int(value) @@ -441,7 +466,7 @@ def parse_int(context, presentation, name, index, explanation, value): # pylint: return value -def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument +def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument if not isinstance(value, bool): raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator) return value @@ -475,7 +500,7 @@ def parse_modelable_entity_name(context, presentation, name, index, value): or {} if (value not in node_templates) and (value not in relationship_templates): raise InvalidValueError( - 'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}' + u'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}' .format(name, index + 1, safe_repr(value)), locator=presentation._locator, level=Issue.BETWEEN_TYPES) return value @@ -548,7 +573,7 @@ def get_modelable_entities(container_holder, name, locator, modelable_entity_nam return modelable_entities - raise InvalidValueError('function "{0}" could not find modelable entity "{1}"' + raise InvalidValueError(u'function "{0}" could not find modelable entity "{1}"' .format(name, modelable_entity_name), locator=locator) @@ -564,9 +589,9 @@ def get_self(container_holder, name, locator): (not isinstance(container, NodeTemplate)) and \ (not isinstance(container, Relationship)) and \ (not isinstance(container, RelationshipTemplate)): - raise InvalidValueError('function "{0}" refers to "SELF" but it is not contained in ' - 'a node or a relationship: {1}'.format(name, - full_type_name(container)), + raise InvalidValueError(u'function "{0}" refers to "SELF" but it is not contained in ' + u'a node or a relationship: {1}'.format(name, + full_type_name(container)), locator=locator) return [container] @@ -586,8 +611,8 @@ def get_hosts(container_holder, name, locator): container = container_holder.container if (not isinstance(container, Node)) and (not isinstance(container, NodeTemplate)): - raise InvalidValueError('function "{0}" refers to "HOST" but it is not contained in ' - 'a node: {1}'.format(name, full_type_name(container)), + raise InvalidValueError(u'function "{0}" refers to "HOST" but it is not contained in ' + u'a node: {1}'.format(name, full_type_name(container)), locator=locator) if not isinstance(container, Node): @@ -611,8 +636,8 @@ def get_source(container_holder, name, locator): container = container_holder.container if (not isinstance(container, Relationship)) and \ (not isinstance(container, RelationshipTemplate)): - raise InvalidValueError('function "{0}" refers to "SOURCE" but it is not contained in ' - 'a relationship: {1}'.format(name, full_type_name(container)), + raise InvalidValueError(u'function "{0}" refers to "SOURCE" but it is not contained in ' + u'a relationship: {1}'.format(name, full_type_name(container)), locator=locator) if not isinstance(container, RelationshipTemplate): @@ -631,8 +656,8 @@ def get_target(container_holder, name, locator): container = container_holder.container if (not isinstance(container, Relationship)) and \ (not isinstance(container, RelationshipTemplate)): - raise InvalidValueError('function "{0}" refers to "TARGET" but it is not contained in ' - 'a relationship: {1}'.format(name, full_type_name(container)), + raise InvalidValueError(u'function "{0}" refers to "TARGET" but it is not contained in ' + u'a relationship: {1}'.format(name, full_type_name(container)), locator=locator) if not isinstance(container, RelationshipTemplate): @@ -642,21 +667,25 @@ def get_target(container_holder, name, locator): return [container.target_node] -def get_modelable_entity_parameter(modelable_entity, parameters, nested_parameter_name_or_index): +def get_modelable_entity_parameter(parameters, nested_parameter_name_or_index): if not parameters: return Evaluation(None, True) found = True final = True value = parameters + last_container = parameters for name_or_index in nested_parameter_name_or_index: if (isinstance(value, dict) and (name_or_index in value)) \ or ((isinstance(value, list) and (name_or_index < len(value)))): - value = value[name_or_index] # Parameter - # We are not using Parameter.value, but rather Parameter._value, because we want to make - # sure to get "final" (it is swallowed by Parameter.value) - value, final = evaluate(value._value, final, value) + value = value[name_or_index] + if isinstance(value, ParameterMixin): + last_container = value + # We are not using Parameter.value, but rather Parameter._value, because we want to + # make sure to get "final" (it is swallowed by Parameter.value) + value = value._value + value, final = evaluate(value, final, last_container) else: found = False break @@ -665,17 +694,17 @@ def get_modelable_entity_parameter(modelable_entity, parameters, nested_paramete def invalid_modelable_entity_name(name, index, value, locator, contexts): - return InvalidValueError('function "{0}" parameter {1:d} can be "{2}" only in {3}' + return InvalidValueError(u'function "{0}" parameter {1:d} can be "{2}" only in {3}' .format(name, index + 1, value, contexts), locator=locator, level=Issue.FIELD) def invalid_value(name, index, the_type, explanation, value, locator): return InvalidValueError( - 'function "{0}" {1} is not {2}{3}: {4}' + u'function "{0}" {1} is not {2}{3}: {4}' .format(name, - 'parameter {0:d}'.format(index + 1) if index is not None else 'argument', + u'parameter {0:d}'.format(index + 1) if index is not None else 'argument', the_type, - ', {0}'.format(explanation) if explanation is not None else '', + u', {0}'.format(explanation) if explanation is not None else '', safe_repr(value)), locator=locator, level=Issue.FIELD) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py new file mode 100644 index 0000000..1d8013b --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py @@ -0,0 +1,43 @@ +# 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. + +from ..presentation.types import convert_name_to_full_type_name + + +# +# GroupType +# + +def get_inherited_members(context, presentation): + """ + Returns our target node types if we have them or those of our parent, if we have one + (recursively). + """ + + parent = presentation._get_parent(context) + + node_types = get_inherited_members(context, parent) if parent is not None else [] + + our_members = presentation.members + if our_members: + all_node_types = context.presentation.get('service_template', 'node_types') or {} + node_types = [] + + for our_member in our_members: + if our_member in all_node_types: + our_member = convert_name_to_full_type_name(context, our_member, all_node_types) + node_types.append(all_node_types[our_member]) + + return node_types http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/1efb1e5e/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py index 23a03b7..4450fc2 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py @@ -18,6 +18,7 @@ from aria.parser.presentation import get_locator from aria.parser.validation import Issue from .parameters import (coerce_parameter_value, convert_parameter_definitions_to_values) +from .data_types import (get_type_by_name, get_primitive_data_type) # @@ -156,7 +157,7 @@ def get_template_interfaces(context, presentation, type_name): interface_definitions = the_type._get_interfaces(context) if the_type is not None else None # Copy over interfaces from the type (will initialize inputs with default values) - if interface_definitions is not None: + if interface_definitions: for interface_name, interface_definition in interface_definitions.iteritems(): # Note that in the case of a RelationshipTemplate, we will already have the values as # InterfaceAssignment. It will not be converted, just cloned. @@ -177,8 +178,8 @@ def get_template_interfaces(context, presentation, type_name): our_interface_assignment, interface_definition, interface_name) else: context.validation.report( - 'interface definition "%s" not declared at %s "%s" in "%s"' - % (interface_name, type_name, presentation.type, presentation._fullname), + u'interface definition "{0}" not declared at {1} "{2}" in "{3}"' + .format(interface_name, type_name, presentation.type, presentation._fullname), locator=our_interface_assignment._locator, level=Issue.BETWEEN_TYPES) # Check that there are no required inputs that we haven't assigned @@ -209,7 +210,7 @@ def convert_interface_definition_from_type_to_template(context, presentation, co return InterfaceAssignment(name=presentation._name, raw=raw, container=container) -def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name +def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name raw = OrderedDict() # Copy default values for inputs @@ -236,7 +237,7 @@ def convert_interface_definition_from_type_to_raw_template(context, presentation return raw -def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name +def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name interface_definitions): if not interface_definitions: return @@ -257,62 +258,71 @@ def merge_interface(context, presentation, interface_assignment, our_interface_a assign_raw_inputs(context, interface_assignment._raw, our_interface_assignment.inputs, interface_definition._get_inputs(context), interface_name, None, presentation) - # Assign operation implementations and inputs our_operation_templates = our_interface_assignment.operations # OperationAssignment + if our_operation_templates is None: + our_operation_templates = {} + # OperationDefinition or OperationAssignment: operation_definitions = interface_definition._get_operations(context) \ if hasattr(interface_definition, '_get_operations') else interface_definition.operations - if our_operation_templates: - # OperationAssignment: - for operation_name, our_operation_template in our_operation_templates.iteritems(): - operation_definition = operation_definitions.get(operation_name) # OperationDefinition + if operation_definitions is None: + operation_definitions = {} - our_input_assignments = our_operation_template.inputs - our_implementation = our_operation_template.implementation + # OperationAssignment: + for operation_name, our_operation_template in our_operation_templates.iteritems(): + operation_definition = operation_definitions.get(operation_name) # OperationDefinition - if operation_definition is None: - context.validation.report( - 'interface definition "%s" refers to an unknown operation "%s" in "%s"' - % (interface_name, operation_name, presentation._fullname), - locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES) + our_input_assignments = our_operation_template.inputs + our_implementation = our_operation_template.implementation - if (our_input_assignments is not None) or (our_implementation is not None): - # Make sure we have the dict - if (operation_name not in interface_assignment._raw) \ - or (interface_assignment._raw[operation_name] is None): - interface_assignment._raw[operation_name] = OrderedDict() + if operation_definition is None: + context.validation.report( + u'interface definition "{0}" refers to an unknown operation "{1}" in "{2}"' + .format(interface_name, operation_name, presentation._fullname), + locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES) + + # Make sure we have the dict + if (operation_name not in interface_assignment._raw) \ + or (interface_assignment._raw[operation_name] is None): + interface_assignment._raw[operation_name] = OrderedDict() - if our_implementation is not None: - interface_assignment._raw[operation_name]['implementation'] = \ - deepcopy_with_locators(our_implementation._raw) + if our_implementation is not None: + interface_assignment._raw[operation_name]['implementation'] = \ + deepcopy_with_locators(our_implementation._raw) - # Assign/merge operation inputs - input_definitions = operation_definition.inputs \ - if operation_definition is not None else None - assign_raw_inputs(context, interface_assignment._raw[operation_name], - our_input_assignments, input_definitions, interface_name, - operation_name, presentation) + # Assign/merge operation inputs + input_definitions = operation_definition.inputs \ + if operation_definition is not None else None + assign_raw_inputs(context, interface_assignment._raw[operation_name], + our_input_assignments, input_definitions, interface_name, + operation_name, presentation) def merge_raw_input_definition(context, the_raw_input, our_input, interface_name, operation_name, presentation, type_name): # Check if we changed the type - # TODO: allow a sub-type? - input_type1 = the_raw_input.get('type') - input_type2 = our_input.type - if input_type1 != input_type2: + input_type1_name = the_raw_input.get('type') + input_type1 = get_type_by_name(context, input_type1_name, 'data_types') + if input_type1 is None: + input_type1 = get_primitive_data_type(input_type1_name) + input_type2 = our_input._get_type(context) + if input_type1 is not input_type2 and \ + (not hasattr(input_type1, '_is_descendant') or \ + not input_type1._is_descendant(context, input_type2)): if operation_name is not None: context.validation.report( - 'interface %s "%s" changes operation input "%s.%s" type from "%s" to "%s" in "%s"' - % (type_name, interface_name, operation_name, our_input._name, input_type1, - input_type2, presentation._fullname), - locator=input_type2._locator, level=Issue.BETWEEN_TYPES) + u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of ' + u'operation {3} "{4}.{5}" in {6}' + .format(our_input.type, input_type1_name, our_input._name, type_name, + interface_name, operation_name, presentation._fullname), + locator=our_input._locator, level=Issue.BETWEEN_TYPES) else: context.validation.report( - 'interface %s "%s" changes input "%s" type from "%s" to "%s" in "%s"' - % (type_name, interface_name, our_input._name, input_type1, input_type2, - presentation._fullname), - locator=input_type2._locator, level=Issue.BETWEEN_TYPES) + u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of ' + u'interface {3} "{4}" in {5}' + .format(our_input.type, input_type1_name, our_input._name, type_name, + interface_name, presentation._fullname), + locator=our_input._locator, level=Issue.BETWEEN_TYPES) # Merge merge(the_raw_input, our_input._raw) @@ -405,8 +415,8 @@ def merge_interface_definition(context, interface, our_source, presentation, typ if (type2 is not None) and not type1._is_descendant(context, type2): context.validation.report( - 'interface definition type "{0}" is not a descendant of overridden ' - 'interface definition type "{1}"' \ + u'interface definition type "{0}" is not a descendant of overridden ' + u'interface definition type "{1}"' \ .format(type1._name, type2._name), locator=our_source._locator, level=Issue.BETWEEN_TYPES) @@ -448,37 +458,42 @@ def merge_interface_definitions_from_their_types(context, interfaces, presentati merge_interface_definition(context, interface, the_type, presentation, 'type') -def assign_raw_inputs(context, values, assignments, definitions, interface_name, operation_name, +def assign_raw_inputs(context, raw, assignments, definitions, interface_name, operation_name, presentation): - if not assignments: - return + if assignments is None: + assignments = {} + if definitions is None: + definitions = {} # Make sure we have the dict - if ('inputs' not in values) or (values['inputs'] is None): - values['inputs'] = OrderedDict() + if ('inputs' not in raw) or (raw['inputs'] is None): + raw['inputs'] = OrderedDict() + + # Defaults + for input_name, definition in definitions.iteritems(): + if ('default' in definition._raw) and (input_name not in raw['inputs']): + raw['inputs'][input_name] = coerce_parameter_value(context, definition, definition, + definition.default, 'default') # Assign inputs for input_name, assignment in assignments.iteritems(): - if (definitions is not None) and (input_name not in definitions): + if (not context.presentation.configuration.get('tosca.adhoc_inputs', True)) and \ + (input_name not in definitions): if operation_name is not None: context.validation.report( - 'interface definition "%s" assigns a value to an unknown operation input' - ' "%s.%s" in "%s"' - % (interface_name, operation_name, input_name, presentation._fullname), + u'interface definition "{0}" assigns a value to an unknown operation input' + u' "{1}.{2}" in "{3}"' + .format(interface_name, operation_name, input_name, presentation._fullname), locator=assignment._locator, level=Issue.BETWEEN_TYPES) else: context.validation.report( - 'interface definition "%s" assigns a value to an unknown input "%s" in "%s"' - % (interface_name, input_name, presentation._fullname), + u'interface definition "{0}" assigns a value to an unknown input "{1}" in "{2}"' + .format(interface_name, input_name, presentation._fullname), locator=assignment._locator, level=Issue.BETWEEN_TYPES) - definition = definitions.get(input_name) if definitions is not None else None - - # Note: default value has already been assigned - - # Coerce value - values['inputs'][input_name] = coerce_parameter_value(context, assignment, definition, - assignment.value) + definition = definitions.get(input_name) # Could be None! + raw['inputs'][input_name] = coerce_parameter_value(context, assignment, definition, + assignment.value) def validate_required_inputs(context, presentation, assignment, definition, original_assignment, @@ -487,7 +502,11 @@ def validate_required_inputs(context, presentation, assignment, definition, orig # (as opposed to topology template and workflow inputs) is done only in the parsing stage. # This reasoning follows the TOSCA spirit, where anything that is declared as required in the # type, must be assigned in the corresponding template. - input_definitions = definition.inputs + + # Note: InterfaceDefinition need _get_inputs, but OperationDefinition doesn't + input_definitions = definition._get_inputs(context) \ + if hasattr(definition, '_get_inputs') \ + else definition.inputs if input_definitions: for input_name, input_definition in input_definitions.iteritems(): if input_definition.required: @@ -498,16 +517,17 @@ def validate_required_inputs(context, presentation, assignment, definition, orig if value is None: if operation_name is not None: context.validation.report( - 'interface definition "%s" does not assign a value to a required' - ' operation input "%s.%s" in "%s"' - % (interface_name, operation_name, input_name, presentation._fullname), + u'interface definition "{0}" does not assign a value to a required' + u' operation input "{1}.{2}" in "{3}"' + .format(interface_name, operation_name, input_name, + presentation._fullname), locator=get_locator(original_assignment, presentation._locator), level=Issue.BETWEEN_TYPES) else: context.validation.report( - 'interface definition "%s" does not assign a value to a required input' - ' "%s" in "%s"' - % (interface_name, input_name, presentation._fullname), + u'interface definition "{0}" does not assign a value to a required' + u' input "{1}" in "{2}"' + .format(interface_name, input_name, presentation._fullname), locator=get_locator(original_assignment, presentation._locator), level=Issue.BETWEEN_TYPES)