http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/filters.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/filters.py b/extensions/aria_extension_tosca/simple_v1_0/filters.py new file mode 100644 index 0000000..7ead633 --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/filters.py @@ -0,0 +1,101 @@ +# 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 import dsl_specification +from aria.parser.utils import cachedmethod +from aria.parser.presentation import (has_fields, object_sequenced_list_field, field_validator) + +from .misc import ConstraintClause +from .presentation.extensible import ExtensiblePresentation +from .presentation.field_validators import (node_filter_properties_validator, + node_filter_capabilities_validator) + +@has_fields +class CapabilityFilter(ExtensiblePresentation): + @object_sequenced_list_field(ConstraintClause) + def properties(self): + pass + + @cachedmethod + def _get_node_type(self, context): + return self._container._get_node_type(context) + + @cachedmethod + def _get_type_for_name(self, context, name): + node_type = self._get_node_type(context) + if node_type is not None: + capabilities = node_type._get_capabilities(context) + capability = capabilities.get(self._name) + properties = capability.properties if capability is not None else None + prop = properties.get(name) if properties is not None else None + return prop._get_type(context) if prop is not None else None + + return None + +@has_fields +@dsl_specification('3.5.4', 'tosca-simple-1.0') +class NodeFilter(ExtensiblePresentation): + """ + A node filter definition defines criteria for selection of a TOSCA Node Template based upon the + template's property values, capabilities and capability properties. + + 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 + #DEFN_ELEMENT_NODE_FILTER_DEFN>`__ + """ + + @field_validator(node_filter_properties_validator) + @object_sequenced_list_field(ConstraintClause) + @dsl_specification('3.5.3', 'tosca-simple-1.0') + def properties(self): + """ + An optional sequenced list of property filters that would be used to select (filter) + matching TOSCA entities (e.g., Node Template, Node Type, Capability Types, etc.) based upon + their property definitions' values. + + 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 + #DEFN_ELEMENT_PROPERTY_FILTER_DEFN>`__ + + :rtype: list of (str, :class:`ConstraintClause`) + """ + + @field_validator(node_filter_capabilities_validator) + @object_sequenced_list_field(CapabilityFilter) + def capabilities(self): + """ + An optional sequenced list of property filters that would be used to select (filter) + matching TOSCA entities (e.g., Node Template, Node Type, Capability Types, etc.) based upon + their capabilities' property definitions' values. + + :rtype: list of (str, :class:`CapabilityDefinition`) + """ + + @cachedmethod + def _get_node_type(self, context): + if hasattr(self._container, '_get_node'): + node_type, node_type_variant = self._container._get_node(context) + return node_type if node_type_variant == 'node_type' else None + return None + + @cachedmethod + def _get_type_for_name(self, context, name): + node_type = self._get_node_type(context) + if node_type is not None: + properties = node_type._get_properties(context) + prop = properties.get(name) + return prop._get_type(context) if prop is not None else None + + return None
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/functions.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/functions.py b/extensions/aria_extension_tosca/simple_v1_0/functions.py new file mode 100644 index 0000000..bf2f69d --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/functions.py @@ -0,0 +1,527 @@ +# 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 cStringIO import StringIO + +from aria.parser import (dsl_specification, InvalidValueError) +from aria.parser.modeling import (Function, CannotEvaluateFunctionException) +from aria.parser.utils import (FrozenList, as_raw, safe_repr) +from aria.parser.validation import Issue + +# +# Intrinsic +# + +@dsl_specification('4.3.1', 'tosca-simple-1.0') +class Concat(Function): + """ + The :code:`concat` function is used to concatenate two or more string values within a TOSCA + service template. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if not isinstance(argument, list): + raise InvalidValueError( + 'function "concat" argument must be a list of string expressions: %s' + % safe_repr(argument), + locator=self.locator) + + string_expressions = [] + for index, an_argument in enumerate(argument): + string_expressions.append(parse_string_expression(context, presentation, 'concat', + index, None, an_argument)) + self.string_expressions = FrozenList(string_expressions) + + @property + def as_raw(self): + string_expressions = [] + for string_expression in self.string_expressions: + if hasattr(string_expression, 'as_raw'): + string_expression = as_raw(string_expression) + string_expressions.append(string_expression) + return {'concat': string_expressions} + + def _evaluate(self, context, container): + value = StringIO() + for e in self.string_expressions: + if hasattr(e, '_evaluate'): + e = e._evaluate(context, container) + value.write(str(e)) + return value.getvalue() + +@dsl_specification('4.3.2', 'tosca-simple-1.0') +class Token(Function): + """ + The :code:`token` function is used within a TOSCA service template on a string to parse out + (tokenize) substrings separated by one or more token characters within a larger string. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if (not isinstance(argument, list)) or (len(argument) != 3): + raise InvalidValueError('function "token" argument must be a list of 3 parameters: %s' + % safe_repr(argument), + locator=self.locator) + + self.string_with_tokens = parse_string_expression(context, presentation, 'token', 0, + 'the string to tokenize', argument[0]) + self.string_of_token_chars = parse_string_expression(context, presentation, 'token', 1, + 'the token separator characters', + argument[1]) + self.substring_index = parse_int(context, presentation, 'token', 2, + 'the 0-based index of the token to return', argument[2]) + + @property + def as_raw(self): + string_with_tokens = self.string_with_tokens + if hasattr(string_with_tokens, 'as_raw'): + string_with_tokens = as_raw(string_with_tokens) + string_of_token_chars = self.string_with_tokens + if hasattr(string_of_token_chars, 'as_raw'): + string_of_token_chars = as_raw(string_of_token_chars) + return {'token': [string_with_tokens, string_of_token_chars, self.substring_index]} + + def _evaluate(self, context, container): + string_with_tokens = self.string_with_tokens + if hasattr(string_with_tokens, '_evaluate'): + string_with_tokens = string_with_tokens._evaluate(context, container) # pylint: disable=no-member + +# +# Property +# + +@dsl_specification('4.4.1', 'tosca-simple-1.0') +class GetInput(Function): + """ + The :code:`get_input` function is used to retrieve the values of properties declared within the + inputs section of a TOSCA Service Template. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + self.input_property_name = parse_string_expression(context, presentation, 'get_input', + None, 'the input property name', + argument) + + if isinstance(self.input_property_name, basestring): + the_input = context.presentation.get_from_dict('service_template', 'topology_template', + 'inputs', self.input_property_name) + if the_input is None: + raise InvalidValueError( + 'function "get_input" argument is not a valid input name: %s' + % safe_repr(argument), + locator=self.locator) + + @property + def as_raw(self): + return {'get_input': as_raw(self.input_property_name)} + + def _evaluate(self, context, container): # pylint: disable=unused-argument + if not context.modeling.instance: + raise CannotEvaluateFunctionException() + the_input = context.modeling.instance.inputs.get( + self.input_property_name, + context.modeling.model.inputs.get(self.input_property_name)) + return the_input.value if the_input is not None else None + +@dsl_specification('4.4.2', 'tosca-simple-1.0') +class GetProperty(Function): + """ + The :code:`get_property` function is used to retrieve property values between modelable entities + defined in the same service template. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if (not isinstance(argument, list)) or (len(argument) < 2): + raise InvalidValueError( + 'function "get_property" argument must be a list of at least 2 string expressions: ' + '%s' + % safe_repr(argument), + locator=self.locator) + + self.modelable_entity_name = parse_modelable_entity_name(context, presentation, + 'get_property', 0, argument[0]) + # The first of these will be tried as a req-or-cap name: + self.nested_property_name_or_index = argument[1:] + + @property + def as_raw(self): + return {'get_property': [self.modelable_entity_name] + self.nested_property_name_or_index} + + def _evaluate(self, context, container): + modelable_entities = get_modelable_entities(context, container, self.locator, + self.modelable_entity_name) + + req_or_cap_name = self.nested_property_name_or_index[0] + + for modelable_entity in modelable_entities: + if hasattr(modelable_entity, 'requirement_templates') \ + and modelable_entity.requirement_templates \ + and (req_or_cap_name in modelable_entity.requirement_templates): + # First argument refers to a requirement + properties = modelable_entity.requirement_templates[req_or_cap_name].properties + nested_property_name_or_index = self.nested_property_name_or_index[1:] + elif hasattr(modelable_entity, 'capability_templates') \ + and modelable_entity.capability_templates \ + and (req_or_cap_name in modelable_entity.capability_templates): + # First argument refers to a capability + properties = modelable_entity.capability_templates[req_or_cap_name].properties + nested_property_name_or_index = self.nested_property_name_or_index[1:] + else: + properties = modelable_entity.properties + nested_property_name_or_index = self.nested_property_name_or_index + + if properties: + found = True + value = properties + for name in nested_property_name_or_index: + if (isinstance(value, dict) and (name in value)) \ + or (isinstance(value, list) and name < len(list)): + value = value[name] + if hasattr(value, '_evaluate'): + value = value._evaluate(context, modelable_entity) + else: + found = False + break + if found: + return value + + raise InvalidValueError( + 'function "get_property" could not find "%s" in modelable entity "%s"' \ + % ('.'.join(self.nested_property_name_or_index), self.modelable_entity_name), + locator=self.locator) + +# +# Attribute +# + +@dsl_specification('4.5.1', 'tosca-simple-1.0') +class GetAttribute(Function): + """ + The :code:`get_attribute` function is used to retrieve the values of named attributes declared + by the referenced node or relationship template name. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if (not isinstance(argument, list)) or (len(argument) < 2): + raise InvalidValueError( + 'function "get_attribute" argument must be a list of at least 2 string expressions:' + ' %s' + % safe_repr(argument), + locator=self.locator) + + self.modelable_entity_name = parse_modelable_entity_name(context, presentation, + 'get_attribute', 0, argument[0]) + # The first of these will be tried as a req-or-cap name: + self.nested_property_name_or_index = argument[1:] + + @property + def as_raw(self): + return {'get_attribute': [self.modelable_entity_name] + self.nested_property_name_or_index} + + def _evaluate(self, context, container): # pylint: disable=no-self-use,unused-argument + raise CannotEvaluateFunctionException() + +# +# Operation +# + +@dsl_specification('4.6.1', 'tosca-simple-1.0') +class GetOperationOutput(Function): + """ + The :code:`get_operation_output` function is used to retrieve the values of variables exposed / + exported from an interface operation. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if (not isinstance(argument, list)) or (len(argument) != 4): + raise InvalidValueError( + 'function "get_operation_output" argument must be a list of 4 parameters: %s' + % safe_repr(argument), + locator=self.locator) + + self.modelable_entity_name = parse_string_expression(context, presentation, + 'get_operation_output', 0, + 'modelable entity name', argument[0]) + self.interface_name = parse_string_expression(context, presentation, 'get_operation_output', + 1, 'the interface name', argument[1]) + self.operation_name = parse_string_expression(context, presentation, 'get_operation_output', + 2, 'the operation name', argument[2]) + self.output_variable_name = parse_string_expression(context, presentation, + 'get_operation_output', 3, + 'the output name', argument[3]) + + @property + def as_raw(self): + interface_name = self.interface_name + if hasattr(interface_name, 'as_raw'): + interface_name = as_raw(interface_name) + operation_name = self.operation_name + if hasattr(operation_name, 'as_raw'): + operation_name = as_raw(operation_name) + output_variable_name = self.output_variable_name + if hasattr(output_variable_name, 'as_raw'): + output_variable_name = as_raw(output_variable_name) + return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name, + output_variable_name]} + +# +# Navigation +# + +@dsl_specification('4.7.1', 'tosca-simple-1.0') +class GetNodesOfType(Function): + """ + The :code:`get_nodes_of_type` function can be used to retrieve a list of all known instances of + nodes of the declared Node Type. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + self.node_type_name = parse_string_expression(context, presentation, 'get_nodes_of_type', + None, 'the node type name', argument) + + if isinstance(self.node_type_name, basestring): + node_types = context.presentation.get('service_template', 'node_types') + if (node_types is None) or (self.node_type_name not in node_types): + raise InvalidValueError( + 'function "get_nodes_of_type" argument is not a valid node type name: %s' + % safe_repr(argument), + locator=self.locator) + + @property + def as_raw(self): + node_type_name = self.node_type_name + if hasattr(node_type_name, 'as_raw'): + node_type_name = as_raw(node_type_name) + return {'get_nodes_of_type': node_type_name} + + def _evaluate(self, context, container): + pass + +# +# Artifact +# + +@dsl_specification('4.8.1', 'tosca-simple-1.0') +class GetArtifact(Function): + """ + The :code:`get_artifact` function is used to retrieve artifact location between modelable + entities defined in the same service template. + """ + + def __init__(self, context, presentation, argument): + self.locator = presentation._locator + + if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4): + raise InvalidValueError( + 'function "get_artifact" argument must be a list of 2 to 4 parameters: %s' + % safe_repr(argument), + locator=self.locator) + + self.modelable_entity_name = parse_string_expression(context, presentation, 'get_artifact', + 0, 'modelable entity name', + argument[0]) + self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1, + 'the artifact name', argument[1]) + self.location = parse_string_expression(context, presentation, 'get_artifact', 2, + 'the location or "LOCAL_FILE"', argument[2]) + self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag', + argument[3]) + + @property + def as_raw(self): + artifact_name = self.artifact_name + if hasattr(artifact_name, 'as_raw'): + artifact_name = as_raw(artifact_name) + location = self.location + if hasattr(location, 'as_raw'): + location = as_raw(location) + return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]} + +# +# Utils +# + +def get_function(context, presentation, value): + functions = context.presentation.presenter.functions + if isinstance(value, dict) and (len(value) == 1): + key = value.keys()[0] + if key in functions: + try: + return True, functions[key](context, presentation, value[key]) + except InvalidValueError as e: + context.validation.report(issue=e.issue) + return True, None + return False, None + +def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument + is_function, func = get_function(context, presentation, value) + if is_function: + return func + else: + value = str(value) + return value + +def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument + if not isinstance(value, int): + try: + value = int(value) + except ValueError: + raise invalid_value(name, index, 'an integer', explanation, value, + presentation._locator) + return value + +def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument + if not isinstance(value, bool): + raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator) + return value + +def parse_modelable_entity_name(context, presentation, name, index, value): + value = parse_string_expression(context, presentation, name, index, 'the modelable entity name', + value) + if value == 'SELF': + the_self, _ = parse_self(presentation) + if the_self is None: + raise invalid_modelable_entity_name(name, index, value, presentation._locator, + 'a node template or a relationship template') + elif value == 'HOST': + _, self_variant = parse_self(presentation) + if self_variant != 'node_template': + raise invalid_modelable_entity_name(name, index, value, presentation._locator, + 'a node template') + elif (value == 'SOURCE') or (value == 'TARGET'): + _, self_variant = parse_self(presentation) + if self_variant != 'relationship_template': + raise invalid_modelable_entity_name(name, index, value, presentation._locator, + 'a relationship template') + elif isinstance(value, basestring): + node_templates = \ + context.presentation.get('service_template', 'topology_template', 'node_templates') \ + or {} + relationship_templates = \ + context.presentation.get('service_template', 'topology_template', + 'relationship_templates') \ + or {} + if (value not in node_templates) and (value not in relationship_templates): + raise InvalidValueError( + 'function "%s" parameter %d is not a valid modelable entity name: %s' + % (name, index + 1, safe_repr(value)), + locator=presentation._locator, level=Issue.BETWEEN_TYPES) + return value + +def parse_self(presentation): + from .templates import NodeTemplate, RelationshipTemplate + from .types import NodeType, RelationshipType + + if presentation is None: + return None, None + elif isinstance(presentation, NodeTemplate) or isinstance(presentation, NodeType): + return presentation, 'node_template' + elif isinstance(presentation, RelationshipTemplate) \ + or isinstance(presentation, RelationshipType): + return presentation, 'relationship_template' + else: + return parse_self(presentation._container) + +@dsl_specification('4.1', 'tosca-simple-1.0') +def get_modelable_entities(context, container, locator, modelable_entity_name): + """ + The following keywords MAY be used in some TOSCA function in place of a TOSCA Node or + Relationship Template name. + """ + + if modelable_entity_name == 'SELF': + return get_self(context, container) + elif modelable_entity_name == 'HOST': + return get_host(context, container) + elif modelable_entity_name == 'SOURCE': + return get_source(context, container) + elif modelable_entity_name == 'TARGET': + return get_target(context, container) + elif isinstance(modelable_entity_name, basestring): + node_templates = \ + context.presentation.get('service_template', 'topology_template', 'node_templates') \ + or {} + if modelable_entity_name in node_templates: + return [node_templates[modelable_entity_name]] + relationship_templates = \ + context.presentation.get('service_template', 'topology_template', + 'relationship_templates') \ + or {} + if modelable_entity_name in relationship_templates: + return [relationship_templates[modelable_entity_name]] + + raise InvalidValueError('function "get_property" could not find modelable entity "%s"' + % modelable_entity_name, + locator=locator) + +def get_self(context, container): # pylint: disable=unused-argument + """ + A TOSCA orchestrator will interpret this keyword as the Node or Relationship Template instance + that contains the function at the time the function is evaluated. + """ + + return [container] + +def get_host(context, container): # pylint: disable=unused-argument + """ + A TOSCA orchestrator will interpret this keyword to refer to the all nodes that "host" the node + using this reference (i.e., as identified by its HostedOn relationship). + + Specifically, TOSCA orchestrators that encounter this keyword when evaluating the get_attribute + or :code:`get_property` functions SHALL search each node along the "HostedOn" relationship chain + starting at the immediate node that hosts the node where the function was evaluated (and then + that node's host node, and so forth) until a match is found or the "HostedOn" relationship chain + ends. + """ + + print container.relationships + exit() + +def get_source(context, container): # pylint: disable=unused-argument + """ + A TOSCA orchestrator will interpret this keyword as the Node Template instance that is at the + source end of the relationship that contains the referencing function. + """ + +def get_target(context, container): # pylint: disable=unused-argument + """ + A TOSCA orchestrator will interpret this keyword as the Node Template instance that is at the + target end of the relationship that contains the referencing function. + """ + +def invalid_modelable_entity_name(name, index, value, locator, contexts): + return InvalidValueError('function "%s" parameter %d can be "%s" only in %s' + % (name, index + 1, value, contexts), + locator=locator, level=Issue.FIELD) + +def invalid_value(name, index, the_type, explanation, value, locator): + return InvalidValueError( + 'function "%s" %s is not %s%s: %s' + % (name, ('parameter %d' % (index + 1)) if index is not None else 'argument', + the_type, (', %s' % explanation) if explanation is not None else '', safe_repr(value)), + locator=locator, level=Issue.FIELD) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/misc.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py new file mode 100644 index 0000000..5e41378 --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py @@ -0,0 +1,409 @@ +# 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 import dsl_specification +from aria.parser.presentation import (AsIsPresentation, has_fields, allow_unknown_fields, + short_form_field, primitive_field, primitive_list_field, + primitive_dict_unknown_fields, object_field, + object_list_field, object_dict_field, field_validator, + type_validator) +from aria.parser.utils import (cachedmethod, puts, as_raw) + +from .modeling.data_types import (get_data_type, get_data_type_value, get_property_constraints, + apply_constraint_to_value) +from .modeling.substitution_mappings import (validate_subtitution_mappings_requirement, + validate_subtitution_mappings_capability) +from .presentation.extensible import ExtensiblePresentation +from .presentation.field_validators import (constraint_clause_field_validator, + constraint_clause_in_range_validator, + constraint_clause_valid_values_validator, + constraint_clause_pattern_validator, + data_type_validator) +from .presentation.types import (convert_shorthand_to_full_type_name, + get_type_by_full_or_shorthand_name) + +@dsl_specification('3.5.1', 'tosca-simple-1.0') +class Description(AsIsPresentation): + """ + 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 + #DEFN_ELEMENT_DESCRIPTION>`__ + """ + + def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument + super(Description, self).__init__(name, raw, container, cls=unicode) + + def _dump(self, context): + value = as_raw(self.value) + puts(context.style.meta(value)) + +@allow_unknown_fields +@has_fields +@dsl_specification('3.9.3.2', 'tosca-simple-1.0') +class MetaData(ExtensiblePresentation): + @primitive_field(str) + @dsl_specification('3.9.3.3', 'tosca-simple-1.0') + def template_name(self): + """ + This optional metadata keyname can be used to declare the name of service template as a + single-line string value. + """ + + @primitive_field(str) + @dsl_specification('3.9.3.4', 'tosca-simple-1.0') + def template_author(self): + """ + This optional metadata keyname can be used to declare the author(s) of the service template + as a single-line string value. + """ + + @primitive_field(str) + @dsl_specification('3.9.3.5', 'tosca-simple-1.0') + def template_version(self): + """ + This optional metadata keyname can be used to declare a domain specific version of the + service template as a single-line string value. + """ + + @primitive_dict_unknown_fields() + def custom(self): + """ + :rtype: dict + """ + +@has_fields +@dsl_specification('3.5.5', 'tosca-simple-1.0') +class Repository(ExtensiblePresentation): + """ + A repository definition defines a named external repository which contains deployment and + implementation artifacts that are referenced within the TOSCA Service Template. + + 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 + #DEFN_ELEMENT_REPOSITORY_DEF>`__ + """ + + @object_field(Description) + def description(self): + """ + The optional description for the repository. + + :rtype: :class:`Description` + """ + + @primitive_field(str, required=True) + def url(self): + """ + The required URL or network address used to access the repository. + + :rtype: str + """ + + @primitive_field() + def credential(self): + """ + The optional Credential used to authorize access to the repository. + + :rtype: tosca.datatypes.Credential + """ + + @cachedmethod + def _get_credential(self, context): + return get_data_type_value(context, self, 'credential', 'tosca.datatypes.Credential') + +@short_form_field('file') +@has_fields +@dsl_specification('3.5.7', 'tosca-simple-1.0') +class Import(ExtensiblePresentation): + """ + An import definition is used within a TOSCA Service Template to locate and uniquely name another + TOSCA Service Template file which has type and template definitions to be imported (included) + and referenced within another Service Template. + + 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 + #DEFN_ELEMENT_IMPORT_DEF>`__ + """ + + @primitive_field(str, required=True) + def file(self): + """ + The required symbolic name for the imported file. + + :rtype: str + """ + + @primitive_field(str) + def repository(self): + """ + The optional symbolic name of the repository definition where the imported file can be found + as a string. + + :rtype: str + """ + + @primitive_field(str) + def namespace_uri(self): + """ + The optional namespace URI to that will be applied to type definitions found within the + imported file as a string. + + :rtype: str + """ + + @primitive_field(str) + def namespace_prefix(self): + """ + The optional namespace prefix (alias) that will be used to indicate the namespace_uri when + forming a qualified name (i.e., qname) when referencing type definitions from the imported + file. + + :rtype: str + """ + +@has_fields +@dsl_specification('3.5.2', 'tosca-simple-1.0') +class ConstraintClause(ExtensiblePresentation): + """ + A constraint clause defines an operation along with one or more compatible values that can be + used to define a constraint on a property or parameter's allowed values when it is defined in a + TOSCA Service Template or one of its entities. + + 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 + #DEFN_ELEMENT_CONSTRAINTS_CLAUSE>`__ + """ + + @field_validator(constraint_clause_field_validator) + @primitive_field() + def equal(self): + """ + Constrains a property or parameter to a value equal to ('=') the value declared. + """ + + @field_validator(constraint_clause_field_validator) + @primitive_field() + def greater_than(self): + """ + Constrains a property or parameter to a value greater than ('>') the value declared. + """ + + @field_validator(constraint_clause_field_validator) + @primitive_field() + def greater_or_equal(self): + """ + Constrains a property or parameter to a value greater than or equal to ('>=') the value + declared. + """ + + @field_validator(constraint_clause_field_validator) + @primitive_field() + def less_than(self): + """ + Constrains a property or parameter to a value less than ('<') the value declared. + """ + + @field_validator(constraint_clause_field_validator) + @primitive_field() + def less_or_equal(self): + """ + Constrains a property or parameter to a value less than or equal to ('<=') the value + declared. + """ + + @field_validator(constraint_clause_in_range_validator) + @primitive_list_field() + def in_range(self): + """ + Constrains a property or parameter to a value in range of (inclusive) the two values + declared. + + Note: subclasses or templates of types that declare a property with the :code:`in_range` + constraint MAY only further restrict the range specified by the parent type. + """ + + @field_validator(constraint_clause_valid_values_validator) + @primitive_list_field() + def valid_values(self): + """ + Constrains a property or parameter to a value that is in the list of declared values. + """ + + @primitive_field(int) + def length(self): + """ + Constrains the property or parameter to a value of a given length. + """ + + @primitive_field(int) + def min_length(self): + """ + Constrains the property or parameter to a value to a minimum length. + """ + + @primitive_field(int) + def max_length(self): + """ + Constrains the property or parameter to a value to a maximum length. + """ + + @field_validator(constraint_clause_pattern_validator) + @primitive_field(str) + def pattern(self): + """ + Constrains the property or parameter to a value that is allowed by the provided regular + expression. + + Note: Future drafts of this specification will detail the use of regular expressions and + reference an appropriate standardized grammar. + """ + + @cachedmethod + def _get_type(self, context): + if hasattr(self._container, '_get_type_for_name'): + # NodeFilter or CapabilityFilter + return self._container._get_type_for_name(context, self._name) + elif hasattr(self._container, '_get_type'): + # Properties + return self._container._get_type(context) + else: + # DataType (the DataType itself is our type) + return self._container + + def _apply_to_value(self, context, presentation, value): + return apply_constraint_to_value(context, presentation, self, value) + +@short_form_field('type') +@has_fields +class EntrySchema(ExtensiblePresentation): + """ + ARIA NOTE: The specification does not properly explain this type, however it is implied by + examples. + """ + + @field_validator(data_type_validator('entry schema data type')) + @primitive_field(str, required=True) + def type(self): + """ + :rtype: str + """ + + @object_field(Description) + def description(self): + """ + :rtype: :class:`Description` + """ + + @object_list_field(ConstraintClause) + def constraints(self): + """ + :rtype: list of (str, :class:`ConstraintClause`) + """ + + @cachedmethod + def _get_type(self, context): + return get_data_type(context, self, 'type') + + @cachedmethod + def _get_constraints(self, context): + return get_property_constraints(context, self) + +@short_form_field('primary') +@has_fields +class OperationImplementation(ExtensiblePresentation): + @primitive_field(str) + def primary(self): + """ + The optional implementation artifact name (i.e., the primary script file name within a + TOSCA CSAR file). + + :rtype: str + """ + + @primitive_list_field(str) + def dependencies(self): + """ + The optional ordered list of one or more dependent or secondary implementation artifact name + which are referenced by the primary implementation artifact (e.g., a library the script + installs or a secondary script). + + :rtype: list of str + """ + +class SubstitutionMappingsRequirement(AsIsPresentation): + @property + @cachedmethod + def node_template(self): + return str(self._raw[0]) + + @property + @cachedmethod + def requirement(self): + return str(self._raw[1]) + + def _validate(self, context): + super(SubstitutionMappingsRequirement, self)._validate(context) + validate_subtitution_mappings_requirement(context, self) + +class SubstitutionMappingsCapability(AsIsPresentation): + @property + @cachedmethod + def node_template(self): + return str(self._raw[0]) + + @property + @cachedmethod + def capability(self): + return str(self._raw[1]) + + def _validate(self, context): + super(SubstitutionMappingsCapability, self)._validate(context) + validate_subtitution_mappings_capability(context, self) + +@has_fields +@dsl_specification('2.10', 'tosca-simple-1.0') +class SubstitutionMappings(ExtensiblePresentation): + @field_validator(type_validator('node type', convert_shorthand_to_full_type_name, 'node_types')) + @primitive_field(str, required=True) + def node_type(self): + """ + :rtype: str + """ + + @object_dict_field(SubstitutionMappingsRequirement) + def requirements(self): + """ + :rtype: dict of str, :class:`SubstitutionMappingsRequirement` + """ + + @object_dict_field(SubstitutionMappingsCapability) + def capabilities(self): + """ + :rtype: dict of str, :class:`SubstitutionMappingsCapability` + """ + + @cachedmethod + def _get_type(self, context): + return get_type_by_full_or_shorthand_name(context, self.node_type, 'node_types') + + def _validate(self, context): + super(SubstitutionMappings, self)._validate(context) + self._get_type(context) + + def _dump(self, context): + self._dump_content(context, ( + 'node_type', + 'requirements', + 'capabilities')) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py new file mode 100644 index 0000000..5c3206b --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.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 aria.parser.modeling import (Type, RelationshipType, PolicyType, ServiceModel, NodeTemplate, + RequirementTemplate, RelationshipTemplate, CapabilityTemplate, + GroupTemplate, PolicyTemplate, SubstitutionTemplate, + MappingTemplate, InterfaceTemplate, OperationTemplate, + ArtifactTemplate, Metadata, Parameter) + +from ..data_types import coerce_value + +def create_service_model(context): # pylint: disable=too-many-locals,too-many-branches + model = ServiceModel() + + model.description = context.presentation.get('service_template', 'description', 'value') + + metadata = context.presentation.get('service_template', 'metadata') + if metadata is not None: + substitution_template = Metadata() + substitution_template.values['template_name'] = metadata.template_name + substitution_template.values['template_author'] = metadata.template_author + substitution_template.values['template_version'] = metadata.template_version + custom = metadata.custom + if custom: + for name, v in custom.iteritems(): + substitution_template.values[name] = v + model.metadata = substitution_template + + create_types(context, + context.modeling.node_types, + context.presentation.get('service_template', 'node_types')) + create_types(context, + context.modeling.group_types, + context.presentation.get('service_template', 'group_types')) + create_types(context, + context.modeling.capability_types, + context.presentation.get('service_template', 'capability_types')) + create_types(context, + context.modeling.relationship_types, + context.presentation.get('service_template', 'relationship_types'), + create_relationship_type) + create_types(context, + context.modeling.policy_types, + context.presentation.get('service_template', 'policy_types'), + create_policy_type) + create_types(context, + context.modeling.artifact_types, + context.presentation.get('service_template', 'artifact_types')) + create_types(context, + context.modeling.interface_types, + context.presentation.get('service_template', 'interface_types')) + + topology_template = context.presentation.get('service_template', 'topology_template') + if topology_template is not None: + create_properties_from_values(model.inputs, topology_template._get_input_values(context)) + create_properties_from_values(model.outputs, topology_template._get_output_values(context)) + + node_templates = context.presentation.get('service_template', 'topology_template', + 'node_templates') + if node_templates: + for node_template_name, node_template in node_templates.iteritems(): + model.node_templates[node_template_name] = create_node_template(context, node_template) + + groups = context.presentation.get('service_template', 'topology_template', 'groups') + if groups: + for group_name, group in groups.iteritems(): + model.group_templates[group_name] = create_group_template(context, group) + + policies = context.presentation.get('service_template', 'topology_template', 'policies') + if policies: + for policy_name, policy in policies.iteritems(): + model.policy_templates[policy_name] = create_policy_template(context, policy) + + substitution_mappings = context.presentation.get('service_template', 'topology_template', + 'substitution_mappings') + if substitution_mappings is not None: + substitution_template = SubstitutionTemplate(substitution_mappings.node_type) + capabilities = substitution_mappings.capabilities + if capabilities: + for mapped_capability_name, capability in capabilities.iteritems(): + substitution_template.capability_templates[mapped_capability_name] = \ + MappingTemplate(mapped_capability_name, capability.node_template, + capability.capability) + requirements = substitution_mappings.requirements + if requirements: + for mapped_requirement_name, requirement in requirements.iteritems(): + substitution_template.requirement_templates[mapped_requirement_name] = \ + MappingTemplate(mapped_requirement_name, requirement.node_template, + requirement.requirement) + model.substitution_template = substitution_template + + return model + +def create_node_template(context, node_template): + model = NodeTemplate(name=node_template._name, type_name=node_template.type) + + if node_template.description: + model.description = node_template.description.value + + create_properties_from_values(model.properties, node_template._get_property_values(context)) + create_interface_templates(context, model.interface_templates, + node_template._get_interfaces(context)) + + artifacts = node_template._get_artifacts(context) + if artifacts: + for artifact_name, artifact in artifacts.iteritems(): + model.artifact_templates[artifact_name] = create_artifact_template(context, artifact) + + requirements = node_template._get_requirements(context) + if requirements: + for _, requirement in requirements: + model.requirement_templates.append(create_requirement_template(context, requirement)) + + capabilities = node_template._get_capabilities(context) + if capabilities: + for capability_name, capability in capabilities.iteritems(): + model.capability_templates[capability_name] = create_capability_template(context, + capability) + + create_node_filter_constraint_lambdas(context, node_template.node_filter, + model.target_node_template_constraints) + + return model + +def create_interface_template(context, interface): + the_type = interface._get_type(context) + + model = InterfaceTemplate(name=interface._name, type_name=the_type._name) + + if the_type.description: + model.description = the_type.description.value + + inputs = interface.inputs + if inputs: + for input_name, the_input in inputs.iteritems(): + model.inputs[input_name] = Parameter(the_input.value.type, the_input.value.value, + the_input.value.description) + + operations = interface.operations + if operations: + for operation_name, operation in operations.iteritems(): + model.operation_templates[operation_name] = create_operation_template(context, + operation) + + return model if model.operation_templates else None + +def create_operation_template(context, operation): # pylint: disable=unused-argument + model = OperationTemplate(name=operation._name) + + if operation.description: + model.description = operation.description.value + + implementation = operation.implementation + if implementation is not None: + model.implementation = implementation.primary + dependencies = implementation.dependencies + if dependencies is not None: + model.dependencies = dependencies + + inputs = operation.inputs + if inputs: + for input_name, the_input in inputs.iteritems(): + model.inputs[input_name] = Parameter(the_input.value.type, the_input.value.value, + the_input.value.description) + + return model + +def create_artifact_template(context, artifact): + model = ArtifactTemplate(name=artifact._name, type_name=artifact.type, + source_path=artifact.file) + + if artifact.description: + model.description = artifact.description.value + + model.target_path = artifact.deploy_path + + repository = artifact._get_repository(context) + if repository is not None: + model.repository_url = repository.url + credential = repository._get_credential(context) + if credential: + for k, v in credential.iteritems(): + model.repository_credential[k] = v + + create_properties_from_values(model.properties, artifact._get_property_values(context)) + + return model + +def create_requirement_template(context, requirement): + model = {'name': requirement._name} + + node, node_variant = requirement._get_node(context) + if node is not None: + if node_variant == 'node_type': + model['target_node_type_name'] = node._name + else: + model['target_node_template_name'] = node._name + + capability, capability_variant = requirement._get_capability(context) + if capability is not None: + if capability_variant == 'capability_type': + model['target_capability_type_name'] = capability._name + else: + model['target_capability_name'] = capability._name + + model = RequirementTemplate(**model) + + create_node_filter_constraint_lambdas(context, requirement.node_filter, + model.target_node_template_constraints) + + relationship = requirement.relationship + if relationship is not None: + model.relationship_template = create_relationship_template(context, relationship) + + return model + +def create_relationship_type(context, relationship_type): # pylint: disable=unused-argument + return RelationshipType(relationship_type._name) + +def create_policy_type(context, policy_type): # pylint: disable=unused-argument + return PolicyType(policy_type._name) + +def create_relationship_template(context, relationship): + relationship_type, relationship_type_variant = relationship._get_type(context) + if relationship_type_variant == 'relationship_type': + model = RelationshipTemplate(type_name=relationship_type._name) + else: + relationship_template = relationship_type + relationship_type = relationship_template._get_type(context) + model = RelationshipTemplate(type_name=relationship_type._name, + template_name=relationship_template._name) + if relationship_template.description: + model.description = relationship_template.description.value + + create_properties_from_assignments(model.properties, relationship.properties) + create_interface_templates(context, model.source_interface_templates, relationship.interfaces) + + return model + +def create_capability_template(context, capability): + capability_type = capability._get_type(context) + model = CapabilityTemplate(name=capability._name, type_name=capability_type._name) + + capability_definition = capability._get_definition(context) + if capability_definition.description: + model.description = capability_definition.description.value + occurrences = capability_definition.occurrences + if occurrences is not None: + model.min_occurrences = occurrences.value[0] + if occurrences.value[1] != 'UNBOUNDED': + model.max_occurrences = occurrences.value[1] + + valid_source_types = capability_definition.valid_source_types + if valid_source_types: + model.valid_source_node_type_names = valid_source_types + + create_properties_from_assignments(model.properties, capability.properties) + + return model + +def create_group_template(context, group): + group_type = group._get_type(context) + model = GroupTemplate(name=group._name, type_name=group_type._name) + + if group.description: + model.description = group.description.value + + create_properties_from_values(model.properties, group._get_property_values(context)) + create_interface_templates(context, model.interface_templates, group._get_interfaces(context)) + + members = group.members + if members: + for member in members: + model.member_node_template_names.append(member) + + return model + +def create_policy_template(context, policy): + policy_type = policy._get_type(context) + model = PolicyTemplate(name=policy._name, type_name=policy_type._name) + + if policy.description: + model.description = policy.description.value + + create_properties_from_values(model.properties, policy._get_property_values(context)) + + node_templates, groups = policy._get_targets(context) + for node_template in node_templates: + model.target_node_template_names.append(node_template._name) + for group in groups: + model.target_group_template_names.append(group._name) + + return model + +# +# Utils +# + +def create_types(context, root, types, normalize=None): + if types is None: + return + + def added_all(): + for name in types: + if root.get_descendant(name) is None: + return False + return True + + while not added_all(): + for name, the_type in types.iteritems(): + if root.get_descendant(name) is None: + parent_type = the_type._get_parent(context) + if normalize: + model = normalize(context, the_type) + else: + model = Type(the_type._name) + if the_type.description: + model.description = the_type.description.value + if parent_type is None: + root.children.append(model) + else: + container = root.get_descendant(parent_type._name) + if container is not None: + container.children.append(model) + +def create_properties_from_values(properties, source_properties): + if source_properties: + for property_name, prop in source_properties.iteritems(): + properties[property_name] = Parameter(prop.type, prop.value, prop.description) + +def create_properties_from_assignments(properties, source_properties): + if source_properties: + for property_name, prop in source_properties.iteritems(): + properties[property_name] = Parameter(prop.value.type, prop.value.value, + prop.value.description) + +def create_interface_templates(context, interfaces, source_interfaces): + if source_interfaces: + for interface_name, interface in source_interfaces.iteritems(): + interface = create_interface_template(context, interface) + if interface is not None: + interfaces[interface_name] = interface + +def create_node_filter_constraint_lambdas(context, node_filter, node_type_constraints): + if node_filter is None: + return + + properties = node_filter.properties + if properties is not None: + for property_name, constraint_clause in properties: + func = create_constraint_clause_lambda(context, node_filter, constraint_clause, + property_name, None) + if func is not None: + node_type_constraints.append(func) + + capabilities = node_filter.capabilities + if capabilities is not None: + for capability_name, capability in capabilities: + properties = capability.properties + if properties is not None: + for property_name, constraint_clause in properties: + func = create_constraint_clause_lambda(context, node_filter, constraint_clause, + property_name, capability_name) + if func is not None: + node_type_constraints.append(func) + +def create_constraint_clause_lambda(context, node_filter, constraint_clause, property_name, # pylint: disable=too-many-return-statements + capability_name): + constraint_key = constraint_clause._raw.keys()[0] + the_type = constraint_clause._get_type(context) + + def coerce_constraint(constraint, container): + constraint = coerce_value(context, node_filter, the_type, None, None, constraint, + constraint_key) if the_type is not None else constraint + if hasattr(constraint, '_evaluate'): + constraint = constraint._evaluate(context, container) + return constraint + + def get_value(node_type): + if capability_name is not None: + capability = node_type.capability_templates.get(capability_name) + prop = capability.properties.get(property_name) if capability is not None else None + return prop.value if prop is not None else None + value = node_type.properties.get(property_name) + return value.value if value is not None else None + + if constraint_key == 'equal': + def equal(node_type, container): + constraint = coerce_constraint(constraint_clause.equal, container) + value = get_value(node_type) + return value == constraint + + return equal + + elif constraint_key == 'greater_than': + def greater_than(node_type, container): + constraint = coerce_constraint(constraint_clause.greater_than, container) + value = get_value(node_type) + return value > constraint + + return greater_than + + elif constraint_key == 'greater_or_equal': + def greater_or_equal(node_type, container): + constraint = coerce_constraint(constraint_clause.greater_or_equal, container) + value = get_value(node_type) + return value >= constraint + + return greater_or_equal + + elif constraint_key == 'less_than': + def less_than(node_type, container): + constraint = coerce_constraint(constraint_clause.less_than, container) + value = get_value(node_type) + return value < constraint + + return less_than + + elif constraint_key == 'less_or_equal': + def less_or_equal(node_type, container): + constraint = coerce_constraint(constraint_clause.less_or_equal, container) + value = get_value(node_type) + return value <= constraint + + return less_or_equal + + elif constraint_key == 'in_range': + def in_range(node_type, container): + lower, upper = constraint_clause.in_range + lower, upper = coerce_constraint(lower, container), coerce_constraint(upper, container) + value = get_value(node_type) + if value < lower: + return False + if (upper != 'UNBOUNDED') and (value > upper): + return False + return True + + return in_range + + elif constraint_key == 'valid_values': + def valid_values(node_type, container): + constraint = tuple(coerce_constraint(v, container) + for v in constraint_clause.valid_values) + value = get_value(node_type) + return value in constraint + + return valid_values + + elif constraint_key == 'length': + def length(node_type, container): # pylint: disable=unused-argument + constraint = constraint_clause.length + value = get_value(node_type) + return len(value) == constraint + + return length + + elif constraint_key == 'min_length': + def min_length(node_type, container): # pylint: disable=unused-argument + constraint = constraint_clause.min_length + value = get_value(node_type) + return len(value) >= constraint + + return min_length + + elif constraint_key == 'max_length': + def max_length(node_type, container): # pylint: disable=unused-argument + constraint = constraint_clause.max_length + value = get_value(node_type) + return len(value) >= constraint + + return max_length + + elif constraint_key == 'pattern': + def pattern(node_type, container): # pylint: disable=unused-argument + constraint = constraint_clause.pattern + # Note: the TOSCA 1.0 spec does not specify the regular expression grammar, so we will + # just use Python's + value = node_type.properties.get(property_name) + return re.match(constraint, str(value)) is not None + + return pattern + + return None http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/modeling/artifacts.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/artifacts.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/artifacts.py new file mode 100644 index 0000000..f28f429 --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/artifacts.py @@ -0,0 +1,41 @@ +# 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 + +# +# NodeType, NodeTemplate +# + +def get_inherited_artifact_definitions(context, presentation, for_presentation=None): + + if hasattr(presentation, '_get_type'): + # In NodeTemplate + parent = presentation._get_type(context) + else: + # In NodeType + parent = presentation._get_parent(context) + + # Get artifact definitions from parent + artifacts = get_inherited_artifact_definitions(context, parent, for_presentation=presentation) \ + if parent is not None else OrderedDict() + + # Add/override our artifact definitions + our_artifacts = presentation.artifacts + if our_artifacts: + for artifact_name, artifact in our_artifacts.iteritems(): + artifacts[artifact_name] = artifact._clone(for_presentation) + + return artifacts http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py new file mode 100644 index 0000000..f9e293d --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py @@ -0,0 +1,177 @@ +# 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.utils import deepcopy_with_locators +from aria.parser.validation import Issue + +from .properties import (convert_property_definitions_to_values, merge_raw_property_definitions, + get_assigned_and_defined_property_values) + +# +# CapabilityType +# + +def get_inherited_valid_source_types(context, presentation): + """ + If we haven't set the :code:`valid_source_types` fields, uses that value from our parent, if + we have one (recursively). + """ + + valid_source_types = presentation.valid_source_types + + if valid_source_types is None: + parent = presentation._get_parent(context) + valid_source_types = get_inherited_valid_source_types(context, parent) \ + if parent is not None else None + + return valid_source_types + +# +# NodeType +# + +def get_inherited_capability_definitions(context, presentation, for_presentation=None): + """ + Returns our capability capability definitions added on top of those of our parent, if we have + one (recursively). + + Allows overriding all aspects of parent capability properties except data type. + """ + + # Get capability definitions from parent + parent = presentation._get_parent(context) + capability_definitions = get_inherited_capability_definitions(context, parent, + for_presentation=presentation) \ + if parent is not None \ + else OrderedDict() + + # Add/merge our capability definitions + our_capability_definitions = presentation.capabilities + if our_capability_definitions: + for capability_name, our_capability_definition in our_capability_definitions.iteritems(): + if capability_name in capability_definitions: + capability_definition = capability_definitions[capability_name] + + # Check if we changed the type + type1 = capability_definition.type + type2 = our_capability_definition.type + if type1 != type2: + context.validation.report( + 'capability definition changes type from "%s" to "%s" in "%s"' + % (type1, type2, presentation._fullname), + locator=our_capability_definition._locator, level=Issue.BETWEEN_TYPES) + + # Already cloned? + #capability_definition = capability_definition._clone(for_presentation) + #capability_definitions[capability_name] = capability_definition + else: + capability_definition = our_capability_definition._clone(for_presentation) + capability_definitions[capability_name] = capability_definition + + merge_capability_definition_from_type(context, presentation, capability_definition) + + for capability_definition in capability_definitions.itervalues(): + capability_definition._reset_method_cache() + + return capability_definitions + +# +# NodeTemplate +# + +def get_template_capabilities(context, presentation): + """ + Returns the node type's capabilities with our assignments to properties and attributes merged + in. + + Capability properties' default values, if available, will be used if we did not assign them. + + Makes sure that required properties indeed end up with a value. + """ + + capability_assignments = OrderedDict() + + the_type = presentation._get_type(context) # NodeType + capability_definitions = the_type._get_capabilities(context) if the_type is not None else None + + # Copy over capability definitions from the type (will initialize properties with default + # values) + if capability_definitions: + for capability_name, capability_definition in capability_definitions.iteritems(): + capability_assignments[capability_name] = \ + convert_capability_from_definition_to_assignment(context, capability_definition, + presentation) + + # Fill in our capability assignments + our_capability_assignments = presentation.capabilities + if our_capability_assignments: + for capability_name, our_capability_assignment in our_capability_assignments.iteritems(): + if capability_name in capability_assignments: + capability_assignment = capability_assignments[capability_name] + + # Assign properties + values = get_assigned_and_defined_property_values(context, + our_capability_assignment) + if values: + capability_assignment._raw['properties'] = values + else: + context.validation.report( + 'capability "%s" not declared at node type "%s" in "%s"' + % (capability_name, presentation.type, presentation._fullname), + locator=our_capability_assignment._locator, level=Issue.BETWEEN_TYPES) + + return capability_assignments + +# +# Utils +# + +def convert_capability_from_definition_to_assignment(context, presentation, container): + from ..assignments import CapabilityAssignment + + raw = OrderedDict() + + properties = presentation.properties + if properties is not None: + raw['properties'] = convert_property_definitions_to_values(context, properties) + + # TODO attributes + + return CapabilityAssignment(name=presentation._name, raw=raw, container=container) + +def merge_capability_definition_from_type(context, presentation, capability_definition): + raw_properties = OrderedDict() + + # Merge properties from type + the_type = capability_definition._get_type(context) + type_property_defintions = the_type._get_properties(context) + merge_raw_property_definitions(context, presentation, raw_properties, type_property_defintions, + 'properties') + + # Merge our properties + merge_raw_property_definitions(context, presentation, raw_properties, + capability_definition.properties, 'properties') + + if raw_properties: + capability_definition._raw['properties'] = raw_properties + + # Override valid_source_types + if capability_definition._raw.get('valid_source_types') is None: + valid_source_types = the_type._get_valid_source_types(context) + if valid_source_types is not None: + capability_definition._raw['valid_source_types'] = \ + deepcopy_with_locators(valid_source_types) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8ee1470e/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py new file mode 100644 index 0000000..b86e8eb --- /dev/null +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.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. + +# +# NodeTemplate, RelationshipTemplate +# + +def get_default_raw_from_copy(presentation, field_name): + """ + Used for the :code:`_get_default_raw` field hook. + """ + + copy = presentation._raw.get('copy') + if copy is not None: + templates = getattr(presentation._container, field_name) + if templates is not None: + template = templates.get(copy) + if template is not None: + return template._raw + return None