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/21 23:49:38 UTC

incubator-ariatosca git commit: Added RelationshipTemplate; added one-to-many support

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-105-integrate-modeling 58e105237 -> f28b0af15


Added RelationshipTemplate; added one-to-many support


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

Branch: refs/heads/ARIA-105-integrate-modeling
Commit: f28b0af157e13c41482921ecba03409c07ca2660
Parents: 58e1052
Author: Tal Liron <ta...@gmail.com>
Authored: Tue Feb 21 17:49:14 2017 -0600
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue Feb 21 17:49:14 2017 -0600

----------------------------------------------------------------------
 aria/modeling/instance_elements.py              |  42 ++-
 aria/modeling/model.py                          |  10 +-
 aria/modeling/structure.py                      |  24 +-
 aria/modeling/template_elements.py              | 292 ++++++++++++++++---
 aria/modeling/type.py                           |   2 +-
 .../simple_v1_0/modeling/__init__.py            |  49 +++-
 6 files changed, 354 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/aria/modeling/instance_elements.py
----------------------------------------------------------------------
diff --git a/aria/modeling/instance_elements.py b/aria/modeling/instance_elements.py
index 0666c8a..5ebe692 100644
--- a/aria/modeling/instance_elements.py
+++ b/aria/modeling/instance_elements.py
@@ -33,11 +33,11 @@ from . import (
     type as aria_types
 )
 
+
 # pylint: disable=no-self-argument, no-member, abstract-method
 
 # region Element instances
 
-
 class ServiceInstanceBase(structure.ModelMixin):
     __tablename__ = 'service_instance'
 
@@ -64,6 +64,7 @@ class ServiceInstanceBase(structure.ModelMixin):
     # endregion
 
     # region foreign keys
+
     @declared_attr
     def substitution_fk(cls):
         return cls.foreign_key('substitution', nullable=True)
@@ -75,12 +76,15 @@ class ServiceInstanceBase(structure.ModelMixin):
     # endregion
 
     # region one-to-one relationships
+
     @declared_attr
     def substitution(cls):
         return cls.one_to_one_relationship('substitution')
+
     # endregion
 
     # region many-to-one relationships
+
     @declared_attr
     def service_template(cls):
         return cls.many_to_one_relationship('service_template')
@@ -184,6 +188,7 @@ class OperationBase(structure.ModelMixin):
         return cls.foreign_key('interface', nullable=True)
 
     # endregion
+
     description = Column(Text)
     implementation = Column(Text)
     dependencies = Column(aria_types.StrictList(item_cls=basestring))
@@ -195,6 +200,7 @@ class OperationBase(structure.ModelMixin):
     operation = Column(Boolean)
 
     # region many-to-one relationships
+
     @declared_attr
     def service_instance(cls):
         return cls.many_to_one_relationship('service_instance')
@@ -202,6 +208,9 @@ class OperationBase(structure.ModelMixin):
     @declared_attr
     def interface(cls):
         return cls.many_to_one_relationship('interface')
+
+    # endregion
+
     # region many-to-many relationships
 
     @declared_attr
@@ -268,6 +277,7 @@ class InterfaceBase(structure.ModelMixin):
 
 
     # region foreign_keys
+
     @declared_attr
     def group_fk(cls):
         return cls.foreign_key('group', nullable=True)
@@ -363,11 +373,13 @@ class CapabilityBase(structure.ModelMixin):
     __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
@@ -375,14 +387,15 @@ class CapabilityBase(structure.ModelMixin):
     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')
@@ -469,13 +482,13 @@ class ArtifactBase(structure.ModelMixin):
     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
@@ -547,11 +560,13 @@ class PolicyBase(structure.ModelMixin):
         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')
@@ -629,6 +644,7 @@ class GroupPolicyBase(structure.ModelMixin):
     type_name = Column(Text)
 
     # region many-to-one relationships
+
     @declared_attr
     def group(cls):
         return cls.many_to_one_relationship('group')
@@ -636,6 +652,7 @@ class GroupPolicyBase(structure.ModelMixin):
     # end region
 
     # region many-to-many relationships
