You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by em...@apache.org on 2017/02/24 23:13:15 UTC

[4/4] incubator-ariatosca git commit: Service instance models

Service instance models


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/f62cdd4b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/f62cdd4b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/f62cdd4b

Branch: refs/heads/ARIA-105-integrate-modeling
Commit: f62cdd4b95c556c3b893654cba6842b1f23f51a0
Parents: f1074fe
Author: Tal Liron <ta...@gmail.com>
Authored: Fri Feb 24 17:12:46 2017 -0600
Committer: Tal Liron <ta...@gmail.com>
Committed: Fri Feb 24 17:12:46 2017 -0600

----------------------------------------------------------------------
 aria/modeling/__init__.py                       |   10 +-
 aria/modeling/base.py                           |  334 ++++
 aria/modeling/model.py                          |  114 +-
 aria/modeling/orchestrator_models.py            |  108 +-
 aria/modeling/service_instance_models.py        | 1225 --------------
 aria/modeling/service_models.py                 | 1138 +++++++++++++
 aria/modeling/service_template_models.py        | 1585 +++++++++---------
 aria/modeling/shared_service_models.py          |  109 --
 aria/modeling/structure.py                      |  351 ----
 aria/modeling/utils.py                          |   15 +-
 .../simple_v1_0/modeling/__init__.py            |   14 +-
 11 files changed, 2410 insertions(+), 2593 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f62cdd4b/aria/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/modeling/__init__.py b/aria/modeling/__init__.py
