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/03/10 22:57:53 UTC

[3/6] incubator-ariatosca git commit: Separate plugin specification form plugin; move dry support to CLI; various renames and refactorings

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index 092de51..c9a02eb 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -30,7 +30,7 @@ from sqlalchemy.ext.declarative import declared_attr
 
 from ..parser import validation
 from ..utils import collections, formatting, console
-from .bases import TemplateModelMixin
+from .mixins import TemplateModelMixin
 from . import (
     utils,
     types as modeling_types
@@ -64,10 +64,10 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
     :vartype inputs: {basestring: :class:`Parameter`}
     :ivar outputs: These parameters are filled in after service installation
     :vartype outputs: {basestring: :class:`Parameter`}
-    :ivar operation_templates: Custom operations that can be performed on the service
-    :vartype operation_templates: {basestring: :class:`OperationTemplate`}
-    :ivar plugins: Plugins required by services
-    :vartype plugins: {basestring: :class:`Plugin`}
+    :ivar workflow_templates: Custom workflows that can be performed on the service
+    :vartype workflow_templates: {basestring: :class:`OperationTemplate`}
+    :ivar plugin_specifications: Plugins required by services
+    :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
     :ivar node_types: Base for the node type hierarchy
     :vartype node_types: :class:`Type`
     :ivar group_types: Base for the group type hierarchy
@@ -82,8 +82,8 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
     :vartype interface_types: :class:`Type`
     :ivar artifact_types: Base for the artifact type hierarchy
     :vartype artifact_types: :class:`Type`
-    :ivar plugins: Plugins required to be installed
-    :vartype plugins: {basestring: :class:`Plugin`}
+    :ivar plugin_specifications: Plugins required to be installed
+    :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
     :ivar created_at: Creation timestamp
     :vartype created_at: :class:`datetime.datetime`
     :ivar updated_at: Update timestamp
@@ -101,69 +101,73 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
     @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', dict_key='name')
+        return cls._create_many_to_many_relationship('metadata', dict_key='name')
 
     @declared_attr
     def node_templates(cls):
-        return cls.one_to_many_relationship('node_template')
+        return cls._create_one_to_many_relationship('node_template')
 
     @declared_attr
     def group_templates(cls):
-        return cls.one_to_many_relationship('group_template')
+        return cls._create_one_to_many_relationship('group_template')
 
     @declared_attr
     def policy_templates(cls):
-        return cls.one_to_many_relationship('policy_template')
+        return cls._create_one_to_many_relationship('policy_template')
 
     @declared_attr
     def substitution_template(cls):
-        return cls.one_to_one_relationship('substitution_template')
+        return cls._create_one_to_one_relationship('substitution_template')
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+                                                     dict_key='name')
 
     @declared_attr
     def outputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='outputs',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='outputs',
+                                                     dict_key='name')
 
     @declared_attr
-    def operation_templates(cls):
-        return cls.one_to_many_relationship('operation_template', dict_key='name')
+    def workflow_templates(cls):
+        return cls._create_one_to_many_relationship('operation_template', dict_key='name')
 
     @declared_attr
-    def plugins(cls):
-        return cls.one_to_many_relationship('plugin')
+    def plugin_specifications(cls):
+        return cls._create_one_to_many_relationship('plugin_specification')
 
     @declared_attr
     def node_types(cls):
-        return cls.one_to_one_relationship('type', key='node_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='node_type_fk', backreference='')
 
     @declared_attr
     def group_types(cls):
-        return cls.one_to_one_relationship('type', key='group_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='group_type_fk', backreference='')
 
     @declared_attr
     def policy_types(cls):
-        return cls.one_to_one_relationship('type', key='policy_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='policy_type_fk', backreference='')
 
     @declared_attr
     def relationship_types(cls):
-        return cls.one_to_one_relationship('type', key='relationship_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='relationship_type_fk',
+                                                   backreference='')
 
     @declared_attr
     def capability_types(cls):
-        return cls.one_to_one_relationship('type', key='capability_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='capability_type_fk',
+                                                   backreference='')
 
     @declared_attr
     def interface_types(cls):
-        return cls.one_to_one_relationship('type', key='interface_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='interface_type_fk',
+                                                   backreference='')
 
     @declared_attr
     def artifact_types(cls):
-        return cls.one_to_one_relationship('type', key='artifact_type_fk', backreference='')
+        return cls._create_one_to_one_relationship('type', key='artifact_type_fk',
+                                                   backreference='')
 
     # region orchestration
 
@@ -186,42 +190,42 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
     # ServiceTemplate one-to-one to SubstitutionTemplate
     @declared_attr
     def substitution_template_fk(cls):
-        return cls.foreign_key('substitution_template', nullable=True)
+        return cls._create_foreign_key('substitution_template', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def node_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def group_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def policy_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def relationship_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def capability_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def interface_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # ServiceTemplate one-to-one to Type
     @declared_attr
     def artifact_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # endregion
 
@@ -250,7 +254,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
             ('substitution_template', formatting.as_raw(self.substitution_template)),
             ('inputs', formatting.as_raw_dict(self.inputs)),
             ('outputs', formatting.as_raw_dict(self.outputs)),
-            ('operation_templates', formatting.as_raw_list(self.operation_templates))))
+            ('workflow_templates', formatting.as_raw_list(self.workflow_templates))))
 
     @property
     def types_as_raw(self):
@@ -283,7 +287,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
 
         utils.instantiate_list(context, self, service.groups, self.group_templates)
         utils.instantiate_list(context, self, service.policies, self.policy_templates)
-        utils.instantiate_dict(context, self, service.operations, self.operation_templates)
+        utils.instantiate_dict(context, self, service.workflows, self.workflow_templates)
 
         if self.substitution_template is not None:
             service.substitution = self.substitution_template.instantiate(context, container)
@@ -308,7 +312,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
             self.substitution_template.validate(context)
         utils.validate_dict_values(context, self.inputs)
         utils.validate_dict_values(context, self.outputs)
-        utils.validate_dict_values(context, self.operation_templates)
+        utils.validate_dict_values(context, self.workflow_templates)
         if self.node_types is not None:
             self.node_types.validate(context)
         if self.group_types is not None:
