Repository: incubator-ariatosca Updated Branches: refs/heads/topology_poc [created] 7d1cfbc42
wip Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/7d1cfbc4 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/7d1cfbc4 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/7d1cfbc4 Branch: refs/heads/topology_poc Commit: 7d1cfbc425d90da09862e4b846fa66b97f364d37 Parents: b841c01 Author: max-orlov <ma...@gigaspaces.com> Authored: Thu Jul 13 16:49:15 2017 +0300 Committer: max-orlov <ma...@gigaspaces.com> Committed: Thu Jul 13 16:49:15 2017 +0300 ---------------------------------------------------------------------- aria/core.py | 7 +- aria/modeling/mixins.py | 6 - aria/modeling/service_template.py | 196 ----------------------- aria/modeling/utils.py | 11 +- aria/parser/topology/topoloy.py | 280 +++++++++++++++++++++++++++++++++ 5 files changed, 293 insertions(+), 207 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d1cfbc4/aria/core.py ---------------------------------------------------------------------- diff --git a/aria/core.py b/aria/core.py index e214b1a..54379ba 100644 --- a/aria/core.py +++ b/aria/core.py @@ -18,7 +18,7 @@ ARIA core module. """ from . import exceptions -from .parser import consumption +from .parser import consumption, topology from .parser.loading.location import UriLocation @@ -67,7 +67,7 @@ class Core(object): self.resource_storage.service_template.delete(entry_id=str(service_template.id)) def create_service(self, service_template_id, inputs, service_name=None): - + import pydevd; pydevd.settrace('localhost', suspend=False) service_template = self.model_storage.service_template.get(service_template_id) # creating an empty ConsumptionContext, initiating a threadlocal context @@ -77,7 +77,8 @@ class Core(object): # setting no autoflush for the duration of instantiation - this helps avoid dependency # constraints as they're being set up with storage_session.no_autoflush: - service = service_template.instantiate(None, self.model_storage, inputs=inputs) + service = topology.Instantiation(self.model_storage).instantiate_service( + service_template, inputs=inputs) consumption.ConsumerChain( context, http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d1cfbc4/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index 883ff4a..b2a0e0d 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -308,12 +308,6 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): ('value', self.value), ('description', self.description))) - def instantiate(self, container): - return self.__class__(name=self.name, # pylint: disable=unexpected-keyword-arg - type_name=self.type_name, - _value=self._value, - description=self.description) - def coerce_values(self, report_issues): value = self._value if value is not None: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d1cfbc4/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index 22912e2..8b26d15 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -331,49 +331,6 @@ class ServiceTemplateBase(TemplateModelMixin): ('interface_types', formatting.as_raw(self.interface_types)), ('artifact_types', formatting.as_raw(self.artifact_types)))) - def instantiate(self, container, model_storage, inputs=None): # pylint: disable=arguments-differ - from . import models - now = datetime.now() - service = models.Service(created_at=now, - updated_at=now, - description=deepcopy_with_locators(self.description), - service_template=self) - - # TODO: we want to remove this use of the context - context = ConsumptionContext.get_thread_local() - context.modeling.instance = service - - service.inputs = utils.merge_parameter_values(inputs, self.inputs, model_cls=models.Input) - # TODO: now that we have inputs, we should scan properties and inputs and evaluate functions - - for plugin_specification in self.plugin_specifications.itervalues(): - if plugin_specification.enabled: - if plugin_specification.resolve(model_storage): - plugin = plugin_specification.plugin - service.plugins[plugin.name] = plugin - else: - context = ConsumptionContext.get_thread_local() - context.validation.report('specified plugin not found: {0}'.format( - plugin_specification.name), level=validation.Issue.EXTERNAL) - - utils.instantiate_dict(self, service.meta_data, self.meta_data) - - for node_template in self.node_templates.itervalues(): - for _ in range(node_template.scaling['default_instances']): - node = node_template.instantiate(container) - service.nodes[node.name] = node - - utils.instantiate_dict(self, service.groups, self.group_templates) - utils.instantiate_dict(self, service.policies, self.policy_templates) - utils.instantiate_dict(self, service.workflows, self.workflow_templates) - - if self.substitution_template is not None: - service.substitution = self.substitution_template.instantiate(container) - - utils.instantiate_dict(self, service.outputs, self.outputs) - - return service - def validate(self): utils.validate_dict_values(self.meta_data) utils.validate_dict_values(self.node_templates) @@ -620,29 +577,6 @@ class NodeTemplateBase(TemplateModelMixin): ('capability_templates', formatting.as_raw_list(self.capability_templates)), ('requirement_templates', formatting.as_raw_list(self.requirement_templates)))) - def instantiate(self, container): - from . import models - node = models.Node(name=self._next_name, - type=self.type, - description=deepcopy_with_locators(self.description), - state=models.Node.INITIAL, - node_template=self) - utils.instantiate_dict(node, node.properties, self.properties) - utils.instantiate_dict(node, node.attributes, self.attributes) - utils.instantiate_dict(node, node.interfaces, self.interface_templates) - utils.instantiate_dict(node, node.artifacts, self.artifact_templates) - utils.instantiate_dict(node, node.capabilities, self.capability_templates) - - # Default attributes - if ('tosca_name' in node.attributes) \ - and (node.attributes['tosca_name'].type_name == 'string'): - node.attributes['tosca_name'].value = self.name - if 'tosca_id' in node.attributes \ - and (node.attributes['tosca_id'].type_name == 'string'): - node.attributes['tosca_id'].value = node.name - - return node - def validate(self): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.attributes) @@ -871,19 +805,6 @@ class GroupTemplateBase(TemplateModelMixin): ('properties', formatting.as_raw_dict(self.properties)), ('interface_templates', formatting.as_raw_list(self.interface_templates)))) - def instantiate(self, container): - from . import models - group = models.Group(name=self.name, - type=self.type, - description=deepcopy_with_locators(self.description), - group_template=self) - utils.instantiate_dict(self, group.properties, self.properties) - utils.instantiate_dict(self, group.interfaces, self.interface_templates) - if self.node_templates: - for node_template in self.node_templates: - group.nodes += node_template.nodes - return group - def validate(self): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.interface_templates) @@ -1017,21 +938,6 @@ class PolicyTemplateBase(TemplateModelMixin): ('type_name', self.type.name), ('properties', formatting.as_raw_dict(self.properties)))) - def instantiate(self, container): - from . import models - policy = models.Policy(name=self.name, - type=self.type, - description=deepcopy_with_locators(self.description), - policy_template=self) - utils.instantiate_dict(self, policy.properties, self.properties) - if self.node_templates: - for node_template in self.node_templates: - policy.nodes += node_template.nodes - if self.group_templates: - for group_template in self.group_templates: - policy.groups += group_template.groups - return policy - def validate(self): utils.validate_dict_values(self.properties) @@ -1129,13 +1035,6 @@ class SubstitutionTemplateBase(TemplateModelMixin): ('node_type_name', self.node_type.name), ('mappings', formatting.as_raw_dict(self.mappings)))) - def instantiate(self, container): - from . import models - substitution = models.Substitution(node_type=self.node_type, - substitution_template=self) - utils.instantiate_dict(container, substitution.mappings, self.mappings) - return substitution - def validate(self): utils.validate_dict_values(self.mappings) @@ -1231,34 +1130,6 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin): def coerce_values(self, report_issues): pass - def instantiate(self, container): - from . import models - context = ConsumptionContext.get_thread_local() - if self.capability_template is not None: - node_template = self.capability_template.node_template - else: - node_template = self.requirement_template.node_template - nodes = node_template.nodes - if len(nodes) == 0: - context.validation.report( - 'mapping "{0}" refers to node template "{1}" but there are no ' - 'node instances'.format(self.mapped_name, self.node_template.name), - level=validation.Issue.BETWEEN_INSTANCES) - return None - # The TOSCA spec does not provide a way to choose the node, - # so we will just pick the first one - node = nodes[0] - capability = None - if self.capability_template: - for a_capability in node.capabilities.itervalues(): - if a_capability.capability_template.name == self.capability_template.name: - capability = a_capability - return models.SubstitutionMapping(name=self.name, - capability=capability, - requirement_template=self.requirement_template, - node=node) - - def validate(self): context = ConsumptionContext.get_thread_local() if (self.capability_template is None) and (self.requirement_template is None): @@ -1601,15 +1472,6 @@ class RelationshipTemplateBase(TemplateModelMixin): ('properties', formatting.as_raw_dict(self.properties)), ('interface_templates', formatting.as_raw_list(self.interface_templates)))) - def instantiate(self, container): - from . import models - relationship_model = models.Relationship(name=self.name, - type=self.type, - relationship_template=self) - utils.instantiate_dict(container, relationship_model.properties, self.properties) - utils.instantiate_dict(container, relationship_model.interfaces, self.interface_templates) - return relationship_model - def validate(self): # TODO: either type or name must be set utils.validate_dict_values(self.properties) @@ -1768,17 +1630,6 @@ class CapabilityTemplateBase(TemplateModelMixin): ('valid_source_node_types', [v.name for v in self.valid_source_node_types]), ('properties', formatting.as_raw_dict(self.properties)))) - def instantiate(self, container): - from . import models - capability = models.Capability(name=self.name, - type=self.type, - min_occurrences=self.min_occurrences, - max_occurrences=self.max_occurrences, - occurrences=0, - capability_template=self) - utils.instantiate_dict(container, capability.properties, self.properties) - return capability - def validate(self): utils.validate_dict_values(self.properties) @@ -1933,16 +1784,6 @@ class InterfaceTemplateBase(TemplateModelMixin): # TODO fix self.properties reference ('operation_templates', formatting.as_raw_list(self.operation_templates)))) - def instantiate(self, container): - from . import models - interface = models.Interface(name=self.name, - type=self.type, - description=deepcopy_with_locators(self.description), - interface_template=self) - utils.instantiate_dict(container, interface.inputs, self.inputs) - utils.instantiate_dict(container, interface.operations, self.operation_templates) - return interface - def validate(self): utils.validate_dict_values(self.inputs) utils.validate_dict_values(self.operation_templates) @@ -2119,30 +1960,6 @@ class OperationTemplateBase(TemplateModelMixin): ('dependencies', self.dependencies), ('inputs', formatting.as_raw_dict(self.inputs)))) - def instantiate(self, container): - from . import models - - plugin = self.plugin_specification.plugin \ - if (self.plugin_specification is not None) and self.plugin_specification.enabled \ - else None - - operation = models.Operation(name=self.name, - description=deepcopy_with_locators(self.description), - relationship_edge=self.relationship_edge, - implementation=self.implementation, - dependencies=self.dependencies, - executor=self.executor, - plugin=plugin, - function=self.function, - max_attempts=self.max_attempts, - retry_interval=self.retry_interval, - operation_template=self) - - utils.instantiate_dict(container, operation.inputs, self.inputs) - utils.instantiate_dict(container, operation.configurations, self.configurations) - - return operation - def validate(self): utils.validate_dict_values(self.inputs) utils.validate_dict_values(self.configurations) @@ -2290,19 +2107,6 @@ class ArtifactTemplateBase(TemplateModelMixin): ('repository_credential', formatting.as_agnostic(self.repository_credential)), ('properties', formatting.as_raw_dict(self.properties)))) - def instantiate(self, container): - from . import models - artifact = models.Artifact(name=self.name, - type=self.type, - description=deepcopy_with_locators(self.description), - source_path=self.source_path, - target_path=self.target_path, - repository_url=self.repository_url, - repository_credential=self.repository_credential, - artifact_template=self) - utils.instantiate_dict(container, artifact.properties, self.properties) - return artifact - def validate(self): utils.validate_dict_values(self.properties) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d1cfbc4/aria/modeling/utils.py ---------------------------------------------------------------------- diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py index e0fd11b..9ba2cfa 100644 --- a/aria/modeling/utils.py +++ b/aria/modeling/utils.py @@ -64,7 +64,7 @@ class NodeTemplateContainerHolder(object): return self.container.service_template -def merge_parameter_values(parameter_values, declared_parameters, model_cls): +def merge_parameter_values(parameter_values, declared_parameters): """ Merges parameter values according to those declared by a type. @@ -94,6 +94,7 @@ def merge_parameter_values(parameter_values, declared_parameters, model_cls): string_list_as_string(declared_parameters.keys()))) parameters = OrderedDict() + model_cls = get_class_from_relationship(declared_parameters) missing_names = [] wrong_type_values = OrderedDict() @@ -121,7 +122,7 @@ def merge_parameter_values(parameter_values, declared_parameters, model_cls): value=value) elif declared_parameter.value is not None: # Copy default value from declaration - parameters[declared_parameter_name] = declared_parameter.instantiate(None) + parameters[declared_parameter_name] = model_cls(**declared_parameter.as_raw) else: # Required value has not been provided missing_names.append(declared_parameter_name) @@ -241,3 +242,9 @@ def fix_doc(cls): cls.__doc__ = cls.__bases__[-1].__doc__ return cls + + +def get_class_from_relationship(property): + class_ = property._sa_adapter.owner_state.class_ + prop_name = property._sa_adapter.attr.key + return getattr(class_, prop_name).property.mapper.class_ http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d1cfbc4/aria/parser/topology/topoloy.py ---------------------------------------------------------------------- diff --git a/aria/parser/topology/topoloy.py b/aria/parser/topology/topoloy.py new file mode 100644 index 0000000..943d25a --- /dev/null +++ b/aria/parser/topology/topoloy.py @@ -0,0 +1,280 @@ +# 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 namedtuple +from datetime import datetime +from functools import partial + +from ... modeling import ( + utils, + models +) +from .. import ( + reading, + consumption, + validation, +) + + + + + +class Instantiation(object): + + def __init__(self, model_storage): + self._model_storage = model_storage + self._initiators = { + 'meta_data': self._instantiate_metadata, + 'node_template': self._instantiate_node, + 'substitution_template': self._instantiate_substitution, + 'workflow_templates': self._instantiate_operation, + 'operation_templates': self._instantiate_operation, + 'interface_templates': self._instantiate_interface, + 'artifact_templates': self._instantiate_artifact, + 'capability_templates': self._instantiate_capability, + 'group_templates': self._instantiate_group, + 'policy_templates': self._instantiate_policy, + 'relationship_template': self._instantiate_relationship, + + # Parameter-Based instantiations + 'inputs': partial(self._instantiate_parameter, model_cls=models.Input), + 'outputs': partial(self._instantiate_parameter, model_cls=models.Output), + 'properties': partial(self._instantiate_parameter, model_cls=models.Property), + 'attributes': partial(self._instantiate_parameter, model_cls=models.Attribute), + 'configurations': partial(self._instantiate_parameter, model_cls=models.Configuration), + } + + def instantiate_service(self, service_template, inputs=None): + now = datetime.now() + service = models.Service( + created_at=now, + updated_at=now, + description=reading.deepcopy_with_locators(service_template.description), + service_template=service_template + ) + + # TODO: we want to remove this use of the context + context = consumption.ConsumptionContext.get_thread_local() + context.modeling.instance = service + + service.inputs = utils.merge_parameter_values(inputs, service_template.inputs) + # TODO: now that we have inputs, we should scan properties and inputs and evaluate functions + + for plugin_specification in service_template.plugin_specifications.itervalues(): + if plugin_specification.enabled: + if plugin_specification.resolve(self._model_storage): + plugin = plugin_specification.plugin + service.plugins[plugin.name] = plugin + else: + context = consumption.ConsumptionContext.get_thread_local() + context.validation.report('specified plugin not found: {0}'.format( + plugin_specification.name), level=validation.Issue.EXTERNAL) + + service.meta_data = self._instantiate(service_template.meta_data) + + for node_template in service_template.node_templates.itervalues(): + for _ in range(node_template.scaling['default_instances']): + node = self._instantiate(node_template) + service.nodes[node.name] = node + + service.groups = self._instantiate(service_template.group_templates) + service.policies = self._instantiate(service_template.policy_templates) + service.workflows = self._instantiate(service_template.workflow_templates) + + if service_template.substitution_template is not None: + service.substitution = self._instantiate(service_template.substitution_template) + service.outputs = self._instantiate(service_template.outputs) + + return service + + def _instantiate(self, source_template): + if isinstance(source_template, (dict, list)): + initiator = self._initiators[source_template._sa_adapter.attr.key] + if isinstance(source_template, dict): + dict_ = utils.OrderedDict() + for name, value in source_template.iteritems(): + value = initiator(value) + if value is not None: + dict_[name] = value + return dict_ + elif isinstance(source_template, list): + list_ = [] + for value in source_template: + value = initiator(value) + if value is not None: + list_.append(value) + return list_ + else: + return self._initiators[source_template.__tablename__](source_template) + + @staticmethod + def _instantiate_artifact(artifact_template): + artifact = models.Artifact( + name=artifact_template.name, + type=artifact_template.type, + description=reading.deepcopy_with_locators(artifact_template.description), + source_path=artifact_template.source_path, + target_path=artifact_template.target_path, + repository_url=artifact_template.repository_url, + repository_credential=artifact_template.repository_credential, + artifact_template=artifact_template) + return artifact + + @staticmethod + def _instantiate_capability(capability_template): + capability = models.Capability(name=capability_template.name, + type=capability_template.type, + min_occurrences=capability_template.min_occurrences, + max_occurrences=capability_template.max_occurrences, + occurrences=0, + capability_template=capability_template) + return capability + + def _instantiate_group(self, group_template): + group = models.Group(name=group_template.name, + type=group_template.type, + description=reading.deepcopy_with_locators(group_template.description), + group_template=group_template) + group.properties = self._instantiate(group_template.properties) + group.interfaces = self._instantiate(group_template.interface_templates) + if group_template.node_templates: + for node_template in group_template.node_templates: + group.nodes += node_template.nodes + return group + + def _instantiate_interface(self, interface_template): + interface = models.Interface( + name=interface_template.name, + type=interface_template.type, + description=reading.deepcopy_with_locators(interface_template.description), + interface_template=interface_template) + interface.inputs = self._instantiate(interface_template.inputs) + interface.operations = self._instantiate(interface_template.operation_templates) + return interface + + def _instantiate_node(self, node_template): + node = models.Node( + name=node_template._next_name, + type=node_template.type, + description=reading.deepcopy_with_locators(node_template.description), + state=models.Node.INITIAL, + node_template=node_template + ) + node.properties = self._instantiate(node_template.properties) + node.attributes = self._instantiate(node_template.attributes) + node.interfaces = self._instantiate(node_template.interface_templates) + node.artifacts = self._instantiate(node_template.artifact_templates) + node.capabilities = self._instantiate(node_template.capability_templates) + + # Default attributes + if ('tosca_name' in node.attributes) \ + and (node.attributes['tosca_name'].type_name == 'string'): + node.attributes['tosca_name'].value = node_template.name + if 'tosca_id' in node.attributes \ + and (node.attributes['tosca_id'].type_name == 'string'): + node.attributes['tosca_id'].value = node.name + + return node + + def _instantiate_policy(self, policy_template): + policy = models.Policy( + name=policy_template.name, + type=policy_template.type, + description=reading.deepcopy_with_locators(policy_template.description), + policy_template=policy_template) + policy.properties = self._instantiate(policy_template.properties) + if policy_template.node_templates: + for node_template in policy_template.node_templates: + policy.nodes += node_template.nodes + if policy_template.group_templates: + for group_template in policy_template.group_templates: + policy.groups += group_template.groups + return policy + + @staticmethod + def _instantiate_parameter(parameter_template, model_cls): + return model_cls(**parameter_template.as_raw) + + @staticmethod + def _instantiate_substitution(substitution_template): + substitution = models.Substitution(node_type=substitution_template.node_type, + substitution_template=substitution_template) + return substitution + + @staticmethod + def _instantiate_metadata(metadata_template): + return models.Metadata(name=metadata_template.name, value=metadata_template.value) + + @staticmethod + def _instantiate_substitution_mapping(substitution_mapping): + context = consumption.ConsumptionContext.get_thread_local() + if substitution_mapping.capability_template is not None: + node_template = substitution_mapping.capability_template.node_template + else: + node_template = substitution_mapping.requirement_template.node_template + nodes = node_template.nodes + if len(nodes) == 0: + context.validation.report( + 'mapping "{0}" refers to node template "{1}" but there are no node instances'. + format(substitution_mapping.mapped_name, + substitution_mapping.node_template.name), + level=validation.Issue.BETWEEN_INSTANCES) + return None + # The TOSCA spec does not provide a way to choose the node, + # so we will just pick the first one + node = nodes[0] + capability = None + if substitution_mapping.capability_template: + for a_capability in node.capabilities.itervalues(): + if a_capability.capability_template.name == \ + substitution_mapping.capability_template.name: + capability = a_capability + return models.SubstitutionMapping( + name=substitution_mapping.name, + capability=capability, + requirement_template=substitution_mapping.requirement_template, + node=node) + + def _instantiate_relationship(self, relationship_template): + relationship_model = models.Relationship(name=relationship_template.name, + type=relationship_template.type, + relationship_template=relationship_template) + relationship_model.properties = self._instantiate(relationship_template.properties) + relationship_model.interfaces = self._instantiate(relationship_template.interface_templates) + return relationship_model + + def _instantiate_operation(self, operation_template): + plugin = None + if (operation_template.plugin_specification is not None and + operation_template.plugin_specification.enabled): + plugin = operation_template.plugin_specification.plugin + + operation = models.Operation( + name=operation_template.name, + description=reading.deepcopy_with_locators(operation_template.description), + relationship_edge=operation_template.relationship_edge, + implementation=operation_template.implementation, + dependencies=operation_template.dependencies, + executor=operation_template.executor, + plugin=plugin, + function=operation_template.function, + max_attempts=operation_template.max_attempts, + retry_interval=operation_template.retry_interval, + operation_template=operation_template) + + operation.inputs = self._instantiate(operation_template.inputs) + operation.configurations = self._instantiate(operation_template.configurations) + + return operation