Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-122-Create-central-instantiation-module [created] c7e28595d
initial commit Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/c7e28595 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/c7e28595 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/c7e28595 Branch: refs/heads/ARIA-122-Create-central-instantiation-module Commit: c7e28595d1e6dcb10aaac1169f0a53a0218b71ee Parents: c0d76ad Author: max-orlov <[email protected]> Authored: Thu Mar 9 15:22:23 2017 +0200 Committer: max-orlov <[email protected]> Committed: Thu Mar 9 15:22:23 2017 +0200 ---------------------------------------------------------------------- aria/orchestrator/instantiation.py | 52 ++++++++++++++++ aria/storage/modeling/structure.py | 4 +- aria/storage/modeling/template_elements.py | 48 ++++++++++----- tests/conftest.py | 3 +- tests/orchestrator/test_instantiation.py | 79 +++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c7e28595/aria/orchestrator/instantiation.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/instantiation.py b/aria/orchestrator/instantiation.py new file mode 100644 index 0000000..a3b36bb --- /dev/null +++ b/aria/orchestrator/instantiation.py @@ -0,0 +1,52 @@ +# 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.storage.modeling import utils +from aria.utils.collections import deepcopy_with_locators +from aria.storage.modeling import model + + [email protected](instance_cls=model.ServiceInstance) +def instantiate_service_instance(self, instance_cls, context, container): + service_instance = instance_cls + context.modeling.instance = service_instance + + service_instance.description = deepcopy_with_locators(self.description) + + if self.metadata is not None: + service_instance.metadata = self.metadata.instantiate(context, container) + + for node_template in self.node_templates.itervalues(): + for _ in range(node_template.default_instances): + node = node_template.instantiate(context, container) + service_instance.nodes[node.id] = node + + utils.instantiate_dict(context, self, service_instance.groups, self.group_templates) + utils.instantiate_dict(context, self, service_instance.policies, self.policy_templates) + utils.instantiate_dict(context, self, service_instance.operations, self.operation_templates) + + if self.substitution_template is not None: + service_instance.substitution = self.substitution_template.instantiate(context, + container) + + utils.instantiate_dict(context, self, service_instance.inputs, self.inputs) + utils.instantiate_dict(context, self, service_instance.outputs, self.outputs) + + for name, the_input in context.modeling.inputs.iteritems(): + if name not in service_instance.inputs: + context.validation.report('input "%s" is not supported' % name) + else: + service_instance.inputs[name].value = the_input + + return service_instance \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c7e28595/aria/storage/modeling/structure.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/structure.py b/aria/storage/modeling/structure.py index eacdb44..4ee6951 100644 --- a/aria/storage/modeling/structure.py +++ b/aria/storage/modeling/structure.py @@ -88,8 +88,8 @@ class ModelElementBase(ElementBase): All model elements can be instantiated into :class:`ServiceInstance` elements. """ - - def instantiate(self, context, container): + @classmethod + def instantiate(cls, *args, **kwargs): raise NotImplementedError http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c7e28595/aria/storage/modeling/template_elements.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/template_elements.py b/aria/storage/modeling/template_elements.py index 4212b15..77d54a7 100644 --- a/aria/storage/modeling/template_elements.py +++ b/aria/storage/modeling/template_elements.py @@ -13,7 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from copy import deepcopy -from types import FunctionType +from functools import partial +from types import FunctionType, MethodType from sqlalchemy import ( Column, @@ -41,7 +42,26 @@ from . import ( # region Element templates -class ServiceTemplateBase(structure.ModelMixin): +class TemplateBase(structure.ModelMixin): + + @classmethod + def instantiates(cls, func=None, instance_cls=None, override=False): + if not override: + try: + cls.instantiate() + except NotImplementedError: + pass + else: + # TODO: raise proper exception + raise BaseException("instantiation method was already registered for {0}" + .format(cls.__name__)) + if func is None: + return partial(cls.instantiates, instance_cls=instance_cls, override=override) + + cls.instantiate = MethodType(partial(func, instance_cls=instance_cls), None, cls) + + +class ServiceTemplateBase(TemplateBase): __tablename__ = 'service_template' @@ -172,7 +192,7 @@ class ServiceTemplateBase(structure.ModelMixin): utils.dump_dict_values(context, self.operation_templates, 'Operation templates') -class InterfaceTemplateBase(structure.ModelMixin): +class InterfaceTemplateBase(TemplateBase): __tablename__ = 'interface_template' __private_fields__ = ['node_template_fk', @@ -256,7 +276,7 @@ class InterfaceTemplateBase(structure.ModelMixin): utils.dump_dict_values(context, self.operation_templates, 'Operation templates') -class OperationTemplateBase(structure.ModelMixin): +class OperationTemplateBase(TemplateBase): __tablename__ = 'operation_template' __private_fields__ = ['service_template_fk', @@ -351,7 +371,7 @@ class OperationTemplateBase(structure.ModelMixin): dump_parameters(context, self.inputs, 'Inputs') -class ArtifactTemplateBase(structure.ModelMixin): +class ArtifactTemplateBase(TemplateBase): """ A file associated with a :class:`NodeTemplate`. @@ -448,7 +468,7 @@ class ArtifactTemplateBase(structure.ModelMixin): dump_parameters(context, self.properties) -class PolicyTemplateBase(structure.ModelMixin): +class PolicyTemplateBase(TemplateBase): """ Policies can be applied to zero or more :class:`NodeTemplate` or :class:`GroupTemplate` instances. @@ -550,7 +570,7 @@ class PolicyTemplateBase(structure.ModelMixin): (str(context.style.node(v)) for v in self.target_group_template_names))) -class GroupPolicyTemplateBase(structure.ModelMixin): +class GroupPolicyTemplateBase(TemplateBase): """ Policies applied to groups. @@ -624,7 +644,7 @@ class GroupPolicyTemplateBase(structure.ModelMixin): utils.dump_dict_values(context, self.triggers, 'Triggers') -class GroupPolicyTriggerTemplateBase(structure.ModelMixin): +class GroupPolicyTriggerTemplateBase(TemplateBase): """ Triggers for :class:`GroupPolicyTemplate`. @@ -697,7 +717,7 @@ class GroupPolicyTriggerTemplateBase(structure.ModelMixin): dump_parameters(context, self.properties) -class MappingTemplateBase(structure.ModelMixin): +class MappingTemplateBase(TemplateBase): """ Used by :class:`SubstitutionTemplate` to map a capability or a requirement to a node. @@ -744,7 +764,7 @@ class MappingTemplateBase(structure.ModelMixin): context.style.node(self.name))) -class SubstitutionTemplateBase(structure.ModelMixin): +class SubstitutionTemplateBase(TemplateBase): """ Used to substitute a single node for the entire deployment. @@ -815,7 +835,7 @@ class SubstitutionTemplateBase(structure.ModelMixin): # region Node templates -class NodeTemplateBase(structure.ModelMixin): +class NodeTemplateBase(TemplateBase): __tablename__ = 'node_template' __private_fields__ = ['service_template_fk', @@ -939,7 +959,7 @@ class NodeTemplateBase(structure.ModelMixin): utils.dump_list_values(context, self.requirement_templates, 'Requirement templates') -class GroupTemplateBase(structure.ModelMixin): +class GroupTemplateBase(TemplateBase): """ A template for creating zero or more :class:`Group` instances. @@ -1048,7 +1068,7 @@ class GroupTemplateBase(structure.ModelMixin): # region Relationship templates -class RequirementTemplateBase(structure.ModelMixin): +class RequirementTemplateBase(TemplateBase): """ A requirement for a :class:`NodeTemplate`. During instantiation will be matched with a capability of another @@ -1214,7 +1234,7 @@ class RequirementTemplateBase(structure.ModelMixin): self.relationship_template.dump(context) -class CapabilityTemplateBase(structure.ModelMixin): +class CapabilityTemplateBase(TemplateBase): """ A capability of a :class:`NodeTemplate`. Nodes expose zero or more capabilities that can be matched with :class:`Requirement` instances of other nodes. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c7e28595/tests/conftest.py ---------------------------------------------------------------------- diff --git a/tests/conftest.py b/tests/conftest.py index c501eeb..9338bd6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,7 +22,8 @@ import aria @pytest.fixture(scope='session', autouse=True) def install_aria_extensions(): - aria.install_aria_extensions() + pass + # aria.install_aria_extensions() @pytest.fixture(autouse=True) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c7e28595/tests/orchestrator/test_instantiation.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/test_instantiation.py b/tests/orchestrator/test_instantiation.py new file mode 100644 index 0000000..e159e04 --- /dev/null +++ b/tests/orchestrator/test_instantiation.py @@ -0,0 +1,79 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from aria.storage.modeling import model_base, structure + + [email protected](autouse=True) +def cleanup_instantiator(request): + instantiator = MockTemplate.instantiate + + def clear_instantiator(): + MockTemplate.instantiate = instantiator + request.addfinalizer(clear_instantiator) + + +class MockTemplate(model_base.template_elements.TemplateBase): + def __init__(self, template_name): + self.template_name = template_name + + +class MockInstnace(structure.ModelMixin): + def __init__(self, instance_name): + self.instance_name = instance_name + + +def test_base_instantiation(): + name = 'my_name' + + @MockTemplate.instantiates(instance_cls=MockInstnace) + def initiator(self, instance_cls): + return instance_cls(self.template_name) + + mock_template = MockTemplate(name) + mock_instance = mock_template.instantiate() + + assert mock_instance.instance_name == mock_template.template_name == name + + +def test_reinstantiate(): + + name = 'my_name' + + @MockTemplate.instantiates(instance_cls=MockInstnace) + def initiator(self, instance_cls): + return instance_cls(self.template_name) + + mock_template = MockTemplate(name) + mock_instance = mock_template.instantiate() + assert mock_instance.instance_name == mock_template.template_name == name + + def new_initiator(self, instance_cls): + return instance_cls('new_{0}'.format(self.template_name)) + + with pytest.raises(BaseException): + MockTemplate.instantiates(func=new_initiator, instance_cls=MockInstnace) + + mock_template = MockTemplate(name) + mock_instance = mock_template.instantiate() + assert mock_instance.instance_name == mock_template.template_name == name + + MockTemplate.instantiates(func=new_initiator, instance_cls=MockInstnace, override=True) + mock_template = MockTemplate(name) + mock_instance = mock_template.instantiate() + assert mock_template.template_name == name + assert mock_instance.instance_name == 'new_{0}'.format(name)
