summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
diff options
context:
space:
mode:
Diffstat (limited to 'azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py')
-rw-r--r--azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py530
1 files changed, 530 insertions, 0 deletions
diff --git a/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
new file mode 100644
index 0000000..23a03b7
--- /dev/null
+++ b/azure/aria/aria-extension-cloudify/src/aria/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
@@ -0,0 +1,530 @@
+# 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.utils.collections import (merge, deepcopy_with_locators, OrderedDict)
+from aria.parser.presentation import get_locator
+from aria.parser.validation import Issue
+
+from .parameters import (coerce_parameter_value, convert_parameter_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) # InterfaceType
+ 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.
+ """
+
+ if for_presentation is None:
+ for_presentation = presentation
+
+ # Get interfaces from parent
+ parent = presentation._get_parent(context)
+ interfaces = get_inherited_interface_definitions(context, parent, type_name, for_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)
+
+ 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
+ interface_inputs = presentation._get_inputs(context)
+ if interface_inputs is not None:
+ raw['inputs'] = convert_parameter_definitions_to_values(context, interface_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_parameter_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
+ type1 = interface._get_type(context)
+ type2 = our_source._get_type(context)
+
+ 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}"' \
+ .format(type1._name, type2._name),
+ locator=our_source._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_parameter_value(context, assignment, definition,
+ assignment.value)
+
+
+def validate_required_inputs(context, presentation, assignment, definition, original_assignment,
+ interface_name, operation_name=None):
+ # The validation of the `required` field of inputs that belong to operations and interfaces
+ # (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
+ 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)