index 393a8cd..bb0c8f1 100644
--- a/aria/modeling/__init__.py
+++ b/aria/modeling/__init__.py
@@ -16,25 +16,25 @@
 from collections import namedtuple
 
 from . import (
-    structure,
+    base,
     type,
     model,
     service_template_models as _service_template_models_base,
-    service_instance_models as _service_instance_models_base,
+    service_models as _service_models_base,
     orchestrator_models as _orchestrator_models_base,
 )
 
 
 _ModelBaseCls = namedtuple('ModelBase', 'service_template_models,'
-                                        'service_instance_models,'
+                                        'service_models,'
                                         'orchestrator_models')
 model_base = _ModelBaseCls(service_template_models=_service_template_models_base,
-                           service_instance_models=_service_instance_models_base,
+                           service_models=_service_models_base,
                            orchestrator_models=_orchestrator_models_base)
                            
 
 __all__ = (
-    'structure',
+    'base',
     'type',
     'model',
     'model_base',

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f62cdd4b/aria/modeling/base.py
----------------------------------------------------------------------
diff --git a/aria/modeling/base.py b/aria/modeling/base.py
new file mode 100644
index 0000000..183c669
--- /dev/null
+++ b/aria/modeling/base.py
@@ -0,0 +1,334 @@
+# 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:
+    * ModelMixin - abstract model implementation.
+    * ModelIDMixin - abstract model implementation with IDs.
+"""
+
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.orm.collections import attribute_mapped_collection
+from sqlalchemy.ext import associationproxy
+from sqlalchemy import (
+    Column,
+    ForeignKey,
+    Integer,
+    Text,
+    Table,
+)
+
+from . import utils
+
+
+class ModelMixin(object):
+
+    @utils.classproperty
+    def __modelname__(cls):                                                                         # pylint: disable=no-self-argument
+        return getattr(cls, '__mapiname__', cls.__tablename__)
+
+    @classmethod
+    def id_column_name(cls):
+        raise NotImplementedError
+
+    @classmethod
+    def name_column_name(cls):
+        raise NotImplementedError
+
+    @classmethod
+    def foreign_key(cls, parent_table_name, nullable=False):
+        """
+        Return a ForeignKey object.
+
+        :param parent_table_name: Parent table name
+        :param nullable: Should the column be allowed to remain empty
+        """
+        return Column(Integer,
+                      ForeignKey('{table}.id'.format(table=parent_table_name),
+                                 ondelete='CASCADE'),
+                      nullable=nullable)
+
+    @classmethod
+    def relationship_to_self(cls,
+                             column_name,
+                             relationship_kwargs=None):
+        relationship_kwargs = relationship_kwargs or {}
+
+        remote_side_str = '{cls}.{remote_column}'.format(
+            cls=cls.__name__,
+            remote_column=cls.id_column_name()
+        )
+
+        primaryjoin_str = '{remote_side_str} == {cls}.{column}'.format(
+            remote_side_str=remote_side_str,
+            cls=cls.__name__,
+            column=column_name
+        )
+
+        return relationship(
+            cls._get_cls_by_tablename(cls.__tablename__).__name__,
+            primaryjoin=primaryjoin_str,
+            remote_side=remote_side_str,
+            post_update=True,
+            **relationship_kwargs
+        )
+
+    @classmethod
+    def one_to_one_relationship(cls,
+                                other_table_name,
+                                backreference=None,
+                                relationship_kwargs=None):
+        relationship_kwargs = relationship_kwargs or {}
+
+        return relationship(
+            lambda: cls._get_cls_by_tablename(other_table_name),
+            backref=backref(backreference or cls.__tablename__, uselist=False),
+            **relationship_kwargs
+        )
+
+    @classmethod
+    def one_to_many_relationship(cls,
+                                 child_table_name,
+                                 foreign_key_name=None,
+                                 backreference=None,
+                                 key_column_name=None,
+                                 relationship_kwargs=None):
+        relationship_kwargs = relationship_kwargs or {}
+        
+        #foreign_keys = lambda: '{0}.{1}'.format(cls._get_cls_by_tablename(child_table_name).__name__,
+        #                                foreign_key_name) \
+        foreign_keys = lambda: getattr(cls._get_cls_by_tablename(child_table_name),
+                                       foreign_key_name) \
+            if foreign_key_name \
+            else None
+        #foreign_keys = foreign_key_name or None
+
+        collection_class = attribute_mapped_collection(key_column_name) \
+            if key_column_name \
+            else list
+
+        return relationship(
+            lambda: cls._get_cls_by_tablename(child_table_name),
+            backref=backref(backreference or cls.__tablename__, uselist=False),
+            foreign_keys=foreign_keys,
+            collection_class=collection_class,
+            **relationship_kwargs
+        )
+
+    @classmethod
+    def many_to_one_relationship(cls,
+                                 parent_table_name,
+                                 foreign_key_column=None,
+                                 backreference=None,
+                                 backref_kwargs=None,
+                                 relationship_kwargs=None):
+        """
+        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)
+        """
+        relationship_kwargs = relationship_kwargs or {}
+
+        if foreign_key_column:
+            relationship_kwargs.setdefault('foreign_keys', getattr(cls, foreign_key_column))
+
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('lazy', 'dynamic')
+        # The following line make sure that when the *parent* is deleted, all its connected children
+        # are deleted as well
+        backref_kwargs.setdefault('cascade', 'all')
+
+        return relationship(
+            lambda: cls._get_cls_by_tablename(parent_table_name),
+            backref=backref(backreference or utils.pluralize(cls.__tablename__), **backref_kwargs),
+            **relationship_kwargs
+        )
+
+    @classmethod
+    def many_to_many_relationship(cls,
+                                  other_table_name,
+                                  table_prefix=None,
+                                  key_column_name=None,
+                                  relationship_kwargs=None):
+        """
+        Return a many-to-many SQL relationship object
+
+        Notes:
+
+        1. The backreference name is the current table's table name
+        2. This method creates a new helper table in the DB
+
+        :param cls: The class of the table we're connecting from
+        :param other_table_name: The class of the table we're connecting to
+        :param table_prefix: Custom prefix for the helper table name and the
+                             backreference name
+        :param key_column_name: If provided, will use a dict class with this column as the key 
+        """
+        relationship_kwargs = relationship_kwargs or {}
+        
+        current_table_name = cls.__tablename__
+        current_column_name = '{0}_id'.format(current_table_name)
+        current_foreign_key = '{0}.id'.format(current_table_name)
+
+        other_column_name = '{0}_id'.format(other_table_name)
+        other_foreign_key = '{0}.id'.format(other_table_name)
+
+        helper_table_name = '{0}_{1}'.format(current_table_name, other_table_name)
+
+        backref_name = current_table_name
+        if table_prefix:
+            helper_table_name = '{0}_{1}'.format(table_prefix, helper_table_name)
+            backref_name = '{0}_{1}'.format(table_prefix, backref_name)
+
+        secondary_table = cls.get_secondary_table(
+            cls.metadata,
+            helper_table_name,
+            current_column_name,
+            other_column_name,
+            current_foreign_key,
+            other_foreign_key
+        )
+
+        collection_class = attribute_mapped_collection(key_column_name) \
+            if key_column_name \
+            else list
+
+        return relationship(
+            lambda: cls._get_cls_by_tablename(other_table_name),
+            secondary=secondary_table,
+            backref=backref(backref_name),
+            collection_class=collection_class,
+            **relationship_kwargs
+        )
+
+    @staticmethod
+    def get_secondary_table(metadata,
+                            helper_table_name,
+                            first_column_name,
+                            second_column_name,
+                            first_foreign_key,
+                            second_foreign_key):
+        """
+        Create a helper table for a many-to-many relationship
+
+        :param helper_table_name: The name of the table
+        :param first_column_name: The name of the first column in the table
+        :param second_column_name: The name of the second column in the table
+        :param first_foreign_key: The string representing the first foreign key,
+               for example `blueprint.storage_id`, or `tenants.id`
+        :param second_foreign_key: The string representing the second foreign key
+        :return: A Table object
+        """
+        return Table(
+            helper_table_name,
+            metadata,
+            Column(
+                first_column_name,
+                Integer,
+                ForeignKey(first_foreign_key)
+            ),
+            Column(
+                second_column_name,
+                Integer,
+                ForeignKey(second_foreign_key)
+            )
+        )
+
+    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__', []))
+
+    @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 == getattr(table_cls, '__tablename__', None):
+                return table_cls
+
+    def __repr__(self):
+        return '<{cls} id=`{id}`>'.format(
+            cls=self.__class__.__name__,
+            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/f62cdd4b/aria/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/modeling/model.py b/aria/modeling/model.py
index 8459325..fdd8af8 100644
--- a/aria/modeling/model.py
+++ b/aria/modeling/model.py
@@ -17,70 +17,74 @@ from sqlalchemy.ext.declarative import declarative_base
 
 from . import (
     service_template_models,
-    service_instance_models,
+    service_models,
     orchestrator_models,
-    shared_service_models,
-    structure,
+    base,
 )
 
 __all__ = (
     'aria_declarative_base',
 
-    'Parameter',
-    'Metadata',
-
-    'MappingTemplate',
-    'InterfaceTemplate',
-    'OperationTemplate',
+     # Service template models
     'ServiceTemplate',
     'NodeTemplate',
     'GroupTemplate',
-    'ArtifactTemplate',
     'PolicyTemplate',
+    'SubstitutionTemplate',
+    'SubstitutionTemplateMapping',
     'RequirementTemplate',
-    'CapabilityTemplate',
     'RelationshipTemplate',
+    'CapabilityTemplate',
+    'InterfaceTemplate',
+    'OperationTemplate',
+    'ArtifactTemplate',
 
-    'Mapping',
-    'Substitution',
-    'ServiceInstance',
+    # Service template and instance models
+    'Parameter',
+    'Metadata',
+
+    # Service instance models
+    'Service',
     'Node',
-    'Relationship',
-    'Artifact',
     'Group',
+    'Policy',
+    'Substitution',
+    'SubstitutionMapping',
+    'Relationship',
+    'Capability',
     'Interface',
     'Operation',
-    'Capability',
-    'Policy',
+    'Artifact',
 
+    # Orchestrator models
     'Execution',
-    'ServiceInstanceUpdate',
-    'ServiceInstanceUpdateStep',
-    'ServiceInstanceModification',
+    'ServiceUpdate',
+    'ServiceUpdateStep',
+    'ServiceModification',
     'Plugin',
     'Task'
 )
 
-aria_declarative_base = declarative_base(cls=structure.ModelIDMixin)
+aria_declarative_base = declarative_base(cls=base.ModelIDMixin)
 
 # pylint: disable=abstract-method
 
 
-# region shared service models
+# region service template models
 
-class Parameter(aria_declarative_base, shared_service_models.ParameterBase):
+class ServiceTemplate(aria_declarative_base, service_template_models.ServiceTemplateBase):
     pass
 
 
-class Metadata(aria_declarative_base, shared_service_models.MetadataBase):
+class NodeTemplate(aria_declarative_base, service_template_models.NodeTemplateBase):
     pass
 
-# endregion
 
+class GroupTemplate(aria_declarative_base, service_template_models.GroupTemplateBase):
+    pass
 
-# region service template models
 
-class MappingTemplate(aria_declarative_base, service_template_models.MappingTemplateBase):
+class PolicyTemplate(aria_declarative_base, service_template_models.PolicyTemplateBase):
     pass
 
 
@@ -88,43 +92,44 @@ class SubstitutionTemplate(aria_declarative_base, service_template_models.Substi
     pass
 
 
-class InterfaceTemplate(aria_declarative_base, service_template_models.InterfaceTemplateBase):
+class SubstitutionTemplateMapping(aria_declarative_base,
+                                  service_template_models.SubstitutionTemplateMappingBase):
     pass
 
 
-class OperationTemplate(aria_declarative_base, service_template_models.OperationTemplateBase):
+class RequirementTemplate(aria_declarative_base, service_template_models.RequirementTemplateBase):
     pass
 
 
-class ServiceTemplate(aria_declarative_base, service_template_models.ServiceTemplateBase):
+class RelationshipTemplate(aria_declarative_base, service_template_models.RelationshipTemplateBase):
     pass
 
 
-class NodeTemplate(aria_declarative_base, service_template_models.NodeTemplateBase):
+class CapabilityTemplate(aria_declarative_base, service_template_models.CapabilityTemplateBase):
     pass
 
 
-class GroupTemplate(aria_declarative_base, service_template_models.GroupTemplateBase):
+class InterfaceTemplate(aria_declarative_base, service_template_models.InterfaceTemplateBase):
     pass
 
 
-class ArtifactTemplate(aria_declarative_base, service_template_models.ArtifactTemplateBase):
+class OperationTemplate(aria_declarative_base, service_template_models.OperationTemplateBase):
     pass
 
 
-class PolicyTemplate(aria_declarative_base, service_template_models.PolicyTemplateBase):
+class ArtifactTemplate(aria_declarative_base, service_template_models.ArtifactTemplateBase):
     pass
 
+# endregion
 
-class RequirementTemplate(aria_declarative_base, service_template_models.RequirementTemplateBase):
-    pass
 
+# region service template and instance models
 
-class CapabilityTemplate(aria_declarative_base, service_template_models.CapabilityTemplateBase):
+class Parameter(aria_declarative_base, service_template_models.ParameterBase):
     pass
 
 
-class RelationshipTemplate(aria_declarative_base, service_template_models.RelationshipTemplateBase):
+class Metadata(aria_declarative_base, service_template_models.MetadataBase):
     pass
 
 # endregion
@@ -132,47 +137,47 @@ class RelationshipTemplate(aria_declarative_base, service_template_models.Relati
 
 # region service instance models
 
-class Mapping(aria_declarative_base, service_instance_models.MappingBase):
+class Service(aria_declarative_base, service_models.ServiceBase):
     pass
 
 
-class Substitution(aria_declarative_base, service_instance_models.SubstitutionBase):
+class Node(aria_declarative_base, service_models.NodeBase):
     pass
 
 
-class ServiceInstance(aria_declarative_base, service_instance_models.ServiceInstanceBase):
+class Group(aria_declarative_base, service_models.GroupBase):
     pass
 
 
-class Node(aria_declarative_base, service_instance_models.NodeBase):
+class Policy(aria_declarative_base, service_models.PolicyBase):
     pass
 
 
-class Relationship(aria_declarative_base, service_instance_models.RelationshipBase):
+class Substitution(aria_declarative_base, service_models.SubstitutionBase):
     pass
 
 
-class Artifact(aria_declarative_base, service_instance_models.ArtifactBase):
+class SubstitutionMapping(aria_declarative_base, service_models.SubstitutionMappingBase):
     pass
 
 
-class Group(aria_declarative_base, service_instance_models.GroupBase):
+class Relationship(aria_declarative_base, service_models.RelationshipBase):
     pass
 
 
-class Interface(aria_declarative_base, service_instance_models.InterfaceBase):
+class Capability(aria_declarative_base, service_models.CapabilityBase):
     pass
 
 
-class Operation(aria_declarative_base, service_instance_models.OperationBase):
+class Interface(aria_declarative_base, service_models.InterfaceBase):
     pass
 
 
-class Capability(aria_declarative_base, service_instance_models.CapabilityBase):
+class Operation(aria_declarative_base, service_models.OperationBase):
     pass
 
 
-class Policy(aria_declarative_base, service_instance_models.PolicyBase):
+class Artifact(aria_declarative_base, service_models.ArtifactBase):
     pass
 
 # endregion
@@ -184,18 +189,15 @@ class Execution(aria_declarative_base, orchestrator_models.Execution):
     pass
 
 
-class ServiceInstanceUpdate(aria_declarative_base,
-                            orchestrator_models.ServiceInstanceUpdateBase):
+class ServiceUpdate(aria_declarative_base, orchestrator_models.ServiceUpdateBase):
     pass
 
 
-class ServiceInstanceUpdateStep(aria_declarative_base,
-                                orchestrator_models.ServiceInstanceUpdateStepBase):
+class ServiceUpdateStep(aria_declarative_base, orchestrator_models.ServiceUpdateStepBase):
     pass
 
 
-class ServiceInstanceModification(aria_declarative_base,
-                                  orchestrator_models.ServiceInstanceModificationBase):
+class ServiceModification(aria_declarative_base, orchestrator_models.ServiceModificationBase):
     pass
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f62cdd4b/aria/modeling/orchestrator_models.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestrator_models.py b/aria/modeling/orchestrator_models.py
index d76c234..4e8243f 100644
--- a/aria/modeling/orchestrator_models.py
+++ b/aria/modeling/orchestrator_models.py
@@ -14,26 +14,18 @@
 # limitations under the License.
 
 """