@@ -333,7 +337,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
             self.substitution_template.coerce_values(context, container, report_issues)
         utils.coerce_dict_values(context, container, self.inputs, report_issues)
         utils.coerce_dict_values(context, container, self.outputs, report_issues)
-        utils.coerce_dict_values(context, container, self.operation_templates, report_issues)
+        utils.coerce_dict_values(context, container, self.workflow_templates, report_issues)
 
     def dump(self, context):
         if self.description is not None:
@@ -349,7 +353,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
             self.substitution_template.dump(context)
         utils.dump_dict_values(context, self.inputs, 'Inputs')
         utils.dump_dict_values(context, self.outputs, 'Outputs')
-        utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+        utils.dump_dict_values(context, self.workflow_templates, 'Workflow templates')
 
     def dump_types(self, context):
         if self.node_types.children:
@@ -407,8 +411,8 @@ class NodeTemplateBase(TemplateModelMixin):
     :vartype requirement_templates: [:class:`RequirementTemplate`]
     :ivar target_node_template_constraints: Constraints for filtering relationship targets
     :vartype target_node_template_constraints: [:class:`FunctionType`]
-    :ivar plugins: Plugins required to be installed on the node's host
-    :vartype plugins: {basestring: :class:`Plugin`}
+    :ivar plugin_specifications: Plugins required to be installed on the node's host
+    :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
 
     :ivar service_template: Containing service template
     :vartype service_template: :class:`ServiceTemplate`
@@ -426,7 +430,7 @@ class NodeTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
     default_instances = Column(Integer, default=1)
@@ -435,32 +439,32 @@ class NodeTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     @declared_attr
     def interface_templates(cls):
-        return cls.one_to_many_relationship('interface_template', dict_key='name')
+        return cls._create_one_to_many_relationship('interface_template', dict_key='name')
 
     @declared_attr
     def artifact_templates(cls):
-        return cls.one_to_many_relationship('artifact_template', dict_key='name')
+        return cls._create_one_to_many_relationship('artifact_template', dict_key='name')
 
     @declared_attr
     def capability_templates(cls):
-        return cls.one_to_many_relationship('capability_template', dict_key='name')
+        return cls._create_one_to_many_relationship('capability_template', dict_key='name')
 
     @declared_attr
     def requirement_templates(cls):
-        return cls.one_to_many_relationship('requirement_template',
-                                            foreign_key='node_template_fk',
-                                            backreference='node_template')
+        return cls._create_one_to_many_relationship('requirement_template',
+                                                    foreign_key='node_template_fk',
+                                                    backreference='node_template')
 
     target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
 
     @declared_attr
-    def plugins(cls):
-        return cls.many_to_many_relationship('plugin')
+    def plugin_specifications(cls):
+        return cls._create_many_to_many_relationship('plugin_specification')
 
     # region foreign_keys
 
@@ -470,12 +474,12 @@ class NodeTemplateBase(TemplateModelMixin):
     # NodeTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # ServiceTemplate one-to-many to NodeTemplate
     @declared_attr
     def service_template_fk(cls):
-        return cls.foreign_key('service_template')
+        return cls._create_foreign_key('service_template')
 
     # endregion
 
@@ -579,22 +583,22 @@ class GroupTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
 
     @declared_attr
     def node_templates(cls):
-        return cls.many_to_many_relationship('node_template')
+        return cls._create_many_to_many_relationship('node_template')
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     @declared_attr
     def interface_templates(cls):
-        return cls.one_to_many_relationship('interface_template', dict_key='name')
+        return cls._create_one_to_many_relationship('interface_template', dict_key='name')
 
     # region foreign keys
 
@@ -604,12 +608,12 @@ class GroupTemplateBase(TemplateModelMixin):
     # GroupTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # ServiceTemplate one-to-many to GroupTemplate
     @declared_attr
     def service_template_fk(cls):
-        return cls.foreign_key('service_template')
+        return cls._create_foreign_key('service_template')
 
     # endregion
 
@@ -684,22 +688,22 @@ class PolicyTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
 
     @declared_attr
     def node_templates(cls):
-        return cls.many_to_many_relationship('node_template')
+        return cls._create_many_to_many_relationship('node_template')
 
     @declared_attr
     def group_templates(cls):
-        return cls.many_to_many_relationship('group_template')
+        return cls._create_many_to_many_relationship('group_template')
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     # region foreign keys
 
@@ -709,12 +713,12 @@ class PolicyTemplateBase(TemplateModelMixin):
     # PolicyTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # ServiceTemplate one-to-many to PolicyTemplate
     @declared_attr
     def service_template_fk(cls):
-        return cls.foreign_key('service_template')
+        return cls._create_foreign_key('service_template')
 
     # endregion
 
@@ -781,11 +785,12 @@ class SubstitutionTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def node_type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     @declared_attr
     def mappings(cls):
-        return cls.one_to_many_relationship('substitution_template_mapping', dict_key='name')
+        return cls._create_one_to_many_relationship('substitution_template_mapping',
+                                                    dict_key='name')
 
     # region foreign keys
 
@@ -794,7 +799,7 @@ class SubstitutionTemplateBase(TemplateModelMixin):
     # SubstitutionTemplate many-to-one to Type
     @declared_attr
     def node_type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # endregion
 
@@ -847,15 +852,15 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
 
     @declared_attr
     def node_template(cls):
-        return cls.one_to_one_relationship('node_template')
+        return cls._create_one_to_one_relationship('node_template')
 
     @declared_attr
     def capability_template(cls):
-        return cls.one_to_one_relationship('capability_template')
+        return cls._create_one_to_one_relationship('capability_template')
 
     @declared_attr
     def requirement_template(cls):
-        return cls.one_to_one_relationship('requirement_template')
+        return cls._create_one_to_one_relationship('requirement_template')
 
     # region foreign keys
 
@@ -867,22 +872,22 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
     # SubstitutionTemplate one-to-many to SubstitutionTemplateMapping
     @declared_attr
     def substitution_template_fk(cls):
