You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by ra...@apache.org on 2016/11/15 02:24:44 UTC
[03/11] incubator-ariatosca git commit: ARIA-18 Migrate DSL parser
and TOSCA extension code
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/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
new file mode 100644
index 0000000..9b0edb8
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
@@ -0,0 +1,497 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+from collections import OrderedDict
+
+from aria.parser import dsl_specification
+from aria.parser.presentation import (get_locator, validate_primitive)
+from aria.parser.utils import (import_fullname, full_type_name, safe_repr)
+from aria.parser.validation import Issue
+
+from ..functions import get_function
+from ..presentation.types import get_type_by_full_or_shorthand_name
+
+#
+# DataType
+#
+
+def get_inherited_constraints(context, presentation):
+ """
+ If we don't have constraints, will return our parent's constraints (if we have one),
+ recursively.
+
+ Implication: if we define even one constraint, the parent's constraints will not be inherited.
+ """
+
+ constraints = presentation.constraints
+
+ if constraints is None:
+ # If we don't have any, use our parent's
+ parent = presentation._get_parent(context)
+ parent_constraints = get_inherited_constraints(context, parent) \
+ if parent is not None else None
+ if parent_constraints is not None:
+ constraints = parent_constraints
+
+ return constraints
+
+def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+ aspect):
+ """
+ Handles the :code:`_coerce_data()` hook for complex data types.
+
+ There are two kinds of handling:
+
+ 1. If we have a primitive type as our great ancestor, then we do primitive type coersion, and
+ just check for constraints.
+
+ 2. Otherwise, for normal complex data types we return the assigned property values while making
+ sure they are defined in our type. The property definition's default value, if available,
+ will be used if we did not assign it. We also make sure that required definitions indeed end
+ up with a value.
+ """
+
+ primitive_type = data_type._get_primitive_ancestor(context)
+ if primitive_type is not None:
+ # Must be coercible to primitive ancestor
+ value = coerce_to_primitive(context, presentation, primitive_type, constraints, value,
+ aspect)
+ else:
+ definitions = data_type._get_properties(context)
+ if isinstance(value, dict):
+ temp = OrderedDict()
+
+ # Fill in our values, but make sure they are defined
+ for name, v in value.iteritems():
+ if name in definitions:
+ definition = definitions[name]
+ definition_type = definition._get_type(context)
+ definition_entry_schema = definition.entry_schema
+ definition_constraints = definition._get_constraints(context)
+ temp[name] = coerce_value(context, presentation, definition_type,
+ definition_entry_schema, definition_constraints, v,
+ aspect)
+ else:
+ context.validation.report(
+ 'assignment to undefined property "%s" in type "%s" in "%s"'
+ % (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
+ # assigned
+ for name, definition in definitions.iteritems():
+ if (temp.get(name) is None) and hasattr(definition, 'default') \
+ and (definition.default is not None):
+ definition_type = definition._get_type(context)
+ definition_entry_schema = definition.entry_schema
+ definition_constraints = definition._get_constraints(context)
+ temp[name] = coerce_value(context, presentation, definition_type,
+ definition_entry_schema, definition_constraints,
+ definition.default, 'default')
+
+ 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),
+ 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),
+ locator=get_locator(value, presentation),
+ level=Issue.BETWEEN_TYPES)
+ value = None
+
+ return value
+
+def validate_data_type_name(context, presentation):
+ """
+ Makes sure the complex data type's name is not that of a built-in type.
+ """
+
+ 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),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+#
+# PropertyDefinition, AttributeDefinition, EntrySchema, DataType
+#
+
+def get_data_type(context, presentation, field_name, allow_none=False):
+ """
+ Returns the type, whether it's a complex data type (a DataType instance) or a primitive (a
+ Python primitive type class).
+
+ If the type is not specified, defaults to :class:`str`, per note in section 3.2.1.1 of the
+ `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #_Toc379455072>`__
+ """
+
+ type_name = getattr(presentation, field_name)
+
+ if type_name is None:
+ if allow_none:
+ return None
+ else:
+ return str
+
+ # Make sure not derived from self
+ if type_name == presentation._name:
+ return None
+
+ # Avoid circular definitions
+ container_data_type = get_container_data_type(presentation)
+ if (container_data_type is not None) and (container_data_type._name == type_name):
+ return None
+
+ # Try complex data type
+ data_type = get_type_by_full_or_shorthand_name(context, type_name, 'data_types')
+ if data_type is not None:
+ return data_type
+
+ # Try primitive data type
+ return get_primitive_data_type(type_name)
+
+#
+# PropertyDefinition, EntrySchema
+#
+
+def get_property_constraints(context, presentation):
+ """
+ If we don't have constraints, will return our type's constraints (if we have one), recursively.
+
+ Implication: if we define even one constraint, the type's constraints will not be inherited.
+ """
+
+ constraints = presentation.constraints
+
+ if constraints is None:
+ # If we don't have any, use our type's
+ the_type = presentation._get_type(context)
+ type_constraints = the_type._get_constraints(context) \
+ if hasattr(the_type, '_get_constraints') else None
+ if type_constraints is not None:
+ constraints = type_constraints
+
+ return constraints
+
+#
+# ConstraintClause
+#
+
+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.
+ """
+
+ constraint_key = constraint_clause._raw.keys()[0]
+ the_type = constraint_clause._get_type(context)
+ # PropertyAssignment does not have this:
+ entry_schema = getattr(presentation, 'entry_schema', None)
+
+ def coerce_constraint(constraint):
+ return coerce_value(context, presentation, the_type, entry_schema, None, constraint,
+ 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)),
+ locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
+
+ if constraint_key == 'equal':
+ constraint = coerce_constraint(constraint_clause.equal)
+ if value != constraint:
+ report('is not equal to', constraint)
+ return False
+
+ elif constraint_key == 'greater_than':
+ constraint = coerce_constraint(constraint_clause.greater_than)
+ if value <= constraint:
+ report('is not greater than', constraint)
+ return False
+
+ elif constraint_key == 'greater_or_equal':
+ constraint = coerce_constraint(constraint_clause.greater_or_equal)
+ if value < constraint:
+ report('is not greater than or equal to', constraint)
+ return False
+
+ elif constraint_key == 'less_than':
+ constraint = coerce_constraint(constraint_clause.less_than)
+ if value >= constraint:
+ report('is not less than', constraint)
+ return False
+
+ elif constraint_key == 'less_or_equal':
+ constraint = coerce_constraint(constraint_clause.less_or_equal)
+ if value > constraint:
+ report('is not less than or equal to', constraint)
+ return False
+
+ elif constraint_key == 'in_range':
+ lower, upper = constraint_clause.in_range
+ lower, upper = coerce_constraint(lower), coerce_constraint(upper)
+ if value < lower:
+ report('is not greater than or equal to lower bound', lower)
+ return False
+ if (upper != 'UNBOUNDED') and (value > upper):
+ report('is not lesser than or equal to upper bound', upper)
+ return False
+
+ elif constraint_key == 'valid_values':
+ constraint = tuple(coerce_constraint(v) for v in constraint_clause.valid_values)
+ if value not in constraint:
+ report('is not one of', constraint)
+ return False
+
+ elif constraint_key == 'length':
+ constraint = constraint_clause.length
+ try:
+ if len(value) != constraint:
+ report('is not of length', constraint)
+ return False
+ except TypeError:
+ pass # should be validated elsewhere
+
+ elif constraint_key == 'min_length':
+ constraint = constraint_clause.min_length
+ try:
+ if len(value) < constraint:
+ report('has a length lesser than', constraint)
+ return False
+ except TypeError:
+ pass # should be validated elsewhere
+
+ elif constraint_key == 'max_length':
+ constraint = constraint_clause.max_length
+ try:
+ if len(value) > constraint:
+ report('has a length greater than', constraint)
+ return False
+ except TypeError:
+ pass # should be validated elsewhere
+
+ elif constraint_key == 'pattern':
+ constraint = constraint_clause.pattern
+ try:
+ # Note: the TOSCA 1.0 spec does not specify the regular expression grammar, so we will
+ # just use Python's
+ if re.match(constraint, str(value)) is None:
+ report('does not match regular expression', constraint)
+ return False
+ except re.error:
+ pass # should be validated elsewhere
+
+ return True
+
+#
+# Repository
+#
+
+def get_data_type_value(context, presentation, field_name, type_name):
+ the_type = get_type_by_full_or_shorthand_name(context, type_name, 'data_types')
+ if the_type is not None:
+ value = getattr(presentation, field_name)
+ if value 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)
+ return None
+
+#
+# Utils
+#
+
+PRIMITIVE_DATA_TYPES = {
+ # YAML 1.2:
+ 'tag:yaml.org,2002:str': str,
+ 'tag:yaml.org,2002:integer': int,
+ 'tag:yaml.org,2002:float': float,
+ 'tag:yaml.org,2002:bool': bool,
+ 'tag:yaml.org,2002:null': None.__class__,
+
+ # TOSCA aliases:
+ 'string': str,
+ 'integer': int,
+ 'float': float,
+ 'boolean': bool,
+ 'null': None.__class__}
+
+@dsl_specification('3.2.1', 'tosca-simple-1.0')
+def get_primitive_data_type(type_name):
+ """
+ Many of the types we use in this profile are built-in types from the YAML 1.2 specification
+ (i.e., those identified by the "tag:yaml.org,2002" version tag) [YAML-1.2].
+
+ See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
+ /TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
+ #_Toc373867862>`__
+ """
+
+ return PRIMITIVE_DATA_TYPES.get(type_name)
+
+def get_data_type_name(the_type):
+ """
+ Returns the name of the type, whether it's a DataType, a primitive type, or another class.
+ """
+
+ 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
+ """
+ Returns the value after it's coerced to its type, reporting validation errors if it cannot be
+ coerced.
+
+ Supports both complex data types and primitives.
+
+ Data types can use the :code:`coerce_value` extension to hook their own specialized function.
+ If the extension is present, we will delegate to that hook.
+ """
+
+ is_function, func = get_function(context, presentation, value)
+ if is_function:
+ return func
+
+ if the_type is None:
+ return value
+
+ 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)),
+ locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
+ return None
+
+ # Delegate to 'coerce_value' extension
+ if hasattr(the_type, '_get_extension'):
+ coerce_value_fn_name = the_type._get_extension('coerce_value')
+ if coerce_value_fn_name is not None:
+ if value is None:
+ return None
+ coerce_value_fn = import_fullname(coerce_value_fn_name)
+ return coerce_value_fn(context, presentation, the_type, entry_schema, constraints,
+ value, aspect)
+
+ if hasattr(the_type, '_coerce_value'):
+ # Delegate to '_coerce_value' (likely a DataType instance)
+ return the_type._coerce_value(context, presentation, entry_schema, constraints, value,
+ aspect)
+
+ # Coerce to primitive type
+ return coerce_to_primitive(context, presentation, the_type, constraints, value, aspect)
+
+def coerce_to_primitive(context, presentation, primitive_type, constraints, value, aspect=None):
+ """
+ Returns the value after it's coerced to a primitive type, translating exceptions to validation
+ errors if it cannot be coerced.
+ """
+
+ if value is None:
+ return None
+
+ try:
+ # Coerce
+ value = validate_primitive(value, primitive_type,
+ context.validation.allow_primitive_coersion)
+
+ # Check constraints
+ apply_constraints_to_value(context, presentation, constraints, value)
+ except ValueError as e:
+ report_issue_for_bad_format(context, presentation, primitive_type, value, aspect, e)
+ value = None
+ except TypeError as e:
+ report_issue_for_bad_format(context, presentation, primitive_type, value, aspect, e)
+ value = None
+
+ return value
+
+def coerce_to_data_type_class(context, presentation, cls, entry_schema, constraints, value,
+ aspect=None):
+ """
+ Returns the value after it's coerced to a data type class, reporting validation errors if it
+ cannot be coerced. Constraints will be applied after coersion.
+
+ Will either call a :code:`_create` static function in the class, or instantiate it using a
+ constructor if :code:`_create` is not available.
+
+ This will usually be called by a :code:`coerce_value` extension hook in a :class:`DataType`.
+ """
+
+ try:
+ if hasattr(cls, '_create'):
+ # Instantiate using creator function
+ value = cls._create(context, presentation, entry_schema, constraints, value, aspect)
+ else:
+ # Normal instantiation
+ value = cls(entry_schema, constraints, value, aspect)
+ except ValueError as e:
+ report_issue_for_bad_format(context, presentation, cls, value, aspect, e)
+ value = None
+
+ # Check constraints
+ value = apply_constraints_to_value(context, presentation, constraints, value)
+
+ return value
+
+def apply_constraints_to_value(context, presentation, constraints, value):
+ """
+ Applies all constraints to the value. If the value conforms, returns the value. If it does not
+ conform, returns None.
+ """
+
+ if (value is not None) and (constraints is not None):
+ valid = True
+ for constraint in constraints:
+ if not constraint._apply_to_value(context, presentation, value):
+ valid = False
+ if not valid:
+ value = None
+ return value
+
+def get_container_data_type(presentation):
+ if presentation is None:
+ return None
+ if type(presentation).__name__ == 'DataType':
+ return presentation
+ return get_container_data_type(presentation._container)
+
+def report_issue_for_bad_format(context, presentation, the_type, value, aspect, e):
+ if aspect == 'default':
+ aspect = '"default" value'
+ elif aspect is not None:
+ aspect = '"%s" aspect' % 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)),
+ 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)),
+ locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
+ exception=e)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/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
new file mode 100644
index 0000000..1d734cf
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
@@ -0,0 +1,504 @@
+# 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 collections import OrderedDict
+
+from aria.parser.presentation import get_locator
+from aria.parser.utils import (merge, deepcopy_with_locators)
+from aria.parser.validation import Issue
+
+from .properties import (coerce_property_value, convert_property_definitions_to_values)
+
+#
+# InterfaceType
+#
+
+def get_inherited_operations(context, presentation):
+ """
+ Returns our operation definitions added on top of those of our parent, if we have one
+ (recursively).
+
+ Allows overriding all aspects of parent operations except input data types.
+ """
+
+ # Get operations from parent
+ parent = presentation._get_parent(context)
+ operations = get_inherited_operations(context, parent) if parent is not None else OrderedDict()
+
+ # Add/merge our operations
+ our_operations = presentation.operations # OperationDefinition
+ merge_operation_definitions(context, operations, our_operations, presentation._name,
+ presentation, 'type')
+
+ for operation in operations.itervalues():
+ operation._reset_method_cache()
+
+ return operations
+
+#
+# InterfaceDefinition
+#
+
+def get_and_override_input_definitions_from_type(context, presentation):
+ """
+ Returns our input definitions added on top of those of the interface type, if specified.
+
+ Allows overriding all aspects of parent interface type inputs except data types.
+ """
+
+ inputs = OrderedDict()
+
+ # Get inputs from type
+ the_type = presentation._get_type(context) # IntefaceType
+ type_inputs = the_type._get_inputs(context) if the_type is not None else None
+ if type_inputs:
+ for input_name, type_input in type_inputs.iteritems():
+ inputs[input_name] = type_input._clone(presentation)
+
+ # Add/merge our inputs
+ our_inputs = presentation.inputs # PropertyDefinition
+ if our_inputs:
+ merge_input_definitions(context, inputs, our_inputs, presentation._name, None, presentation,
+ 'definition')
+
+ return inputs
+
+def get_and_override_operation_definitions_from_type(context, presentation):
+ """
+ Returns our operation definitions added on top of those of the interface type, if specified.
+
+ Allows overriding all aspects of parent interface type inputs except data types.
+ """
+
+ operations = OrderedDict()
+
+ # Get operations from type
+ the_type = presentation._get_type(context) # InterfaceType
+ type_operations = the_type._get_operations(context) if the_type is not None else None
+ if type_operations:
+ for operations_name, type_operation in type_operations.iteritems():
+ operations[operations_name] = type_operation._clone(presentation)
+
+ # Add/merge our operations
+ our_operations = presentation.operations # OperationDefinition
+ merge_operation_definitions(context, operations, our_operations, presentation._name,
+ presentation, 'definition')
+
+ return operations
+
+#
+# NodeType, RelationshipType, GroupType
+#
+
+def get_inherited_interface_definitions(context, presentation, type_name, for_presentation=None):
+ """
+ Returns our interface definitions added on top of those of our parent, if we have one
+ (recursively).
+
+ Allows overriding all aspects of parent interfaces except interface and operation input data
+ types.
+ """
+
+ # Get interfaces from parent
+ parent = presentation._get_parent(context)
+ interfaces = get_inherited_interface_definitions(context, parent, type_name, presentation) \
+ if parent is not None else OrderedDict()
+
+ # Add/merge interfaces from their types
+ merge_interface_definitions_from_their_types(context, interfaces, presentation)
+
+ # Add/merge our interfaces
+ our_interfaces = presentation.interfaces
+ merge_interface_definitions(context, interfaces, our_interfaces, presentation,
+ for_presentation=for_presentation)
+
+ return interfaces
+
+#
+# NodeTemplate, RelationshipTemplate, GroupTemplate
+#
+
+def get_template_interfaces(context, presentation, type_name):
+ """
+ Returns the assigned interface_template values while making sure they are defined in the type.
+ This includes the interfaces themselves, their operations, and inputs for interfaces and
+ operations.
+
+ Interface and operation inputs' default values, if available, will be used if we did not assign
+ them.
+
+ Makes sure that required inputs indeed end up with a value.
+
+ This code is especially complex due to the many levels of nesting involved.
+ """
+
+ template_interfaces = OrderedDict()
+
+ the_type = presentation._get_type(context) # NodeType, RelationshipType, GroupType
+ # InterfaceDefinition (or InterfaceAssignment in the case of RelationshipTemplate):
+ 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:
+ 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.
+ template_interfaces[interface_name] = \
+ convert_interface_definition_from_type_to_template(context, interface_definition,
+ presentation)
+
+ # Fill in our interfaces
+ our_interface_assignments = presentation.interfaces
+ if our_interface_assignments:
+ # InterfaceAssignment:
+ for interface_name, our_interface_assignment in our_interface_assignments.iteritems():
+ if interface_name in template_interfaces:
+ interface_assignment = template_interfaces[interface_name] # InterfaceAssignment
+ # InterfaceDefinition (or InterfaceAssignment in the case of RelationshipTemplate):
+ interface_definition = interface_definitions[interface_name]
+ merge_interface(context, presentation, interface_assignment,
+ 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),
+ locator=our_interface_assignment._locator, level=Issue.BETWEEN_TYPES)
+
+ # Check that there are no required inputs that we haven't assigned
+ for interface_name, interface_template in template_interfaces.iteritems():
+ if interface_name in interface_definitions:
+ # InterfaceDefinition (or InterfaceAssignment in the case of RelationshipTemplate):
+ interface_definition = interface_definitions[interface_name]
+ our_interface_assignment = our_interface_assignments.get(interface_name) \
+ if our_interface_assignments is not None else None
+ validate_required_inputs(context, presentation, interface_template,
+ interface_definition, our_interface_assignment, interface_name)
+
+ return template_interfaces
+
+#
+# Utils
+#
+
+def convert_interface_definition_from_type_to_template(context, presentation, container):
+ from ..assignments import InterfaceAssignment
+
+ if isinstance(presentation, InterfaceAssignment):
+ # Nothing to convert, so just clone
+ return presentation._clone(container)
+
+ raw = convert_interface_definition_from_type_to_raw_template(context, presentation)
+ return InterfaceAssignment(name=presentation._name, raw=raw, container=container)
+
+def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name
+ raw = OrderedDict()
+
+ # Copy default values for inputs
+ inputs = presentation._get_inputs(context)
+ if inputs is not None:
+ raw['inputs'] = convert_property_definitions_to_values(context, inputs)
+
+ # Copy operations
+ operations = presentation._get_operations(context)
+ if operations:
+ for operation_name, operation in operations.iteritems():
+ raw[operation_name] = OrderedDict()
+ description = operation.description
+ if description is not None:
+ raw[operation_name]['description'] = deepcopy_with_locators(description._raw)
+ implementation = operation.implementation
+ if implementation is not None:
+ raw[operation_name]['implementation'] = deepcopy_with_locators(implementation._raw)
+ inputs = operation.inputs
+ if inputs is not None:
+ raw[operation_name]['inputs'] = convert_property_definitions_to_values(context,
+ inputs)
+
+ return raw
+
+def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name
+ interface_definitions):
+ if not interface_definitions:
+ return
+ if 'interfaces' not in raw_requirement:
+ raw_requirement['interfaces'] = OrderedDict()
+ for interface_name, interface_definition in interface_definitions.iteritems():
+ raw_interface = convert_interface_definition_from_type_to_raw_template(context,
+ interface_definition)
+ if interface_name in raw_requirement['interfaces']:
+ merge(raw_requirement['interfaces'][interface_name], raw_interface)
+ else:
+ raw_requirement['interfaces'][interface_name] = raw_interface
+
+def merge_interface(context, presentation, interface_assignment, our_interface_assignment,
+ interface_definition, interface_name):
+ # Assign/merge interface inputs
+ 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
+ # 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
+
+ our_input_assignments = our_operation_template.inputs
+ our_implementation = our_operation_template.implementation
+
+ 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)
+
+ 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 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)
+
+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:
+ 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)
+ 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)
+
+ # Merge
+ merge(the_raw_input, our_input._raw)
+
+def merge_input_definitions(context, inputs, our_inputs, interface_name, operation_name,
+ presentation, type_name):
+ for input_name, our_input in our_inputs.iteritems():
+ if input_name in inputs:
+ merge_raw_input_definition(context, inputs[input_name]._raw, our_input, interface_name,
+ operation_name, presentation, type_name)
+ else:
+ inputs[input_name] = our_input._clone(presentation)
+
+def merge_raw_input_definitions(context, raw_inputs, our_inputs, interface_name, operation_name,
+ presentation, type_name):
+ for input_name, our_input in our_inputs.iteritems():
+ if input_name in raw_inputs:
+ merge_raw_input_definition(context, raw_inputs[input_name], our_input, interface_name,
+ operation_name, presentation, type_name)
+ else:
+ raw_inputs[input_name] = deepcopy_with_locators(our_input._raw)
+
+def merge_raw_operation_definition(context, raw_operation, our_operation, interface_name,
+ presentation, type_name):
+ if not isinstance(our_operation._raw, dict):
+ # Convert short form to long form
+ raw_operation['implementation'] = deepcopy_with_locators(our_operation._raw)
+ return
+
+ # Add/merge inputs
+ our_operation_inputs = our_operation.inputs
+ if our_operation_inputs:
+ # Make sure we have the dict
+ if ('inputs' not in raw_operation) or (raw_operation.get('inputs') is None):
+ raw_operation['inputs'] = OrderedDict()
+
+ merge_raw_input_definitions(context, raw_operation['inputs'], our_operation_inputs,
+ interface_name, our_operation._name, presentation, type_name)
+
+ # Override the description
+ if our_operation._raw.get('description') is not None:
+ raw_operation['description'] = deepcopy_with_locators(our_operation._raw['description'])
+
+ # Add/merge implementation
+ if our_operation._raw.get('implementation') is not None:
+ if raw_operation.get('implementation') is not None:
+ merge(raw_operation['implementation'],
+ deepcopy_with_locators(our_operation._raw['implementation']))
+ else:
+ raw_operation['implementation'] = \
+ deepcopy_with_locators(our_operation._raw['implementation'])
+
+def merge_operation_definitions(context, operations, our_operations, interface_name, presentation,
+ type_name):
+ if not our_operations:
+ return
+ for operation_name, our_operation in our_operations.iteritems():
+ if operation_name in operations:
+ merge_raw_operation_definition(context, operations[operation_name]._raw, our_operation,
+ interface_name, presentation, type_name)
+ else:
+ operations[operation_name] = our_operation._clone(presentation)
+
+def merge_raw_operation_definitions(context, raw_operations, our_operations, interface_name,
+ presentation, type_name):
+ for operation_name, our_operation in our_operations.iteritems():
+ if operation_name in raw_operations:
+ raw_operation = raw_operations[operation_name]
+ if isinstance(raw_operation, basestring):
+ # Convert short form to long form
+ raw_operations[operation_name] = OrderedDict((('implementation', raw_operation),))
+ raw_operation = raw_operations[operation_name]
+ merge_raw_operation_definition(context, raw_operation, our_operation, interface_name,
+ presentation, type_name)
+ else:
+ raw_operations[operation_name] = deepcopy_with_locators(our_operation._raw)
+
+# From either an InterfaceType or an InterfaceDefinition:
+def merge_interface_definition(context, interface, our_source, presentation, type_name):
+ if hasattr(our_source, 'type'):
+ # Check if we changed the interface type
+ input_type1 = interface.type
+ input_type2 = our_source.type
+ if (input_type1 is not None) and (input_type2 is not None) and (input_type1 != input_type2):
+ context.validation.report(
+ 'interface definition "%s" changes type from "%s" to "%s" in "%s"'
+ % (interface._name, input_type1, input_type2, presentation._fullname),
+ locator=input_type2._locator, level=Issue.BETWEEN_TYPES)
+
+ # Add/merge inputs
+ our_interface_inputs = our_source._get_inputs(context) \
+ if hasattr(our_source, '_get_inputs') else our_source.inputs
+ if our_interface_inputs:
+ # Make sure we have the dict
+ if ('inputs' not in interface._raw) or (interface._raw.get('inputs') is None):
+ interface._raw['inputs'] = OrderedDict()
+
+ merge_raw_input_definitions(context, interface._raw['inputs'], our_interface_inputs,
+ our_source._name, None, presentation, type_name)
+
+ # Add/merge operations
+ our_operations = our_source._get_operations(context) \
+ if hasattr(our_source, '_get_operations') else our_source.operations
+ if our_operations is not None:
+ merge_raw_operation_definitions(context, interface._raw, our_operations, our_source._name,
+ presentation, type_name)
+
+def merge_interface_definitions(context, interfaces, our_interfaces, presentation,
+ for_presentation=None):
+ if not our_interfaces:
+ return
+ for name, our_interface in our_interfaces.iteritems():
+ if name in interfaces:
+ merge_interface_definition(context, interfaces[name], our_interface, presentation,
+ 'definition')
+ else:
+ interfaces[name] = our_interface._clone(for_presentation)
+
+def merge_interface_definitions_from_their_types(context, interfaces, presentation):
+ for interface in interfaces.itervalues():
+ the_type = interface._get_type(context) # InterfaceType
+ if the_type is not None:
+ merge_interface_definition(context, interface, the_type, presentation, 'type')
+
+def assign_raw_inputs(context, values, assignments, definitions, interface_name, operation_name,
+ presentation):
+ if not assignments:
+ return
+
+ # Make sure we have the dict
+ if ('inputs' not in values) or (values['inputs'] is None):
+ values['inputs'] = OrderedDict()
+
+ # Assign inputs
+ for input_name, assignment in assignments.iteritems():
+ if (definitions is not None) 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),
+ 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),
+ 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_property_value(context, assignment, definition,
+ assignment.value)
+
+def validate_required_inputs(context, presentation, assignment, definition, original_assignment,
+ interface_name, operation_name=None):
+ input_definitions = definition.inputs
+ if input_definitions:
+ for input_name, input_definition in input_definitions.iteritems():
+ if input_definition.required:
+ prop = assignment.inputs.get(input_name) \
+ if ((assignment is not None) and (assignment.inputs is not None)) else None
+ value = prop.value if prop is not None else None
+ value = value.value if value is not None else None
+ 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),
+ 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),
+ locator=get_locator(original_assignment, presentation._locator),
+ level=Issue.BETWEEN_TYPES)
+
+ if operation_name is not None:
+ return
+
+ assignment_operations = assignment.operations
+ operation_definitions = definition._get_operations(context)
+ if operation_definitions:
+ for operation_name, operation_definition in operation_definitions.iteritems():
+ assignment_operation = assignment_operations.get(operation_name) \
+ if assignment_operations is not None else None
+ original_operation = \
+ original_assignment.operations.get(operation_name, original_assignment) \
+ if (original_assignment is not None) \
+ and (original_assignment.operations is not None) \
+ else original_assignment
+ validate_required_inputs(context, presentation, assignment_operation,
+ operation_definition, original_operation, interface_name,
+ operation_name)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/extensions/aria_extension_tosca/simple_v1_0/modeling/policies.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/policies.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/policies.py
new file mode 100644
index 0000000..fba1972
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/policies.py
@@ -0,0 +1,79 @@
+# 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_shorthand_to_full_type_name
+
+#
+# PolicyType
+#
+
+def get_inherited_targets(context, presentation):
+ """
+ Returns our target node types and group types if we have them or those of our parent, if we have
+ one (recursively).
+ """
+
+ parent = presentation._get_parent(context)
+
+ node_types, group_types = get_inherited_targets(context, parent) \
+ if parent is not None else ([], [])
+
+ our_targets = presentation.targets
+ if our_targets:
+ all_node_types = context.presentation.get('service_template', 'node_types') or {}
+ all_group_types = context.presentation.get('service_template', 'group_types') or {}
+ node_types = []
+ group_types = []
+
+ for our_target in our_targets:
+ if our_target in all_node_types:
+ our_target = convert_shorthand_to_full_type_name(context, our_target,
+ all_node_types)
+ node_types.append(all_node_types[our_target])
+ elif our_target in all_group_types:
+ our_target = convert_shorthand_to_full_type_name(context, our_target,
+ all_group_types)
+ group_types.append(all_group_types[our_target])
+
+ return node_types, group_types
+
+#
+# PolicyTemplate
+#
+
+def get_policy_targets(context, presentation):
+ """
+ Returns our target node templates and groups if we have them.
+ """
+
+ node_templates = []
+ groups = []
+
+ our_targets = presentation.targets
+ if our_targets:
+ all_node_templates = \
+ context.presentation.get('service_template', 'topology_template', 'node_templates') \
+ or {}
+ all_groups = \
+ context.presentation.get('service_template', 'topology_template', 'groups') \
+ or {}
+
+ for our_target in our_targets:
+ if our_target in all_node_templates:
+ node_templates.append(all_node_templates[our_target])
+ elif our_target in all_groups:
+ groups.append(all_groups[our_target])
+
+ return node_templates, groups
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
new file mode 100644
index 0000000..d803609
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
@@ -0,0 +1,201 @@
+# 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 collections import OrderedDict
+
+from aria.parser.presentation import Value
+from aria.parser.utils import (merge, deepcopy_with_locators)
+from aria.parser.validation import Issue
+
+from .data_types import coerce_value
+
+#
+# ArtifactType, DataType, CapabilityType, RelationshipType, NodeType, GroupType, PolicyType
+#
+
+# Works on properties, parameters, inputs, and attributes
+def get_inherited_property_definitions(context, presentation, field_name, for_presentation=None):
+ """
+ Returns our property definitions added on top of those of our parent, if we have one
+ (recursively).
+
+ Allows overriding all aspects of parent properties except data type.
+ """
+
+ # Get definitions from parent
+ # If we inherit from a primitive, it does not have a parent:
+ parent = presentation._get_parent(context) if hasattr(presentation, '_get_parent') else None
+ definitions = get_inherited_property_definitions(context, parent, field_name,
+ for_presentation=presentation) \
+ if parent is not None else OrderedDict()
+
+ # Add/merge our definitions
+ # If we inherit from a primitive, it does not have our field
+ our_definitions = getattr(presentation, field_name, None)
+ if our_definitions:
+ our_definitions_clone = OrderedDict()
+ for name, our_definition in our_definitions.iteritems():
+ our_definitions_clone[name] = our_definition._clone(for_presentation)
+ our_definitions = our_definitions_clone
+ merge_property_definitions(context, presentation, definitions, our_definitions, field_name)
+
+ for definition in definitions.itervalues():
+ definition._reset_method_cache()
+
+ return definitions
+
+#
+# NodeTemplate, RelationshipTemplate, GroupTemplate, PolicyTemplate
+#
+
+def get_assigned_and_defined_property_values(context, presentation):
+ """
+ Returns the assigned property values while making sure they are defined in our type.
+
+ The property definition's default value, if available, will be used if we did not assign it.
+
+ Makes sure that required properties indeed end up with a value.
+ """
+
+ values = OrderedDict()
+
+ the_type = presentation._get_type(context)
+ assignments = presentation.properties
+ definitions = the_type._get_properties(context) if the_type is not None else None
+
+ # Fill in our assignments, but make sure they are defined
+ if assignments:
+ for name, value in assignments.iteritems():
+ if (definitions is not None) and (name in definitions):
+ definition = definitions[name]
+ values[name] = coerce_property_value(context, value, definition, value.value)
+ else:
+ context.validation.report('assignment to undefined property "%s" in "%s"'
+ % (name, presentation._fullname),
+ locator=value._locator, level=Issue.BETWEEN_TYPES)
+
+ # Fill in defaults from the definitions
+ if definitions:
+ for name, definition in definitions.iteritems():
+ if (values.get(name) is None) and (definition.default is not None):
+ values[name] = coerce_property_value(context, presentation, definition,
+ definition.default)
+
+ validate_required_values(context, presentation, values, definitions)
+
+ return values
+
+#
+# TopologyTemplate
+#
+
+def get_parameter_values(context, presentation, field_name):
+ values = OrderedDict()
+
+ parameters = getattr(presentation, field_name)
+
+ # Fill in defaults and values
+ if parameters:
+ for name, parameter in parameters.iteritems():
+ if values.get(name) is None:
+ if hasattr(parameter, 'value') and (parameter.value is not None):
+ # For parameters only:
+ values[name] = coerce_property_value(context, presentation, parameter,
+ parameter.value)
+ else:
+ default = parameter.default if hasattr(parameter, 'default') else None
+ values[name] = coerce_property_value(context, presentation, parameter, default)
+
+ return values
+
+#
+# Utils
+#
+
+def validate_required_values(context, presentation, values, definitions):
+ """
+ Check if required properties have not been assigned.
+ """
+
+ if not definitions:
+ return
+ 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),
+ locator=presentation._get_child_locator('properties'),
+ level=Issue.BETWEEN_TYPES)
+
+def merge_raw_property_definition(context, presentation, raw_property_definition,
+ our_property_definition, field_name, property_name):
+ # Check if we changed the type
+ # TODO: allow a sub-type?
+ type1 = raw_property_definition.get('type')
+ type2 = our_property_definition.type
+ if type1 != type2:
+ context.validation.report(
+ 'override changes type from "%s" to "%s" for property "%s" in "%s"'
+ % (type1, type2, property_name, presentation._fullname),
+ locator=presentation._get_child_locator(field_name, property_name),
+ level=Issue.BETWEEN_TYPES)
+
+ merge(raw_property_definition, our_property_definition._raw)
+
+def merge_raw_property_definitions(context, presentation, raw_property_definitions,
+ our_property_definitions, field_name):
+ if not our_property_definitions:
+ return
+ for property_name, our_property_definition in our_property_definitions.iteritems():
+ if property_name in raw_property_definitions:
+ raw_property_definition = raw_property_definitions[property_name]
+ merge_raw_property_definition(context, presentation, raw_property_definition,
+ our_property_definition, field_name, property_name)
+ else:
+ raw_property_definitions[property_name] = \
+ deepcopy_with_locators(our_property_definition._raw)
+
+def merge_property_definitions(context, presentation, property_definitions,
+ our_property_definitions, field_name):
+ if not our_property_definitions:
+ return
+ for property_name, our_property_definition in our_property_definitions.iteritems():
+ if property_name in property_definitions:
+ property_definition = property_definitions[property_name]
+ merge_raw_property_definition(context, presentation, property_definition._raw,
+ our_property_definition, field_name, property_name)
+ else:
+ property_definitions[property_name] = our_property_definition
+
+# Works on properties, inputs, and parameters
+def coerce_property_value(context, presentation, definition, value, aspect=None):
+ the_type = definition._get_type(context) if definition is not None else None
+ entry_schema = definition.entry_schema if definition is not None else None
+ constraints = definition._get_constraints(context) if definition is not None else None
+ value = coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect)
+ if (the_type is not None) and hasattr(the_type, '_name'):
+ type_name = the_type._name
+ else:
+ type_name = getattr(definition, 'type', None)
+ description = getattr(definition, 'description', None)
+ description = description.value if description is not None else None
+ return Value(type_name, value, description)
+
+def convert_property_definitions_to_values(context, definitions):
+ values = OrderedDict()
+ for name, definition in definitions.iteritems():
+ default = definition.default
+ values[name] = coerce_property_value(context, definition, definition, default)
+ return values
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/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
new file mode 100644
index 0000000..721ea09
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
@@ -0,0 +1,358 @@
+# 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 collections import OrderedDict
+
+from aria.parser.validation import Issue
+from aria.parser.utils import deepcopy_with_locators
+
+from .properties import (convert_property_definitions_to_values, validate_required_values,
+ coerce_property_value)
+from .interfaces import (convert_requirement_interface_definitions_from_type_to_raw_template,
+ merge_interface_definitions, merge_interface, validate_required_inputs)
+
+#
+# NodeType
+#
+
+def get_inherited_requirement_definitions(context, presentation):
+ """
+ Returns our requirement definitions added on top of those of our parent, if we have one
+ (recursively).
+
+ Allows overriding requirement definitions if they have the same name.
+ """
+
+ parent = presentation._get_parent(context)
+ requirement_definitions = get_inherited_requirement_definitions(context, parent) \
+ if parent is not None else []
+
+ our_requirement_definitions = presentation.requirements
+ if our_requirement_definitions:
+ for requirement_name, our_requirement_definition in our_requirement_definitions:
+ # Remove existing requirement definitions of this name if they exist
+ for name, requirement_definition in requirement_definitions:
+ if name == requirement_name:
+ requirement_definitions.remove((name, requirement_definition))
+
+ requirement_definitions.append((requirement_name, our_requirement_definition))
+
+ return requirement_definitions
+
+#
+# NodeTemplate
+#
+
+def get_template_requirements(context, presentation):
+ """
+ Returns our requirements added on top of those of the node type if they exist there.
+
+ If the requirement has a relationship, the relationship properties and interfaces are assigned.
+
+ Returns the assigned property, interface input, and interface operation input values while
+ making sure they are defined in our type. Default values, if available, will be used if we did
+ not assign them. Also makes sure that required properties and inputs indeed end up with a value.
+ """
+
+ requirement_assignments = []
+
+ the_type = presentation._get_type(context) # NodeType
+ requirement_definitions = the_type._get_requirements(context) if the_type is not None else None
+
+ # Add our requirement assignments
+ our_requirement_assignments = presentation.requirements
+ if our_requirement_assignments:
+ add_requirement_assignments(context, presentation, requirement_assignments,
+ requirement_definitions, our_requirement_assignments)
+
+ # Validate occurrences
+ if requirement_definitions:
+ 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
+ for name, _ in requirement_assignments:
+ if name == requirement_name:
+ actual_occurrences += 1
+
+ if allowed_occurrences is None:
+ # If not specified, we interpret this to mean that exactly 1 occurrence is required
+ if actual_occurrences == 0:
+ # If it's not there, we will automatically add it (this behavior is not in the
+ # TOSCA spec, but seems implied)
+ requirement_assignment, \
+ relationship_property_definitions, \
+ relationship_interface_definitions = \
+ convert_requirement_from_definition_to_assignment(context,
+ requirement_definition,
+ None, presentation)
+ validate_requirement_assignment(context, presentation, requirement_assignment,
+ relationship_property_definitions,
+ relationship_interface_definitions)
+ 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),
+ 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),
+ 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),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+ return requirement_assignments
+
+#
+# Utils
+#
+
+def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches
+ our_requirement_assignment, container):
+ from ..assignments import RequirementAssignment
+
+ raw = OrderedDict()
+
+ # Capability type name:
+ raw['capability'] = deepcopy_with_locators(requirement_definition.capability)
+
+ node_type = requirement_definition._get_node_type(context)
+ if node_type is not None:
+ raw['node'] = deepcopy_with_locators(node_type._name)
+
+ relationship_type = None
+ relationship_template = None
+ relationship_property_definitions = None
+ relationship_interface_definitions = None
+
+ # First try to find the relationship if we declared it
+ # RelationshipAssignment:
+ our_relationship = our_requirement_assignment.relationship \
+ if our_requirement_assignment is not None else None
+ if our_relationship is not None:
+ relationship_type, relationship_type_variant = our_relationship._get_type(context)
+ if relationship_type_variant == 'relationship_template':
+ relationship_template = relationship_type
+ relationship_type = relationship_template._get_type(context)
+
+ # If not exists, try at the node type
+ relationship_definition = None
+ if relationship_type is None:
+ relationship_definition = requirement_definition.relationship # RelationshipDefinition
+ if relationship_definition is not None:
+ relationship_type = relationship_definition._get_type(context)
+
+ if relationship_type is not None:
+ raw['relationship'] = OrderedDict()
+
+ type_name = our_relationship.type if our_relationship is not None else None
+ if type_name is None:
+ type_name = relationship_type._name
+
+ raw['relationship']['type'] = deepcopy_with_locators(type_name)
+
+ # These are our property definitions
+ relationship_property_definitions = relationship_type._get_properties(context)
+
+ if relationship_template is not None:
+ # Property values from template
+ raw['properties'] = relationship_template._get_property_values(context)
+ else:
+ if relationship_property_definitions:
+ # Convert property definitions to values
+ raw['properties'] = \
+ convert_property_definitions_to_values(context,
+ relationship_property_definitions)
+
+ # These are our interface definitions
+ # InterfaceDefinition:
+ relationship_interface_definitions = OrderedDict(relationship_type._get_interfaces(context))
+
+ if relationship_definition:
+ # Merge extra interface definitions
+ # InterfaceDefinition:
+ relationship_interface_definitions = relationship_definition.interfaces
+ merge_interface_definitions(context, relationship_interface_definitions,
+ relationship_interface_definitions, requirement_definition,
+ container)
+
+ if relationship_template is not None:
+ # Interfaces from template
+ interfaces = relationship_template._get_interfaces(context)
+ if interfaces:
+ raw['relationship']['interfaces'] = OrderedDict()
+ for interface_name, interface in interfaces.iteritems():
+ raw['relationship']['interfaces'][interface_name] = interface._raw
+ else:
+ # Convert interface definitions to templates
+ convert_requirement_interface_definitions_from_type_to_raw_template(
+ context,
+ raw['relationship'],
+ relationship_interface_definitions)
+
+ return \
+ RequirementAssignment(name=requirement_definition._name, raw=raw, container=container), \
+ relationship_property_definitions, \
+ relationship_interface_definitions
+
+def add_requirement_assignments(context, presentation, requirement_assignments,
+ requirement_definitions, our_requirement_assignments):
+ for requirement_name, our_requirement_assignment in our_requirement_assignments:
+ requirement_definition = get_first_requirement(requirement_definitions, requirement_name)
+ if requirement_definition is not None:
+ requirement_assignment, \
+ relationship_property_definitions, \
+ relationship_interface_definitions = \
+ convert_requirement_from_definition_to_assignment(context, requirement_definition,
+ our_requirement_assignment,
+ presentation)
+ merge_requirement_assignment(context,
+ relationship_property_definitions,
+ relationship_interface_definitions,
+ requirement_assignment, our_requirement_assignment)
+ validate_requirement_assignment(context,
+ our_requirement_assignment.relationship \
+ or our_requirement_assignment,
+ requirement_assignment,
+ relationship_property_definitions,
+ relationship_interface_definitions)
+ 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),
+ locator=our_requirement_assignment._locator,
+ level=Issue.BETWEEN_TYPES)
+
+def merge_requirement_assignment(context, relationship_property_definitions,
+ relationship_interface_definitions, requirement, our_requirement):
+ our_capability = our_requirement.capability
+ if our_capability is not None:
+ requirement._raw['capability'] = deepcopy_with_locators(our_capability)
+
+ our_node = our_requirement.node
+ if our_node is not None:
+ requirement._raw['node'] = deepcopy_with_locators(our_node)
+
+ our_node_filter = our_requirement.node_filter
+ if our_node_filter is not None:
+ requirement._raw['node_filter'] = deepcopy_with_locators(our_node_filter._raw)
+
+ our_relationship = our_requirement.relationship # RelationshipAssignment
+ if our_relationship is not None:
+ # Make sure we have a dict
+ if 'relationship' not in requirement._raw:
+ requirement._raw['relationship'] = OrderedDict()
+ elif not isinstance(requirement._raw['relationship'], dict):
+ # Convert existing short form to long form
+ the_type = requirement._raw['relationship']
+ requirement._raw['relationship'] = OrderedDict()
+ requirement._raw['relationship']['type'] = deepcopy_with_locators(the_type)
+
+ merge_requirement_assignment_relationship(context, our_relationship,
+ relationship_property_definitions,
+ relationship_interface_definitions,
+ requirement, our_relationship)
+
+def merge_requirement_assignment_relationship(context, presentation, property_definitions,
+ interface_definitions, requirement, our_relationship):
+ the_type = our_relationship.type
+ if the_type is not None:
+ # Could be a type or a template:
+ requirement._raw['relationship']['type'] = deepcopy_with_locators(the_type)
+
+ our_relationship_properties = our_relationship._raw.get('properties')
+ if our_relationship_properties:
+ # Make sure we have a dict
+ if 'properties' not in requirement._raw['relationship']:
+ requirement._raw['relationship']['properties'] = OrderedDict()
+
+ # Merge our properties
+ for property_name, prop in our_relationship_properties.iteritems():
+ if property_name in property_definitions:
+ definition = property_definitions[property_name]
+ requirement._raw['relationship']['properties'][property_name] = \
+ coerce_property_value(context, presentation, definition, prop)
+ else:
+ context.validation.report(
+ 'relationship property "%s" not declared at definition of requirement "%s"'
+ ' in "%s"'
+ % (property_name, presentation._fullname,
+ presentation._container._container._fullname),
+ locator=our_relationship._get_child_locator('properties', property_name),
+ level=Issue.BETWEEN_TYPES)
+
+ our_interfaces = our_relationship.interfaces
+ if our_interfaces:
+ # Make sure we have a dict
+ if 'interfaces' not in requirement._raw['relationship']:
+ requirement._raw['relationship']['interfaces'] = OrderedDict()
+
+ # Merge interfaces
+ for interface_name, our_interface in our_interfaces.iteritems():
+ if interface_name not in requirement._raw['relationship']['interfaces']:
+ requirement._raw['relationship']['interfaces'][interface_name] = OrderedDict()
+
+ if (interface_definitions is not None) and (interface_name in interface_definitions):
+ interface_definition = interface_definitions[interface_name]
+ interface_assignment = requirement.relationship.interfaces[interface_name]
+ merge_interface(context, presentation, interface_assignment, our_interface,
+ interface_definition, interface_name)
+ else:
+ context.validation.report(
+ 'interface definition "%s" not declared at definition of requirement "%s"'
+ ' in "%s"'
+ % (interface_name, presentation._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 = requirement_assignment.relationship
+ if relationship is None:
+ return
+
+ validate_required_values(context, presentation, relationship.properties,
+ 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
+ validate_required_inputs(context, presentation, interface_assignment,
+ relationship_interface_definition, None, interface_name)
+
+def get_first_requirement(requirement_definitions, name):
+ if requirement_definitions is not None:
+ for requirement_name, requirement_definition in requirement_definitions:
+ if requirement_name == name:
+ return requirement_definition
+ return None
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/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
new file mode 100644
index 0000000..e0252b1
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
@@ -0,0 +1,126 @@
+# 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 aria.parser.utils import safe_repr
+from aria.parser.validation import Issue
+
+def validate_subtitution_mappings_requirement(context, presentation):
+ if not validate_format(context, presentation, 'requirement'):
+ return
+
+ node_template = get_node_template(context, presentation, 'requirement')
+ if node_template is None:
+ return
+
+ node_type = presentation._container._get_type(context)
+ if node_type is None:
+ return
+
+ requirements = node_type._get_requirements(context)
+ type_requirement = None
+ for name, the_requirement in requirements:
+ if name == presentation._name:
+ type_requirement = the_requirement
+ break
+ if type_requirement is None:
+ context.validation.report(
+ 'substitution mappings requirement "%s" is not declared in node type "%s"'
+ % (presentation._name, node_type._name),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+ return
+
+ requirement_name = presentation._raw[1]
+ requirements = node_template._get_requirements(context)
+ requirement = None
+ for name, the_requirement in requirements:
+ if name == requirement_name:
+ requirement = the_requirement
+ break
+
+ if requirement is None:
+ context.validation.report(
+ 'substitution mappings requirement "%s" refers to an unknown requirement of node '
+ 'template "%s": %s'
+ % (presentation._name, node_template._name, safe_repr(requirement_name)),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+ return
+
+def validate_subtitution_mappings_capability(context, presentation):
+ if not validate_format(context, presentation, 'capability'):
+ return
+
+ node_template = get_node_template(context, presentation, 'capability')
+ if node_template is None:
+ return
+
+ node_type = presentation._container._get_type(context)
+ if node_type is None:
+ return
+
+ capabilities = node_type._get_capabilities(context)
+ type_capability = capabilities.get(presentation._name)
+ if type_capability is None:
+ context.validation.report(
+ 'substitution mappings capability "%s" is not declared in node type "%s"'
+ % (presentation._name, node_type._name), locator=presentation._locator,
+ level=Issue.BETWEEN_TYPES)
+ return
+
+ capability_name = presentation._raw[1]
+ capabilities = node_template._get_capabilities(context)
+ capability = capabilities.get(capability_name)
+
+ if capability is None:
+ context.validation.report(
+ 'substitution mappings capability "%s" refers to an unknown capability of node template'
+ ' "%s": %s'
+ % (presentation._name, node_template._name, safe_repr(capability_name)),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+ return
+
+ type_capability_type = type_capability._get_type(context)
+ capability_type = capability._get_type(context)
+
+ if not type_capability_type._is_descendant(context, capability_type):
+ context.validation.report(
+ 'type "%s" of substitution mappings capability "%s" is not a descendant of "%s"'
+ % (capability_type._name, presentation._name, type_capability_type._name),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+#
+# Utils
+#
+
+def validate_format(context, presentation, name):
+ if (not isinstance(presentation._raw, list)) or (len(presentation._raw) != 2) \
+ or (not isinstance(presentation._raw[0], basestring)) \
+ or (not isinstance(presentation._raw[1], basestring)):
+ context.validation.report(
+ 'substitution mappings %s "%s" is not a list of 2 strings: %s'
+ % (name, presentation._name, safe_repr(presentation._raw)),
+ locator=presentation._locator, level=Issue.FIELD)
+ return False
+ return True
+
+def get_node_template(context, presentation, name):
+ node_template_name = presentation._raw[0]
+ node_template = context.presentation.get_from_dict('service_template', 'topology_template',
+ 'node_templates', node_template_name)
+ if node_template is None:
+ context.validation.report(
+ 'substitution mappings %s "%s" refers to an unknown node template: %s'
+ % (name, presentation._name, safe_repr(node_template_name)),
+ locator=presentation._locator, level=Issue.FIELD)
+ return node_template
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/79454a29/extensions/aria_extension_tosca/simple_v1_0/presentation/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/__init__.py
new file mode 100644
index 0000000..ae1e83e
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/__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/79454a29/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
new file mode 100644
index 0000000..22e9606
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
@@ -0,0 +1,32 @@
+# 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 aria.parser.presentation import (Presentation, has_fields, primitive_dict_field)
+from aria.parser.utils import cachedmethod
+
+@has_fields
+class ExtensiblePresentation(Presentation):
+ """
+ A presentation that supports an optional :code:`_extensions` dict field.
+ """
+
+ @primitive_dict_field()
+ def _extensions(self):
+ pass
+
+ @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