ariatosca-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mxm...@apache.org
Subject [3/5] incubator-ariatosca git commit: wip
Date Sun, 29 Jan 2017 09:09:47 GMT
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/storage/modeling/template_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/template_elements.py b/aria/storage/modeling/template_elements.py
new file mode 100644
index 0000000..becd45b
--- /dev/null
+++ b/aria/storage/modeling/template_elements.py
@@ -0,0 +1,1352 @@
+# 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 copy import deepcopy
+from types import FunctionType
+
+from sqlalchemy import (
+    Column,
+    Text,
+    Integer,
+    DateTime,
+    Boolean,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from aria.parser import validation
+from aria.utils import collections, formatting, console
+
+from . import (
+    utils,
+    instance_elements,
+    structure,
+    type as aria_type
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+# region Element templates
+
+
+class ServiceTemplateBase(structure.ModelMixin):
+
+    __tablename__ = 'service_template'
+
+    description = Column(Text)
+    metadata = Column(Text)
+
+    # region orchestrator required columns
+
+    created_at = Column(DateTime, nullable=False, index=True)
+    main_file_name = Column(Text)
+    plan = Column(aria_type.Dict)
+    updated_at = Column(DateTime)
+
+    # endregion
+
+    # region foreign keys
+    @declared_attr
+    def substitution_template_fk(cls):
+        return cls.foreign_key('substitution_template', nullable=True)
+
+    # endregion
+
+    # region one-to-one relationships
+    @declared_attr
+    def substitution_template(cls):
+        return cls.one_to_one_relationship('substitution_template')
+    # endregion
+
+    # # region one-to-many relationships
+    @declared_attr
+    def node_templates(cls):
+        return cls.one_to_many_relationship('node_template')
+
+    @declared_attr
+    def group_templates(cls):
+        return cls.one_to_many_relationship('group_template')
+
+    @declared_attr
+    def policy_templates(cls):
+        return cls.one_to_many_relationship('policy_template')
+
+    @declared_attr
+    def operation_templates(cls):
+        """
+        Custom workflows
+        :return:
+        """
+        return cls.one_to_many_relationship('operation_template')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    @declared_attr
+    def outputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='outputs')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('description', self.description),
+            ('metadata', formatting.as_raw(self.metadata)),
+            ('node_templates', formatting.as_raw_list(self.node_templates)),
+            ('group_templates', formatting.as_raw_list(self.group_templates)),
+            ('policy_templates', formatting.as_raw_list(self.policy_templates)),
+            ('substitution_template', formatting.as_raw(self.substitution_template)),
+            ('inputs', formatting.as_raw_dict(self.inputs)),
+            ('outputs', formatting.as_raw_dict(self.outputs)),
+            ('operation_templates', formatting.as_raw_list(self.operation_templates))))
+
+    def instantiate(self, context, container):
+        service_instance = instance_elements.ServiceInstanceBase()
+        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
+
+    def validate(self, context):
+        if self.metadata is not None:
+            self.metadata.validate(context)
+        utils.validate_dict_values(context, self.node_templates)
+        utils.validate_dict_values(context, self.group_templates)
+        utils.validate_dict_values(context, self.policy_templates)
+        if self.substitution_template is not None:
+            self.substitution_template.validate(context)
+        utils.validate_dict_values(context, self.inputs)
+        utils.validate_dict_values(context, self.outputs)
+        utils.validate_dict_values(context, self.operation_templates)
+
+    def coerce_values(self, context, container, report_issues):
+        if self.metadata is not None:
+            self.metadata.coerce_values(context, container, report_issues)
+        utils.coerce_dict_values(context, container, self.node_templates, report_issues)
+        utils.coerce_dict_values(context, container, self.group_templates, report_issues)
+        utils.coerce_dict_values(context, container, self.policy_templates, report_issues)
+        if self.substitution_template is not None:
+            self.substitution_template.coerce_values(context, container, report_issues)
+        utils.coerce_dict_values(context, container, self.inputs, report_issues)
+        utils.coerce_dict_values(context, container, self.outputs, report_issues)
+        utils.coerce_dict_values(context, container, self.operation_templates, report_issues)
+
+    def dump(self, context):
+        if self.description is not None:
+            console.puts(context.style.meta(self.description))
+        if self.metadata is not None:
+            self.metadata.dump(context)
+        for node_template in self.node_templates.itervalues():
+            node_template.dump(context)
+        for group_template in self.group_templates.itervalues():
+            group_template.dump(context)
+        for policy_template in self.policy_templates.itervalues():
+            policy_template.dump(context)
+        if self.substitution_template is not None:
+            self.substitution_template.dump(context)
+        dump_parameters(context, self.inputs, 'Inputs')
+        dump_parameters(context, self.outputs, 'Outputs')
+        utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+
+
+class InterfaceTemplateBase(structure.ModelMixin):
+    __tablename__ = 'interface_template'
+    # region foreign keys
+
+    @declared_attr
+    def node_template_fk(cls):
+        return cls.foreign_key('node_template', nullable=True)
+
+    @declared_attr
+    def group_template_fk(cls):
+        return cls.foreign_key('group_template', nullable=True)
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+
+    # region one-to-many relationships
+    @declared_attr
+    def operation_templates(cls):
+        return cls.one_to_many_relationship('operation_template')
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('inputs', formatting.as_raw_dict(self.properties)),  # pylint: disable=no-member
+            # TODO fix self.properties reference
+            ('operation_templates', formatting.as_raw_list(self.operation_templates))))
+
+    def instantiate(self, context, container):
+        interface = instance_elements.InterfaceBase(self.name, self.type_name)
+        interface.description = deepcopy_with_locators(self.description)
+        utils.instantiate_dict(context, container, interface.inputs, self.inputs)
+        utils.instantiate_dict(context, container, interface.operations, self.operation_templates)
+        return interface
+
+    def validate(self, context):
+        if self.type_name:
+            if context.modeling.interface_types.get_descendant(self.type_name) is None:
+                context.validation.report('interface "%s" has an unknown type: %s'
+                                          % (self.name, formatting.safe_repr(self.type_name)),
+                                          level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.inputs)
+        utils.validate_dict_values(context, self.operation_templates)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.inputs, report_issues)
+        utils.coerce_dict_values(context, container, self.operation_templates, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Interface type: %s' % context.style.type(self.type_name))
+            dump_parameters(context, self.inputs, 'Inputs')
+            utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+
+
+class OperationTemplateBase(structure.ModelMixin):
+    __tablename__ = 'operation_template'
+
+    # region foreign keys
+
+    @declared_attr
+    def service_template_fk(cls):
+        return cls.foreign_key('service_template', nullable=True)
+
+    @declared_attr
+    def interface_template_fk(cls):
+        return cls.foreign_key('interface_template', nullable=True)
+
+    # endregion
+
+    description = Column(Text)
+    implementation = Column(Text)
+    dependencies = Column(aria_type.StrictList(item_cls=basestring))
+    executor = Column(Text)
+    max_retries = Column(Integer)
+    retry_interval = Column(Integer)
+
+    # region orchestrator required columns
+    plugin = Column(Text)
+    operation = Column(Boolean)
+
+    @declared_attr
+    def service_template(cls):
+        return cls.many_to_one_relationship('service_template')
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('implementation', self.implementation),
+            ('dependencies', self.dependencies),
+            ('executor', self.executor),
+            ('max_retries', self.max_retries),
+            ('retry_interval', self.retry_interval),
+            ('inputs', formatting.as_raw_dict(self.inputs))))
+
+    def instantiate(self, context, container):
+        operation = instance_elements.OperationBase(self.name)
+        operation.description = deepcopy_with_locators(self.description)
+        operation.implementation = self.implementation
+        operation.dependencies = self.dependencies
+        operation.executor = self.executor
+        operation.max_retries = self.max_retries
+        operation.retry_interval = self.retry_interval
+        utils.instantiate_dict(context, container, operation.inputs, self.inputs)
+        return operation
+
+    def validate(self, context):
+        utils.validate_dict_values(context, self.inputs)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.inputs, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            if self.implementation is not None:
+                console.puts('Implementation: %s' % context.style.literal(self.implementation))
+            if self.dependencies:
+                console.puts('Dependencies: %s' % ', '.join(
+                    (str(context.style.literal(v)) for v in self.dependencies)))
+            if self.executor is not None:
+                console.puts('Executor: %s' % context.style.literal(self.executor))
+            if self.max_retries is not None:
+                console.puts('Max retries: %s' % context.style.literal(self.max_retries))
+            if self.retry_interval is not None:
+                console.puts('Retry interval: %s' % context.style.literal(self.retry_interval))
+            dump_parameters(context, self.inputs, 'Inputs')
+
+
+class ArtifactTemplateBase(structure.ModelMixin):
+    """
+    A file associated with a :class:`NodeTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`source_path`: Source path (CSAR or repository)
+    * :code:`target_path`: Path at destination machine
+    * :code:`repository_url`: Repository URL
+    * :code:`repository_credential`: Dict of string
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'artifact_template'
+    # region foreign keys
+
+    @declared_attr
+    def node_template_fk(cls):
+        return cls.foreign_key('node_template')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    source_path = Column(Text)
+    target_path = Column(Text)
+    repository_url = Column(Text)
+    repository_credential = Column(aria_type.StrictDict(basestring, basestring))
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('source_path', self.source_path),
+            ('target_path', self.target_path),
+            ('repository_url', self.repository_url),
+            ('repository_credential', formatting.as_agnostic(self.repository_credential)),
+            ('properties', formatting.as_raw_dict(self.properties.iteritems()))))
+
+    def instantiate(self, context, container):
+        artifact = instance_elements.ArtifactBase(self.name, self.type_name, self.source_path)
+        artifact.description = deepcopy_with_locators(self.description)
+        artifact.target_path = self.target_path
+        artifact.repository_url = self.repository_url
+        artifact.repository_credential = self.repository_credential
+        utils.instantiate_dict(context, container, artifact.properties, self.properties)
+        return artifact
+
+    def validate(self, context):
+        if context.modeling.artifact_types.get_descendant(self.type_name) is None:
+            context.validation.report('artifact "%s" has an unknown type: %s'
+                                      % (self.name, formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Artifact type: %s' % context.style.type(self.type_name))
+            console.puts('Source path: %s' % context.style.literal(self.source_path))
+            if self.target_path is not None:
+                console.puts('Target path: %s' % context.style.literal(self.target_path))
+            if self.repository_url is not None:
+                console.puts('Repository URL: %s' % context.style.literal(self.repository_url))
+            if self.repository_credential:
+                console.puts('Repository credential: %s'
+                             % context.style.literal(self.repository_credential))
+            dump_parameters(context, self.properties)
+
+
+class PolicyTemplateBase(structure.ModelMixin):
+    """
+    Policies can be applied to zero or more :class:`NodeTemplate` or :class:`GroupTemplate`
+    instances.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`target_node_template_names`: Must be represented in the :class:`ServiceModel`
+    * :code:`target_group_template_names`: Must be represented in the :class:`ServiceModel`
+    """
+    __tablename__ = 'policy_template'
+    # region foreign keys
+    @declared_attr
+    def service_template_fk(cls):
+        return cls.foreign_key('service_template')
+
+    @declared_attr
+    def group_template_fk(cls):
+        return cls.foreign_key('group_template')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    target_node_template_names = Column(aria_type.StrictList(basestring))
+    target_group_template_names = Column(aria_type.StrictList(basestring))
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('target_node_template_names', self.target_node_template_names),
+            ('target_group_template_names', self.target_group_template_names)))
+
+    def instantiate(self, context, *args, **kwargs):
+        policy = instance_elements.PolicyBase(self.name, self.type_name)
+        utils.instantiate_dict(context, self, policy.properties, self.properties)
+        for node_template_name in self.target_node_template_names:
+            policy.target_node_ids.extend(
+                context.modeling.instance.get_node_ids(node_template_name))
+        for group_template_name in self.target_group_template_names:
+            policy.target_group_ids.extend(
+                context.modeling.instance.get_group_ids(group_template_name))
+        return policy
+
+    def validate(self, context):
+        if context.modeling.policy_types.get_descendant(self.type_name) is None:
+            context.validation.report('policy template "%s" has an unknown type: %s'
+                                      % (self.name, formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts('Policy template: %s' % context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            dump_parameters(context, self.properties)
+            if self.target_node_template_names:
+                console.puts('Target node templates: %s' % ', '.join(
+                    (str(context.style.node(v)) for v in self.target_node_template_names)))
+            if self.target_group_template_names:
+                console.puts('Target group templates: %s' % ', '.join(
+                    (str(context.style.node(v)) for v in self.target_group_template_names)))
+
+
+class GroupPolicyTemplateBase(structure.ModelMixin):
+    """
+    Policies applied to groups.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`triggers`: Dict of :class:`GroupPolicyTrigger`
+    """
+
+    __tablename__ = 'group_policy_template'
+    # region foreign keys
+    @declared_attr
+    def group_template_fk(cls):
+        return cls.foreign_key('group_template')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+
+    # region one-to-many relationships
+    @declared_attr
+    def triggers(cls):
+        return cls.one_to_many_relationship('group_policy_trigger_template')
+
+    # end region
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('triggers', formatting.as_raw_list(self.triggers))))
+
+    def instantiate(self, context, container):
+        group_policy = instance_elements.GroupPolicyBase(self.name, self.type_name)
+        group_policy.description = deepcopy_with_locators(self.description)
+        utils.instantiate_dict(context, container, group_policy.properties, self.properties)
+        utils.instantiate_dict(context, container, group_policy.triggers, self.triggers)
+        return group_policy
+
+    def validate(self, context):
+        if context.modeling.policy_types.get_descendant(self.type_name) is None:
+            context.validation.report('group policy "%s" has an unknown type: %s'
+                                      % (self.name, formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.triggers)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+        utils.coerce_dict_values(context, container, self.triggers, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Group policy type: %s' % context.style.type(self.type_name))
+            dump_parameters(context, self.properties)
+            utils.dump_dict_values(context, self.triggers, 'Triggers')
+
+
+class GroupPolicyTriggerTemplateBase(structure.ModelMixin):
+    """
+    Triggers for :class:`GroupPolicyTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'group_policy_trigger_template'
+    # region foreign keys
+    @declared_attr
+    def group_policy_template_fk(cls):
+        return cls.foreign_key('group_policy_template')
+
+    # endregion
+
+    description = Column(Text)
+    implementation = Column(Text)
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('implementation', self.implementation),
+            ('properties', formatting.as_raw_dict(self.properties))))
+
+    def instantiate(self, context, container):
+        group_policy_trigger = instance_elements.GroupPolicyTriggerBase(self.name,
+                                                                        self.implementation)
+        group_policy_trigger.description = deepcopy_with_locators(self.description)
+        utils.instantiate_dict(context, container, group_policy_trigger.properties,
+                               self.properties)
+        return group_policy_trigger
+
+    def validate(self, context):
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Implementation: %s' % context.style.literal(self.implementation))
+            dump_parameters(context, self.properties)
+
+
+class MappingTemplateBase(structure.ModelMixin):
+    """
+    Used by :class:`SubstitutionTemplate` to map a capability or a requirement to a node.
+
+    Properties:
+
+    * :code:`mapped_name`: Exposed capability or requirement name
+    * :code:`node_template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`name`: Name of capability or requirement at the node template
+    """
+    __tablename__ = 'mapping_template'
+
+    mapped_name = Column(Text)
+    node_template_name = Column(Text)
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('mapped_name', self.mapped_name),
+            ('node_template_name', self.node_template_name),
+            ('name', self.name)))
+
+    def instantiate(self, context, *args, **kwargs):
+        nodes = context.modeling.instance.find_nodes(self.node_template_name)
+        if len(nodes) == 0:
+            context.validation.report(
+                'mapping "%s" refer to node template "%s" but there are no '
+                'node instances' % (self.mapped_name,
+                                    self.node_template_name),
+                level=validation.Issue.BETWEEN_INSTANCES)
+            return None
+        return instance_elements.MappingBase(self.mapped_name, nodes[0].id, self.name)
+
+    def validate(self, context):
+        if self.node_template_name not in context.modeling.model.node_templates:
+            context.validation.report('mapping "%s" refers to an unknown node template: %s'
+                                      % (
+                                          self.mapped_name,
+                                          formatting.safe_repr(self.node_template_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+    def dump(self, context):
+        console.puts('%s -> %s.%s' % (context.style.node(self.mapped_name),
+                                      context.style.node(self.node_template_name),
+                                      context.style.node(self.name)))
+
+
+class SubstitutionTemplateBase(structure.ModelMixin):
+    """
+    Used to substitute a single node for the entire deployment.
+
+    Properties:
+
+    * :code:`node_type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`capability_templates`: Dict of :class:`MappingTemplate`
+    * :code:`requirement_templates`: Dict of :class:`MappingTemplate`
+    """
+    __tablename__ = 'substitution_template'
+    node_type_name = Column(Text)
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def capability_templates(cls):
+        return cls.many_to_many_relationship('mapping_template',
+                                             table_prefix='capability_templates',
+                                             relationship_kwargs=dict(lazy='dynamic'))
+
+    @declared_attr
+    def requirement_templates(cls):
+        return cls.many_to_many_relationship('mapping_template',
+                                             table_prefix='requirement_templates',
+                                             relationship_kwargs=dict(lazy='dynamic'))
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('node_type_name', self.node_type_name),
+            ('capability_templates', formatting.as_raw_list(self.capability_templates)),
+            ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
+
+    def instantiate(self, context, container):
+        substitution = instance_elements.SubstitutionBase(self.node_type_name)
+        utils.instantiate_dict(context, container, substitution.capabilities,
+                               self.capability_templates)
+        utils.instantiate_dict(context, container, substitution.requirements,
+                               self.requirement_templates)
+        return substitution
+
+    def validate(self, context):
+        if context.modeling.node_types.get_descendant(self.node_type_name) is None:
+            context.validation.report('substitution template has an unknown type: %s'
+                                      % formatting.safe_repr(self.node_type_name),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.capability_templates)
+        utils.validate_dict_values(context, self.requirement_templates)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.capability_templates, report_issues)
+        utils.coerce_dict_values(context, self, self.requirement_templates, report_issues)
+
+    def dump(self, context):
+        console.puts('Substitution template:')
+        with context.style.indent:
+            console.puts('Node type: %s' % context.style.type(self.node_type_name))
+            utils.dump_dict_values(context, self.capability_templates,
+                                   'Capability template mappings')
+            utils.dump_dict_values(context, self.requirement_templates,
+                                   'Requirement template mappings')
+
+
+# endregion
+
+# region Node templates
+
+class NodeTemplateBase(structure.ModelMixin):
+    __tablename__ = 'node_template'
+
+    # region foreign_keys
+    @declared_attr
+    def service_template_fk(cls):
+        return cls.foreign_key('service_template')
+
+    @declared_attr
+    def host_fk(cls):
+        return cls.foreign_key('node_template', nullable=True)
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    default_instances = Column(Integer, default=1)
+    min_instances = Column(Integer, default=0)
+    max_instances = Column(Integer, default=None)
+    target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
+
+    # region orchestrator required columns
+
+    plugins = Column(aria_type.List)
+    type_hierarchy = Column(aria_type.List)
+
+    @declared_attr
+    def host(cls):
+        return cls.relationship_to_self('host_fk')
+
+    @declared_attr
+    def service_template_name(cls):
+        return association_proxy('service_template', cls.name_column_name())
+
+    # endregion
+
+    # region one-to-many relationships
+    @declared_attr
+    def interface_templates(cls):
+        return cls.one_to_many_relationship('interface_template')
+
+    @declared_attr
+    def artifact_templates(cls):
+        return cls.one_to_many_relationship('artifact_template')
+
+    @declared_attr
+    def capability_templates(cls):
+        return cls.one_to_many_relationship('capability_template')
+
+    @declared_attr
+    def requirement_templates(cls):
+        return cls.one_to_many_relationship('requirement_template')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def service_template(cls):
+        return cls.many_to_one_relationship('service_template')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    def is_target_node_valid(self, target_node_template):
+        if self.target_node_template_constraints:
+            for node_type_constraint in self.target_node_template_constraints:
+                if not node_type_constraint(target_node_template, self):
+                    return False
+        return True
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('default_instances', self.default_instances),
+            ('min_instances', self.min_instances),
+            ('max_instances', self.max_instances),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('interface_templates', formatting.as_raw_list(self.interface_templates)),
+            ('artifact_templates', formatting.as_raw_list(self.artifact_templates)),
+            ('capability_templates', formatting.as_raw_list(self.capability_templates)),
+            ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
+
+    def instantiate(self, context, *args, **kwargs):
+        node = instance_elements.NodeBase(context, self.type_name, self.name)
+        utils.instantiate_dict(context, node, node.properties, self.properties)
+        utils.instantiate_dict(context, node, node.interfaces, self.interface_templates)
+        utils.instantiate_dict(context, node, node.artifacts, self.artifact_templates)
+        utils.instantiate_dict(context, node, node.capabilities, self.capability_templates)
+        return node
+
+    def validate(self, context):
+        if context.modeling.node_types.get_descendant(self.type_name) is None:
+            context.validation.report('node template "%s" has an unknown type: %s'
+                                      % (self.name,
+                                         formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.interface_templates)
+        utils.validate_dict_values(context, self.artifact_templates)
+        utils.validate_dict_values(context, self.capability_templates)
+        utils.validate_list_values(context, self.requirement_templates)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.properties, report_issues)
+        utils.coerce_dict_values(context, self, self.interface_templates, report_issues)
+        utils.coerce_dict_values(context, self, self.artifact_templates, report_issues)
+        utils.coerce_dict_values(context, self, self.capability_templates, report_issues)
+        utils.coerce_list_values(context, self, self.requirement_templates, report_issues)
+
+    def dump(self, context):
+        console.puts('Node template: %s' % context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            console.puts('Instances: %d (%d%s)'
+                         % (self.default_instances,
+                            self.min_instances,
+                            (' to %d' % self.max_instances
+                             if self.max_instances is not None
+                             else ' or more')))
+            dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.interface_templates)
+            utils.dump_dict_values(context, self.artifact_templates, 'Artifact tempaltes')
+            utils.dump_dict_values(context, self.capability_templates, 'Capability templates')
+            utils.dump_list_values(context, self.requirement_templates, 'Requirement templates')
+
+
+class GroupTemplateBase(structure.ModelMixin):
+    """
+    A template for creating zero or more :class:`Group` instances.
+
+    Groups are logical containers for zero or more nodes that allow applying zero or more
+    :class:`GroupPolicy` instances to the nodes together.
+
+    Properties:
+
+    * :code:`name`: Name (will be used as a prefix for group IDs)
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`interface_templates`: Dict of :class:`InterfaceTemplate`
+    * :code:`policy_templates`: Dict of :class:`GroupPolicyTemplate`
+    * :code:`member_node_template_names`: Must be represented in the :class:`ServiceModel`
+    * :code:`member_group_template_names`: Must be represented in the :class:`ServiceModel`
+    """
+    __tablename__ = 'group_template'
+    # region foreign keys
+
+    @declared_attr
+    def service_template_fk(cls):
+        return cls.foreign_key('service_template')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    member_node_template_names = Column(aria_type.StrictList(basestring))
+    member_group_template_names = Column(aria_type.StrictList(basestring))
+
+    # region one-to-many relationships
+    @declared_attr
+    def interface_templates(cls):
+        return cls.one_to_many_relationship('interface_template')
+
+    @declared_attr
+    def policy_templates(cls):
+        return cls.one_to_many_relationship('policy_template')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('interface_templates', formatting.as_raw_list(self.interface_templates)),
+            ('policy_templates', formatting.as_raw_list(self.policy_templates)),
+            ('member_node_template_names', self.member_node_template_names),
+            ('member_group_template_names', self.member_group_template_names)))
+
+    def instantiate(self, context, *args, **kwargs):
+        group = instance_elements.GroupBase(context, self.type_name, self.name)
+        utils.instantiate_dict(context, self, group.properties, self.properties)
+        utils.instantiate_dict(context, self, group.interfaces, self.interface_templates)
+        utils.instantiate_dict(context, self, group.policies, self.policy_templates)
+        for member_node_template_name in self.member_node_template_names:
+            group.member_node_ids += \
+                context.modeling.instance.get_node_ids(member_node_template_name)
+        for member_group_template_name in self.member_group_template_names:
+            group.member_group_ids += \
+                context.modeling.instance.get_group_ids(member_group_template_name)
+        return group
+
+    def validate(self, context):
+        if context.modeling.group_types.get_descendant(self.type_name) is None:
+            context.validation.report('group template "%s" has an unknown type: %s'
+                                      % (self.name, formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.interface_templates)
+        utils.validate_dict_values(context, self.policy_templates)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.properties, report_issues)
+        utils.coerce_dict_values(context, self, self.interface_templates, report_issues)
+        utils.coerce_dict_values(context, self, self.policy_templates, report_issues)
+
+    def dump(self, context):
+        console.puts('Group template: %s' % context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            if self.type_name:
+                console.puts('Type: %s' % context.style.type(self.type_name))
+            dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.interface_templates)
+            utils.dump_dict_values(context, self.policy_templates, 'Policy templates')
+            if self.member_node_template_names:
+                console.puts('Member node templates: %s' % ', '.join(
+                    (str(context.style.node(v)) for v in self.member_node_template_names)))
+
+
+# endregion
+
+# region Relationship templates
+
+class RequirementTemplateBase(structure.ModelMixin):
+    """
+    A requirement for a :class:`NodeTemplate`. During instantiation will be matched with a
+    capability of another
+    node.
+
+    Requirements may optionally contain a :class:`RelationshipTemplate` that will be created between
+    the nodes.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`target_node_type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`target_node_template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`target_node_template_constraints`: List of :class:`FunctionType`
+    * :code:`target_capability_type_name`: Type of capability in target node
+    * :code:`target_capability_name`: Name of capability in target node
+    * :code:`relationship_template`: :class:`RelationshipTemplate`
+    """
+    __tablename__ = 'requirement_template'
+    # region foreign keys
+    @declared_attr
+    def node_template_fk(cls):
+        return cls.foreign_key('node_template', nullable=True)
+
+    # endregion
+
+    target_node_type_name = Column(Text)
+    target_node_template_name = Column(Text)
+    target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
+    target_capability_type_name = Column(Text)
+    target_capability_name = Column(Text)
+    # CHECK: ???
+    relationship_template = Column(Text)  # optional
+
+    def instantiate(self, context, container):
+        raise NotImplementedError
+
+    def find_target(self, context, source_node_template):
+        # We might already have a specific node template, so we'll just verify it
+        if self.target_node_template_name is not None:
+            target_node_template = \
+                context.modeling.model.node_templates.get(self.target_node_template_name)
+
+            if not source_node_template.is_target_node_valid(target_node_template):
+                context.validation.report('requirement "%s" of node template "%s" is for node '
+                                          'template "%s" but it does not match constraints'
+                                          % (self.name,
+                                             self.target_node_template_name,
+                                             source_node_template.name),
+                                          level=validation.Issue.BETWEEN_TYPES)
+                return None, None
+
+            if self.target_capability_type_name is not None \
+                    or self.target_capability_name is not None:
+                target_node_capability = self.find_target_capability(context,
+                                                                     source_node_template,
+                                                                     target_node_template)
+                if target_node_capability is None:
+                    return None, None
+            else:
+                target_node_capability = None
+
+            return target_node_template, target_node_capability
+
+        # Find first node that matches the type
+        elif self.target_node_type_name is not None:
+            for target_node_template in context.modeling.model.node_templates.itervalues():
+                if not context.modeling.node_types.is_descendant(self.target_node_type_name,
+                                                                 target_node_template.type_name):
+                    continue
+
+                if not source_node_template.is_target_node_valid(target_node_template):
+                    continue
+
+                target_node_capability = self.find_target_capability(context,
+                                                                     source_node_template,
+                                                                     target_node_template)
+                if target_node_capability is None:
+                    continue
+
+                return target_node_template, target_node_capability
+
+        return None, None
+
+    def find_target_capability(self, context, source_node_template, target_node_template):
+        for capability_template in target_node_template.capability_templates.itervalues():
+            if capability_template.satisfies_requirement(context,
+                                                         source_node_template,
+                                                         self,
+                                                         target_node_template):
+                return capability_template
+        return None
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('target_node_type_name', self.target_node_type_name),
+            ('target_node_template_name', self.target_node_template_name),
+            ('target_capability_type_name', self.target_capability_type_name),
+            ('target_capability_name', self.target_capability_name),
+            ('relationship_template', formatting.as_raw(self.relationship_template))))
+
+    def validate(self, context):
+        node_types = context.modeling.node_types
+        capability_types = context.modeling.capability_types
+        if self.target_node_type_name \
+                and node_types.get_descendant(self.target_node_type_name) is None:
+            context.validation.report('requirement "%s" refers to an unknown node type: %s'
+                                      % (self.name,
+                                         formatting.safe_repr(self.target_node_type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+        if self.target_capability_type_name and \
+                capability_types.get_descendant(self.target_capability_type_name is None):
+            context.validation.report('requirement "%s" refers to an unknown capability type: %s'
+                                      % (self.name,
+                                         formatting.safe_repr(self.target_capability_type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+        if self.relationship_template:
+            self.relationship_template.validate(context)
+
+    def coerce_values(self, context, container, report_issues):
+        if self.relationship_template is not None:
+            self.relationship_template.coerce_values(context, container, report_issues)
+
+    def dump(self, context):
+        if self.name:
+            console.puts(context.style.node(self.name))
+        else:
+            console.puts('Requirement:')
+        with context.style.indent:
+            if self.target_node_type_name is not None:
+                console.puts('Target node type: %s'
+                             % context.style.type(self.target_node_type_name))
+            elif self.target_node_template_name is not None:
+                console.puts('Target node template: %s'
+                             % context.style.node(self.target_node_template_name))
+            if self.target_capability_type_name is not None:
+                console.puts('Target capability type: %s'
+                             % context.style.type(self.target_capability_type_name))
+            elif self.target_capability_name is not None:
+                console.puts('Target capability name: %s'
+                             % context.style.node(self.target_capability_name))
+            if self.target_node_template_constraints:
+                console.puts('Target node template constraints:')
+                with context.style.indent:
+                    for constraint in self.target_node_template_constraints:
+                        console.puts(context.style.literal(constraint))
+            if self.relationship_template:
+                console.puts('Relationship:')
+                with context.style.indent:
+                    self.relationship_template.dump(context)
+
+
+class CapabilityTemplateBase(structure.ModelMixin):
+    """
+    A capability of a :class:`NodeTemplate`. Nodes expose zero or more capabilities that can be
+    matched with :class:`Requirement` instances of other nodes.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`min_occurrences`: Minimum number of requirement matches required
+    * :code:`max_occurrences`: Maximum number of requirement matches allowed
+    * :code:`valid_source_node_type_names`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'capability_template'
+    # region foreign keys
+    @declared_attr
+    def node_template_fk(cls):
+        return cls.foreign_key('node_template', nullable=True)
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    min_occurrences = Column(Integer, default=None)  # optional
+    max_occurrences = Column(Integer, default=None)  # optional
+    # CHECK: type?
+    valid_source_node_type_names = Column(Text)
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    def satisfies_requirement(self,
+                              context,
+                              source_node_template,
+                              requirement,
+                              target_node_template):
+        # Do we match the required capability type?
+        capability_types = context.modeling.capability_types
+        if not capability_types.is_descendant(requirement.target_capability_type_name,
+                                              self.type_name):
+            return False
+
+        # Are we in valid_source_node_type_names?
+        if self.valid_source_node_type_names:
+            for valid_source_node_type_name in self.valid_source_node_type_names:
+                if not context.modeling.node_types.is_descendant(valid_source_node_type_name,
+                                                                 source_node_template.type_name):
+                    return False
+
+        # Apply requirement constraints
+        if requirement.target_node_template_constraints:
+            for node_type_constraint in requirement.target_node_template_constraints:
+                if not node_type_constraint(target_node_template, source_node_template):
+                    return False
+
+        return True
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('min_occurrences', self.min_occurrences),
+            ('max_occurrences', self.max_occurrences),
+            ('valid_source_node_type_names', self.valid_source_node_type_names),
+            ('properties', formatting.as_raw_dict(self.properties))))
+
+    def instantiate(self, context, container):
+        capability = instance_elements.CapabilityBase(self.name, self.type_name)
+        capability.min_occurrences = self.min_occurrences
+        capability.max_occurrences = self.max_occurrences
+        utils.instantiate_dict(context, container, capability.properties, self.properties)
+        return capability
+
+    def validate(self, context):
+        if context.modeling.capability_types.get_descendant(self.type_name) is None:
+            context.validation.report('capability "%s" refers to an unknown type: %s'
+                                      % (self.name, formatting.safe_repr(self.type)),  # pylint: disable=no-member
+                                      #  TODO fix self.type reference
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            console.puts(
+                'Occurrences: %d%s'
+                % (self.min_occurrences or 0, (' to %d' % self.max_occurrences)
+                   if self.max_occurrences is not None else ' or more'))
+            if self.valid_source_node_type_names:
+                console.puts('Valid source node types: %s'
+                             % ', '.join((str(context.style.type(v))
+                                          for v in self.valid_source_node_type_names)))
+            dump_parameters(context, self.properties)
+
+# endregion
+
+
+def dump_parameters(context, parameters, name='Properties'):
+    if not parameters:
+        return
+    console.puts('%s:' % name)
+    with context.style.indent:
+        for parameter_name, parameter in parameters.items():
+            if parameter.type_name is not None:
+                console.puts('%s = %s (%s)' % (context.style.property(parameter_name),
+                                               context.style.literal(parameter.value),
+                                               context.style.type(parameter.type_name)))
+            else:
+                console.puts('%s = %s' % (context.style.property(parameter_name),
+                                          context.style.literal(parameter.value)))
+            if parameter.description:
+                console.puts(context.style.meta(parameter.description))
+
+
+# TODO (left for tal): Move following two methods to some place parser specific
+def deepcopy_with_locators(value):
+    """
+    Like :code:`deepcopy`, but also copies over locators.
+    """
+
+    res = deepcopy(value)
+    copy_locators(res, value)
+    return res
+
+
+def copy_locators(target, source):
+    """
+    Copies over :code:`_locator` for all elements, recursively.
+
+    Assumes that target and source have exactly the same list/dict structure.
+    """
+
+    locator = getattr(source, '_locator', None)
+    if locator is not None:
+        try:
+            setattr(target, '_locator', locator)
+        except AttributeError:
+            pass
+
+    if isinstance(target, list) and isinstance(source, list):
+        for i, _ in enumerate(target):
+            copy_locators(target[i], source[i])
+    elif isinstance(target, dict) and isinstance(source, dict):
+        for k, v in target.items():
+            copy_locators(v, source[k])

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/storage/modeling/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/type.py b/aria/storage/modeling/type.py
new file mode 100644
index 0000000..0b6c58f
--- /dev/null
+++ b/aria/storage/modeling/type.py
@@ -0,0 +1,275 @@
+# 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 json
+from collections import namedtuple
+from types import NoneType
+
+from sqlalchemy import (
+    TypeDecorator,
+    VARCHAR,
+    event
+)
+from sqlalchemy.ext import mutable
+
+from aria.storage import exceptions
+
+
+class _MutableType(TypeDecorator):
+    """
+    Dict representation of type.
+    """
+    @property
+    def python_type(self):
+        raise NotImplementedError
+
+    def process_literal_param(self, value, dialect):
+        pass
+
+    impl = VARCHAR
+
+    def process_bind_param(self, value, dialect):
+        if value is not None:
+            value = json.dumps(value)
+        return value
+
+    def process_result_value(self, value, dialect):
+        if value is not None:
+            value = json.loads(value)
+        return value
+
+
+class Dict(_MutableType):
+    @property
+    def python_type(self):
+        return dict
+
+
+class List(_MutableType):
+    @property
+    def python_type(self):
+        return list
+
+
+class _StrictDictMixin(object):
+    ANY_TYPE = 'any_type'
+
+    _key_cls = ANY_TYPE
+    _value_cls = ANY_TYPE
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, dict):
+                    for k, v in value.items():
+                        cls._assert_strict_key(k)
+                        cls._assert_strict_value(v)
+                    return cls(value)
+                return mutable.MutableDict.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).__setitem__(key, value)
+
+    def setdefault(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).setdefault(key, value)
+
+    def update(self, *args, **kwargs):
+        for k, v in kwargs.items():
+            self._assert_strict_key(k)
+            self._assert_strict_value(v)
+        super(_StrictDictMixin, self).update(*args, **kwargs)
+
+    @classmethod
+    def _assert_strict_key(cls, key):
+        if not isinstance(key, (cls._key_cls, NoneType)):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._key_cls, type(key)
+            ))
+
+    @classmethod
+    def _assert_strict_value(cls, value):
+        if not isinstance(value, (cls._value_cls, NoneType)):
+            raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
+                cls._value_cls, type(value)
+            ))
+
+
+class _MutableDict(mutable.MutableDict):
+    """
+    Enables tracking for dict values.
+    """
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            return mutable.MutableDict.coerce(key, value)
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+
+class _StrictListMixin(object):
+    ANY_TYPE = 'any_type'
+
+    _item_cls = ANY_TYPE
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, list):
+                    for item in value:
+                        cls._assert_item(item)
+                    return cls(value)
+                return mutable.MutableList.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, index, value):
+        """Detect list set events and emit change events."""
+        self._assert_item(value)
+        super(_StrictListMixin, self).__setitem__(index, value)
+
+    def append(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).append(item)
+
+    def extend(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).extend(item)
+
+    def insert(self, index, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).insert(index, item)
+
+    @classmethod
+    def _assert_item(cls, item):
+        if not isinstance(item, (cls._item_cls, NoneType)):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._item_cls, type(item)
+            ))
+
+
+class _MutableList(mutable.MutableList):
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, list):
+                    return cls(value)
+
+                return mutable.Mutable.coerce(key, value)
+            else:
+                return value
+
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+StrictDictID = namedtuple('strict_dict_id', 'key_cls, value_cls')
+
+
+class _StrictDict(object):
+    _strict_map = {}
+
+    def __call__(self, key_cls=NoneType, value_cls=NoneType, *args, **kwargs):
+        strict_dict_map_key = StrictDictID(key_cls=key_cls, value_cls=value_cls)
+        if strict_dict_map_key not in self._strict_map:
+            strict_dict_cls = type(
+                'StrictDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
+                (Dict, ),
+                {}
+            )
+            type(
+                'StrictMutableDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
+                (_StrictDictMixin, _MutableDict),
+                {'_key_cls': key_cls, '_value_cls': value_cls}
+            ).associate_with(strict_dict_cls)
+            self._strict_map[strict_dict_map_key] = strict_dict_cls
+
+        return self._strict_map[strict_dict_map_key]
+
+StrictDict = _StrictDict()
+
+
+class _StrictList(object):
+    _strict_map = {}
+
+    def __call__(self, item_cls):
+        if item_cls not in self._strict_map:
+            strict_list_cls = type(
+                'StrictList_{0}'.format(item_cls.__name__),
+                (List, ),
+                {}
+            )
+            type(
+                'StrictMutableList_{0}'.format(item_cls.__name__),
+                (_StrictListMixin, _MutableList),
+                {'_item_cls': item_cls}
+            ).associate_with(strict_list_cls)
+            self._strict_map[item_cls] = strict_list_cls
+
+        return self._strict_map[item_cls]
+
+StrictList = _StrictList()
+
+def _mutable_association_listener(mapper, cls):
+    for prop in mapper.column_attrs:
+        column_type = prop.columns[0].type
+        if isinstance(column_type, Dict):
+            _MutableDict.associate_with_attribute(getattr(cls, prop.key))
+        if isinstance(column_type, List):
+            _MutableList.associate_with_attribute(getattr(cls, prop.key))
+_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
+
+
+def _register_mutable_association_listener():
+    event.listen(*_LISTENER_ARGS)
+
+
+def remove_mutable_association_listener():
+    """
+    Remove the event listener that associates ``Dict`` and ``List`` column types with
+    ``MutableDict`` and ``MutableList``, respectively.
+
+    This call must happen before any model instance is instantiated.
+    This is because once it does, that would trigger the listener we are trying to remove.
+    Once it is triggered, many other listeners will then be registered.
+    At that point, it is too late.
+
+    The reason this function exists is that the association listener, interferes with ARIA change
+    tracking instrumentation, so a way to disable it is required.
+
+    Note that the event listener this call removes is registered by default.
+    """
+    if event.contains(*_LISTENER_ARGS):
+        event.remove(*_LISTENER_ARGS)
+
+_register_mutable_association_listener()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/storage/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/utils.py b/aria/storage/modeling/utils.py
new file mode 100644
index 0000000..f4c97c4
--- /dev/null
+++ b/aria/storage/modeling/utils.py
@@ -0,0 +1,146 @@
+# 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 random import randrange
+
+from shortuuid import ShortUUID
+
+from aria.utils.collections import OrderedDict
+from aria.utils.console import puts
+from aria.parser.exceptions import InvalidValueError
+from aria.parser.presentation import Value
+from aria.parser.modeling.exceptions import CannotEvaluateFunctionException
+
+# UUID = ShortUUID() # default alphabet is base57, which is alphanumeric without visually ambiguous
+# characters; ID length is 22
+UUID = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123456789')  # alphanumeric; ID length is 25
+
+
+def generate_id_string(length=None):
+    """
+    A random string with a strong guarantee of universal uniqueness (uses UUID).
+
+    The default length is 25 characters.
+    """
+
+    the_id = UUID.uuid()
+    if length is not None:
+        the_id = the_id[:length]
+    return the_id
+
+
+def generate_hex_string():
+    """
+    A random string of 5 hex digits with no guarantee of universal uniqueness.
+    """
+
+    return '%05x' % randrange(16 ** 5)
+
+
+def coerce_value(context, container, value, report_issues=False):
+    if isinstance(value, Value):
+        value = value.value
+
+    if isinstance(value, list):
+        return [coerce_value(context, container, v, report_issues) for v in value]
+    elif isinstance(value, dict):
+        return OrderedDict((k, coerce_value(context, container, v, report_issues))
+                           for k, v in value.iteritems())
+    elif hasattr(value, '_evaluate'):
+        try:
+            value = value._evaluate(context, container)
+            value = coerce_value(context, container, value, report_issues)
+        except CannotEvaluateFunctionException:
+            pass
+        except InvalidValueError as e:
+            if report_issues:
+                context.validation.report(e.issue)
+    return value
+
+
+def validate_dict_values(context, the_dict):
+    if not the_dict:
+        return
+    validate_list_values(context, the_dict.itervalues())
+
+
+def validate_list_values(context, the_list):
+    if not the_list:
+        return
+    for value in the_list:
+        value.validate(context)
+
+
+def coerce_dict_values(context, container, the_dict, report_issues=False):
+    if not the_dict:
+        return
+    coerce_list_values(context, container, the_dict.itervalues(), report_issues)
+
+
+def coerce_list_values(context, container, the_list, report_issues=False):
+    if not the_list:
+        return
+    for value in the_list:
+        value.coerce_values(context, container, report_issues)
+
+
+def instantiate_dict(context, container, the_dict, from_dict):
+    if not from_dict:
+        return
+    for name, value in from_dict.iteritems():
+        value = value.instantiate(context, container)
+        if value is not None:
+            the_dict[name] = value
+
+
+def dump_list_values(context, the_list, name):
+    if not the_list:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for value in the_list:
+            value.dump(context)
+
+
+def dump_dict_values(context, the_dict, name):
+    if not the_dict:
+        return
+    dump_list_values(context, the_dict.itervalues(), name)
+
+
+def dump_parameters(context, parameters, name='Properties'):
+    if not parameters:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for parameter_name, parameter in parameters.iteritems():
+            if parameter.type_name is not None:
+                puts('%s = %s (%s)' % (context.style.property(parameter_name),
+                                       context.style.literal(parameter.value),
+                                       context.style.type(parameter.type_name)))
+            else:
+                puts('%s = %s' % (context.style.property(parameter_name),
+                                  context.style.literal(parameter.value)))
+            if parameter.description:
+                puts(context.style.meta(parameter.description))
+
+
+def dump_interfaces(context, interfaces, name='Interfaces'):
+    if not interfaces:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for interface in interfaces.itervalues():
+            interface.dump(context)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/storage/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/structure.py b/aria/storage/structure.py
deleted file mode 100644
index fa592ac..0000000
--- a/aria/storage/structure.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# 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.
-
-"""
-Aria's storage.structures module
-Path: aria.storage.structures
-
-models module holds aria's models.
-
-classes:
-    * Field - represents a single field.
-    * IterField - represents an iterable field.
-    * PointerField - represents a single pointer field.
-    * IterPointerField - represents an iterable pointers field.
-    * Model - abstract model implementation.
-"""
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.ext import associationproxy
-from sqlalchemy import (
-    Column,
-    ForeignKey,
-    Integer,
-    Text
-)
-
-
-class ModelMixin(object):
-
-    @classmethod
-    def id_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def name_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def _get_cls_by_tablename(cls, tablename):
-        """Return class reference mapped to table.
-
-         :param tablename: String with name of table.
-         :return: Class reference or None.
-         """
-        if tablename in (cls.__name__, cls.__tablename__):
-            return cls
-
-        for table_cls in cls._decl_class_registry.values():
-            if tablename in (getattr(table_cls, '__name__', None),
-                             getattr(table_cls, '__tablename__', None)):
-                return table_cls
-
-    @classmethod
-    def foreign_key(cls, table, nullable=False):
-        """Return a ForeignKey object with the relevant
-
-        :param table: Unique id column in the parent table
-        :param nullable: Should the column be allowed to remain empty
-        """
-        table_cls = cls._get_cls_by_tablename(table.__tablename__)
-        foreign_key_str = '{tablename}.{unique_id}'.format(tablename=table_cls.__tablename__,
-                                                           unique_id=table_cls.id_column_name())
-        column = Column(ForeignKey(foreign_key_str, ondelete='CASCADE'),
-                        nullable=nullable)
-        column.__remote_table_name = table_cls.__name__
-        return column
-
-    @classmethod
-    def one_to_many_relationship(cls,
-                                 foreign_key_column,
-                                 backreference=None,
-                                 backref_kwargs=None,
-                                 **kwargs):
-        """Return a one-to-many SQL relationship object
-        Meant to be used from inside the *child* object
-
-        :param parent_class: Class of the parent table
-        :param cls: Class of the child table
-        :param foreign_key_column: The column of the foreign key (from the child table)
-        :param backreference: The name to give to the reference to the child (on the parent table)
-        """
-        backref_kwargs = backref_kwargs or {}
-        parent_table = cls._get_cls_by_tablename(
-            getattr(cls, foreign_key_column).__remote_table_name)
-        primaryjoin_str = '{parent_class_name}.{parent_unique_id} == ' \
-                          '{child_class.__name__}.{foreign_key_column}'\
-            .format(
-                parent_class_name=parent_table.__name__,
-                parent_unique_id=parent_table.id_column_name(),
-                child_class=cls,
-                foreign_key_column=foreign_key_column
-            )
-        return relationship(
-            parent_table.__name__,
-            primaryjoin=primaryjoin_str,
-            foreign_keys=[getattr(cls, foreign_key_column)],
-            # The following line make sure that when the *parent* is
-            # deleted, all its connected children are deleted as well
-            backref=backref(backreference or cls.__tablename__, cascade='all', **backref_kwargs),
-            **kwargs
-        )
-
-    @classmethod
-    def relationship_to_self(cls, local_column):
-
-        remote_side_str = '{cls.__name__}.{remote_column}'.format(
-            cls=cls,
-            remote_column=cls.id_column_name()
-        )
-        primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format(
-            remote_side_str=remote_side_str,
-            cls=cls,
-            local_column=local_column)
-        return relationship(cls.__name__,
-                            primaryjoin=primaryjoin_str,
-                            remote_side=remote_side_str,
-                            post_update=True)
-
-    def to_dict(self, fields=None, suppress_error=False):
-        """Return a dict representation of the model
-
-        :param suppress_error: If set to True, sets `None` to attributes that
-        it's unable to retrieve (e.g., if a relationship wasn't established
-        yet, and so it's impossible to access a property through it)
-        """
-        res = dict()
-        fields = fields or self.fields()
-        for field in fields:
-            try:
-                field_value = getattr(self, field)
-            except AttributeError:
-                if suppress_error:
-                    field_value = None
-                else:
-                    raise
-            if isinstance(field_value, list):
-                field_value = list(field_value)
-            elif isinstance(field_value, dict):
-                field_value = dict(field_value)
-            elif isinstance(field_value, ModelMixin):
-                field_value = field_value.to_dict()
-            res[field] = field_value
-
-        return res
-
-    @classmethod
-    def _association_proxies(cls):
-        for col, value in vars(cls).items():
-            if isinstance(value, associationproxy.AssociationProxy):
-                yield col
-
-    @classmethod
-    def fields(cls):
-        """Return the list of field names for this table
-
-        Mostly for backwards compatibility in the code (that uses `fields`)
-        """
-        fields = set(cls._association_proxies())
-        fields.update(cls.__table__.columns.keys())
-        return fields - set(getattr(cls, '_private_fields', []))
-
-    def __repr__(self):
-        return '<{__class__.__name__} id=`{id}`>'.format(
-            __class__=self.__class__,
-            id=getattr(self, self.name_column_name()))
-
-
-class ModelIDMixin(object):
-    id = Column(Integer, primary_key=True, autoincrement=True)
-    name = Column(Text, nullable=True, index=True)
-
-    @classmethod
-    def id_column_name(cls):
-        return 'id'
-
-    @classmethod
-    def name_column_name(cls):
-        return 'name'

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/storage/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/type.py b/aria/storage/type.py
deleted file mode 100644
index dd2a5ce..0000000
--- a/aria/storage/type.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# 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 json
-
-from sqlalchemy import (
-    TypeDecorator,
-    VARCHAR,
-    event
-)
-
-from sqlalchemy.ext import mutable
-
-from . import exceptions
-
-
-class _MutableType(TypeDecorator):
-    """
-    Dict representation of type.
-    """
-    @property
-    def python_type(self):
-        raise NotImplementedError
-
-    def process_literal_param(self, value, dialect):
-        pass
-
-    impl = VARCHAR
-
-    def process_bind_param(self, value, dialect):
-        if value is not None:
-            value = json.dumps(value)
-        return value
-
-    def process_result_value(self, value, dialect):
-        if value is not None:
-            value = json.loads(value)
-        return value
-
-
-class Dict(_MutableType):
-    @property
-    def python_type(self):
-        return dict
-
-
-class List(_MutableType):
-    @property
-    def python_type(self):
-        return list
-
-
-class _MutableDict(mutable.MutableDict):
-    """
-    Enables tracking for dict values.
-    """
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            return mutable.MutableDict.coerce(key, value)
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-
-class _MutableList(mutable.MutableList):
-
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            return mutable.MutableList.coerce(key, value)
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-
-def _mutable_association_listener(mapper, cls):
-    for prop in mapper.column_attrs:
-        column_type = prop.columns[0].type
-        if isinstance(column_type, Dict):
-            _MutableDict.associate_with_attribute(getattr(cls, prop.key))
-        if isinstance(column_type, List):
-            _MutableList.associate_with_attribute(getattr(cls, prop.key))
-_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
-
-
-def _register_mutable_association_listener():
-    event.listen(*_LISTENER_ARGS)
-
-
-def remove_mutable_association_listener():
-    """
-    Remove the event listener that associates ``Dict`` and ``List`` column types with
-    ``MutableDict`` and ``MutableList``, respectively.
-
-    This call must happen before any model instance is instantiated.
-    This is because once it does, that would trigger the listener we are trying to remove.
-    Once it is triggered, many other listeners will then be registered.
-    At that point, it is too late.
-
-    The reason this function exists is that the association listener, interferes with ARIA change
-    tracking instrumentation, so a way to disable it is required.
-
-    Note that the event listener this call removes is registered by default.
-    """
-    if event.contains(*_LISTENER_ARGS):
-        event.remove(*_LISTENER_ARGS)
-
-_register_mutable_association_listener()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/82d9be7d/aria/utils/application.py
----------------------------------------------------------------------
diff --git a/aria/utils/application.py b/aria/utils/application.py
index 113e054..161a9cb 100644
--- a/aria/utils/application.py
+++ b/aria/utils/application.py
@@ -102,14 +102,14 @@ class StorageManager(LoggerMixin):
         assert hasattr(self.model_storage, 'blueprint')
 
         self.logger.debug('creating blueprint resource storage entry')
-        self.resource_storage.blueprint.upload(
+        self.resource_storage.service_template.upload(
             entry_id=self.blueprint_id,
             source=os.path.dirname(source))
         self.logger.debug('created blueprint resource storage entry')
 
         self.logger.debug('creating blueprint model storage entry')
         now = datetime.utcnow()
-        blueprint = self.model_storage.blueprint.model_cls(
+        blueprint = self.model_storage.service_template.model_cls(
             plan=self.blueprint_plan,
             id=self.blueprint_id,
             description=self.blueprint_plan.get('description'),
@@ -117,7 +117,7 @@ class StorageManager(LoggerMixin):
             updated_at=now,
             main_file_name=main_file_name,
         )
-        self.model_storage.blueprint.put(blueprint)
+        self.model_storage.service_template.put(blueprint)
         self.logger.debug('created blueprint model storage entry')
 
     def create_nodes_storage(self):
@@ -164,10 +164,10 @@ class StorageManager(LoggerMixin):
         self.logger.debug('creating deployment resource storage entry')
         temp_dir = tempfile.mkdtemp()
         try:
-            self.resource_storage.blueprint.download(
+            self.resource_storage.service_template.download(
                 entry_id=self.blueprint_id,
                 destination=temp_dir)
-            self.resource_storage.deployment.upload(
+            self.resource_storage.service_instance.upload(
                 entry_id=self.deployment_id,
                 source=temp_dir)
         finally:
@@ -176,7 +176,7 @@ class StorageManager(LoggerMixin):
 
         self.logger.debug('creating deployment model storage entry')
         now = datetime.utcnow()
-        deployment = self.model_storage.deployment.model_cls(
+        deployment = self.model_storage.service_instance.model_cls(
             id=self.deployment_id,
             blueprint_id=self.blueprint_id,
             description=self.deployment_plan['description'],
@@ -190,7 +190,7 @@ class StorageManager(LoggerMixin):
             created_at=now,
             updated_at=now
         )
-        self.model_storage.deployment.put(deployment)
+        self.model_storage.service_instance.put(deployment)
         self.logger.debug('created deployment model storage entry')
 
     def create_node_instances_storage(self):


Mime
View raw message