+
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties')
@@ -796,7 +813,6 @@ class SubstitutionBase(structure.ModelMixin):
                                              table_prefix='requirements',
                                              relationship_kwargs=dict(lazy='dynamic'))
 
-
     # endregion
 
     @property
@@ -828,9 +844,9 @@ class SubstitutionBase(structure.ModelMixin):
             utils.dump_dict_values(context, self.capabilities, 'Capability mappings')
             utils.dump_dict_values(context, self.requirements, 'Requirement mappings')
 
-
 # endregion
 
+
 # region Node instances
 
 class NodeBase(structure.ModelMixin):
@@ -857,6 +873,7 @@ class NodeBase(structure.ModelMixin):
                           'node_template_fk']
 
     # region foreign_keys
+
     @declared_attr
     def service_instance_fk(cls):
         return cls.foreign_key('service_instance')
@@ -875,6 +892,7 @@ class NodeBase(structure.ModelMixin):
     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)
@@ -912,9 +930,11 @@ class NodeBase(structure.ModelMixin):
     @declared_attr
     def service_template(cls):
         return association_proxy('service_instance', 'service_template')
+
     # endregion
 
     # region many-to-one relationships
+
     @declared_attr
     def service_instance(cls):
         return cls.many_to_one_relationship('service_instance')
@@ -1091,11 +1111,15 @@ class GroupBase(structure.ModelMixin):
     member_group_ids = Column(aria_types.StrictList(basestring))
 
     # 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')
@@ -1147,8 +1171,8 @@ class GroupBase(structure.ModelMixin):
 
 # endregion
 
-# region Relationship instances
 
+# region Relationship instances
 
 class RelationshipBase(structure.ModelMixin):
     """
@@ -1179,7 +1203,8 @@ class RelationshipBase(structure.ModelMixin):
     type_name = Column(Text)
     template_name = Column(Text)
 
-    # # region orchestrator required columns
+    # region orchestrator required columns
+
     source_position = Column(Integer)
     target_position = Column(Integer)
 
@@ -1222,9 +1247,10 @@ class RelationshipBase(structure.ModelMixin):
     @declared_attr
     def target_node_name(cls):
         return association_proxy('target_node', cls.name_column_name())
+
     # endregion
 
-    # region many-to-many relationship
+    # region many-to-many relationships
 
     @declared_attr
     def properties(cls):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/aria/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/modeling/model.py b/aria/modeling/model.py
index b16fed0..2777353 100644
--- a/aria/modeling/model.py
+++ b/aria/modeling/model.py
@@ -41,6 +41,7 @@ __all__ = (
     'GroupPolicyTriggerTemplate',
     'RequirementTemplate',
     'CapabilityTemplate',
+    'RelationshipTemplate',
 
     'Mapping',
     'Substitution',
@@ -68,8 +69,8 @@ aria_declarative_base = declarative_base(cls=structure.ModelIDMixin)
 
 # pylint: disable=abstract-method
 
-# region elements
 
+# region elements
 
 class Parameter(aria_declarative_base, elements.ParameterBase):
     pass
@@ -137,8 +138,12 @@ class CapabilityTemplate(aria_declarative_base, template_elements.CapabilityTemp
     pass
 
 
+class RelationshipTemplate(aria_declarative_base, template_elements.RelationshipTemplateBase):
+    pass
+
 # endregion
 
+
 # region instance models
 
 class Mapping(aria_declarative_base, instance_elements.MappingBase):
@@ -192,9 +197,9 @@ class GroupPolicy(aria_declarative_base, instance_elements.GroupPolicyBase):
 class GroupPolicyTrigger(aria_declarative_base, instance_elements.GroupPolicyTriggerBase):
     pass
 
-
 # endregion
 
+
 # region orchestrator models
 
 class Execution(aria_declarative_base, orchestrator_elements.Execution):
@@ -222,4 +227,5 @@ class Plugin(aria_declarative_base, orchestrator_elements.PluginBase):
 
 class Task(aria_declarative_base, orchestrator_elements.TaskBase):
     pass
+
 # endregion

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/aria/modeling/structure.py
----------------------------------------------------------------------
diff --git a/aria/modeling/structure.py b/aria/modeling/structure.py
index eacdb44..8d2401a 100644
--- a/aria/modeling/structure.py
+++ b/aria/modeling/structure.py
@@ -28,6 +28,7 @@ classes:
 """
 
 from sqlalchemy.orm import relationship, backref