-Aria's storage.models module
+ARIA's storage.models module
 Path: aria.storage.models
 
-models module holds aria's models.
+models module holds ARIA's models.
 
 classes:
-    * Field - represents a single field.
-    * IterField - represents an iterable field.
-    * Model - abstract model implementation.
-    * Snapshot - snapshots implementation model.
-    * Deployment - deployment implementation model.
-    * DeploymentUpdateStep - deployment update step implementation model.
-    * DeploymentUpdate - deployment update implementation model.
-    * DeploymentModification - deployment modification implementation model.
     * Execution - execution implementation model.
-    * Node - node implementation model.
-    * Relationship - relationship implementation model.
-    * NodeInstance - node instance implementation model.
-    * RelationshipInstance - relationship instance implementation model.
+    * ServiceUpdate - service update implementation model.
+    * ServiceUpdateStep - service update step implementation model.
+    * ServiceModification - service modification implementation model.
     * Plugin - plugin implementation model.
+    * Task - a task
 """
 from collections import namedtuple
 from datetime import datetime
@@ -55,13 +47,13 @@ from sqlalchemy.ext.declarative import declared_attr
 from ..orchestrator.exceptions import TaskAbortException, TaskRetryException
 
 from .type import List, Dict
-from .structure import ModelMixin
+from .base import ModelMixin
 
 __all__ = (
-    'ServiceInstanceUpdateStepBase',
-    'ServiceInstanceUpdateBase',
-    'ServiceInstanceModificationBase',
     'Execution',
+    'ServiceUpdateBase',
+    'ServiceUpdateStepBase',
+    'ServiceModificationBase',
     'PluginBase',
     'TaskBase'
 )
@@ -76,7 +68,7 @@ class Execution(ModelMixin):
     # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
     __tablename__ = 'execution'
 
-    __private_fields__ = ['service_instance_fk']
+    __private_fields__ = ['service_fk']
 
     TERMINATED = 'terminated'
     FAILED = 'failed'
@@ -123,23 +115,23 @@ class Execution(ModelMixin):
 
     @declared_attr
     def service_template(cls):
-        return association_proxy('service_instance', 'service_template')
+        return association_proxy('service', 'service_template')
 
     @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
+    def service_fk(cls):
+        return cls.foreign_key('service')
 
     @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance')
+    def service(cls):
+        return cls.many_to_one_relationship('service')
 
     @declared_attr
-    def service_instance_name(cls):
-        return association_proxy('service_instance', cls.name_column_name())
+    def service_name(cls):
+        return association_proxy('service', cls.name_column_name())
 
     @declared_attr
     def service_template_name(cls):
-        return association_proxy('service_instance', 'service_template_name')
+        return association_proxy('service', 'service_template_name')
 
     def __str__(self):
         return '<{0} id=`{1}` (status={2})>'.format(
@@ -149,24 +141,24 @@ class Execution(ModelMixin):
         )
 
 
-class ServiceInstanceUpdateBase(ModelMixin):
+class ServiceUpdateBase(ModelMixin):
     """
     Deployment update model representation.
     """
     # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
     steps = None
 
-    __tablename__ = 'service_instance_update'
-    __private_fields__ = ['service_instance_fk',
+    __tablename__ = 'service_update'
+    __private_fields__ = ['service_fk',
                           'execution_fk']
 
     _private_fields = ['execution_fk', 'deployment_fk']
 
     created_at = Column(DateTime, nullable=False, index=True)
-    service_instance_plan = Column(Dict, nullable=False)
-    service_instance_update_node_instances = Column(Dict)
-    service_instance_update_service_instance = Column(Dict)
-    service_instance_update_nodes = Column(List)
+    service_plan = Column(Dict, nullable=False)
+    service_update_node_instances = Column(Dict)
+    service_update_service_instance = Column(Dict)
+    service_update_nodes = Column(List)
     modified_entity_ids = Column(Dict)
     state = Column(Text)
 
@@ -183,31 +175,31 @@ class ServiceInstanceUpdateBase(ModelMixin):
         return association_proxy('execution', cls.name_column_name())
 
     @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
+    def service_fk(cls):
+        return cls.foreign_key('service')
 
     @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance')
+    def service(cls):
+        return cls.many_to_one_relationship('service')
 
     @declared_attr
-    def service_instance_name(cls):
-        return association_proxy('service_instance', cls.name_column_name())
+    def service_name(cls):
+        return association_proxy('service', cls.name_column_name())
 
     def to_dict(self, suppress_error=False, **kwargs):
-        dep_update_dict = super(ServiceInstanceUpdateBase, self).to_dict(suppress_error)     #pylint: disable=no-member
+        dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error)     #pylint: disable=no-member
         # Taking care of the fact the DeploymentSteps are _BaseModels
         dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
         return dep_update_dict
 
 
-class ServiceInstanceUpdateStepBase(ModelMixin):
+class ServiceUpdateStepBase(ModelMixin):
     """
     Deployment update step model representation.
     """
     # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
-    __tablename__ = 'service_instance_update_step'
-    __private_fields__ = ['service_instance_update_fk']
+    __tablename__ = 'service_update_step'
+    __private_fields__ = ['service_update_fk']
 
     _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
     ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
@@ -234,12 +226,12 @@ class ServiceInstanceUpdateStepBase(ModelMixin):
     entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
 
     @declared_attr
-    def service_instance_update_fk(cls):
-        return cls.foreign_key('service_instance_update')
+    def service_update_fk(cls):
+        return cls.foreign_key('service_update')
 
     @declared_attr
-    def service_instance_update(cls):
-        return cls.many_to_one_relationship('service_instance_update',
+    def service_update(cls):
+        return cls.many_to_one_relationship('service_update',
                                             backreference='steps')
 
     @declared_attr
@@ -274,12 +266,12 @@ class ServiceInstanceUpdateStepBase(ModelMixin):
         return False
 
 
-class ServiceInstanceModificationBase(ModelMixin):
+class ServiceModificationBase(ModelMixin):
     """
     Deployment modification model representation.
     """
-    __tablename__ = 'service_instance_modification'
-    __private_fields__ = ['service_instance_fk']
+    __tablename__ = 'service_modification'
+    __private_fields__ = ['service_fk']
 
     STARTED = 'started'
     FINISHED = 'finished'
@@ -296,17 +288,17 @@ class ServiceInstanceModificationBase(ModelMixin):
     status = Column(Enum(*STATES, name='deployment_modification_status'))
 
     @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
+    def service_fk(cls):
+        return cls.foreign_key('service')
 
     @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance',
+    def service(cls):
+        return cls.many_to_one_relationship('service',
                                             backreference='modifications')
 
     @declared_attr
-    def service_instance_name(cls):
-        return association_proxy('service_instance', cls.name_column_name())
+    def service_name(cls):
+        return association_proxy('service', cls.name_column_name())
 
 
 class PluginBase(ModelMixin):
@@ -356,7 +348,7 @@ class TaskBase(ModelMixin):
 
     @declared_attr
     def relationship_name(cls):
-        return association_proxy('relationships', cls.name_column_name())
+        return association_proxy('relationship', cls.name_column_name())
 
     @declared_attr
     def relationship(cls):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f62cdd4b/aria/modeling/service_instance_models.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance_models.py b/aria/modeling/service_instance_models.py
deleted file mode 100644
index 52b88af..0000000
--- a/aria/modeling/service_instance_models.py
+++ /dev/null
@@ -1,1225 +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.
-
-from sqlalchemy import (
-    Column,
-    Text,
-    Integer,
-    Boolean,
-)
-from sqlalchemy import DateTime
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy.ext.orderinglist import ordering_list
-
-from ..parser import validation
-from ..utils import collections, formatting, console
-
-from . import (
-    utils,
-    structure,
-    type as aria_types
-)
-
-# pylint: disable=no-self-argument, no-member, abstract-method
-
-
-class ServiceInstanceBase(structure.InstanceModelMixin):
-    """
-    A service instance is an instance of a :class:`ServiceTemplate`.
-
-    You will usually not create it programmatically, but instead instantiate it from the template.
-
-    Properties:
-
-    * :code:`description`: Human-readable description
-    * :code:`meta_data`: Dict of :class:`Metadata`
-    * :code:`nodes`: Dict of :class:`Node`
-    * :code:`groups`: Dict of :class:`Group`
-    * :code:`policies`: Dict of :class:`Policy`
-    * :code:`substitution`: :class:`Substitution`
-    * :code:`inputs`: Dict of :class:`Parameter`
-    * :code:`outputs`: Dict of :class:`Parameter`
-    * :code:`operations`: Dict of :class:`Operation`
-    """
-
-    __tablename__ = 'service_instance'
-
-    __private_fields__ = ['substituion_fk',
-                          'service_template_fk']
-
-    description = Column(Text)
-
-    # region orchestrator required columns
-
-    created_at = Column(DateTime, nullable=False, index=True)
-    permalink = Column(Text)
-    policy_triggers = Column(aria_types.Dict)
-    policy_types = Column(aria_types.Dict)
-    scaling_groups = Column(aria_types.Dict)
-    updated_at = Column(DateTime)
-    workflows = Column(aria_types.Dict)
-
-    @declared_attr
-    def service_template_name(cls):
-        return association_proxy('service_template', 'name')
-
-    # endregion
-
-    # region foreign keys
-
-    @declared_attr
-    def substitution_fk(cls):
-        return cls.foreign_key('substitution', nullable=True)
-
-    @declared_attr
-    def service_template_fk(cls):
-        return cls.foreign_key('service_template')
-
-    # endregion
-
-    # region one-to-one relationships
-
-    @declared_attr
-    def substitution(cls):
-        return cls.one_to_one_relationship('substitution')
-
-    # endregion
-
-    # region one-to-many relationships
-
-    @declared_attr
-    def interfaces(cls):
-        return cls.one_to_many_relationship('interfaces', key_column_name='name')
-
-    # endregion
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def service_template(cls):
-        return cls.many_to_one_relationship('service_template')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def meta_data(cls):
-        # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy!
-        return cls.many_to_many_relationship('metadata', key_column_name='name')
-    
-    @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
-
-    # association proxies
-
-    def satisfy_requirements(self, context):
-        satisfied = True
-        for node in self.nodes.all():
-            if not node.satisfy_requirements(context):
-                satisfied = False
-        return satisfied
-
-    def validate_capabilities(self, context):
-        satisfied = True
-        for node in self.nodes.all():
-            if not node.validate_capabilities(context):
-                satisfied = False
-        return satisfied
-
-    def find_nodes(self, node_template_name):
-        nodes = []
-        for node in self.nodes.all():
-            if node.template_name == node_template_name:
-                nodes.append(node)
-        return collections.FrozenList(nodes)
-
-    def get_node_ids(self, node_template_name):
-        return collections.FrozenList((node.id for node in self.find_nodes(node_template_name)))
-
-    def find_groups(self, group_template_name):
-        groups = []
-        for group in self.groups.all():
-            if group.template_name == group_template_name:
-                groups.append(group)
-        return collections.FrozenList(groups)
-
-    def get_group_ids(self, group_template_name):
-        return collections.FrozenList((group.id for group in self.find_groups(group_template_name)))
-
-    def is_node_a_target(self, context, target_node):
-        for node in self.nodes.all():
-            if self._is_node_a_target(context, node, target_node):
-                return True
-        return False
-
-    def _is_node_a_target(self, context, source_node, target_node):
-        if source_node.relationships:
-            for relationship in source_node.relationships:
-                if relationship.target_node_id == target_node.id:
-                    return True
-                else:
-                    node = context.modeling.instance.nodes.get(relationship.target_node_id)
-                    if node is not None:
-                        if self._is_node_a_target(context, node, target_node):
-                            return True
-        return False
-
-
-class OperationBase(structure.InstanceModelMixin):
-    """
-    An operation in a :class:`Interface`.
-
-    Properties:
-
-    * :code:`name`: Name
-    * :code:`description`: Description
-    * :code:`implementation`: Implementation string (interpreted by the orchestrator)
-    * :code:`dependencies`: List of strings (interpreted by the orchestrator)
-    * :code:`executor`: Executor string (interpreted by the orchestrator)
-    * :code:`max_retries`: Maximum number of retries allowed in case of failure
-    * :code:`retry_interval`: Interval between retries
-    * :code:`inputs`: Dict of :class:`Parameter`
-    """
-
-    __tablename__ = 'operation'
-
-    __private_fields__ = ['service_template_fk',
-                          'interface_instance_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance', nullable=True)
-
-    @declared_attr
-    def interface_instance_fk(cls):
-        return cls.foreign_key('interface', nullable=True)
-
-    # endregion
-
-    description = Column(Text)
-    implementation = Column(Text)
-    dependencies = Column(aria_types.StrictList(item_cls=basestring))
-
-    executor = Column(Text)
-    max_retries = Column(Integer, default=None)
-    retry_interval = Column(Integer, default=None)
-    plugin = Column(Text)
-    operation = Column(Boolean)
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance')
-
-    @declared_attr
-    def interface(cls):
-        return cls.many_to_one_relationship('interface')
-
-    # endregion
-
-    # 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 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))
-            utils.dump_parameters(context, self.inputs, 'Inputs')
-
-
-class InterfaceBase(structure.InstanceModelMixin):
-    """
-    A typed set of :class:`Operation`.
-
-    Properties:
-
-    * :code:`name`: Name
-    * :code:`description`: Description
-    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`inputs`: Dict of :class:`Parameter`
-    * :code:`operations`: Dict of :class:`Operation`
-    """
-
-    __tablename__ = 'interface'
-
-    __private_fields__ = ['group_fk',
-                          'node_fk',
-                          'relationship_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def group_fk(cls):
-        return cls.foreign_key('group', nullable=True)
-
-    @declared_attr
-    def node_fk(cls):
-        return cls.foreign_key('node', nullable=True)
-
-    @declared_attr
-    def relationship_fk(cls):
-        return cls.foreign_key('relationship', nullable=True)
-
-    # endregion
-
-    description = Column(Text)
-    type_name = Column(Text)
-    edge = Column(Text)
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def node(cls):
-        return cls.many_to_one_relationship('node')
-
-    @declared_attr
-    def relationship(cls):
-        return cls.many_to_one_relationship('relationship')
-
-    @declared_attr
-    def group(cls):
-        return cls.many_to_one_relationship('group')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
-                                             key_column_name='name')
-
-    # 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.inputs)),
-            ('operations', formatting.as_raw_list(self.operations))))
-
-    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.operations)
-
-    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.operations, 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))
-            utils.dump_parameters(context, self.inputs, 'Inputs')
-            utils.dump_dict_values(context, self.operations, 'Operations')
-
-
-class CapabilityBase(structure.InstanceModelMixin):
-    """
-    A capability of a :class:`Node`.
-
-    An instance of a :class:`CapabilityTemplate`.
-
-    Properties:
-
-    * :code:`name`: Name
-    * :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:`properties`: Dict of :class:`Parameter`
-    """
-
-    __tablename__ = 'capability'
-
-    __private_fields__ = ['node_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def node_fk(cls):
-        return cls.foreign_key('node')
-
-    # endregion
-
-    type_name = Column(Text)
-
-    min_occurrences = Column(Integer, default=None) # optional
-    max_occurrences = Column(Integer, default=None) # optional
-    occurrences = Column(Integer, default=0)
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def node(cls):
-        return cls.many_to_one_relationship('node')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # endregion
-
-    @property
-    def has_enough_relationships(self):
-        if self.min_occurrences is not None:
-            return self.occurrences >= self.min_occurrences
-        return True
-
-    def relate(self):
-        if self.max_occurrences is not None:
-            if self.occurrences == self.max_occurrences:
-                return False
-        self.occurrences += 1
-        return True
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('type_name', self.type_name),
-            ('properties', formatting.as_raw_dict(self.properties))))
-
-    def validate(self, context):
-        if context.modeling.capability_types.get_descendant(self.type_name) is None:
-            context.validation.report('capability "%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))
-        with context.style.indent:
-            console.puts('Type: %s' % context.style.type(self.type_name))
-            console.puts('Occurrences: %s (%s%s)'
-                         % (self.occurrences,
-                            self.min_occurrences or 0,
-                            (' to %d' % self.max_occurrences)
-                            if self.max_occurrences is not None
-                            else ' or more'))
-            utils.dump_parameters(context, self.properties)
-
-
-class ArtifactBase(structure.InstanceModelMixin):
-    """
-    A file associated with a :class:`Node`.
-
-    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'
-
-    __private_fields__ = ['node_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def node_fk(cls):
-        return cls.foreign_key('node')
-
-    # endregion
-
-    description = Column(Text)
-    type_name = Column(Text)
-    source_path = Column(Text)
-    target_path = Column(Text)
-    repository_url = Column(Text)
-    repository_credential = Column(aria_types.StrictDict(basestring, basestring))
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def node(cls):
-        return cls.many_to_one_relationship('node')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # 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))))
-
-    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))
-            utils.dump_parameters(context, self.properties)
-
-
-class PolicyBase(structure.InstanceModelMixin):
-    """
-    An instance of a :class:`PolicyTemplate`.
-
-    Properties:
-
-    * :code:`name`: Name
-    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`properties`: Dict of :class:`Parameter`
-    * :code:`target_node_ids`: Must be represented in the :class:`ServiceInstance`
-    * :code:`target_group_ids`: Must be represented in the :class:`ServiceInstance`
-    """
-
-    __tablename__ = 'policy'
-
-    __private_fields__ = ['service_instance_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
-
-    # endregion
-
-    type_name = Column(Text)
-    target_node_ids = Column(aria_types.StrictList(basestring))
-    target_group_ids = Column(aria_types.StrictList(basestring))
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def service_instnce(cls):
-        return cls.many_to_one_relationship('service_instance')
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # endregion
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('type_name', self.type_name),
-            ('properties', formatting.as_raw_dict(self.properties)),
-            ('target_node_ids', self.target_node_ids),
-            ('target_group_ids', self.target_group_ids)))
-
-    def validate(self, context):
-        if context.modeling.policy_types.get_descendant(self.type_name) is None:
-            context.validation.report('policy "%s" has an unknown type: %s'
-                                      % (self.name, utils.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('Policy: %s' % context.style.node(self.name))
-        with context.style.indent:
-            console.puts('Type: %s' % context.style.type(self.type_name))
-            utils.dump_parameters(context, self.properties)
-            if self.target_node_ids:
-                console.puts('Target nodes:')
-                with context.style.indent:
-                    for node_id in self.target_node_ids:
-                        console.puts(context.style.node(node_id))
-            if self.target_group_ids:
-                console.puts('Target groups:')
-                with context.style.indent:
-                    for group_id in self.target_group_ids:
-                        console.puts(context.style.node(group_id))
-
-
-class MappingBase(structure.InstanceModelMixin):
-    """
-    An instance of a :class:`MappingTemplate`.
-
-    Properties:
-
-    * :code:`mapped_name`: Exposed capability or requirement name
-    * :code:`node_id`: Must be represented in the :class:`ServiceInstance`
-    * :code:`name`: Name of capability or requirement at the node
-    """
-
-    __tablename__ = 'mapping'
-
-    mapped_name = Column(Text)
-    node_id = Column(Text)
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('mapped_name', self.mapped_name),
-            ('node_id', self.node_id),
-            ('name', self.name)))
-
-    def dump(self, context):
-        console.puts('%s -> %s.%s'
-                     % (context.style.node(self.mapped_name),
-                        context.style.node(self.node_id),
-                        context.style.node(self.name)))
-
-
-class SubstitutionBase(structure.InstanceModelMixin):
-    """
-    An instance of a :class:`SubstitutionTemplate`.
-
-    Properties:
-
-    * :code:`node_type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`capabilities`: Dict of :class:`Mapping`
-    * :code:`requirements`: Dict of :class:`Mapping`
-    """
-
-    __tablename__ = 'substitution'
-
-    node_type_name = Column(Text)
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def capabilities(cls):
-        return cls.many_to_many_relationship('mapping', table_prefix='capabilities')
-
-    @declared_attr
-    def requirements(cls):
-        return cls.many_to_many_relationship('mapping',
-                                             table_prefix='requirements',
-                                             relationship_kwargs=dict(lazy='dynamic'))
-
-    # endregion
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('node_type_name', self.node_type_name),
-            ('capabilities', formatting.as_raw_list(self.capabilities)),
-            ('requirements', formatting.as_raw_list(self.requirements))))
-
-    def validate(self, context):
-        if context.modeling.node_types.get_descendant(self.node_type_name) is None:
-            context.validation.report('substitution "%s" has an unknown type: %s'
-                                      % (self.name,  # pylint: disable=no-member
-                                         # TODO fix self.name reference
-                                         formatting.safe_repr(self.node_type_name)),
-                                      level=validation.Issue.BETWEEN_TYPES)
-
-        utils.validate_dict_values(context, self.capabilities)
-        utils.validate_dict_values(context, self.requirements)
-
-    def coerce_values(self, context, container, report_issues):
-        utils.coerce_dict_values(context, container, self.capabilities, report_issues)
-        utils.coerce_dict_values(context, container, self.requirements, report_issues)
-
-    def dump(self, context):
-        console.puts('Substitution:')
-        with context.style.indent:
-            console.puts('Node type: %s' % context.style.type(self.node_type_name))
-            utils.dump_dict_values(context, self.capabilities, 'Capability mappings')
-            utils.dump_dict_values(context, self.requirements, 'Requirement mappings')
-
-
-class NodeBase(structure.InstanceModelMixin):
-    """
-    An instance of a :class:`NodeTemplate`.
-
-    Nodes may have zero or more :class:`Relationship` instances to other nodes.
-
-    Properties:
-
-    * :code:`id`: Unique ID (prefixed with the template name)
-    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`template_name`: Must be represented in the :class:`ServiceTemplate`
-    * :code:`properties`: Dict of :class:`Parameter`
-    * :code:`interfaces`: Dict of :class:`Interface`
-    * :code:`artifacts`: Dict of :class:`Artifact`
-    * :code:`capabilities`: Dict of :class:`CapabilityTemplate`
-    * :code:`relationships`: List of :class:`Relationship`
-    """
-
-    __tablename__ = 'node'
-
-    __private_fields__ = ['service_instance_fk',
-                          'host_fk',
-                          'node_template_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
-
-    @declared_attr
-    def host_fk(cls):
-        return cls.foreign_key('node', nullable=True)
-
-    @declared_attr
-    def node_template_fk(cls):
-        return cls.foreign_key('node_template')
-
-    # endregion
-
-    type_name = Column(Text)
-    template_name = Column(Text)
-
-    # region orchestrator required columns
-
-    runtime_properties = Column(aria_types.Dict)
-    scaling_groups = Column(aria_types.List)
-    state = Column(Text, nullable=False)
-    version = Column(Integer, default=1)
-
-    @declared_attr
-    def plugins(cls):
-        return association_proxy('node_template', 'plugins')
-
-    @declared_attr
-    def host(cls):
-        return cls.relationship_to_self('host_fk')
-
-    @declared_attr
-    def service_instance_name(cls):
-        return association_proxy('service_instance', 'name')
-
-    @property
-    def ip(self):
-        if not self.host_fk:
-            return None
-        host_node = self.host
-        if 'ip' in host_node.runtime_properties:  # pylint: disable=no-member
-            return host_node.runtime_properties['ip']  # pylint: disable=no-member
-        host_node = host_node.node_template  # pylint: disable=no-member
-        host_ip_property = [prop for prop in host_node.properties if prop.name == 'ip']
-        if host_ip_property:
-            return host_ip_property[0].value
-        return None
-
-    @declared_attr
-    def node_template(cls):
-        return cls.many_to_one_relationship('node_template')
-
-    @declared_attr
-    def service_template(cls):
-        return association_proxy('service_instance', 'service_template')
-
-    # endregion
-
-    # region one-to-many relationships
-
-    @declared_attr
-    def interfaces(cls):
-        return cls.one_to_many_relationship('interfaces', key_column_name='name')
-
-    # endregion
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # endregion
-
-    def satisfy_requirements(self, context):
-        node_template = context.modeling.model.node_templates.get(self.template_name)
-        satisfied = True
-        for i in range(len(node_template.requirement_templates)):
-            requirement_template = node_template.requirement_templates[i]
-
-            # Find target template
-            target_node_template, target_node_capability = \
-                requirement_template.find_target(context, node_template)
-            if target_node_template is not None:
-                satisfied = self._satisfy_capability(context,
-                                                     target_node_capability,
-                                                     target_node_template,
-                                                     requirement_template,
-                                                     requirement_template_index=i)
-            else:
-                context.validation.report('requirement "%s" of node "%s" has no target node '
-                                          'template' % (requirement_template.name,
-                                                        self.id),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                satisfied = False
-        return satisfied
-
-    def _satisfy_capability(self, context, target_node_capability, target_node_template,
-                            requirement_template, requirement_template_index):
-        # Find target nodes
-        target_nodes = context.modeling.instance.find_nodes(target_node_template.name)
-        if target_nodes:
-            target_node = None
-            target_capability = None
-
-            if target_node_capability is not None:
-                # Relate to the first target node that has capacity
-                for node in target_nodes:
-                    target_capability = node.capabilities.get(target_node_capability.name)
-                    if target_capability.relate():
-                        target_node = node
-                        break
-            else:
-                # Use first target node
-                target_node = target_nodes[0]
-
-            if target_node is not None:
-                relationship = RelationshipBase(
-                    name=requirement_template.name,
-                    source_requirement_index=requirement_template_index,
-                    target_node_id=target_node.id,
-                    target_capability_name=target_capability.name
-                )
-                self.relationships.append(relationship)
-            else:
-                context.validation.report('requirement "%s" of node "%s" targets node '
-                                          'template "%s" but its instantiated nodes do not '
-                                          'have enough capacity'
-                                          % (requirement_template.name,
-                                             self.id,
-                                             target_node_template.name),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                return False
-        else:
-            context.validation.report('requirement "%s" of node "%s" targets node template '
-                                      '"%s" but it has no instantiated nodes'
-                                      % (requirement_template.name,
-                                         self.id,
-                                         target_node_template.name),
-                                      level=validation.Issue.BETWEEN_INSTANCES)
-            return False
-
-    def validate_capabilities(self, context):
-        satisfied = False
-        for capability in self.capabilities.itervalues():
-            if not capability.has_enough_relationships:
-                context.validation.report('capability "%s" of node "%s" requires at least %d '
-                                          'relationships but has %d'
-                                          % (capability.name,
-                                             self.id,
-                                             capability.min_occurrences,
-                                             capability.occurrences),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                satisfied = False
-        return satisfied
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('id', self.id),
-            ('type_name', self.type_name),
-            ('template_name', self.template_name),
-            ('properties', formatting.as_raw_dict(self.properties)),
-            ('interfaces', formatting.as_raw_list(self.interfaces)),
-            ('artifacts', formatting.as_raw_list(self.artifacts)),
-            ('capabilities', formatting.as_raw_list(self.capabilities)),
-            ('relationships', formatting.as_raw_list(self.relationships))))
-
-    def validate(self, context):
-        if len(self.id) > context.modeling.id_max_length:
-            context.validation.report('"%s" has an ID longer than the limit of %d characters: %d'
-                                      % (self.id,
-                                         context.modeling.id_max_length,
-                                         len(self.id)),
-                                      level=validation.Issue.BETWEEN_INSTANCES)
-
-        # TODO: validate that node template is of type?
-
-        utils.validate_dict_values(context, self.properties)
-        utils.validate_dict_values(context, self.interfaces)
-        utils.validate_dict_values(context, self.artifacts)
-        utils.validate_dict_values(context, self.capabilities)
-        utils.validate_list_values(context, self.relationships)
-
-    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.interfaces, report_issues)
-        utils.coerce_dict_values(context, self, self.artifacts, report_issues)
-        utils.coerce_dict_values(context, self, self.capabilities, report_issues)
-        utils.coerce_list_values(context, self, self.relationships, report_issues)
-
-    def dump(self, context):
-        console.puts('Node: %s' % context.style.node(self.id))
-        with context.style.indent:
-            console.puts('Template: %s' % context.style.node(self.template_name))
-            console.puts('Type: %s' % context.style.type(self.type_name))
-            utils.dump_parameters(context, self.properties)
-            utils.dump_interfaces(context, self.interfaces)
-            utils.dump_dict_values(context, self.artifacts, 'Artifacts')
-            utils.dump_dict_values(context, self.capabilities, 'Capabilities')
-            utils.dump_list_values(context, self.relationships, 'Relationships')
-
-
-class GroupBase(structure.InstanceModelMixin):
-    """
-    An instance of a :class:`GroupTemplate`.
-
-    Properties:
-
-    * :code:`id`: Unique ID (prefixed with the template name)
-    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`template_name`: Must be represented in the :class:`ServiceTemplate`
-    * :code:`properties`: Dict of :class:`Parameter`
-    * :code:`interfaces`: Dict of :class:`Interface`
-    * :code:`policies`: Dict of :class:`GroupPolicy`
-    * :code:`member_node_ids`: Must be represented in the :class:`ServiceInstance`
-    * :code:`member_group_ids`: Must be represented in the :class:`ServiceInstance`
-    """
-
-    __tablename__ = 'group'
-
-    __private_fields__ = ['service_instance_fk']
-
-    # region foreign_keys
-
-    @declared_attr
-    def service_instance_fk(cls):
-        return cls.foreign_key('service_instance')
-
-    # endregion
-
-    type_name = Column(Text)
-    template_name = Column(Text)
-    member_node_ids = Column(aria_types.StrictList(basestring))
-    member_group_ids = Column(aria_types.StrictList(basestring))
-
-    # region one-to-many relationships
-
-    @declared_attr
-    def interfaces(cls):
-        return cls.one_to_many_relationship('interfaces', key_column_name='name')
-
-    # endregion
-
-    # region many-to-one relationships
-
-    @declared_attr
-    def service_instance(cls):
-        return cls.many_to_one_relationship('service_instance')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # endregion
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('id', self.id),
-            ('type_name', self.type_name),
-            ('template_name', self.template_name),
-            ('properties', formatting.as_raw_dict(self.properties)),
-            ('interfaces', formatting.as_raw_list(self.interfaces)),
-            ('policies', formatting.as_raw_list(self.policies)),
-            ('member_node_ids', self.member_node_ids),
-            ('member_group_ids', self.member_group_ids)))
-
-    def validate(self, context):
-        if context.modeling.group_types.get_descendant(self.type_name) is None:
-            context.validation.report('group "%s" has an unknown type: %s'
-                                      % (self.name,  # pylint: disable=no-member
-                                         # TODO fix self.name reference
-                                         formatting.safe_repr(self.type_name)),
-                                      level=validation.Issue.BETWEEN_TYPES)
-
-        utils.validate_dict_values(context, self.properties)
-        utils.validate_dict_values(context, self.interfaces)
-        utils.validate_dict_values(context, self.policies)
-
-    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.interfaces, report_issues)
-        utils.coerce_dict_values(context, container, self.policies, report_issues)
-
-    def dump(self, context):
-        console.puts('Group: %s' % context.style.node(self.id))
-        with context.style.indent:
-            console.puts('Type: %s' % context.style.type(self.type_name))
-            console.puts('Template: %s' % context.style.type(self.template_name))
-            utils.dump_parameters(context, self.properties)
-            utils.dump_interfaces(context, self.interfaces)
-            utils.dump_dict_values(context, self.policies, 'Policies')
-            if self.member_node_ids:
-                console.puts('Member nodes:')
-                with context.style.indent:
-                    for node_id in self.member_node_ids:
-                        console.puts(context.style.node(node_id))
-
-
-class RelationshipBase(structure.InstanceModelMixin):
-    """
-    Connects :class:`Node` to another node.
-
-    An instance of a :class:`RelationshipTemplate`.
-
-    Properties:
-
-    * :code:`name`: Name (usually the name of the requirement at the source node template)
-    * :code:`source_requirement_index`: Must be represented in the source node template
-    * :code:`target_node_id`: Must be represented in the :class:`ServiceInstance`
-    * :code:`target_capability_name`: Matches the capability at the target node
-    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
-    * :code:`template_name`: Must be represented in the :class:`ServiceTemplate`
-    * :code:`properties`: Dict of :class:`Parameter`
-    * :code:`source_interfaces`: Dict of :class:`Interface`
-    * :code:`target_interfaces`: Dict of :class:`Interface`
-    """
-
-    __tablename__ = 'relationship'
-
-    __private_fields__ = ['source_node_fk',
-                          'target_node_fk']
-
-    source_requirement_index = Column(Integer)
-    target_node_id = Column(Text)
-    target_capability_name = Column(Text)
-    type_name = Column(Text)
-    template_name = Column(Text)
-
-    # region orchestrator required columns
-
-    source_position = Column(Integer)
-    target_position = Column(Integer)
-
-    @declared_attr
-    def source_node_fk(cls):
-        return cls.foreign_key('node', nullable=True)
-
-    @declared_attr
-    def source_node(cls):
-        return cls.many_to_one_relationship(
-            'node',
-            'source_node_fk',
-            backreference='outbound_relationships',
-            backref_kwargs=dict(
-                order_by=cls.source_position,
-                collection_class=ordering_list('source_position', count_from=0),
-            )
-        )
-
-    @declared_attr
-    def source_node_name(cls):
-        return association_proxy('source_node', cls.name_column_name())
-
-    @declared_attr
-    def target_node_fk(cls):
-        return cls.foreign_key('node', nullable=True)
-
-    @declared_attr
-    def target_node(cls):
-        return cls.many_to_one_relationship(
-            'node',
-            'target_node_fk',
-            backreference='inbound_relationships',
-            backref_kwargs=dict(
-                order_by=cls.target_position,
-                collection_class=ordering_list('target_position', count_from=0),
-            )
-        )
-
-    @declared_attr
-    def target_node_name(cls):
-        return association_proxy('target_node', cls.name_column_name())
-
-    # endregion
-
-    # region one-to-many relationships
-
-    @declared_attr
-    def interfaces(cls):
-        return cls.one_to_many_relationship('interfaces', key_column_name='name')
-
-    # endregion
-
-    # region many-to-many relationships
-
-    @declared_attr
-    def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             key_column_name='name')
-
-    # endregion
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('source_requirement_index', self.source_requirement_index),
-            ('target_node_id', self.target_node_id),
-            ('target_capability_name', self.target_capability_name),
-            ('type_name', self.type_name),
-            ('template_name', self.template_name),
-            ('properties', formatting.as_raw_dict(self.properties)),
-            ('interfaces', formatting.as_raw_list(self.interfaces))))
-
-    def validate(self, context):
-        if self.type_name:
-            if context.modeling.relationship_types.get_descendant(self.type_name) is None:
-                context.validation.report('relationship "%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.interfaces)
-
-    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.interfaces, report_issues)
-
-    def dump(self, context):
-        if self.name:
-            if self.source_requirement_index is not None:
-                console.puts('%s (%d) ->' % (
-                    context.style.node(self.name),
-                    self.source_requirement_index))
-            else:
-                console.puts('%s ->' % context.style.node(self.name))
-        else:
-            console.puts('->')
-        with context.style.indent:
-            console.puts('Node: %s' % context.style.node(self.target_node_id))
-            if self.target_capability_name is not None:
-                console.puts('Capability: %s' % context.style.node(self.target_capability_name))
-            if self.type_name is not None:
-                console.puts('Relationship type: %s' % context.style.type(self.type_name))
-            if self.template_name is not None:
-                console.puts('Relationship template: %s' % context.style.node(self.template_name))
-            utils.dump_parameters(context, self.properties)
-            utils.dump_interfaces(context, self.interfaces, 'Interfaces')