-        return cls.foreign_key('substitution_template')
+        return cls._create_foreign_key('substitution_template')
 
     # SubstitutionTemplate one-to-one to NodeTemplate
     @declared_attr
     def node_template_fk(cls):
-        return cls.foreign_key('node_template')
+        return cls._create_foreign_key('node_template')
 
     # SubstitutionTemplate one-to-one to CapabilityTemplate
     @declared_attr
     def capability_template_fk(cls):
-        return cls.foreign_key('capability_template', nullable=True)
+        return cls._create_foreign_key('capability_template', nullable=True)
 
     # SubstitutionTemplate one-to-one to RequirementTemplate
     @declared_attr
     def requirement_template_fk(cls):
-        return cls.foreign_key('requirement_template', nullable=True)
+        return cls._create_foreign_key('requirement_template', nullable=True)
 
     # endregion
 
@@ -965,24 +970,25 @@ class RequirementTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def target_node_type(cls):
-        return cls.many_to_one_relationship('type', key='target_node_type_fk', backreference='')
+        return cls._create_many_to_one_relationship('type', key='target_node_type_fk',
+                                                    backreference='')
 
     @declared_attr
     def target_node_template(cls):
-        return cls.one_to_one_relationship('node_template', key='target_node_template_fk',
-                                           backreference='')
+        return cls._create_one_to_one_relationship('node_template', key='target_node_template_fk',
+                                                   backreference='')
 
     @declared_attr
     def target_capability_type(cls):
-        return cls.one_to_one_relationship('type', key='target_capability_type_fk',
-                                           backreference='')
+        return cls._create_one_to_one_relationship('type', key='target_capability_type_fk',
+                                                   backreference='')
 
     target_capability_name = Column(Text)
     target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
 
     @declared_attr
     def relationship_template(cls):
-        return cls.one_to_one_relationship('relationship_template')
+        return cls._create_one_to_one_relationship('relationship_template')
 
     # region foreign keys
 
@@ -995,27 +1001,27 @@ class RequirementTemplateBase(TemplateModelMixin):
     # RequirementTemplate many-to-one to Type
     @declared_attr
     def target_node_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # RequirementTemplate one-to-one to NodeTemplate
     @declared_attr
     def target_node_template_fk(cls):
-        return cls.foreign_key('node_template', nullable=True)
+        return cls._create_foreign_key('node_template', nullable=True)
 
     # RequirementTemplate one-to-one to NodeTemplate
     @declared_attr
     def target_capability_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # NodeTemplate one-to-many to RequirementTemplate
     @declared_attr
     def node_template_fk(cls):
-        return cls.foreign_key('node_template')
+        return cls._create_foreign_key('node_template')
 
     # RequirementTemplate one-to-one to RelationshipTemplate
     @declared_attr
     def relationship_template_fk(cls):
-        return cls.foreign_key('relationship_template', nullable=True)
+        return cls._create_foreign_key('relationship_template', nullable=True)
 
     # endregion
 
@@ -1146,18 +1152,18 @@ class RelationshipTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     @declared_attr
     def interface_templates(cls):
-        return cls.one_to_many_relationship('interface_template', dict_key='name')
+        return cls._create_one_to_many_relationship('interface_template', dict_key='name')
 
     # region foreign keys
 
@@ -1166,7 +1172,7 @@ class RelationshipTemplateBase(TemplateModelMixin):
     # RelationshipTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
+        return cls._create_foreign_key('type', nullable=True)
 
     # endregion
 
@@ -1243,7 +1249,7 @@ class CapabilityTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
     min_occurrences = Column(Integer, default=None)  # optional
@@ -1251,12 +1257,12 @@ class CapabilityTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def valid_source_node_types(cls):
-        return cls.many_to_many_relationship('type', table_prefix='valid_sources')
+        return cls._create_many_to_many_relationship('type', table_prefix='valid_sources')
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     # region foreign keys
 
@@ -1266,12 +1272,12 @@ class CapabilityTemplateBase(TemplateModelMixin):
     # CapabilityTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # NodeTemplate one-to-many to CapabilityTemplate
     @declared_attr
     def node_template_fk(cls):
-        return cls.foreign_key('node_template')
+        return cls._create_foreign_key('node_template')
 
     # endregion
 
@@ -1375,17 +1381,17 @@ class InterfaceTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+                                                     dict_key='name')
     @declared_attr
     def operation_templates(cls):
-        return cls.one_to_many_relationship('operation_template', dict_key='name')
+        return cls._create_one_to_many_relationship('operation_template', dict_key='name')
 
     # region foreign keys
 
@@ -1397,22 +1403,22 @@ class InterfaceTemplateBase(TemplateModelMixin):
     # InterfaceTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # NodeTemplate one-to-many to InterfaceTemplate
     @declared_attr
     def node_template_fk(cls):
-        return cls.foreign_key('node_template', nullable=True)
+        return cls._create_foreign_key('node_template', nullable=True)
 
     # GroupTemplate one-to-many to InterfaceTemplate
     @declared_attr
     def group_template_fk(cls):
-        return cls.foreign_key('group_template', nullable=True)
+        return cls._create_foreign_key('group_template', nullable=True)
 
     # RelationshipTemplate one-to-many to InterfaceTemplate
     @declared_attr
     def relationship_template_fk(cls):
-        return cls.foreign_key('relationship_template', nullable=True)
+        return cls._create_foreign_key('relationship_template', nullable=True)
 
     # endregion
 