+from sqlalchemy.orm.collections import attribute_mapped_collection
 from sqlalchemy.ext import associationproxy
 from sqlalchemy import (
     Column,
@@ -138,6 +139,17 @@ class ModelMixin(ModelElementBase):
                             backref=backref(backreference or cls.__tablename__, uselist=False))
 
     @classmethod
+    def one_to_many_relationship(cls, other_table_name, backreference=None, key_column_name=None):
+        # See: http://docs.sqlalchemy.org/en/latest/orm/collections.html        
+        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),
+                            backref=backref(backreference or cls.__tablename__, uselist=False),
+                            collection_class=collection_class)
+
+    @classmethod
     def many_to_one_relationship(cls,
                                  parent_table_name,
                                  foreign_key_column=None,
@@ -184,7 +196,8 @@ class ModelMixin(ModelElementBase):
                             post_update=True)
 
     @classmethod
-    def many_to_many_relationship(cls, other_table_name, table_prefix, relationship_kwargs=None):
+    def many_to_many_relationship(cls, other_table_name, table_prefix, key_column_name=None,
+                                  relationship_kwargs=None):
         """Return a many-to-many SQL relationship object
 
         Notes:
@@ -194,7 +207,8 @@ class ModelMixin(ModelElementBase):
         :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
+                             backreference name
+        :param key_column_name: If provided, will use a dict class with this column as the key 
         """
         current_table_name = cls.__tablename__
         current_column_name = '{0}_id'.format(current_table_name)
@@ -219,10 +233,16 @@ class ModelMixin(ModelElementBase):
             other_foreign_key
         )
 
+        # See: http://docs.sqlalchemy.org/en/latest/orm/collections.html        
+        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 or {})
         )
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/aria/modeling/template_elements.py
----------------------------------------------------------------------
diff --git a/aria/modeling/template_elements.py b/aria/modeling/template_elements.py
index 60e31f8..8d750b1 100644
--- a/aria/modeling/template_elements.py
+++ b/aria/modeling/template_elements.py
@@ -12,6 +12,7 @@
 # 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
 
@@ -25,9 +26,8 @@ from sqlalchemy import (
 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 ..parser import validation
+from ..utils import collections, formatting, console
 from . import (
     utils,
     instance_elements,
@@ -35,13 +35,31 @@ from . import (
     type as aria_type
 )
 
-# pylint: disable=no-self-argument, no-member, abstract-method
 
+# pylint: disable=no-self-argument, no-member, abstract-method
 
 # region Element templates
 
-
 class ServiceTemplateBase(structure.ModelMixin):
+    """
+    A service model is a normalized service template from which :class:`ServiceInstance` instances
+    can be created.
+
+    It is usually created by various DSL parsers, such as ARIA's TOSCA extension. However, it
+    can also be created programmatically.
+
+    Properties:
+
+    * :code:`description`: Human-readable description
+    * :code:`metadata`: :class:`Metadata`
+    * :code:`node_templates`: Dict of :class:`NodeTemplate`
+    * :code:`group_templates`: Dict of :class:`GroupTemplate`
+    * :code:`policy_templates`: Dict of :class:`PolicyTemplate`
+    * :code:`substitution_template`: :class:`SubstituionTemplate`
+    * :code:`inputs`: Dict of :class:`Parameter`
+    * :code:`outputs`: Dict of :class:`Parameter`
+    * :code:`operation_templates`: Dict of :class:`Operation`
+    """
 
     __tablename__ = 'service_template'
 
@@ -60,6 +78,7 @@ class ServiceTemplateBase(structure.ModelMixin):
     # endregion
 
     # region foreign keys
+
     @declared_attr
     def substitution_template_fk(cls):
         return cls.foreign_key('substitution_template', nullable=True)
@@ -67,20 +86,24 @@ class ServiceTemplateBase(structure.ModelMixin):
     # endregion
 
     # region one-to-one relationships
+
     @declared_attr
     def substitution_template(cls):
         return cls.one_to_one_relationship('substitution_template')
+
     # endregion
 
     # region many-to-many relationships
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
+                                             key_column_name='name')
 
     @declared_attr
     def outputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='outputs')
+        return cls.many_to_many_relationship('parameter', table_prefix='outputs',
+                                             key_column_name='name')
 
     # endregion
 
@@ -173,10 +196,23 @@ class ServiceTemplateBase(structure.ModelMixin):
 
 
 class InterfaceTemplateBase(structure.ModelMixin):
+    """
+    A typed set of :class:`OperationTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`inputs`: Dict of :class:`Parameter`
+    * :code:`operation_templates`: Dict of :class:`OperationTemplate`
+    """
+
     __tablename__ = 'interface_template'
 
     __private_fields__ = ['node_template_fk',
-                          'group_template_fk']
+                          'group_template_fk',
+                          'relationship_template_fk']
 
     # region foreign keys
 
@@ -188,15 +224,28 @@ class InterfaceTemplateBase(structure.ModelMixin):
     def group_template_fk(cls):
         return cls.foreign_key('group_template', nullable=True)
 
+    @declared_attr
+    def relationship_template_fk(cls):
+        return cls.foreign_key('relationship_template', nullable=True)
+
     # endregion
 
     description = Column(Text)
     type_name = Column(Text)
 
-    # region many-to-one relationship
+    # region one-to-many relationships
+
     @declared_attr
-    def node_template(cls):
-        return cls.many_to_one_relationship('node_template')
+    def operation_templates(cls):
+        return cls.one_to_many_relationship('operation_template', key_column_name='name')
+
+    # endregion
+
+    # region many-to-one relationships
+   
+    #@declared_attr
+    #def node_template(cls):
+    #    return cls.many_to_one_relationship('node_template')
 
     @declared_attr
     def group_template(cls):
@@ -208,12 +257,13 @@ class InterfaceTemplateBase(structure.ModelMixin):
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
+                                             key_column_name='name')
 
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -259,6 +309,21 @@ class InterfaceTemplateBase(structure.ModelMixin):
 
 
 class OperationTemplateBase(structure.ModelMixin):
+    """
+    An operation in a :class:`InterfaceTemplate`.
+
+    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_template'
 
     __private_fields__ = ['service_template_fk',
@@ -284,24 +349,30 @@ class OperationTemplateBase(structure.ModelMixin):
     retry_interval = Column(Integer)
 
     # region orchestrator required columns
+
     plugin = Column(Text)
     operation = Column(Boolean)
 
+    # endregion
+
     # region many-to-one relationships
+
     @declared_attr
     def service_template(cls):
         return cls.many_to_one_relationship('service_template')
 
-    @declared_attr
-    def interface_template(cls):
-        return cls.many_to_one_relationship('interface_template')
+    #@declared_attr
+    #def interface_template(cls):
+    #    return cls.many_to_one_relationship('interface_template')
+
     # endregion
 
     # region many-to-many relationships
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
+                                             key_column_name='name')
 
     # endregion
 
@@ -368,6 +439,7 @@ class ArtifactTemplateBase(structure.ModelMixin):
     * :code:`repository_credential`: Dict of string
     * :code:`properties`: Dict of :class:`Parameter`
     """
+
     __tablename__ = 'artifact_template'
 
     __private_fields__ = ['node_template_fk']
@@ -387,10 +459,12 @@ class ArtifactTemplateBase(structure.ModelMixin):
     repository_url = Column(Text)
     repository_credential = Column(aria_type.StrictDict(basestring, basestring))
 
-    # region many-to-one relationship
-    @declared_attr
-    def node_template(cls):
-        return cls.many_to_one_relationship('node_template')
+    # region many-to-one relationships
+
+    #@declared_attr
+    #def node_template(cls):
+    #    return cls.many_to_one_relationship('node_template')
+
     # endregion
 
     # region many-to-many relationships
@@ -398,7 +472,7 @@ class ArtifactTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -487,7 +561,8 @@ class PolicyTemplateBase(structure.ModelMixin):
     target_node_template_names = Column(aria_type.StrictList(basestring))
     target_group_template_names = Column(aria_type.StrictList(basestring))
 