@@ -1458,14 +1464,14 @@ class OperationTemplateBase(TemplateModelMixin):
     """
     An operation in a :class:`InterfaceTemplate`.
 
-    Operations are executed by an associated :class:`Plugin` via an executor.
+    Operations are executed by an associated :class:`PluginSpecification` via an executor.
 
     :ivar name: Name (unique for the interface or service template)
     :vartype name: basestring
     :ivar description: Human-readable description
     :vartype description: basestring
-    :ivar plugin: Associated plugin
-    :vartype plugin: :class:`Plugin`
+    :ivar plugin_specification: Associated plugin
+    :vartype plugin_specification: :class:`PluginSpecification`
     :ivar implementation: Implementation string (interpreted by the plugin)
     :vartype implementation: basestring
     :ivar dependencies: Dependency strings (interpreted by the plugin)
@@ -1492,16 +1498,16 @@ class OperationTemplateBase(TemplateModelMixin):
     description = Column(Text)
 
     @declared_attr
-    def plugin(cls):
-        return cls.one_to_one_relationship('plugin')
+    def plugin_specification(cls):
+        return cls._create_one_to_one_relationship('plugin_specification')
 
     implementation = Column(Text)
     dependencies = Column(modeling_types.StrictList(item_cls=basestring))
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+                                                     dict_key='name')
 
     executor = Column(Text)
     max_retries = Column(Integer)
@@ -1516,17 +1522,17 @@ class OperationTemplateBase(TemplateModelMixin):
     # ServiceTemplate one-to-many to OperationTemplate
     @declared_attr
     def service_template_fk(cls):
-        return cls.foreign_key('service_template', nullable=True)
+        return cls._create_foreign_key('service_template', nullable=True)
 
     # InterfaceTemplate one-to-many to OperationTemplate
     @declared_attr
     def interface_template_fk(cls):
-        return cls.foreign_key('interface_template', nullable=True)
+        return cls._create_foreign_key('interface_template', nullable=True)
 
-    # OperationTemplate one-to-one to Plugin
+    # OperationTemplate one-to-one to PluginSpecification
     @declared_attr
-    def plugin_fk(cls):
-        return cls.foreign_key('plugin', nullable=True)
+    def plugin_specification_fk(cls):
+        return cls._create_foreign_key('plugin_specification', nullable=True)
 
     # endregion
 
@@ -1548,7 +1554,7 @@ class OperationTemplateBase(TemplateModelMixin):
                                      description=utils.deepcopy_with_locators(self.description),
                                      implementation=self.implementation,
                                      dependencies=self.dependencies,
-                                     plugin=self.plugin,
+                                     plugin_specification=self.plugin_specification,
                                      executor=self.executor,
                                      max_retries=self.max_retries,
                                      retry_interval=self.retry_interval,
@@ -1614,7 +1620,7 @@ class ArtifactTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def type(cls):
-        return cls.many_to_one_relationship('type')
+        return cls._create_many_to_one_relationship('type')
 
     description = Column(Text)
     source_path = Column(Text)
@@ -1624,8 +1630,8 @@ class ArtifactTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def properties(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='properties',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+                                                     dict_key='name')
 
     # region foreign keys
 
@@ -1635,12 +1641,12 @@ class ArtifactTemplateBase(TemplateModelMixin):
     # ArtifactTemplate many-to-one to Type
     @declared_attr
     def type_fk(cls):
-        return cls.foreign_key('type')
+        return cls._create_foreign_key('type')
 
     # NodeTemplate one-to-many to ArtifactTemplate
     @declared_attr
     def node_template_fk(cls):
-        return cls.foreign_key('node_template')
+        return cls._create_foreign_key('node_template')
 
     # endregion
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index d434da8..744d1b4 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -59,10 +59,7 @@ class OperationTask(BaseTask):
     Represents an operation task in the task_graph
     """
 
-    SOURCE_OPERATION = 'source'
-    TARGET_OPERATION = 'target'
-
-    NAME_FORMAT = '{type}:{id}->{interface}/{operation}'
+    NAME_FORMAT = '{interface}:{operation}@{type}:{id}'
 
     def __init__(self,
                  name,
@@ -73,8 +70,7 @@ class OperationTask(BaseTask):
                  ignore_failure=None,
                  inputs=None,
                  plugin=None,
-                 runs_on=None,
-                 dry=False):
+                 runs_on=None):
         """
         Creates an operation task using the name, details, node instance and any additional kwargs.
 
@@ -84,12 +80,9 @@ class OperationTask(BaseTask):
         """
 
         assert isinstance(actor, (models.Node, models.Relationship))
+        assert (runs_on is None) or (runs_on in models.Task.RUNS_ON)
         super(OperationTask, self).__init__()
 
-        if dry:
-            from ..dry import convert_to_dry
-            plugin, implementation, inputs = convert_to_dry(plugin, implementation, inputs)
-
         # Coerce inputs
         if inputs is None:
             inputs = {}
@@ -131,13 +124,21 @@ class OperationTask(BaseTask):
                 'Could not find operation "{0}" on interface "{1}" for node "{2}"'.format(
                     operation_name, interface_name, node.name))
 
+        plugin = None
+        if operation.plugin_specification:
+            plugin = cls._find_plugin(operation.plugin_specification, kwargs)
+            if plugin is None:
+                raise exceptions.TaskException(
+                    'Could not find plugin of operation "{0}" on interface "{1}" for node "{2}"'
+                    .format(operation_name, interface_name, node.name))
+
         return cls(
             actor=node,
             name=cls.NAME_FORMAT.format(type='node',
-                                        id=node.id,
+                                        id=node.name,
                                         interface=interface_name,
                                         operation=operation_name),
-            plugin=operation.plugin,
+            plugin=plugin,
             implementation=operation.implementation,
             inputs=cls._merge_inputs(operation.inputs, inputs),
             runs_on=models.Task.RUNS_ON_NODE,
@@ -165,13 +166,21 @@ class OperationTask(BaseTask):
                 'Could not find operation "{0}" on interface "{1}" for relationship "{2}"'.format(
                     operation_name, interface_name, relationship.name))
 
+        plugin = None
+        if operation.plugin_specification:
+            plugin = cls._find_plugin(operation.plugin_specification, kwargs)
+            if plugin is None:
+                raise exceptions.TaskException(
+                    'Could not find plugin of operation "{0}" on interface "{1}" for relationship '
+                    '"{2}"'.format(operation_name, interface_name, relationship.name))
+
         return cls(
             actor=relationship,
             name=cls.NAME_FORMAT.format(type='relationship',
-                                        id=relationship.id,
+                                        id=relationship.name,
                                         interface=interface_name,
                                         operation=operation_name),
-            plugin=operation.plugin,
+            plugin=plugin,
             implementation=operation.implementation,
             inputs=cls._merge_inputs(operation.inputs, inputs),
             runs_on=runs_on,
@@ -186,6 +195,13 @@ class OperationTask(BaseTask):
         return None
 
     @classmethod
+    def _find_plugin(cls, plugin_specification, kwargs):
+        workflow_context = kwargs.get('ctx') if kwargs else None
+        if workflow_context is None:
+            workflow_context = context.workflow.current.get()
+        return plugin_specification.find_plugin(workflow_context.model.plugin.list())
+
+    @classmethod
     def _merge_inputs(cls, operation_inputs, override_inputs=None):
         final_inputs = OrderedDict(operation_inputs)
         if override_inputs:

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/execute_operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py
index ed4ada3..348f47a 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -58,8 +58,7 @@ def execute_operation(
         type_names=type_names))
 
     if run_by_dependency_order:
-        filtered_node_ids = set(node_instance.id
-                                          for node_instance in filtered_nodes)
+        filtered_node_ids = set(node_instance.id for node_instance in filtered_nodes)
         for node in ctx.nodes:
             if node.id not in filtered_node_ids:
                 subgraphs[node.id] = ctx.task_graph(

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/utils.py b/aria/orchestrator/workflows/builtin/utils.py
index 045d47b..84d8293 100644
--- a/aria/orchestrator/workflows/builtin/utils.py
+++ b/aria/orchestrator/workflows/builtin/utils.py
@@ -17,7 +17,7 @@ from ..api.task import OperationTask
 from .. import exceptions
 
 
-def create_node_task(interface_name, operation_name, node, dry=False):
+def create_node_task(interface_name, operation_name, node):
     """
     Returns a new operation task if the operation exists in the node, otherwise returns None.
     """
@@ -25,14 +25,12 @@ def create_node_task(interface_name, operation_name, node, dry=False):
     try:
         return OperationTask.for_node(node=node,
                                       interface_name=interface_name,
-                                      operation_name=operation_name,
-                                      dry=dry)
+                                      operation_name=operation_name)
     except exceptions.TaskException:
-        pass
-    return None
+        return None
 
 
-def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry=False):
+def create_relationship_tasks(interface_name, operation_name, runs_on, node):
     """
     Returns a list of operation tasks for each outbound relationship of the node if the operation
     exists there.
@@ -45,8 +43,7 @@ def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry
                 OperationTask.for_relationship(relationship=relationship,
                                                interface_name=interface_name,
                                                operation_name=operation_name,
-                                               runs_on=runs_on,
-                                               dry=dry))
+                                               runs_on=runs_on))
         except exceptions.TaskException:
             pass
     return sequence

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/workflows.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py
index f19c031..6065343 100644
--- a/aria/orchestrator/workflows/builtin/workflows.py
+++ b/aria/orchestrator/workflows/builtin/workflows.py
@@ -67,128 +67,108 @@ __all__ = (
 
 @workflow(suffix_template='{node.name}')
 def install_node(graph, node, **kwargs):
-    dry = kwargs.get('dry', True)
-
     sequence = []
 
     # Create
     sequence.append(
         create_node_task(
             NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CREATE,
-            node,
-            dry))
+            node))
 
     # Configure
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_SOURCE,
             Task.RUNS_ON_SOURCE,
-            node,
-            dry)
+            node)
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_TARGET,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
     sequence.append(
         create_node_task(
             NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CONFIGURE,
-            node,
-            dry))
+            node))
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_SOURCE,
             Task.RUNS_ON_SOURCE,
-            node,
-            dry)
+            node)
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_TARGET,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
 
     # Start
-    sequence += _create_start_tasks(node, dry)
+    sequence += _create_start_tasks(node)
 
     graph.sequence(*sequence)
 
 
 @workflow(suffix_template='{node.name}')
 def uninstall_node(graph, node, **kwargs):
-    dry = kwargs.get('dry', True)
-
     # Stop
-    sequence = _create_stop_tasks(node, dry)
+    sequence = _create_stop_tasks(node)
 
     # Delete
     sequence.append(
         create_node_task(
             NORMATIVE_STANDARD_INTERFACE, NORMATIVE_DELETE,
-            node,
-            dry))
+            node))
 
     graph.sequence(*sequence)
 
 
 @workflow(suffix_template='{node.name}')
 def start_node(graph, node, **kwargs):
-    dry = kwargs.get('dry', True)
-    graph.sequence(*_create_start_tasks(node, dry))
+    graph.sequence(*_create_start_tasks(node))
 
 
 @workflow(suffix_template='{node.name}')
 def stop_node(graph, node, **kwargs):
-    dry = kwargs.get('dry', True)
-    graph.sequence(*_create_stop_tasks(node, dry))
+    graph.sequence(*_create_stop_tasks(node))
 
 
-def _create_start_tasks(node, dry):
+def _create_start_tasks(node):
     sequence = []
     sequence.append(
         create_node_task(
             NORMATIVE_STANDARD_INTERFACE, NORMATIVE_START,
-            node,
-            dry))
+            node))
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_SOURCE,
             Task.RUNS_ON_SOURCE,
-            node,
-            dry)
+            node)
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_TARGET,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
     return sequence
 
 
-def _create_stop_tasks(node, dry):
+def _create_stop_tasks(node):
     sequence = []
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_REMOVE_TARGET,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
     sequence += \
         create_relationship_tasks(
             NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED,
             Task.RUNS_ON_TARGET,
-            node,
-            dry)
+            node)
     sequence.append(
         create_node_task(
             NORMATIVE_STANDARD_INTERFACE, NORMATIVE_STOP,
-            node,
-            dry))
+            node))
     return sequence

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/core/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py
index 7d8380c..64f2818 100644
--- a/aria/orchestrator/workflows/core/task.py
+++ b/aria/orchestrator/workflows/core/task.py
@@ -113,15 +113,15 @@ class OperationTask(BaseTask):
         base_task_model = model_storage.task.model_cls
         if isinstance(api_task.actor, models.Node):
             context_cls = operation_context.NodeOperationContext
-            task_model_cls = base_task_model.as_node_task
+            create_task_model = base_task_model.for_node
         elif isinstance(api_task.actor, models.Relationship):
             context_cls = operation_context.RelationshipOperationContext
-            task_model_cls = base_task_model.as_relationship_task
+            create_task_model = base_task_model.for_relationship
         else:
             raise RuntimeError('No operation context could be created for {actor.model_cls}'
                                .format(actor=api_task.actor))
 
-        operation_task = task_model_cls(
+        task_model = create_task_model(
             name=api_task.name,
             implementation=api_task.implementation,
             instance=api_task.actor,
@@ -131,20 +131,19 @@ class OperationTask(BaseTask):
             retry_interval=api_task.retry_interval,
             ignore_failure=api_task.ignore_failure,
             plugin=plugin,
-            plugin_name=plugin.name if plugin is not None else 'execution',
             execution=self._workflow_context.execution,
             runs_on=api_task.runs_on
         )
-        self._workflow_context.model.task.put(operation_task)
+        self._workflow_context.model.task.put(task_model)
 
         self._ctx = context_cls(name=api_task.name,
                                 model_storage=self._workflow_context.model,
                                 resource_storage=self._workflow_context.resource,
                                 service_id=self._workflow_context._service_id,
-                                task_id=operation_task.id,
+                                task_id=task_model.id,
                                 actor_id=api_task.actor.id,
                                 workdir=self._workflow_context._workdir)
-        self._task_id = operation_task.id
+        self._task_id = task_model.id
         self._update_fields = None
 
     @contextmanager

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/dry.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/dry.py b/aria/orchestrator/workflows/dry.py
deleted file mode 100644
index 766ea0c..0000000
--- a/aria/orchestrator/workflows/dry.py
+++ /dev/null
@@ -1,53 +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 threading import RLock
-
-from ..decorators import operation
-from ...utils.collections import OrderedDict
-from ...utils.console import puts, Colored
-from ...utils.formatting import safe_repr
-
-
-_TERMINAL_LOCK = RLock()
-
-
-def convert_to_dry(plugin, implementation, inputs): # pylint: disable=unused-argument
-    dry_implementation = '{0}.{1}'.format(__name__, 'dry_operation')
-    dry_inputs = OrderedDict()
-    dry_inputs['_implementation'] = implementation
-    dry_inputs['_plugin'] = plugin.name if plugin is not None else None
-    return None, dry_implementation, dry_inputs
-
-
-@operation
-def dry_operation(ctx, _plugin, _implementation, **kwargs):
-    with _TERMINAL_LOCK:
-        print ctx.name
-        if hasattr(ctx, 'relationship'):
-            puts('> Relationship: {0} -> {1}'.format(
-                Colored.red(ctx.relationship.source_node.name),
-                Colored.red(ctx.relationship.target_node.name)))
-        else:
-            puts('> Node: {0}'.format(Colored.red(ctx.node.name)))
-        puts('  Operation: {0}'.format(Colored.green(ctx.name)))
-        _dump_implementation(_plugin, _implementation)
-
-
-def _dump_implementation(plugin, implementation):
-    if plugin:
-        puts('  Plugin: {0}'.format(Colored.magenta(plugin, bold=True)))
-    if implementation:
-        puts('  Implementation: {0}'.format(Colored.magenta(safe_repr(implementation))))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/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 0206e03..a7b2a11 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -27,7 +27,7 @@ from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate,
                                   RequirementTemplate, RelationshipTemplate, CapabilityTemplate,
                                   GroupTemplate, PolicyTemplate, SubstitutionTemplate,
                                   SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate,
-                                  ArtifactTemplate, Metadata, Parameter, Plugin)
+                                  ArtifactTemplate, Metadata, Parameter, PluginSpecification)
 
 from ..data_types import coerce_value
 
@@ -81,12 +81,13 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to
         create_parameter_models_from_values(model.outputs,
                                             topology_template._get_output_values(context))
 
-    # Plugins
+    # Plugin specifications
     policies = context.presentation.get('service_template', 'topology_template', 'policies')
     if policies:
         for policy in policies.itervalues():
             if model.policy_types.get_descendant(policy.type).role == 'plugin':
-                model.plugins.append(create_plugin_model(context, policy))
+                model.plugin_specifications.append(
+                    create_plugin_specification_model(context, policy))
 
     # Node templates
     node_templates = context.presentation.get('service_template', 'topology_template',
@@ -349,7 +350,7 @@ def create_operation_template_model(context, service_template, operation): # pyl
 
     implementation = operation.implementation
     if (implementation is not None) and operation.implementation.primary:
-        model.plugin, model.implementation = \
+        model.plugin_specification, model.implementation = \
             parse_implementation_string(context, service_template, operation.implementation.primary)
 
         dependencies = implementation.dependencies
@@ -427,7 +428,7 @@ def create_substitution_template_model(context, service_template, substitution_m
     return model
 
 
-def create_plugin_model(context, policy):
+def create_plugin_specification_model(context, policy):
     properties = policy.properties
 
     def get(name):
@@ -436,18 +437,16 @@ def create_plugin_model(context, policy):
 
     now = datetime.now()
 
-    model = Plugin(name=policy._name,
-                   archive_name=get('archive_name') or '',
-                   distribution=get('distribution'),
-                   distribution_release=get('distribution_release'),
-                   distribution_version=get('distribution_version'),
-                   package_name=get('package_name') or '',
-                   package_source=get('package_source'),
-                   package_version=get('package_version'),
-                   supported_platform=get('supported_platform'),
-                   supported_py_versions=get('supported_py_versions'),
-                   uploaded_at=now,
-                   wheels=get('wheels') or [])
+    model = PluginSpecification(name=policy._name,
+                                archive_name=get('archive_name') or '',
+                                distribution=get('distribution'),
+                                distribution_release=get('distribution_release'),
+                                distribution_version=get('distribution_version'),
+                                package_name=get('package_name') or '',
+                                package_source=get('package_source'),
+                                package_version=get('package_version'),
+                                supported_platform=get('supported_platform'),
+                                supported_py_versions=get('supported_py_versions'))
 
     return model
 
@@ -665,15 +664,15 @@ def parse_implementation_string(context, service_template, implementation):
     plugin_name = implementation[:index].strip()
     
     if plugin_name == 'execution':
-        plugin = None
+        plugin_specification = None
     else:
-        plugin = None
-        for the_plugin in service_template.plugins:
-            if the_plugin.name == plugin_name:
-                plugin = the_plugin
+        plugin_specification = None
+        for a_plugin_specification in service_template.plugin_specifications:
+            if a_plugin_specification.name == plugin_name:
+                plugin_specification = a_plugin_specification
                 break
-        if plugin is None:
+        if plugin_specification is None:
             raise ValueError('unknown plugin: "{0}"'.format(plugin_name))
 
     implementation = implementation[index+1:].strip()
-    return plugin, implementation
+    return plugin_specification, implementation

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/end2end/test_orchestrator.py
----------------------------------------------------------------------
diff --git a/tests/end2end/test_orchestrator.py b/tests/end2end/test_orchestrator.py
index fac6207..f25134f 100644
--- a/tests/end2end/test_orchestrator.py
+++ b/tests/end2end/test_orchestrator.py
@@ -19,6 +19,7 @@ from aria.orchestrator.runner import Runner
 from aria.orchestrator.workflows.builtin import BUILTIN_WORKFLOWS
 from aria.utils.imports import import_fullname
 from aria.utils.collections import OrderedDict
+from aria.cli.dry import convert_to_dry
 
 from tests.parser.service_templates import consume_node_cellar
 
@@ -37,6 +38,8 @@ def test_custom():
 def _workflow(workflow_name):
     context, _ = consume_node_cellar()
 
+    convert_to_dry(context.modeling.instance)
+
     # TODO: this logic will eventually stabilize and be part of the ARIA API,
     # likely somewhere in aria.orchestrator.workflows
     if workflow_name in BUILTIN_WORKFLOWS:

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 716254e..483da7d 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -191,6 +191,7 @@ def create_execution(service):
 
 def create_plugin(package_name='package', package_version='0.1'):
     return models.Plugin(
+        name='test_plugin',
         archive_name='archive_name',
         distribution='distribution',
         distribution_release='dist_release',
@@ -205,5 +206,20 @@ def create_plugin(package_name='package', package_version='0.1'):
     )
 
 
+def create_plugin_specification(package_name='package', package_version='0.1'):
+    return models.PluginSpecification(
+        name='test_plugin',
+        archive_name='archive_name',
+        distribution='distribution',
+        distribution_release='dist_release',
+        distribution_version='dist_version',
+        package_name=package_name,
+        package_source='source',
+        package_version=package_version,
+        supported_platform='any',
+        supported_py_versions=['python27']
+    )
+
+
 def _dictify(item):
     return dict(((item.name, item),))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/tests/modeling/__init__.py b/tests/modeling/__init__.py
new file mode 100644
index 0000000..072ef54
--- /dev/null
+++ b/tests/modeling/__init__.py
@@ -0,0 +1,34 @@
+# 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,
+)
+
+from aria.modeling import (
+    models,
+    types as modeling_types,
+    mixins
+)
+
+
+class MockModel(models.aria_declarative_base, mixins.ModelMixin): #pylint: disable=abstract-method
+    __tablename__ = 'mock_model'
+    model_dict = Column(modeling_types.Dict)
+    model_list = Column(modeling_types.List)
+    value = Column(Integer)
+    name = Column(Text)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_mixins.py
----------------------------------------------------------------------
diff --git a/tests/modeling/test_mixins.py b/tests/modeling/test_mixins.py
new file mode 100644
index 0000000..a60412f
--- /dev/null
+++ b/tests/modeling/test_mixins.py
@@ -0,0 +1,219 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+import sqlalchemy
+
+from aria.storage import (
+    ModelStorage,
+    sql_mapi,
+    exceptions
+)
+from aria import modeling
+
+from ..storage import (
+    release_sqlite_storage,
+    init_inmemory_model_storage
+)
+from . import MockModel
+from ..mock import (
+    models,
+    context as mock_context
+)
+
+
+@pytest.fixture
+def storage():
+    base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+                                initiator=init_inmemory_model_storage)
+    base_storage.register(MockModel)
+    yield base_storage
+    release_sqlite_storage(base_storage)
+
+
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+    modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__)                      # pylint: disable=no-member
+
+
+@pytest.fixture
+def context(tmpdir):
+    ctx = mock_context.simple(str(tmpdir))
+    yield ctx
+    release_sqlite_storage(ctx.model)
+
+
+def test_inner_dict_update(storage):
+    inner_dict = {'inner_value': 1}
+
+    mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
+    storage.mock_model.put(mock_model)
+
+    storage_mm = storage.mock_model.get(mock_model.id)
+    assert storage_mm == mock_model
+
+    storage_mm.model_dict['inner_dict']['inner_value'] = 2
+    storage_mm.model_dict['value'] = -1
+    storage.mock_model.update(storage_mm)
+    storage_mm = storage.mock_model.get(storage_mm.id)
+
+    assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
+    assert storage_mm.model_dict['value'] == -1
+
+
+def test_inner_list_update(storage):
+    mock_model = MockModel(model_list=[0, [1]])
+    storage.mock_model.put(mock_model)
+
+    storage_mm = storage.mock_model.get(mock_model.id)
+    assert storage_mm == mock_model
+
+    storage_mm.model_list[1][0] = 'new_inner_value'
+    storage_mm.model_list[0] = 'new_value'
+    storage.mock_model.update(storage_mm)
+    storage_mm = storage.mock_model.get(storage_mm.id)
+
+    assert storage_mm.model_list[1][0] == 'new_inner_value'
+    assert storage_mm.model_list[0] == 'new_value'
+
+
+def test_model_to_dict(context):
+    service = context.service
+    service = service.to_dict()
+
+    expected_keys = [
+        'description',
+        'created_at',
+        'permalink',
+        'scaling_groups',
+        'updated_at'
+    ]
+
+    for expected_key in expected_keys:
+        assert expected_key in service
+
+
+def test_relationship_model_ordering(context):
+    service = context.model.service.get_by_name(models.SERVICE_NAME)
+    source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
+    target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
+
+    new_node_template = modeling.models.NodeTemplate(
+        name='new_node_template',
+        type=source_node.type,
+        default_instances=1,
+        min_instances=1,
+        max_instances=1,
+        service_template=service.service_template
+    )
+
+    new_node = modeling.models.Node(
+        name='new_node',
+        type=source_node.type,
+        runtime_properties={},
+        service=service,
+        version=None,
+        node_template=new_node_template,
+        state='',
+        scaling_groups=[]
+    )
+
+    source_node.outbound_relationships.append(modeling.models.Relationship(
+        source_node=source_node,
+        target_node=new_node,
+    ))
+
+    new_node.outbound_relationships.append(modeling.models.Relationship(                            # pylint: disable=no-member
+        source_node=new_node,
+        target_node=target_node,
+    ))
+
+    context.model.node_template.put(new_node_template)
+    context.model.node.put(new_node)
+    context.model.node.refresh(source_node)
+    context.model.node.refresh(target_node)
+
+    def flip_and_assert(node, direction):
+        """
+        Reversed the order of relationships and assert effects took place.
+        :param node: the node instance to operate on
+        :param direction: the type of relationships to flip (inbound/outbound)
+        :return:
+        """
+        assert direction in ('inbound', 'outbound')
+
+        relationships = getattr(node, direction + '_relationships')
+        assert len(relationships) == 2
+
+        reversed_relationship = list(reversed(relationships))
+        assert relationships != reversed_relationship
+
+        relationships[:] = reversed_relationship
+        context.model.node.update(node)
+        assert relationships == reversed_relationship
+
+    flip_and_assert(source_node, 'outbound')
+    flip_and_assert(target_node, 'inbound')
+
+
+class StrictClass(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin):
+    __tablename__ = 'strict_class'
+
+    strict_dict = sqlalchemy.Column(modeling.types.StrictDict(basestring, basestring))
+    strict_list = sqlalchemy.Column(modeling.types.StrictList(basestring))
+
+
+def test_strict_dict():
+
+    strict_class = StrictClass()
+
+    def assert_strict(sc):
+        with pytest.raises(exceptions.StorageError):
+            sc.strict_dict = {'key': 1}
+
+        with pytest.raises(exceptions.StorageError):
+            sc.strict_dict = {1: 'value'}
+
+        with pytest.raises(exceptions.StorageError):
+            sc.strict_dict = {1: 1}
+
+    assert_strict(strict_class)
+    strict_class.strict_dict = {'key': 'value'}
+    assert strict_class.strict_dict == {'key': 'value'}
+
+    assert_strict(strict_class)
+    with pytest.raises(exceptions.StorageError):
+        strict_class.strict_dict['key'] = 1
+    with pytest.raises(exceptions.StorageError):
+        strict_class.strict_dict[1] = 'value'
+    with pytest.raises(exceptions.StorageError):
+        strict_class.strict_dict[1] = 1
+
+
+def test_strict_list():
+    strict_class = StrictClass()
+
+    def assert_strict(sc):
+        with pytest.raises(exceptions.StorageError):
+            sc.strict_list = [1]
+
+    assert_strict(strict_class)
+    strict_class.strict_list = ['item']
+    assert strict_class.strict_list == ['item']
+
+    assert_strict(strict_class)
+    with pytest.raises(exceptions.StorageError):
+        strict_class.strict_list[0] = 1

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/modeling/test_model_storage.py b/tests/modeling/test_model_storage.py
new file mode 100644
index 0000000..bb778d4
--- /dev/null
+++ b/tests/modeling/test_model_storage.py
@@ -0,0 +1,102 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from aria.storage import (
+    ModelStorage,
+    exceptions,
+    sql_mapi
+)
+from aria import (application_model_storage, modeling)
+from ..storage import (release_sqlite_storage, init_inmemory_model_storage)
+
+from . import MockModel
+
+
+@pytest.fixture
+def storage():
+    base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+                                initiator=init_inmemory_model_storage)
+    base_storage.register(MockModel)
+    yield base_storage
+    release_sqlite_storage(base_storage)
+
+
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+    modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
+
+
+def test_storage_base(storage):
+    with pytest.raises(AttributeError):
+        storage.non_existent_attribute()
+
+
+def test_model_storage(storage):
+    mock_model = MockModel(value=0, name='model_name')
+    storage.mock_model.put(mock_model)
+
+    assert storage.mock_model.get_by_name('model_name') == mock_model
+
+    assert [mm_from_storage for mm_from_storage in storage.mock_model.iter()] == [mock_model]
+    assert [mm_from_storage for mm_from_storage in storage.mock_model] == [mock_model]
+
+    storage.mock_model.delete(mock_model)
+    with pytest.raises(exceptions.StorageError):
+        storage.mock_model.get(mock_model.id)
+
+
+def test_application_storage_factory():
+    storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
+                                        initiator=init_inmemory_model_storage)
+
+    assert storage.service_template
+    assert storage.node_template
+    assert storage.group_template
+    assert storage.policy_template
+    assert storage.substitution_template
+    assert storage.substitution_template_mapping
+    assert storage.requirement_template
+    assert storage.relationship_template
+    assert storage.capability_template
+    assert storage.interface_template
+    assert storage.operation_template
+    assert storage.artifact_template
+
+    assert storage.service
+    assert storage.node
+    assert storage.group
+    assert storage.policy
+    assert storage.substitution
+    assert storage.substitution_mapping
+    assert storage.relationship
+    assert storage.capability
+    assert storage.interface
+    assert storage.operation
+    assert storage.artifact
+
+    assert storage.execution
+    assert storage.service_update
+    assert storage.service_update_step
+    assert storage.service_modification
+    assert storage.plugin
+    assert storage.task
+
+    assert storage.parameter
+    assert storage.type
+    assert storage.metadata
+
+    release_sqlite_storage(storage)