-    # region many-to-one relationship
+    # region many-to-one relationships
+
     @declared_attr
     def service_template(cls):
         return cls.many_to_one_relationship('service_template')
@@ -503,7 +578,7 @@ class PolicyTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -572,6 +647,7 @@ class GroupPolicyTemplateBase(structure.ModelMixin):
     __private_fields__ = ['group_template_fk']
 
     # region foreign keys
+
     @declared_attr
     def group_template_fk(cls):
         return cls.foreign_key('group_template')
@@ -586,7 +662,7 @@ class GroupPolicyTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -655,7 +731,8 @@ class GroupPolicyTriggerTemplateBase(structure.ModelMixin):
     description = Column(Text)
     implementation = Column(Text)
 
-    # region many-to-one relationship
+    # region many-to-one relationships
+
     @declared_attr
     def group_policy_template(cls):
         return cls.many_to_one_relationship('group_policy_template')
@@ -668,7 +745,7 @@ class GroupPolicyTriggerTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -713,6 +790,7 @@ class MappingTemplateBase(structure.ModelMixin):
     * :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)
@@ -760,6 +838,7 @@ class SubstitutionTemplateBase(structure.ModelMixin):
     * :code:`capability_templates`: Dict of :class:`MappingTemplate`
     * :code:`requirement_templates`: Dict of :class:`MappingTemplate`
     """
+
     __tablename__ = 'substitution_template'
     node_type_name = Column(Text)
 
@@ -822,6 +901,25 @@ class SubstitutionTemplateBase(structure.ModelMixin):
 # region Node templates
 
 class NodeTemplateBase(structure.ModelMixin):
+    """
+    A template for creating zero or more :class:`Node` instances.
+
+    Properties:
+
+    * :code:`name`: Name (will be used as a prefix for node IDs)
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`default_instances`: Default number nodes that will appear in the deployment plan
+    * :code:`min_instances`: Minimum number nodes that will appear in the deployment plan
+    * :code:`max_instances`: Maximum number nodes that will appear in the deployment plan
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`interface_templates`: Dict of :class:`InterfaceTemplate`
+    * :code:`artifact_templates`: Dict of :class:`ArtifactTemplate`
+    * :code:`capability_templates`: Dict of :class:`CapabilityTemplate`
+    * :code:`requirement_templates`: List of :class:`RequirementTemplate`
+    * :code:`target_node_template_constraints`: List of :class:`FunctionType`
+    """
+
     __tablename__ = 'node_template'
 
     __private_fields__ = ['service_template_fk',
@@ -861,19 +959,36 @@ class NodeTemplateBase(structure.ModelMixin):
 
     # endregion
 
-    # region many-to-one relationship
+    # region many-to-one relationships
+
     @declared_attr
     def service_template(cls):
         return cls.many_to_one_relationship('service_template')
 
     # endregion
 
+    # region one-to-many relationships
+
+    @declared_attr
+    def interface_templates(cls):
+        return cls.one_to_many_relationship('interface_template', key_column_name='name')
+
+    @declared_attr
+    def artifact_templates(cls):
+        return cls.one_to_many_relationship('artifact_template', key_column_name='name')
+
+    @declared_attr
+    def capability_templates(cls):
+        return cls.one_to_many_relationship('capability_template', 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',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -964,6 +1079,7 @@ class GroupTemplateBase(structure.ModelMixin):
     * :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'
 
     __private_fields__ = ['service_template_fk']
@@ -981,7 +1097,8 @@ class GroupTemplateBase(structure.ModelMixin):
     member_node_template_names = Column(aria_type.StrictList(basestring))
     member_group_template_names = Column(aria_type.StrictList(basestring))
 
-    # region many-to-one relationship
+    # region many-to-one relationships
+
     @declared_attr
     def service_template(cls):
         return cls.many_to_one_relationship('service_template')
@@ -993,7 +1110,7 @@ class GroupTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -1075,6 +1192,7 @@ class RequirementTemplateBase(structure.ModelMixin):
     * :code:`target_capability_name`: Name of capability in target node
     * :code:`relationship_template`: :class:`RelationshipTemplate`
     """
+
     __tablename__ = 'requirement_template'
 
     __private_fields__ = ['node_template_fk']
@@ -1087,7 +1205,6 @@ class RequirementTemplateBase(structure.ModelMixin):
 
     # endregion
 
-
     target_node_type_name = Column(Text)
     target_node_template_name = Column(Text)
     target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
@@ -1096,10 +1213,12 @@ class RequirementTemplateBase(structure.ModelMixin):
     # CHECK: ???
     relationship_template = Column(Text)  # optional
 
-    # region many-to-one relationship
+    # region many-to-one relationships
+
     @declared_attr
     def node_template(cls):
         return cls.many_to_one_relationship('node_template')
+
     # endregion
 
     def instantiate(self, context, container):
@@ -1237,6 +1356,7 @@ class CapabilityTemplateBase(structure.ModelMixin):
     * :code:`valid_source_node_type_names`: Must be represented in the :class:`ModelingContext`
     * :code:`properties`: Dict of :class:`Parameter`
     """
+
     __tablename__ = 'capability_template'
 
     __private_fields__ = ['node_template_fk']
@@ -1256,10 +1376,12 @@ class CapabilityTemplateBase(structure.ModelMixin):
     # CHECK: type?
     valid_source_node_type_names = Column(Text)
 
-    # region many-to-one relationship
-    @declared_attr
-    def node_template(cls):
-        return cls.many_to_one_relationship('node_template')
+    # region many-to-one relationships
+
+    #@declared_attr
+    #def node_template(cls):
+    #    return cls.many_to_one_relationship('node_template')
+
     # endregion
 
     # region many-to-many relationships
@@ -1267,7 +1389,7 @@ class CapabilityTemplateBase(structure.ModelMixin):
     @declared_attr
     def properties(cls):
         return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             collection_class=dict)
+                                             key_column_name='name')
 
     # endregion
 
@@ -1343,6 +1465,100 @@ class CapabilityTemplateBase(structure.ModelMixin):
                                           for v in self.valid_source_node_type_names)))
             dump_parameters(context, self.properties)
 
+
+class RelationshipTemplateBase(structure.ModelMixin):
+    """
+    Optional addition to a :class:`Requirement` in :class:`NodeTemplate` that can be applied when
+    the requirement is matched with a capability.
+
+    Properties:
+
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`description`: Description
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`source_interface_templates`: Dict of :class:`InterfaceTemplate`
+    * :code:`target_interface_templates`: Dict of :class:`InterfaceTemplate`
+    """
+
+    __tablename__ = 'relationship_template'
+
+    description = Column(Text)
+    type_name = Column(Text)
+
+    # 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
+
+    # region one-to-many relationships
+
+    @declared_attr
+    def interface_templates(cls):
+        return cls.one_to_many_relationship('interface_template', key_column_name='name')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('type_name', self.type_name),
+            ('template_name', self.template_name),
+            ('description', self.description),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('source_interface_templates',
+             formatting.as_raw_list(self.source_interface_templates)),
+            ('target_interface_templates',
+             formatting.as_raw_list(self.target_interface_templates))))
+
+    def instantiate(self, context, container):
+        relationship = instance_elements.RelationshipBase(name=self.template_name,
+                                                          type_name=self.type_name)
+        utils.instantiate_dict(context, container,
+                               relationship.properties, self.properties)
+        utils.instantiate_dict(context, container,
+                               relationship.source_interfaces, self.source_interface_templates)
+        utils.instantiate_dict(context, container,
+                               relationship.target_interfaces, self.target_interface_templates)
+        return relationship
+
+    def validate(self, context):
+        if context.modeling.relationship_types.get_descendant(self.type_name) is None:
+            context.validation.report(
+                'relationship template "{0}" has an unknown type: {1}'.format(
+                    self.name,
+                    formatting.safe_repr(self.type_name)),  # pylint: disable=no-member
+                # TODO fix self.name reference
+                level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.source_interface_templates)
+        utils.validate_dict_values(context, self.target_interface_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.source_interface_templates, report_issues)
+        utils.coerce_dict_values(context, self, self.target_interface_templates, report_issues)
+
+    def dump(self, context):
+        if self.type_name is not None:
+            console.puts('Relationship type: {0}'.format(context.style.type(self.type_name)))
+        else:
+            console.puts('Relationship template: {0}'.format(
+                context.style.node(self.template_name)))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            utils.dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.source_interface_templates,
+                                  'Source interface templates')
+            utils.dump_interfaces(context, self.target_interface_templates,
+                                  'Target interface templates')
+
 # endregion
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/aria/modeling/type.py
----------------------------------------------------------------------
diff --git a/aria/modeling/type.py b/aria/modeling/type.py
index 9e3de3d..2c6453b 100644
--- a/aria/modeling/type.py
+++ b/aria/modeling/type.py
@@ -23,7 +23,7 @@ from sqlalchemy import (
 )
 from sqlalchemy.ext import mutable
 
-from .. import exceptions
+from ..storage import exceptions
 
 
 class _MutableType(TypeDecorator):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/f28b0af1/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
index 0d33182..9676c0b 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -14,10 +14,12 @@
 # limitations under the License.
 
 import re
+from types import FunctionType
 
-from aria.parser.modeling import Type, RelationshipType, PolicyType, RelationshipTemplate
+from aria.parser.modeling import Type, RelationshipType, PolicyType
+from aria.modeling import type as aria_type
 from aria.modeling.model import (ServiceTemplate as ServiceModel, NodeTemplate,
-                                 RequirementTemplate, CapabilityTemplate,
+                                 RequirementTemplate, RelationshipTemplate, CapabilityTemplate,
                                  GroupTemplate, PolicyTemplate, SubstitutionTemplate,
                                  MappingTemplate, InterfaceTemplate, OperationTemplate,
                                  ArtifactTemplate, Metadata, Parameter)
@@ -31,6 +33,7 @@ from aria.modeling.model import (ServiceTemplate as ServiceModel, NodeTemplate,
 from ..data_types import coerce_value
 from platform import node
 
+
 def create_service_model(context): # pylint: disable=too-many-locals,too-many-branches
     model = ServiceModel()
 
@@ -80,8 +83,8 @@ def create_service_model(context): # pylint: disable=too-many-locals,too-many-br
     node_templates = context.presentation.get('service_template', 'topology_template',
                                               'node_templates')
     if node_templates:
-        for node_template_name, node_template in node_templates.iteritems():
-            model.node_templates[node_template_name] = create_node_template(context, node_template)
+        for node_template in node_templates.itervalues():
+            model.node_templates.append(create_node_template(context, node_template))
 
     groups = context.presentation.get('service_template', 'topology_template', 'groups')
     if groups:
@@ -113,6 +116,7 @@ def create_service_model(context): # pylint: disable=too-many-locals,too-many-br
 
     return model
 
+
 def create_node_template(context, node_template):
     node_type = node_template._get_type(context)
     model = NodeTemplate(name=node_template._name, type_name=node_type._name)
@@ -140,11 +144,13 @@ def create_node_template(context, node_template):
             model.capability_templates[capability_name] = create_capability_template(context,
                                                                                      capability)
 
+    model.target_node_template_constraints = aria_type.StrictList(FunctionType)
     create_node_filter_constraint_lambdas(context, node_template.node_filter,
                                           model.target_node_template_constraints)
 
     return model
 
+
 def create_interface_template(context, interface):
     interface_type = interface._get_type(context)
     model = InterfaceTemplate(name=interface._name, type_name=interface_type._name)
@@ -166,6 +172,7 @@ def create_interface_template(context, interface):
 
     return model if model.operation_templates else None
 
+
 def create_operation_template(context, operation): # pylint: disable=unused-argument
     model = OperationTemplate(name=operation._name)
 
@@ -187,6 +194,7 @@ def create_operation_template(context, operation): # pylint: disable=unused-argu
 
     return model
 
+
 def create_artifact_template(context, artifact):
     model = ArtifactTemplate(name=artifact._name, type_name=artifact.type,
                              source_path=artifact.file)
@@ -208,6 +216,7 @@ def create_artifact_template(context, artifact):
 
     return model
 
+
 def create_requirement_template(context, requirement):
     model = {'name': requirement._name}
 
@@ -236,12 +245,15 @@ def create_requirement_template(context, requirement):
 
     return model
 
+
 def create_relationship_type(context, relationship_type): # pylint: disable=unused-argument
     return RelationshipType(relationship_type._name)
 
+
 def create_policy_type(context, policy_type): # pylint: disable=unused-argument
     return PolicyType(policy_type._name)
 
+
 def create_relationship_template(context, relationship):
     relationship_type, relationship_type_variant = relationship._get_type(context)
     if relationship_type_variant == 'relationship_type':
@@ -255,10 +267,11 @@ def create_relationship_template(context, relationship):
             model.description = relationship_template.description.value
 
     create_properties_from_assignments(model.properties, relationship.properties)
-    create_interface_templates(context, model.source_interface_templates, relationship.interfaces)
+    create_interface_templates(context, model.interface_templates, relationship.interfaces)
 
     return model
 
+
 def create_capability_template(context, capability):
     capability_type = capability._get_type(context)
     model = CapabilityTemplate(name=capability._name, type_name=capability_type._name)
@@ -280,6 +293,7 @@ def create_capability_template(context, capability):
 
     return model
 
+
 def create_group_template(context, group):
     group_type = group._get_type(context)
     model = GroupTemplate(name=group._name, type_name=group_type._name)
@@ -297,6 +311,7 @@ def create_group_template(context, group):
 
     return model
 
+
 def create_policy_template(context, policy):
     policy_type = policy._get_type(context)
     model = PolicyTemplate(name=policy._name, type_name=policy_type._name)
@@ -314,6 +329,7 @@ def create_policy_template(context, policy):
 
     return model
 
+
 #
 # Utils
 #
@@ -346,19 +362,22 @@ def create_types(context, root, types, normalize=None):
                     if container is not None:
                         container.children.append(model)
 
+
 def create_properties_from_values(properties, source_properties):
     if source_properties:
         for property_name, prop in source_properties.iteritems():
-            properties.append(Parameter(name=property_name,
-                                        type=prop.type,
-                                        str_value=prop.value,
-                                        description=prop.description))
+            properties[property_name] = Parameter(type=prop.type,
+                                                  str_value=prop.value,
+                                                  description=prop.description)
+
 
 def create_properties_from_assignments(properties, source_properties):
     if source_properties:
         for property_name, prop in source_properties.iteritems():
-            properties[property_name] = Parameter(prop.value.type, prop.value.value,
-                                                  prop.value.description)
+            properties[property_name] = Parameter(type=prop.value.type,
+                                                  str_value=prop.value.value,
+                                                  description=prop.value.description)
+
 
 def create_interface_templates(context, interfaces, source_interfaces):
     if source_interfaces:
@@ -367,7 +386,8 @@ def create_interface_templates(context, interfaces, source_interfaces):
             if interface is not None:
                 interfaces[interface_name] = interface
 
-def create_node_filter_constraint_lambdas(context, node_filter, node_type_constraints):
+
+def create_node_filter_constraint_lambdas(context, node_filter, target_node_template_constraints):
     if node_filter is None:
         return
 
@@ -377,7 +397,7 @@ def create_node_filter_constraint_lambdas(context, node_filter, node_type_constr
             func = create_constraint_clause_lambda(context, node_filter, constraint_clause,
                                                    property_name, None)
             if func is not None:
-                node_type_constraints.append(func)
+                target_node_template_constraints.append(func)
 
     capabilities = node_filter.capabilities
     if capabilities is not None:
@@ -388,7 +408,8 @@ def create_node_filter_constraint_lambdas(context, node_filter, node_type_constr
                     func = create_constraint_clause_lambda(context, node_filter, constraint_clause,
                                                            property_name, capability_name)
                     if func is not None:
-                        node_type_constraints.append(func)
+                        target_node_template_constraints.append(func)
+
 
 def create_constraint_clause_lambda(context, node_filter, constraint_clause, property_name, # pylint: disable=too-many-return-statements
                                     capability_name):