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

[1/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model [Forced Update!]

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-106-Create-sqla-logging-handler 6bff90672 -> 67b754254 (forced update)


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/core/test_task.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_task.py b/tests/orchestrator/workflows/core/test_task.py
index b39a81f..26564c5 100644
--- a/tests/orchestrator/workflows/core/test_task.py
+++ b/tests/orchestrator/workflows/core/test_task.py
@@ -28,30 +28,43 @@ from aria.orchestrator.workflows import (
 
 from tests import mock, storage
 
+OP_NAME = 'tosca.interfaces.node.lifecycle.Standard.create'
+RELATIONSHIP_OP_NAME = 'tosca.interfaces.relationship.Configure.pre_configure'
+
 
 @pytest.fixture
 def ctx(tmpdir):
     context = mock.context.simple(str(tmpdir))
+
+    relationship = context.model.relationship.list()[0]
+    relationship.interfaces = [
+        mock.models.get_interface(RELATIONSHIP_OP_NAME, edge='source'),
+        mock.models.get_interface(RELATIONSHIP_OP_NAME, edge='target')
+    ]
+    context.model.relationship.update(relationship)
+
+    dependent_node = context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    dependent_node.interfaces = [mock.models.get_interface(OP_NAME)]
+    context.model.node.update(dependent_node)
+
     yield context
     storage.release_sqlite_storage(context.model)
 
 
 class TestOperationTask(object):
 
-    def _create_node_operation_task(self, ctx, node_instance):
+    def _create_node_operation_task(self, ctx, node):
         with workflow_context.current.push(ctx):
-            api_task = api.task.OperationTask.node_instance(
-                instance=node_instance,
+            api_task = api.task.OperationTask.node(
+                instance=node,
                 name='tosca.interfaces.node.lifecycle.Standard.create')
             core_task = core.task.OperationTask(api_task=api_task)
         return api_task, core_task
 
-    def _create_relationship_operation_task(self, ctx, relationship_instance, operation_end):
+    def _create_relationship_operation_task(self, ctx, relationship, operation_name, edge):
         with workflow_context.current.push(ctx):
-            api_task = api.task.OperationTask.relationship_instance(
-                instance=relationship_instance,
-                name='tosca.interfaces.relationship.Configure.pre_configure_source',
-                operation_end=operation_end)
+            api_task = api.task.OperationTask.relationship(
+                instance=relationship, name=operation_name, edge=edge)
             core_task = core.task.OperationTask(api_task=api_task)
         return api_task, core_task
 
@@ -60,45 +73,47 @@ class TestOperationTask(object):
         storage_plugin_other = mock.models.get_plugin(package_name='p0', package_version='0.0')
         ctx.model.plugin.put(storage_plugin_other)
         ctx.model.plugin.put(storage_plugin)
-        node_instance = ctx.model.node_instance.get_by_name(
-            mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-        node = node_instance.node
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        node_template = node.node_template
         plugin_name = 'plugin1'
-        node.plugins = [{'name': plugin_name,
-                         'package_name': 'p1',
-                         'package_version': '0.1'}]
-        node.operations['tosca.interfaces.node.lifecycle.Standard.create'] = {'plugin': plugin_name}
-        api_task, core_task = self._create_node_operation_task(ctx, node_instance)
+        node_template.plugins = [{'name': 'plugin1',
+                                  'package_name': 'p1',
+                                  'package_version': '0.1'}]
+        node.interfaces = [mock.models.get_interface(
+            'tosca.interfaces.node.lifecycle.Standard.create',
+            operation_kwargs=dict(plugin='plugin1')
+        )]
+        ctx.model.node_template.update(node_template)
+        ctx.model.node.update(node)
+        api_task, core_task = self._create_node_operation_task(ctx, node)
         storage_task = ctx.model.task.get_by_name(core_task.name)
         assert storage_task.plugin_name == plugin_name
         assert storage_task.execution_name == ctx.execution.name
-        assert storage_task.runs_on.id == core_task.context.node_instance.id
+        assert storage_task.runs_on == core_task.context.node
         assert core_task.model_task == storage_task
         assert core_task.name == api_task.name
-        assert core_task.operation_mapping == api_task.operation_mapping
-        assert core_task.actor == api_task.actor == node_instance
+        assert core_task.implementation == api_task.implementation
+        assert core_task.actor == api_task.actor == node
         assert core_task.inputs == api_task.inputs == storage_task.inputs
         assert core_task.plugin == storage_plugin
 
     def test_source_relationship_operation_task_creation(self, ctx):
-        relationship_instance = ctx.model.relationship_instance.list()[0]
+        relationship = ctx.model.relationship.list()[0]
+        ctx.model.relationship.update(relationship)
         _, core_task = self._create_relationship_operation_task(
-            ctx, relationship_instance,
-            api.task.OperationTask.SOURCE_OPERATION)
-        assert core_task.model_task.runs_on.id == relationship_instance.source_node_instance.id
+            ctx, relationship, RELATIONSHIP_OP_NAME, 'source')
+        assert core_task.model_task.runs_on == relationship.source_node
 
     def test_target_relationship_operation_task_creation(self, ctx):
-        relationship_instance = ctx.model.relationship_instance.list()[0]
+        relationship = ctx.model.relationship.list()[0]
         _, core_task = self._create_relationship_operation_task(
-            ctx, relationship_instance,
-            api.task.OperationTask.TARGET_OPERATION)
-        assert core_task.model_task.runs_on.id == relationship_instance.target_node_instance.id
+            ctx, relationship, RELATIONSHIP_OP_NAME, 'target')
+        assert core_task.model_task.runs_on == relationship.target_node
 
     def test_operation_task_edit_locked_attribute(self, ctx):
-        node_instance = \
-            ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
 
-        _, core_task = self._create_node_operation_task(ctx, node_instance)
+        _, core_task = self._create_node_operation_task(ctx, node)
         now = datetime.utcnow()
         with pytest.raises(exceptions.TaskException):
             core_task.status = core_task.STARTED
@@ -112,10 +127,9 @@ class TestOperationTask(object):
             core_task.due_at = now
 
     def test_operation_task_edit_attributes(self, ctx):
-        node_instance = \
-            ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
 
-        _, core_task = self._create_node_operation_task(ctx, node_instance)
+        _, core_task = self._create_node_operation_task(ctx, node)
         future_time = datetime.utcnow() + timedelta(seconds=3)
 
         with core_task._update():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py b/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
index 57be075..167004e 100644
--- a/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
+++ b/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
@@ -25,21 +25,23 @@ from tests import storage
 def test_task_graph_into_execution_graph(tmpdir):
     operation_name = 'tosca.interfaces.node.lifecycle.Standard.create'
     task_context = mock.context.simple(str(tmpdir))
-    node_instance = \
-        task_context.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    node = task_context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    node.interfaces = [mock.models.get_interface(operation_name)]
+    task_context.model.node.update(node)
+
     def sub_workflow(name, **_):
         return api.task_graph.TaskGraph(name)
 
     with context.workflow.current.push(task_context):
         test_task_graph = api.task.WorkflowTask(sub_workflow, name='test_task_graph')
-        simple_before_task = api.task.OperationTask.node_instance(instance=node_instance,
-                                                                  name=operation_name)
-        simple_after_task = api.task.OperationTask.node_instance(instance=node_instance,
-                                                                 name=operation_name)
+        simple_before_task = api.task.OperationTask.node(instance=node,
+                                                         name=operation_name)
+        simple_after_task = api.task.OperationTask.node(instance=node,
+                                                        name=operation_name)
 
         inner_task_graph = api.task.WorkflowTask(sub_workflow, name='test_inner_task_graph')
-        inner_task = api.task.OperationTask.node_instance(instance=node_instance,
-                                                          name=operation_name)
+        inner_task = api.task.OperationTask.node(instance=node,
+                                                 name=operation_name)
         inner_task_graph.add_tasks(inner_task)
 
     test_task_graph.add_tasks(simple_before_task)
@@ -91,7 +93,7 @@ def test_task_graph_into_execution_graph(tmpdir):
 def _assert_execution_is_api_task(execution_task, api_task):
     assert execution_task.id == api_task.id
     assert execution_task.name == api_task.name
-    assert execution_task.operation_mapping == api_task.operation_mapping
+    assert execution_task.implementation == api_task.implementation
     assert execution_task.actor == api_task.actor
     assert execution_task.inputs == api_task.inputs
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_executor.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_executor.py b/tests/orchestrator/workflows/executor/test_executor.py
index d983fe9..580bf8b 100644
--- a/tests/orchestrator/workflows/executor/test_executor.py
+++ b/tests/orchestrator/workflows/executor/test_executor.py
@@ -28,7 +28,7 @@ except ImportError:
     _celery = None
     app = None
 
-from aria.storage import model
+from aria.storage.modeling import model
 from aria.orchestrator import events
 from aria.orchestrator.workflows.executor import (
     thread,
@@ -43,7 +43,7 @@ def test_execute(executor):
     expected_value = 'value'
     successful_task = MockTask(mock_successful_task)
     failing_task = MockTask(mock_failing_task)
-    task_with_inputs = MockTask(mock_task_with_input, inputs={'input': expected_value})
+    task_with_inputs = MockTask(mock_task_with_input, inputs=dict(input='value'))
 
     for task in [successful_task, failing_task, task_with_inputs]:
         executor.execute(task)
@@ -105,8 +105,9 @@ class MockTask(object):
         self.exception = None
         self.id = str(uuid.uuid4())
         name = func.__name__
-        operation = 'tests.orchestrator.workflows.executor.test_executor.{name}'.format(name=name)
-        self.operation_mapping = operation
+        implementation = 'tests.orchestrator.workflows.executor.test_executor.{name}'.format(
+            name=name)
+        self.implementation = implementation
         self.logger = logging.getLogger()
         self.name = name
         self.inputs = inputs or {}

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor.py b/tests/orchestrator/workflows/executor/test_process_executor.py
index ff5dce6..e904eb3 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor.py
@@ -42,7 +42,7 @@ class TestProcessExecutor(object):
 
     def test_plugin_execution(self, executor, mock_plugin):
         task = MockTask(plugin=mock_plugin,
-                        operation='mock_plugin1.operation')
+                        implementation='mock_plugin1.operation')
 
         queue = Queue.Queue()
 
@@ -131,11 +131,11 @@ class MockTask(object):
 
     INFINITE_RETRIES = aria_model.Task.INFINITE_RETRIES
 
-    def __init__(self, plugin, operation):
+    def __init__(self, plugin, implementation):
         self.id = str(uuid.uuid4())
-        self.operation_mapping = operation
+        self.implementation = implementation
         self.logger = logging.getLogger()
-        self.name = operation
+        self.name = implementation
         self.inputs = {}
         self.context = MockContext()
         self.retry_count = 0

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor_extension.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_extension.py b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
index 18957f1..ac95554 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_extension.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
@@ -30,15 +30,18 @@ def test_decorate_extension(context, executor):
     inputs = {'input1': 1, 'input2': 2}
 
     def get_node_instance(ctx):
-        return ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        return ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
 
     @workflow
     def mock_workflow(ctx, graph):
         node_instance = get_node_instance(ctx)
         op = 'test.op'
-        op_dict = {'operation': '{0}.{1}'.format(__name__, _mock_operation.__name__)}
-        node_instance.node.operations['test.op'] = op_dict
-        task = api.task.OperationTask.node_instance(instance=node_instance, name=op, inputs=inputs)
+        node_instance.interfaces = [mock.models.get_interface(
+            op,
+            operation_kwargs=dict(implementation='{0}.{1}'.format(__name__,
+                                                                  _mock_operation.__name__))
+        )]
+        task = api.task.OperationTask.node(instance=node_instance, name=op, inputs=inputs)
         graph.add_tasks(task)
         return graph
     graph = mock_workflow(ctx=context)  # pylint: disable=no-value-for-parameter
@@ -55,7 +58,7 @@ class MockProcessExecutorExtension(object):
     def decorate(self):
         def decorator(function):
             def wrapper(ctx, **operation_inputs):
-                ctx.node_instance.runtime_properties['out'] = {'wrapper_inputs': operation_inputs}
+                ctx.node.runtime_properties['out'] = {'wrapper_inputs': operation_inputs}
                 function(ctx=ctx, **operation_inputs)
             return wrapper
         return decorator
@@ -63,7 +66,7 @@ class MockProcessExecutorExtension(object):
 
 @operation
 def _mock_operation(ctx, **operation_inputs):
-    ctx.node_instance.runtime_properties['out']['function_inputs'] = operation_inputs
+    ctx.node.runtime_properties['out']['function_inputs'] = operation_inputs
 
 
 @pytest.fixture

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
index e383859..d3b3300 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
@@ -45,13 +45,13 @@ def test_track_changes_of_failed_operation(context, executor):
 
 
 def _assert_tracked_changes_are_applied(context):
-    instance = context.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    instance = context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
     assert instance.runtime_properties == _TEST_RUNTIME_PROPERTIES
 
 
 def _update_runtime_properties(context):
-    context.node_instance.runtime_properties.clear()
-    context.node_instance.runtime_properties.update(_TEST_RUNTIME_PROPERTIES)
+    context.node.runtime_properties.clear()
+    context.node.runtime_properties.update(_TEST_RUNTIME_PROPERTIES)
 
 
 def test_refresh_state_of_tracked_attributes(context, executor):
@@ -66,7 +66,7 @@ def test_apply_tracked_changes_during_an_operation(context, executor):
         'changed_but_refreshed': {'some': 'newer', 'properties': 'right there'}
     }
 
-    expected_initial = context.model.node_instance.get_by_name(
+    expected_initial = context.model.node.get_by_name(
         mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
 
     out = _run_workflow(context=context, executor=executor, op_func=_mock_updating_operation,
@@ -87,17 +87,18 @@ def test_apply_tracked_changes_during_an_operation(context, executor):
 def _run_workflow(context, executor, op_func, inputs=None):
     @workflow
     def mock_workflow(ctx, graph):
-        node_instance = ctx.model.node_instance.get_by_name(
-            mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-        node_instance.node.operations['test.op'] = {'operation': _operation_mapping(op_func)}
-        task = api.task.OperationTask.node_instance(instance=node_instance, name='test.op',
-                                                    inputs=inputs or {})
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        node.interfaces = [mock.models.get_interface(
+            'test.op', operation_kwargs=dict(implementation=_operation_mapping(op_func)))]
+        task = api.task.OperationTask.node(instance=node,
+                                           name='test.op',
+                                           inputs=inputs or {})
         graph.add_tasks(task)
         return graph
     graph = mock_workflow(ctx=context)  # pylint: disable=no-value-for-parameter
     eng = engine.Engine(executor=executor, workflow_context=context, tasks_graph=graph)
     eng.execute()
-    return context.model.node_instance.get_by_name(
+    return context.model.node.get_by_name(
         mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties.get('out')
 
 
@@ -114,25 +115,25 @@ def _mock_fail_operation(ctx):
 
 @operation
 def _mock_refreshing_operation(ctx):
-    out = {'initial': copy.deepcopy(ctx.node_instance.runtime_properties)}
-    ctx.node_instance.runtime_properties.update({'some': 'new', 'properties': 'right here'})
-    out['after_change'] = copy.deepcopy(ctx.node_instance.runtime_properties)
-    ctx.model.node_instance.refresh(ctx.node_instance)
-    out['after_refresh'] = copy.deepcopy(ctx.node_instance.runtime_properties)
-    ctx.node_instance.runtime_properties['out'] = out
+    out = {'initial': copy.deepcopy(ctx.node.runtime_properties)}
+    ctx.node.runtime_properties.update({'some': 'new', 'properties': 'right here'})
+    out['after_change'] = copy.deepcopy(ctx.node.runtime_properties)
+    ctx.model.node.refresh(ctx.node)
+    out['after_refresh'] = copy.deepcopy(ctx.node.runtime_properties)
+    ctx.node.runtime_properties['out'] = out
 
 
 @operation
 def _mock_updating_operation(ctx, committed, changed_but_refreshed):
-    out = {'initial': copy.deepcopy(ctx.node_instance.runtime_properties)}
-    ctx.node_instance.runtime_properties.update(committed)
-    ctx.model.node_instance.update(ctx.node_instance)
-    out['after_update'] = copy.deepcopy(ctx.node_instance.runtime_properties)
-    ctx.node_instance.runtime_properties.update(changed_but_refreshed)
-    out['after_change'] = copy.deepcopy(ctx.node_instance.runtime_properties)
-    ctx.model.node_instance.refresh(ctx.node_instance)
-    out['after_refresh'] = copy.deepcopy(ctx.node_instance.runtime_properties)
-    ctx.node_instance.runtime_properties['out'] = out
+    out = {'initial': copy.deepcopy(ctx.node.runtime_properties)}
+    ctx.node.runtime_properties.update(committed)
+    ctx.model.node.update(ctx.node)
+    out['after_update'] = copy.deepcopy(ctx.node.runtime_properties)
+    ctx.node.runtime_properties.update(changed_but_refreshed)
+    out['after_change'] = copy.deepcopy(ctx.node.runtime_properties)
+    ctx.model.node.refresh(ctx.node)
+    out['after_refresh'] = copy.deepcopy(ctx.node.runtime_properties)
+    ctx.node.runtime_properties['out'] = out
 
 
 def _operation_mapping(func):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/resources/scripts/test_ssh.sh
----------------------------------------------------------------------
diff --git a/tests/resources/scripts/test_ssh.sh b/tests/resources/scripts/test_ssh.sh
index 6f18278..90202c7 100644
--- a/tests/resources/scripts/test_ssh.sh
+++ b/tests/resources/scripts/test_ssh.sh
@@ -4,7 +4,7 @@ set -u
 set -e
 
 test_run_script_basic() {
-    ctx node-instance runtime-properties test_value $test_value
+    ctx node runtime-properties test_value $test_value
 }
 
 test_run_script_as_sudo() {
@@ -12,7 +12,7 @@ test_run_script_as_sudo() {
 }
 
 test_run_script_default_base_dir() {
-    ctx node-instance runtime-properties work_dir $PWD
+    ctx node runtime-properties work_dir $PWD
 }
 
 test_run_script_with_hide() {
@@ -20,44 +20,44 @@ test_run_script_with_hide() {
 }
 
 test_run_script_process_config() {
-    ctx node-instance runtime-properties env_value $test_value_env
-    ctx node-instance runtime-properties bash_version $BASH_VERSION
-    ctx node-instance runtime-properties arg1_value $1
-    ctx node-instance runtime-properties arg2_value $2
-    ctx node-instance runtime-properties cwd $PWD
-    ctx node-instance runtime-properties ctx_path $(which ctx)
+    ctx node runtime-properties env_value $test_value_env
+    ctx node runtime-properties bash_version $BASH_VERSION
+    ctx node runtime-properties arg1_value $1
+    ctx node runtime-properties arg2_value $2
+    ctx node runtime-properties cwd $PWD
+    ctx node runtime-properties ctx_path $(which ctx)
 }
 
 test_run_script_command_prefix() {
-    ctx node-instance runtime-properties dollar_dash $-
+    ctx node runtime-properties dollar_dash $-
 }
 
 test_run_script_reuse_existing_ctx_1() {
-    ctx node-instance runtime-properties test_value1 $test_value1
+    ctx node runtime-properties test_value1 $test_value1
 }
 
 test_run_script_reuse_existing_ctx_2() {
-    ctx node-instance runtime-properties test_value2 $test_value2
+    ctx node runtime-properties test_value2 $test_value2
 }
 
 test_run_script_download_resource_plain() {
     local destination=$(mktemp)
     ctx download-resource ${destination} test_resource
-    ctx node-instance runtime-properties test_value "$(cat ${destination})"
+    ctx node runtime-properties test_value "$(cat ${destination})"
 }
 
 test_run_script_download_resource_and_render() {
     local destination=$(mktemp)
     ctx download-resource-and-render ${destination} test_resource
-    ctx node-instance runtime-properties test_value "$(cat ${destination})"
+    ctx node runtime-properties test_value "$(cat ${destination})"
 }
 
 test_run_script_inputs_as_env_variables_no_override() {
-    ctx node-instance runtime-properties test_value "$custom_env_var"
+    ctx node runtime-properties test_value "$custom_env_var"
 }
 
 test_run_script_inputs_as_env_variables_process_env_override() {
-    ctx node-instance runtime-properties test_value "$custom_env_var"
+    ctx node runtime-properties test_value "$custom_env_var"
 }
 
 test_run_script_error_in_script() {

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/resources/service_templates/node-cellar/workflows.py
----------------------------------------------------------------------
diff --git a/tests/resources/service_templates/node-cellar/workflows.py b/tests/resources/service_templates/node-cellar/workflows.py
index b3546bb..fff78bf 100644
--- a/tests/resources/service_templates/node-cellar/workflows.py
+++ b/tests/resources/service_templates/node-cellar/workflows.py
@@ -11,9 +11,6 @@ def maintenance(ctx, graph, enabled):
 
     operation = 'Maintenance.enable' if enabled else 'Maintenance.disable'
 
-    for node_instance in ctx.model.node_instance.iter():
-        if operation in node_instance.node.operations:
-            task = OperationTask.node_instance(
-                instance=node_instance,
-                name=operation)
-            graph.add_tasks(task)
+    for node in ctx.model.node.iter():
+        for interface in node.interfaces.filter_by(name='Maintenance', type_name='Maintenance'):
+            graph.add_tasks(OperationTask.node(instance=node, name=operation))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index b798e01..4278831 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -14,8 +14,8 @@
 # limitations under the License.
 import os
 import platform
-from tempfile import mkdtemp
 from shutil import rmtree
+from tempfile import mkdtemp
 
 from sqlalchemy import (
     create_engine,
@@ -29,13 +29,14 @@ from sqlalchemy import (
 
 from aria.storage import (
     model,
-    structure,
     type as aria_type,
+    structure,
+    modeling
 )
 
 
-class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method
-    __tablename__ = 'mock_models'
+class MockModel(model.aria_declarative_base, structure.ModelMixin): #pylint: disable=abstract-method
+    __tablename__ = 'mock_model'
     model_dict = Column(aria_type.Dict)
     model_list = Column(aria_type.List)
     value = Column(Integer)
@@ -64,7 +65,7 @@ def release_sqlite_storage(storage):
             session.rollback()
             session.close()
         for engine in set(mapi._engine for mapi in mapis):
-            model.DeclarativeBase.metadata.drop_all(engine)
+            model.aria_declarative_base.metadata.drop_all(engine)
 
 
 def init_inmemory_model_storage():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
index 771342c..08d5ae0 100644
--- a/tests/storage/test_instrumentation.py
+++ b/tests/storage/test_instrumentation.py
@@ -17,17 +17,16 @@ import pytest
 from sqlalchemy import Column, Text, Integer, event
 
 from aria.storage import (
-    model,
     structure,
-    type as aria_type,
     ModelStorage,
     sql_mapi,
     instrumentation,
-    exceptions
+    exceptions,
+    type as aria_type,
+    model
 )
 from ..storage import release_sqlite_storage, init_inmemory_model_storage
 
-
 STUB = instrumentation._STUB
 Value = instrumentation._Value
 instruments_holder = []
@@ -346,15 +345,15 @@ class _MockModel(structure.ModelMixin):
     string2 = Column(Text)
 
 
-class MockModel1(model.DeclarativeBase, _MockModel):
-    __tablename__ = 'mock_model1'
+class MockModel1(_MockModel, model.aria_declarative_base):
+    __tablename__ = 'mock_model_1'
 
 
-class MockModel2(model.DeclarativeBase, _MockModel):
-    __tablename__ = 'mock_model2'
+class MockModel2(_MockModel, model.aria_declarative_base):
+    __tablename__ = 'mock_model_2'
 
 
-class StrictMockModel(model.DeclarativeBase):
+class StrictMockModel(structure.ModelMixin, model.aria_declarative_base):
     __tablename__ = 'strict_mock_model'
 
     strict_dict = Column(aria_type.StrictDict(basestring, basestring))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 34cc5df..f88080a 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -17,9 +17,9 @@ import pytest
 
 from aria.storage import (
     ModelStorage,
-    model,
     exceptions,
     sql_mapi,
+    modeling,
 )
 from aria import application_model_storage
 from ..storage import release_sqlite_storage, init_inmemory_model_storage
@@ -38,7 +38,7 @@ def storage():
 
 @pytest.fixture(scope='module', autouse=True)
 def module_cleanup():
-    model.DeclarativeBase.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
+    modeling.model.aria_declarative_base.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
 
 
 def test_storage_base(storage):
@@ -63,14 +63,41 @@ def test_model_storage(storage):
 def test_application_storage_factory():
     storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
                                         initiator=init_inmemory_model_storage)
+
+    assert storage.parameter
+    assert storage.mapping_template
+    assert storage.substitution_template
+    assert storage.service_template
+    assert storage.node_template
+    assert storage.group_template
+    assert storage.interface_template
+    assert storage.operation_template
+    assert storage.artifact_template
+    assert storage.policy_template
+    assert storage.group_policy_template
+    assert storage.group_policy_trigger_template
+    assert storage.requirement_template
+    assert storage.capability_template
+
+    assert storage.mapping
+    assert storage.substitution
+    assert storage.service_instance
     assert storage.node
-    assert storage.node_instance
-    assert storage.plugin
-    assert storage.blueprint
-    assert storage.deployment
-    assert storage.deployment_update
-    assert storage.deployment_update_step
-    assert storage.deployment_modification
+    assert storage.group
+    assert storage.interface
+    assert storage.operation
+    assert storage.capability
+    assert storage.artifact
+    assert storage.policy
+    assert storage.group_policy
+    assert storage.group_policy_trigger
+    assert storage.relationship
+
     assert storage.execution
+    assert storage.service_instance_update
+    assert storage.service_instance_update_step
+    assert storage.service_instance_modification
+    assert storage.plugin
+    assert storage.task
 
     release_sqlite_storage(storage)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_models.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py
index 6450152..1e8d6b5 100644
--- a/tests/storage/test_models.py
+++ b/tests/storage/test_models.py
@@ -22,22 +22,21 @@ from aria.storage import (
     exceptions,
     sql_mapi,
 )
-from aria.storage.model import (
-    DeploymentUpdateStep,
-    Blueprint,
+from aria.storage.modeling.model import (
+    ServiceTemplate,
+    ServiceInstance,
+    ServiceInstanceUpdate,
+    ServiceInstanceUpdateStep,
+    ServiceInstanceModification,
     Execution,
     Task,
     Plugin,
-    Deployment,
-    Node,
-    NodeInstance,
     Relationship,
-    RelationshipInstance,
-    DeploymentUpdate,
-    DeploymentModification,
+    NodeTemplate,
+    Node,
+    Parameter
 )
 
-
 from tests import mock
 from ..storage import release_sqlite_storage, init_inmemory_model_storage
 
@@ -58,61 +57,56 @@ def _empty_storage():
                                      initiator=init_inmemory_model_storage)
 
 
-def _blueprint_storage():
+def _service_template_storage():
     storage = _empty_storage()
-    blueprint = mock.models.get_blueprint()
-    storage.blueprint.put(blueprint)
+    service_template = mock.models.get_blueprint()
+    storage.service_template.put(service_template)
     return storage
 
 
-def _deployment_storage():
-    storage = _blueprint_storage()
-    deployment = mock.models.get_deployment(storage.blueprint.list()[0])
-    storage.deployment.put(deployment)
+def _service_instance_storage():
+    storage = _service_template_storage()
+    service_instance = mock.models.get_deployment(
+        storage.service_template.get_by_name(mock.models.BLUEPRINT_NAME))
+    storage.service_instance.put(service_instance)
     return storage
 
 
-def _deployment_update_storage():
-    storage = _deployment_storage()
-    deployment_update = DeploymentUpdate(
-        deployment=storage.deployment.list()[0],
+def _service_instance_update_storage():
+    storage = _service_instance_storage()
+    service_instance_update = ServiceInstanceUpdate(
+        service_instance=storage.service_instance.list()[0],
         created_at=now,
-        deployment_plan={},
+        service_instance_plan={},
     )
-    storage.deployment_update.put(deployment_update)
+    storage.service_instance_update.put(service_instance_update)
     return storage
 
 
-def _node_storage():
-    storage = _deployment_storage()
-    node = mock.models.get_dependency_node(storage.deployment.list()[0])
-    storage.node.put(node)
+def _node_template_storage():
+    storage = _service_instance_storage()
+    node_template = mock.models.get_dependency_node(storage.service_instance.list()[0])
+    storage.node_template.put(node_template)
     return storage
 
 
 def _nodes_storage():
-    storage = _deployment_storage()
-    dependent_node = mock.models.get_dependent_node(storage.deployment.list()[0])
-    dependency_node = mock.models.get_dependency_node(storage.deployment.list()[0])
-    storage.node.put(dependent_node)
-    storage.node.put(dependency_node)
-    return storage
-
-
-def _node_instances_storage():
     storage = _nodes_storage()
-    dependent_node = storage.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
-    dependency_node = storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-    dependency_node_instance = mock.models.get_dependency_node_instance(dependency_node)
-    dependent_node_instance = mock.models.get_dependent_node_instance(dependent_node)
-    storage.node_instance.put(dependency_node_instance)
-    storage.node_instance.put(dependent_node_instance)
+    service_instance = storage.service_instance.get_by_name(mock.models.DEPLOYMENT_NAME)
+    dependent_node_template = storage.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+    dependency_node_template = storage.node_template.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+    dependency_node = mock.models.get_dependency_node_instance(dependency_node_template,
+                                                               service_instance)
+    dependent_node = mock.models.get_dependent_node_instance(dependent_node_template,
+                                                             service_instance)
+    storage.node.put(dependency_node)
+    storage.node.put(dependent_node)
     return storage
 
 
 def _execution_storage():
-    storage = _deployment_storage()
-    execution = mock.models.get_execution(storage.deployment.list()[0])
+    storage = _service_instance_storage()
+    execution = mock.models.get_execution(storage.service_instance.list()[0])
     plugin = mock.models.get_plugin()
     storage.execution.put(execution)
     storage.plugin.put(plugin)
@@ -126,26 +120,26 @@ def empty_storage():
 
 
 @pytest.fixture
-def blueprint_storage():
-    with sql_storage(_blueprint_storage) as storage:
+def service_template_storage():
+    with sql_storage(_service_template_storage) as storage:
         yield storage
 
 
 @pytest.fixture
-def deployment_storage():
-    with sql_storage(_deployment_storage) as storage:
+def service_instance_storage():
+    with sql_storage(_service_instance_storage) as storage:
         yield storage
 
 
 @pytest.fixture
-def deployment_update_storage():
-    with sql_storage(_deployment_update_storage) as storage:
+def service_instance_update_storage():
+    with sql_storage(_service_instance_update_storage) as storage:
         yield storage
 
 
 @pytest.fixture
-def node_storage():
-    with sql_storage(_node_storage) as storage:
+def node_template_storage():
+    with sql_storage(_node_template_storage) as storage:
         yield storage
 
 
@@ -156,12 +150,6 @@ def nodes_storage():
 
 
 @pytest.fixture
-def node_instances_storage():
-    with sql_storage(_node_instances_storage) as storage:
-        yield storage
-
-
-@pytest.fixture
 def execution_storage():
     with sql_storage(_execution_storage) as storage:
         yield storage
@@ -171,17 +159,17 @@ m_cls = type('MockClass')
 now = datetime.utcnow()
 
 
-def _test_model(is_valid, storage, model_name, model_cls, model_kwargs):
+def _test_model(is_valid, storage, model_cls, model_kwargs):
     if is_valid:
         model = model_cls(**model_kwargs)
-        getattr(storage, model_name).put(model)
+        getattr(storage, model_cls.__modelname__).put(model)
         return model
     else:
-        with pytest.raises(exceptions.StorageError):
-            getattr(storage, model_name).put(model_cls(**model_kwargs))
+        with pytest.raises((exceptions.StorageError, TypeError),):
+            getattr(storage, model_cls.__modelname__).put(model_cls(**model_kwargs))
 
 
-class TestBlueprint(object):
+class TestServiceTemplate(object):
 
     @pytest.mark.parametrize(
         'is_valid, plan, description, created_at, updated_at, main_file_name',
@@ -198,74 +186,71 @@ class TestBlueprint(object):
                                       updated_at, main_file_name):
         _test_model(is_valid=is_valid,
                     storage=empty_storage,
-                    model_name='blueprint',
-                    model_cls=Blueprint,
-                    model_kwargs=dict(plan=plan,
-                                      description=description,
-                                      created_at=created_at,
-                                      updated_at=updated_at,
-                                      main_file_name=main_file_name))
+                    model_cls=ServiceTemplate,
+                    model_kwargs=dict(
+                        plan=plan,
+                        description=description,
+                        created_at=created_at,
+                        updated_at=updated_at,
+                        main_file_name=main_file_name)
+                   )
 
 
-class TestDeployment(object):
+class TestServiceInstance(object):
 
     @pytest.mark.parametrize(
-        'is_valid, name, created_at, description, inputs, groups, permalink, policy_triggers, '
+        'is_valid, name, created_at, description, inputs, permalink, policy_triggers, '
         'policy_types, outputs, scaling_groups, updated_at, workflows',
         [
-            (False, m_cls, now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (False, 'name', m_cls, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (False, 'name', now, m_cls, {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (False, 'name', now, 'desc', m_cls, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (False, 'name', now, 'desc', {}, m_cls, 'perlnk', {}, {}, {}, {}, now, {}),
-            (False, 'name', now, 'desc', {}, {}, m_cls, {}, {}, {}, {}, now, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', m_cls, {}, {}, {}, now, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, m_cls, {}, {}, now, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, m_cls, {}, now, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, m_cls, now, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, m_cls, {}),
-            (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, m_cls),
-
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, None, now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, 'name', now, None, {}, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', None, {}, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, None, 'perlnk', {}, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, None, {}, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', None, {}, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, None, {}, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, None, {}, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, None, now, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, None, {}),
-            (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, None),
+            (False, m_cls, now, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (False, 'name', m_cls, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (False, 'name', now, m_cls, [], 'perlnk', {}, {}, [], {}, now, {}),
+            (False, 'name', now, 'desc', [], m_cls, {}, {}, [], {}, now, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', m_cls, {}, [], {}, now, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', {}, m_cls, [], {}, now, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', {}, {}, m_cls, {}, now, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], m_cls, now, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, m_cls, {}),
+            (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, m_cls),
+
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (True, None, now, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (True, 'name', now, None, [], 'perlnk', {}, {}, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], None, {}, {}, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', None, {}, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, None, [], {}, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], None, now, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, None, {}),
+            (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, None),
         ]
     )
-    def test_deployment_model_creation(self, deployment_storage, is_valid, name, created_at,
-                                       description, inputs, groups, permalink, policy_triggers,
+    def test_deployment_model_creation(self, service_instance_storage, is_valid, name, created_at,
+                                       description, inputs, permalink, policy_triggers,
                                        policy_types, outputs, scaling_groups, updated_at,
                                        workflows):
-        deployment = _test_model(is_valid=is_valid,
-                                 storage=deployment_storage,
-                                 model_name='deployment',
-                                 model_cls=Deployment,
-                                 model_kwargs=dict(
-                                     name=name,
-                                     blueprint=deployment_storage.blueprint.list()[0],
-                                     created_at=created_at,
-                                     description=description,
-                                     inputs=inputs,
-                                     groups=groups,
-                                     permalink=permalink,
-                                     policy_triggers=policy_triggers,
-                                     policy_types=policy_types,
-                                     outputs=outputs,
-                                     scaling_groups=scaling_groups,
-                                     updated_at=updated_at,
-                                     workflows=workflows
-                                 ))
+        service_instance = _test_model(
+            is_valid=is_valid,
+            storage=service_instance_storage,
+            model_cls=ServiceInstance,
+            model_kwargs=dict(
+                name=name,
+                service_template=service_instance_storage.service_template.list()[0],
+                created_at=created_at,
+                description=description,
+                inputs=inputs,
+                permalink=permalink,
+                policy_triggers=policy_triggers,
+                policy_types=policy_types,
+                outputs=outputs,
+                scaling_groups=scaling_groups,
+                updated_at=updated_at,
+                workflows=workflows
+            ))
         if is_valid:
-            assert deployment.blueprint == deployment_storage.blueprint.list()[0]
+            assert service_instance.service_template == \
+                   service_instance_storage.service_template.list()[0]
 
 
 class TestExecution(object):
@@ -289,27 +274,27 @@ class TestExecution(object):
             (True, now, now, now, 'error', False, None, Execution.STARTED, 'wf_name'),
         ]
     )
-    def test_execution_model_creation(self, deployment_storage, is_valid, created_at, started_at,
-                                      ended_at, error, is_system_workflow, parameters, status,
-                                      workflow_name):
-        execution = _test_model(is_valid=is_valid,
-                                storage=deployment_storage,
-                                model_name='execution',
-                                model_cls=Execution,
-                                model_kwargs=dict(
-                                    deployment=deployment_storage.deployment.list()[0],
-                                    created_at=created_at,
-                                    started_at=started_at,
-                                    ended_at=ended_at,
-                                    error=error,
-                                    is_system_workflow=is_system_workflow,
-                                    parameters=parameters,
-                                    status=status,
-                                    workflow_name=workflow_name,
-                                ))
+    def test_execution_model_creation(self, service_instance_storage, is_valid, created_at,
+                                      started_at, ended_at, error, is_system_workflow, parameters,
+                                      status, workflow_name):
+        execution = _test_model(
+            is_valid=is_valid,
+            storage=service_instance_storage,
+            model_cls=Execution,
+            model_kwargs=dict(
+                service_instance=service_instance_storage.service_instance.list()[0],
+                created_at=created_at,
+                started_at=started_at,
+                ended_at=ended_at,
+                error=error,
+                is_system_workflow=is_system_workflow,
+                parameters=parameters,
+                status=status,
+                workflow_name=workflow_name,
+            ))
         if is_valid:
-            assert execution.deployment == deployment_storage.deployment.list()[0]
-            assert execution.blueprint == deployment_storage.blueprint.list()[0]
+            assert execution.service_instance == service_instance_storage.service_instance.list()[0]
+            assert execution.service_template == service_instance_storage.service_template.list()[0]
 
     def test_execution_status_transition(self):
         def create_execution(status):
@@ -376,10 +361,11 @@ class TestExecution(object):
                     execution.status = transitioned_status
 
 
-class TestDeploymentUpdate(object):
+class TestServiceInstanceUpdate(object):
     @pytest.mark.parametrize(
-        'is_valid, created_at, deployment_plan, deployment_update_node_instances, '
-        'deployment_update_deployment, deployment_update_nodes, modified_entity_ids, state',
+        'is_valid, created_at, deployment_plan, service_instance_update_node_instances, '
+        'service_instance_update_service_instance, service_instance_update_nodes, '
+        'modified_entity_ids, state',
         [
             (False, m_cls, {}, {}, {}, [], {}, 'state'),
             (False, now, m_cls, {}, {}, [], {}, 'state'),
@@ -397,74 +383,74 @@ class TestDeploymentUpdate(object):
             (True, now, {}, {}, {}, [], {}, None),
         ]
     )
-    def test_deployment_update_model_creation(self, deployment_storage, is_valid, created_at,
-                                              deployment_plan, deployment_update_node_instances,
-                                              deployment_update_deployment, deployment_update_nodes,
-                                              modified_entity_ids, state):
-        deployment_update = _test_model(
+    def test_service_instance_update_model_creation(
+            self, service_instance_storage, is_valid, created_at, deployment_plan,
+            service_instance_update_node_instances, service_instance_update_service_instance,
+            service_instance_update_nodes, modified_entity_ids, state):
+        service_instance_update = _test_model(
             is_valid=is_valid,
-            storage=deployment_storage,
-            model_name='deployment_update',
-            model_cls=DeploymentUpdate,
+            storage=service_instance_storage,
+            model_cls=ServiceInstanceUpdate,
             model_kwargs=dict(
-                deployment=deployment_storage.deployment.list()[0],
+                service_instance=service_instance_storage.service_instance.list()[0],
                 created_at=created_at,
-                deployment_plan=deployment_plan,
-                deployment_update_node_instances=deployment_update_node_instances,
-                deployment_update_deployment=deployment_update_deployment,
-                deployment_update_nodes=deployment_update_nodes,
+                service_instance_plan=deployment_plan,
+                service_instance_update_node_instances=service_instance_update_node_instances,
+                service_instance_update_service_instance=service_instance_update_service_instance,
+                service_instance_update_nodes=service_instance_update_nodes,
                 modified_entity_ids=modified_entity_ids,
                 state=state,
             ))
         if is_valid:
-            assert deployment_update.deployment == deployment_storage.deployment.list()[0]
+            assert service_instance_update.service_instance == \
+                   service_instance_storage.service_instance.list()[0]
 
 
-class TestDeploymentUpdateStep(object):
+class TestServiceInstanceUpdateStep(object):
 
     @pytest.mark.parametrize(
         'is_valid, action, entity_id, entity_type',
         [
-            (False, m_cls, 'id', DeploymentUpdateStep.ENTITY_TYPES.NODE),
-            (False, DeploymentUpdateStep.ACTION_TYPES.ADD, m_cls,
-             DeploymentUpdateStep.ENTITY_TYPES.NODE),
-            (False, DeploymentUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
+            (False, m_cls, 'id', ServiceInstanceUpdateStep.ENTITY_TYPES.NODE),
+            (False, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, m_cls,
+             ServiceInstanceUpdateStep.ENTITY_TYPES.NODE),
+            (False, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
 
-            (True, DeploymentUpdateStep.ACTION_TYPES.ADD, 'id',
-             DeploymentUpdateStep.ENTITY_TYPES.NODE)
+            (True, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, 'id',
+             ServiceInstanceUpdateStep.ENTITY_TYPES.NODE)
         ]
     )
-    def test_deployment_update_step_model_creation(self, deployment_update_storage, is_valid,
+    def test_deployment_update_step_model_creation(self, service_instance_update_storage, is_valid,
                                                    action, entity_id, entity_type):
-        deployment_update_step = _test_model(
+        service_instance_update_step = _test_model(
             is_valid=is_valid,
-            storage=deployment_update_storage,
-            model_name='deployment_update_step',
-            model_cls=DeploymentUpdateStep,
+            storage=service_instance_update_storage,
+            model_cls=ServiceInstanceUpdateStep,
             model_kwargs=dict(
-                deployment_update=deployment_update_storage.deployment_update.list()[0],
+                service_instance_update=
+                service_instance_update_storage.service_instance_update.list()[0],
                 action=action,
                 entity_id=entity_id,
                 entity_type=entity_type
             ))
         if is_valid:
-            assert deployment_update_step.deployment_update == \
-                  deployment_update_storage.deployment_update.list()[0]
+            assert service_instance_update_step.service_instance_update == \
+                   service_instance_update_storage.service_instance_update.list()[0]
 
     def test_deployment_update_step_order(self):
-        add_node = DeploymentUpdateStep(
+        add_node = ServiceInstanceUpdateStep(
             id='add_step',
             action='add',
             entity_type='node',
             entity_id='node_id')
 
-        modify_node = DeploymentUpdateStep(
+        modify_node = ServiceInstanceUpdateStep(
             id='modify_step',
             action='modify',
             entity_type='node',
             entity_id='node_id')
 
-        remove_node = DeploymentUpdateStep(
+        remove_node = ServiceInstanceUpdateStep(
             id='remove_step',
             action='remove',
             entity_type='node',
@@ -476,13 +462,13 @@ class TestDeploymentUpdateStep(object):
         assert remove_node < modify_node < add_node
         assert not remove_node > modify_node > add_node
 
-        add_rel = DeploymentUpdateStep(
+        add_rel = ServiceInstanceUpdateStep(
             id='add_step',
             action='add',
             entity_type='relationship',
             entity_id='relationship_id')
 
-        remove_rel = DeploymentUpdateStep(
+        remove_rel = ServiceInstanceUpdateStep(
             id='remove_step',
             action='remove',
             entity_type='relationship',
@@ -494,140 +480,84 @@ class TestDeploymentUpdateStep(object):
 
 class TestDeploymentModification(object):
     @pytest.mark.parametrize(
-        'is_valid, context, created_at, ended_at, modified_nodes, node_instances, status',
+        'is_valid, context, created_at, ended_at, modified_node_templates, nodes, status',
         [
-            (False, m_cls, now, now, {}, {}, DeploymentModification.STARTED),
-            (False, {}, m_cls, now, {}, {}, DeploymentModification.STARTED),
-            (False, {}, now, m_cls, {}, {}, DeploymentModification.STARTED),
-            (False, {}, now, now, m_cls, {}, DeploymentModification.STARTED),
-            (False, {}, now, now, {}, m_cls, DeploymentModification.STARTED),
+            (False, m_cls, now, now, {}, {}, ServiceInstanceModification.STARTED),
+            (False, {}, m_cls, now, {}, {}, ServiceInstanceModification.STARTED),
+            (False, {}, now, m_cls, {}, {}, ServiceInstanceModification.STARTED),
+            (False, {}, now, now, m_cls, {}, ServiceInstanceModification.STARTED),
+            (False, {}, now, now, {}, m_cls, ServiceInstanceModification.STARTED),
             (False, {}, now, now, {}, {}, m_cls),
 
-            (True, {}, now, now, {}, {}, DeploymentModification.STARTED),
-            (True, {}, now, None, {}, {}, DeploymentModification.STARTED),
-            (True, {}, now, now, None, {}, DeploymentModification.STARTED),
-            (True, {}, now, now, {}, None, DeploymentModification.STARTED),
+            (True, {}, now, now, {}, {}, ServiceInstanceModification.STARTED),
+            (True, {}, now, None, {}, {}, ServiceInstanceModification.STARTED),
+            (True, {}, now, now, None, {}, ServiceInstanceModification.STARTED),
+            (True, {}, now, now, {}, None, ServiceInstanceModification.STARTED),
         ]
     )
-    def test_deployment_modification_model_creation(self, deployment_storage, is_valid, context,
-                                                    created_at, ended_at, modified_nodes,
-                                                    node_instances, status):
+    def test_deployment_modification_model_creation(
+            self, service_instance_storage, is_valid, context, created_at, ended_at,
+            modified_node_templates, nodes, status):
         deployment_modification = _test_model(
             is_valid=is_valid,
-            storage=deployment_storage,
-            model_name='deployment_modification',
-            model_cls=DeploymentModification,
+            storage=service_instance_storage,
+            model_cls=ServiceInstanceModification,
             model_kwargs=dict(
-                deployment=deployment_storage.deployment.list()[0],
+                service_instance=service_instance_storage.service_instance.list()[0],
                 context=context,
                 created_at=created_at,
                 ended_at=ended_at,
-                modified_nodes=modified_nodes,
-                node_instances=node_instances,
+                modified_nodes=modified_node_templates,
+                node_instances=nodes,
                 status=status,
             ))
         if is_valid:
-            assert deployment_modification.deployment == deployment_storage.deployment.list()[0]
+            assert deployment_modification.service_instance == \
+                   service_instance_storage.service_instance.list()[0]
 
 
-class TestNode(object):
+class TestNodeTemplate(object):
     @pytest.mark.parametrize(
-        'is_valid, name, deploy_number_of_instances, max_number_of_instances, '
-        'min_number_of_instances, number_of_instances, planned_number_of_instances, plugins, '
-        'properties, operations, type, type_hierarchy',
+        'is_valid, name, default_instances, max_instances, min_instances, plugins, properties, '
+        'type_name, type_hierarchy',
         [
-            (False, m_cls, 1, 1, 1, 1, 1, [], {}, {}, 'type', []),
-            (False, 'name', m_cls, 1, 1, 1, 1, [], {}, {}, 'type', []),
-            (False, 'name', 1, m_cls, 1, 1, 1, [], {}, {}, 'type', []),
-            (False, 'name', 1, 1, m_cls, 1, 1, [], {}, {}, 'type', []),
-            (False, 'name', 1, 1, 1, m_cls, 1, [], {}, {}, 'type', []),
-            (False, 'name', 1, 1, 1, 1, m_cls, [], {}, {}, 'type', []),
-            (False, 'name', 1, 1, 1, 1, 1, m_cls, {}, {}, 'type', []),
-            (False, 'name', 1, 1, 1, 1, 1, [], m_cls, {}, 'type', []),
-            (False, 'name', 1, 1, 1, 1, 1, [], {}, m_cls, 'type', []),
-            (False, 'name', 1, 1, 1, 1, 1, [], {}, {}, m_cls, []),
-            (False, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', m_cls),
-
-            (True, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', []),
-            (True, 'name', 1, 1, 1, 1, 1, None, {}, {}, 'type', []),
-            (True, 'name', 1, 1, 1, 1, 1, [], None, {}, 'type', []),
-            (True, 'name', 1, 1, 1, 1, 1, [], {}, None, 'type', []),
-            (True, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', None),
+            (False, m_cls, 1, 1, 1, [], [], 'type', []),
+            (False, 'name', m_cls, 1, 1, [], [], 'type', []),
+            (False, 'name', 1, m_cls, 1, [], [], 'type', []),
+            (False, 'name', 1, 1, m_cls, [], [], 'type', []),
+            (False, 'name', 1, 1, 1, m_cls, [], 'type', []),
+            (False, 'name', 1, 1, 1, [], [], m_cls, []),
+            (False, 'name', 1, 1, 1, [], [], 'type', m_cls),
+            #
+            (True, 'name', 1, 1, 1, [], [], 'type', []),
+            (True, 'name', 1, 1, 1, None, [], 'type', []),
+            (True, 'name', 1, 1, 1, [], [], 'type', None),
         ]
     )
-    def test_node_model_creation(self, deployment_storage, is_valid, name,
-                                 deploy_number_of_instances, max_number_of_instances,
-                                 min_number_of_instances, number_of_instances,
-                                 planned_number_of_instances, plugins,
-                                 properties, operations, type, type_hierarchy):
-        node = _test_model(
+    def test_node_model_creation(self, service_instance_storage, is_valid, name,
+                                 default_instances, max_instances, min_instances, plugins,
+                                 properties, type_name, type_hierarchy):
+        node_template = _test_model(
             is_valid=is_valid,
-            storage=deployment_storage,
-            model_name='node',
-            model_cls=Node,
+            storage=service_instance_storage,
+            model_cls=NodeTemplate,
             model_kwargs=dict(
                 name=name,
-                deploy_number_of_instances=deploy_number_of_instances,
-                max_number_of_instances=max_number_of_instances,
-                min_number_of_instances=min_number_of_instances,
-                number_of_instances=number_of_instances,
-                planned_number_of_instances=planned_number_of_instances,
+                default_instances=default_instances,
+                max_instances=max_instances,
+                min_instances=min_instances,
                 plugins=plugins,
                 properties=properties,
-                operations=operations,
-                type=type,
+                type_name=type_name,
                 type_hierarchy=type_hierarchy,
-                deployment=deployment_storage.deployment.list()[0]
+                service_template=service_instance_storage.service_template.list()[0]
             ))
         if is_valid:
-            assert node.deployment == deployment_storage.deployment.list()[0]
+            assert node_template.service_template == \
+                   service_instance_storage.service_template.list()[0]
 
 
-class TestRelationship(object):
-    @pytest.mark.parametrize(
-        'is_valid, source_interfaces, source_operations, target_interfaces, target_operations, '
-        'type, type_hierarchy, properties',
-        [
-            (False, m_cls, {}, {}, {}, 'type', [], {}),
-            (False, {}, m_cls, {}, {}, 'type', [], {}),
-            (False, {}, {}, m_cls, {}, 'type', [], {}),
-            (False, {}, {}, {}, m_cls, 'type', [], {}),
-            (False, {}, {}, {}, {}, m_cls, [], {}),
-            (False, {}, {}, {}, {}, 'type', m_cls, {}),
-            (False, {}, {}, {}, {}, 'type', [], m_cls),
-
-            (True, {}, {}, {}, {}, 'type', [], {}),
-            (True, None, {}, {}, {}, 'type', [], {}),
-            (True, {}, {}, None, {}, 'type', [], {}),
-            (True, {}, {}, {}, {}, 'type', None, {}),
-            (True, {}, {}, {}, {}, 'type', [], None),
-        ]
-        )
-    def test_relationship_model_ceration(self, nodes_storage, is_valid, source_interfaces,
-                                         source_operations, target_interfaces, target_operations,
-                                         type, type_hierarchy, properties):
-        relationship = _test_model(
-            is_valid=is_valid,
-            storage=nodes_storage,
-            model_name='relationship',
-            model_cls=Relationship,
-            model_kwargs=dict(
-                source_node=nodes_storage.node.list()[1],
-                target_node=nodes_storage.node.list()[0],
-                source_interfaces=source_interfaces,
-                source_operations=source_operations,
-                target_interfaces=target_interfaces,
-                target_operations=target_operations,
-                type=type,
-                type_hierarchy=type_hierarchy,
-                properties=properties,
-            ))
-        if is_valid:
-            assert relationship.source_node == nodes_storage.node.list()[1]
-            assert relationship.target_node == nodes_storage.node.list()[0]
-
-
-class TestNodeInstance(object):
+class TestNode(object):
     @pytest.mark.parametrize(
         'is_valid, name, runtime_properties, scaling_groups, state, version',
         [
@@ -644,94 +574,90 @@ class TestNodeInstance(object):
             (True, 'name', {}, [], 'state', None),
         ]
     )
-    def test_node_instance_model_creation(self, node_storage, is_valid, name, runtime_properties,
-                                          scaling_groups, state, version):
+    def test_node_instance_model_creation(self, node_template_storage, is_valid, name,
+                                          runtime_properties, scaling_groups, state, version):
         node_instance = _test_model(
             is_valid=is_valid,
-            storage=node_storage,
-            model_name='node_instance',
-            model_cls=NodeInstance,
+            storage=node_template_storage,
+            model_cls=Node,
             model_kwargs=dict(
-                node=node_storage.node.list()[0],
+                node_template=node_template_storage.node_template.list()[0],
                 name=name,
                 runtime_properties=runtime_properties,
                 scaling_groups=scaling_groups,
                 state=state,
                 version=version,
+                service_instance=node_template_storage.service_instance.list()[0]
             ))
         if is_valid:
-            assert node_instance.node == node_storage.node.list()[0]
-            assert node_instance.deployment == node_storage.deployment.list()[0]
+            assert node_instance.node_template == node_template_storage.node_template.list()[0]
+            assert node_instance.service_instance == \
+                   node_template_storage.service_instance.list()[0]
 
 
 class TestNodeInstanceIP(object):
 
     ip = '1.1.1.1'
 
-    def test_ip_on_none_hosted_node_instance(self, deployment_storage):
-        node = self._node(deployment_storage, ip='not considered')
-        node_instance = self._node_instance(deployment_storage, node,
-                                            is_host=False,
-                                            ip='not considered')
-        assert node_instance.ip is None
-
-    def test_property_ip_on_host_node_instance(self, deployment_storage):
-        node = self._node(deployment_storage, ip=self.ip)
-        node_instance = self._node_instance(deployment_storage, node,
-                                            is_host=True,
-                                            ip=None)
-        assert node_instance.ip == self.ip
-
-    def test_runtime_property_ip_on_host_node_instance(self, deployment_storage):
-        node = self._node(deployment_storage, ip='not considered')
-        node_instance = self._node_instance(deployment_storage, node,
-                                            is_host=True,
-                                            ip=self.ip)
-        assert node_instance.ip == self.ip
-
-    def test_no_ip_configured_on_host_node_instance(self, deployment_storage):
-        node = self._node(deployment_storage, ip=None)
-        node_instance = self._node_instance(deployment_storage, node,
-                                            is_host=True,
-                                            ip=None)
-        assert node_instance.ip is None
-
-    def test_runtime_property_on_hosted_node_instance(self, deployment_storage):
-        host_node = self._node(deployment_storage, ip=None)
-        host_node_instance = self._node_instance(deployment_storage, host_node,
-                                                 is_host=True,
-                                                 ip=self.ip)
-        node = self._node(deployment_storage, ip=None)
-        node_instance = self._node_instance(deployment_storage, node,
-                                            is_host=False,
-                                            ip=None,
-                                            host_fk=host_node_instance.id)
-        assert node_instance.ip == self.ip
-
-    def _node(self, storage, ip):
+    def test_ip_on_none_hosted_node_instance(self, service_instance_storage):
+        node_template = self._node_template(service_instance_storage, ip='not considered')
+        node = self._node(service_instance_storage,
+                          node_template,
+                          is_host=False,
+                          ip='not considered')
+        assert node.ip is None
+
+    def test_property_ip_on_host_node_instance(self, service_instance_storage):
+        node_template = self._node_template(service_instance_storage, ip=self.ip)
+        node = self._node(service_instance_storage, node_template, is_host=True, ip=None)
+        assert node.ip == self.ip
+
+    def test_runtime_property_ip_on_host_node_instance(self, service_instance_storage):
+        node_template = self._node_template(service_instance_storage, ip='not considered')
+        node = self._node(service_instance_storage, node_template, is_host=True, ip=self.ip)
+        assert node.ip == self.ip
+
+    def test_no_ip_configured_on_host_node_instance(self, service_instance_storage):
+        node_template = self._node_template(service_instance_storage, ip=None)
+        node = self._node(service_instance_storage, node_template, is_host=True, ip=None)
+        assert node.ip is None
+
+    def test_runtime_property_on_hosted_node_instance(self, service_instance_storage):
+        host_node_template = self._node_template(service_instance_storage, ip=None)
+        host_node = self._node(service_instance_storage,
+                               host_node_template,
+                               is_host=True,
+                               ip=self.ip)
+        node_template = self._node_template(service_instance_storage, ip=None)
+        node = self._node(service_instance_storage,
+                          node_template,
+                          is_host=False,
+                          ip=None,
+                          host_fk=host_node.id)
+        assert node.ip == self.ip
+
+    def _node_template(self, storage, ip):
         kwargs = dict(
-            name='node',
-            deploy_number_of_instances=1,
-            max_number_of_instances=1,
-            min_number_of_instances=1,
-            number_of_instances=1,
-            planned_number_of_instances=1,
-            properties={},
-            type='',
-            deployment=storage.deployment.list()[0]
+            name='node_template',
+            default_instances=1,
+            max_instances=1,
+            min_instances=1,
+            type_name='',
+            service_template=storage.service_template.list()[0]
         )
         if ip:
-            kwargs['properties']['ip'] = ip
-        node = Node(**kwargs)
-        storage.node.put(node)
+            kwargs['properties'] = [Parameter(name='ip', type='str', str_value=str(ip))]
+        node = NodeTemplate(**kwargs)
+        storage.node_template.put(node)
         return node
 
-    def _node_instance(self, storage, node, is_host, ip, host_fk=None):
+    def _node(self, storage, node, is_host, ip, host_fk=None):
         kwargs = dict(
-            name='node_instance',
-            node=node,
+            name='node',
+            node_template=node,
             runtime_properties={},
-            state=''
+            state='',
+            service_instance=storage.service_instance.list()[0]
         )
         if ip:
             kwargs['runtime_properties']['ip'] = ip
@@ -739,27 +665,26 @@ class TestNodeInstanceIP(object):
             kwargs['host_fk'] = 1
         elif host_fk:
             kwargs['host_fk'] = host_fk
-        node_instance = NodeInstance(**kwargs)
-        storage.node_instance.put(node_instance)
+        node_instance = Node(**kwargs)
+        storage.node.put(node_instance)
         return node_instance
 
 
+@pytest.mark.skip('Should be reworked into relationship')
 class TestRelationshipInstance(object):
-    def test_relatiship_instance_model_creation(self, node_instances_storage):
+    def test_relatiship_instance_model_creation(self, nodes_storage):
         relationship = mock.models.get_relationship(
-            source=node_instances_storage.node.get_by_name(mock.models.DEPENDENT_NODE_NAME),
-            target=node_instances_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+            target=nodes_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
         )
-        node_instances_storage.relationship.put(relationship)
-        node_instances = node_instances_storage.node_instance
+        nodes_storage.relationship.put(relationship)
+        node_instances = nodes_storage.node
         source_node_instance = node_instances.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
         target_node_instance = node_instances.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
 
         relationship_instance = _test_model(
             is_valid=True,
-            storage=node_instances_storage,
-            model_name='relationship_instance',
-            model_cls=RelationshipInstance,
+            storage=nodes_storage,
+            model_cls=Relationship,
             model_kwargs=dict(
                 relationship=relationship,
                 source_node_instance=source_node_instance,
@@ -827,7 +752,6 @@ class TestPlugin(object):
                                    supported_platform, supported_py_versions, uploaded_at, wheels):
         _test_model(is_valid=is_valid,
                     storage=empty_storage,
-                    model_name='plugin',
                     model_cls=Plugin,
                     model_kwargs=dict(
                         archive_name=archive_name,
@@ -881,7 +805,6 @@ class TestTask(object):
         task = _test_model(
             is_valid=is_valid,
             storage=execution_storage,
-            model_name='task',
             model_cls=Task,
             model_kwargs=dict(
                 status=status,
@@ -894,7 +817,7 @@ class TestTask(object):
                 retry_interval=retry_interval,
                 ignore_failure=ignore_failure,
                 name=name,
-                operation_mapping=operation_mapping,
+                implementation=operation_mapping,
                 inputs=inputs,
                 plugin_fk=plugin_id,
             ))
@@ -907,7 +830,7 @@ class TestTask(object):
         def create_task(max_attempts):
             Task(execution_fk='eid',
                  name='name',
-                 operation_mapping='',
+                 implementation='',
                  inputs={},
                  max_attempts=max_attempts)
         create_task(max_attempts=1)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_structures.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_structures.py b/tests/storage/test_structures.py
index 4127905..30f0064 100644
--- a/tests/storage/test_structures.py
+++ b/tests/storage/test_structures.py
@@ -20,23 +20,23 @@ import sqlalchemy
 from aria.storage import (
     ModelStorage,
     sql_mapi,
-    model,
+    exceptions,
     type,
-    exceptions
+    modeling,
 )
 
 from ..storage import release_sqlite_storage, structure, init_inmemory_model_storage
 from . import MockModel
 from ..mock import (
     models,
-    operations,
     context as mock_context
 )
 
 
 @pytest.fixture
 def storage():
-    base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, initiator=init_inmemory_model_storage)
+    base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+                                initiator=init_inmemory_model_storage)
     base_storage.register(MockModel)
     yield base_storage
     release_sqlite_storage(base_storage)
@@ -44,7 +44,7 @@ def storage():
 
 @pytest.fixture(scope='module', autouse=True)
 def module_cleanup():
-    model.DeclarativeBase.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
+    modeling.model.aria_declarative_base.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
 
 
 @pytest.fixture
@@ -89,144 +89,87 @@ def test_inner_list_update(storage):
 
 
 def test_model_to_dict(context):
-    deployment = context.deployment
-    deployment_dict = deployment.to_dict()
+    service_instance = context.service_instance
+    service_instance = service_instance.to_dict()
 
     expected_keys = [
-        'created_at',
         'description',
-        'inputs',
-        'groups',
+        '_metadata',
+        'created_at',
         'permalink',
         'policy_triggers',
         'policy_types',
-        'outputs',
         'scaling_groups',
         'updated_at',
         'workflows',
-        'blueprint_name',
     ]
 
     for expected_key in expected_keys:
-        assert expected_key in deployment_dict
-
-    assert 'blueprint_fk' not in deployment_dict
+        assert expected_key in service_instance
 
 
 def test_relationship_model_ordering(context):
-    deployment = context.model.deployment.get_by_name(models.DEPLOYMENT_NAME)
-    source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
-    source_node_instance = context.model.node_instance.get_by_name(
-        models.DEPENDENT_NODE_INSTANCE_NAME)
-    target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
-    target_node_instance = context.model.node_instance.get_by_name(
-        models.DEPENDENCY_NODE_INSTANCE_NAME)
-    new_node = model.Node(
+    service_instance = context.model.service_instance.get_by_name(models.DEPLOYMENT_NAME)
+    source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_INSTANCE_NAME)
+    target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_INSTANCE_NAME)
+    new_node_template = modeling.model.NodeTemplate(
         name='new_node',
-        type='test_node_type',
-        type_hierarchy=[],
-        number_of_instances=1,
-        planned_number_of_instances=1,
-        deploy_number_of_instances=1,
-        properties={},
-        operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
-        min_number_of_instances=1,
-        max_number_of_instances=1,
-        deployment=deployment
-    )
-    source_to_new_relationship = model.Relationship(
-        source_node=source_node,
-        target_node=new_node,
-        source_interfaces={},
-        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        target_interfaces={},
-        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        type='rel_type',
+        type_name='test_node_type',
         type_hierarchy=[],
-        properties={},
+        default_instances=1,
+        min_instances=1,
+        max_instances=1,
+        service_template=service_instance.service_template
     )
-    new_node_instance = model.NodeInstance(
+    new_node = modeling.model.Node(
         name='new_node_instance',
         runtime_properties={},
+        service_instance=service_instance,
         version=None,
-        node=new_node,
+        node_template=new_node_template,
         state='',
         scaling_groups=[]
     )
-    source_to_new_relationship_instance = model.RelationshipInstance(
-        relationship=source_to_new_relationship,
-        source_node_instance=source_node_instance,
-        target_node_instance=new_node_instance,
+
+    source_to_new_relationship = modeling.model.Relationship(
+        target_node=new_node,
+        source_node=source_node,
     )
 
-    new_to_target_relationship = model.Relationship(
+    new_to_target_relationship = modeling.model.Relationship(
         source_node=new_node,
         target_node=target_node,
-        source_interfaces={},
-        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        target_interfaces={},
-        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        type='rel_type',
-        type_hierarchy=[],
-        properties={},
-    )
-    new_to_target_relationship_instance = model.RelationshipInstance(
-        relationship=new_to_target_relationship,
-        source_node_instance=new_node_instance,
-        target_node_instance=target_node_instance,
     )
 
+    context.model.node_template.put(new_node_template)
     context.model.node.put(new_node)
-    context.model.node_instance.put(new_node_instance)
     context.model.relationship.put(source_to_new_relationship)
     context.model.relationship.put(new_to_target_relationship)
-    context.model.relationship_instance.put(source_to_new_relationship_instance)
-    context.model.relationship_instance.put(new_to_target_relationship_instance)
 
-    def flip_and_assert(node_instance, direction):
+    def flip_and_assert(node, direction):
         """
         Reversed the order of relationships and assert effects took place.
-        :param node_instance: the node instance to operatate on
+        :param node: the node instance to operatate on
         :param direction: the type of relationships to flip (inbound/outbount)
         :return:
         """
         assert direction in ('inbound', 'outbound')
 
-        relationships = getattr(node_instance.node, direction + '_relationships')
-        relationship_instances = getattr(node_instance, direction + '_relationship_instances')
+        relationships = getattr(node, direction + '_relationships').all()
         assert len(relationships) == 2
-        assert len(relationship_instances) == 2
-
-        first_rel, second_rel = relationships
-        first_rel_instance, second_rel_instance = relationship_instances
-        assert getattr(first_rel, relationships.ordering_attr) == 0
-        assert getattr(second_rel, relationships.ordering_attr) == 1
-        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 0
-        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 1
-
-        reversed_relationships = list(reversed(relationships))
-        reversed_relationship_instances = list(reversed(relationship_instances))
-
-        assert relationships != reversed_relationships
-        assert relationship_instances != reversed_relationship_instances
-
-        relationships[:] = reversed_relationships
-        relationship_instances[:] = reversed_relationship_instances
-        context.model.node_instance.update(node_instance)
 
-        assert relationships == reversed_relationships
-        assert relationship_instances == reversed_relationship_instances
+        reversed_relationship_instances = list(reversed(relationships))
+        assert relationships != reversed_relationship_instances
 
-        assert getattr(first_rel, relationships.ordering_attr) == 1
-        assert getattr(second_rel, relationships.ordering_attr) == 0
-        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 1
-        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 0
+        relationships[:] = reversed_relationship_instances
+        context.model.node.update(node)
+        assert relationships == reversed_relationship_instances
 
-    flip_and_assert(source_node_instance, 'outbound')
-    flip_and_assert(target_node_instance, 'inbound')
+    flip_and_assert(source_node, 'outbound')
+    flip_and_assert(target_node, 'inbound')
 
 
-class StrictClass(model.DeclarativeBase, structure.ModelMixin):
+class StrictClass(modeling.model.aria_declarative_base, structure.ModelMixin):
     __tablename__ = 'strict_class'
 
     strict_dict = sqlalchemy.Column(type.StrictDict(basestring, basestring))


[5/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model

Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/base_model.py
----------------------------------------------------------------------
diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py
deleted file mode 100644
index f7d0e5b..0000000
--- a/aria/storage/base_model.py
+++ /dev/null
@@ -1,757 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Aria's storage.models module
-Path: aria.storage.models
-
-models module holds aria's models.
-
-classes:
-    * Field - represents a single field.
-    * IterField - represents an iterable field.
-    * Model - abstract model implementation.
-    * Snapshot - snapshots implementation model.
-    * Deployment - deployment implementation model.
-    * DeploymentUpdateStep - deployment update step implementation model.
-    * DeploymentUpdate - deployment update implementation model.
-    * DeploymentModification - deployment modification implementation model.
-    * Execution - execution implementation model.
-    * Node - node implementation model.
-    * Relationship - relationship implementation model.
-    * NodeInstance - node instance implementation model.
-    * RelationshipInstance - relationship instance implementation model.
-    * Plugin - plugin implementation model.
-"""
-from collections import namedtuple
-from datetime import datetime
-
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy import (
-    Column,
-    Integer,
-    Text,
-    DateTime,
-    Boolean,
-    Enum,
-    String,
-    Float,
-    orm,
-)
-from sqlalchemy.ext.orderinglist import ordering_list
-
-from ..orchestrator.exceptions import TaskAbortException, TaskRetryException
-from .structure import ModelMixin
-from .type import (
-    List,
-    Dict
-)
-
-__all__ = (
-    'BlueprintBase',
-    'DeploymentBase',
-    'DeploymentUpdateStepBase',
-    'DeploymentUpdateBase',
-    'DeploymentModificationBase',
-    'ExecutionBase',
-    'NodeBase',
-    'RelationshipBase',
-    'NodeInstanceBase',
-    'RelationshipInstanceBase',
-    'PluginBase',
-    'TaskBase'
-)
-
-#pylint: disable=no-self-argument, abstract-method
-
-
-class BlueprintBase(ModelMixin):
-    """
-    Blueprint model representation.
-    """
-    __tablename__ = 'blueprints'
-
-    created_at = Column(DateTime, nullable=False, index=True)
-    main_file_name = Column(Text, nullable=False)
-    plan = Column(Dict, nullable=False)
-    updated_at = Column(DateTime)
-    description = Column(Text)
-
-
-class DeploymentBase(ModelMixin):
-    """
-    Deployment model representation.
-    """
-    __tablename__ = 'deployments'
-
-    _private_fields = ['blueprint_fk']
-
-    created_at = Column(DateTime, nullable=False, index=True)
-    description = Column(Text)
-    inputs = Column(Dict)
-    groups = Column(Dict)
-    permalink = Column(Text)
-    policy_triggers = Column(Dict)
-    policy_types = Column(Dict)
-    outputs = Column(Dict)
-    scaling_groups = Column(Dict)
-    updated_at = Column(DateTime)
-    workflows = Column(Dict)
-
-    @declared_attr
-    def blueprint_fk(cls):
-        return cls.foreign_key(BlueprintBase, nullable=False)
-
-    @declared_attr
-    def blueprint(cls):
-        return cls.one_to_many_relationship('blueprint_fk')
-
-    @declared_attr
-    def blueprint_name(cls):
-        return association_proxy('blueprint', cls.name_column_name())
-
-
-class ExecutionBase(ModelMixin):
-    """
-    Execution model representation.
-    """
-    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
-    __tablename__ = 'executions'
-    _private_fields = ['deployment_fk']
-
-    TERMINATED = 'terminated'
-    FAILED = 'failed'
-    CANCELLED = 'cancelled'
-    PENDING = 'pending'
-    STARTED = 'started'
-    CANCELLING = 'cancelling'
-    FORCE_CANCELLING = 'force_cancelling'
-
-    STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING]
-    END_STATES = [TERMINATED, FAILED, CANCELLED]
-    ACTIVE_STATES = [state for state in STATES if state not in END_STATES]
-
-    VALID_TRANSITIONS = {
-        PENDING: [STARTED, CANCELLED],
-        STARTED: END_STATES + [CANCELLING],
-        CANCELLING: END_STATES + [FORCE_CANCELLING]
-    }
-
-    @orm.validates('status')
-    def validate_status(self, key, value):
-        """Validation function that verifies execution status transitions are OK"""
-        try:
-            current_status = getattr(self, key)
-        except AttributeError:
-            return
-        valid_transitions = self.VALID_TRANSITIONS.get(current_status, [])
-        if all([current_status is not None,
-                current_status != value,
-                value not in valid_transitions]):
-            raise ValueError('Cannot change execution status from {current} to {new}'.format(
-                current=current_status,
-                new=value))
-        return value
-
-    created_at = Column(DateTime, index=True)
-    started_at = Column(DateTime, nullable=True, index=True)
-    ended_at = Column(DateTime, nullable=True, index=True)
-    error = Column(Text, nullable=True)
-    is_system_workflow = Column(Boolean, nullable=False, default=False)
-    parameters = Column(Dict)
-    status = Column(Enum(*STATES, name='execution_status'), default=PENDING)
-    workflow_name = Column(Text)
-
-    @declared_attr
-    def blueprint(cls):
-        return association_proxy('deployment', 'blueprint')
-
-    @declared_attr
-    def deployment_fk(cls):
-        return cls.foreign_key(DeploymentBase, nullable=True)
-
-    @declared_attr
-    def deployment(cls):
-        return cls.one_to_many_relationship('deployment_fk')
-
-    @declared_attr
-    def deployment_name(cls):
-        return association_proxy('deployment', cls.name_column_name())
-
-    @declared_attr
-    def blueprint_name(cls):
-        return association_proxy('deployment', 'blueprint_name')
-
-    def __str__(self):
-        return '<{0} id=`{1}` (status={2})>'.format(
-            self.__class__.__name__,
-            getattr(self, self.name_column_name()),
-            self.status
-        )
-
-
-class DeploymentUpdateBase(ModelMixin):
-    """
-    Deployment update model representation.
-    """
-    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
-    steps = None
-
-    __tablename__ = 'deployment_updates'
-
-    _private_fields = ['execution_fk', 'deployment_fk']
-
-    created_at = Column(DateTime, nullable=False, index=True)
-    deployment_plan = Column(Dict, nullable=False)
-    deployment_update_node_instances = Column(Dict)
-    deployment_update_deployment = Column(Dict)
-    deployment_update_nodes = Column(List)
-    modified_entity_ids = Column(Dict)
-    state = Column(Text)
-
-    @declared_attr
-    def execution_fk(cls):
-        return cls.foreign_key(ExecutionBase, nullable=True)
-
-    @declared_attr
-    def execution(cls):
-        return cls.one_to_many_relationship('execution_fk')
-
-    @declared_attr
-    def execution_name(cls):
-        return association_proxy('execution', cls.name_column_name())
-
-    @declared_attr
-    def deployment_fk(cls):
-        return cls.foreign_key(DeploymentBase)
-
-    @declared_attr
-    def deployment(cls):
-        return cls.one_to_many_relationship('deployment_fk')
-
-    @declared_attr
-    def deployment_name(cls):
-        return association_proxy('deployment', cls.name_column_name())
-
-    def to_dict(self, suppress_error=False, **kwargs):
-        dep_update_dict = super(DeploymentUpdateBase, self).to_dict(suppress_error)     #pylint: disable=no-member
-        # Taking care of the fact the DeploymentSteps are _BaseModels
-        dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
-        return dep_update_dict
-
-
-class DeploymentUpdateStepBase(ModelMixin):
-    """
-    Deployment update step model representation.
-    """
-    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
-    __tablename__ = 'deployment_update_steps'
-    _private_fields = ['deployment_update_fk']
-
-    _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
-    ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
-    _entity_types = namedtuple(
-        'ENTITY_TYPES',
-        'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, '
-        'POLICY_TYPE, POLICY_TRIGGER, PLUGIN')
-    ENTITY_TYPES = _entity_types(
-        NODE='node',
-        RELATIONSHIP='relationship',
-        PROPERTY='property',
-        OPERATION='operation',
-        WORKFLOW='workflow',
-        OUTPUT='output',
-        DESCRIPTION='description',
-        GROUP='group',
-        POLICY_TYPE='policy_type',
-        POLICY_TRIGGER='policy_trigger',
-        PLUGIN='plugin'
-    )
-
-    action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False)
-    entity_id = Column(Text, nullable=False)
-    entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
-
-    @declared_attr
-    def deployment_update_fk(cls):
-        return cls.foreign_key(DeploymentUpdateBase)
-
-    @declared_attr
-    def deployment_update(cls):
-        return cls.one_to_many_relationship('deployment_update_fk', backreference='steps')
-
-    @declared_attr
-    def deployment_update_name(cls):
-        return association_proxy('deployment_update', cls.name_column_name())
-
-    def __hash__(self):
-        return hash((getattr(self, self.id_column_name()), self.entity_id))
-
-    def __lt__(self, other):
-        """
-        the order is 'remove' < 'modify' < 'add'
-        :param other:
-        :return:
-        """
-        if not isinstance(other, self.__class__):
-            return not self >= other
-
-        if self.action != other.action:
-            if self.action == 'remove':
-                return_value = True
-            elif self.action == 'add':
-                return_value = False
-            else:
-                return_value = other.action == 'add'
-            return return_value
-
-        if self.action == 'add':
-            return self.entity_type == 'node' and other.entity_type == 'relationship'
-        if self.action == 'remove':
-            return self.entity_type == 'relationship' and other.entity_type == 'node'
-        return False
-
-
-class DeploymentModificationBase(ModelMixin):
-    """
-    Deployment modification model representation.
-    """
-    __tablename__ = 'deployment_modifications'
-    _private_fields = ['deployment_fk']
-
-    STARTED = 'started'
-    FINISHED = 'finished'
-    ROLLEDBACK = 'rolledback'
-
-    STATES = [STARTED, FINISHED, ROLLEDBACK]
-    END_STATES = [FINISHED, ROLLEDBACK]
-
-    context = Column(Dict)
-    created_at = Column(DateTime, nullable=False, index=True)
-    ended_at = Column(DateTime, index=True)
-    modified_nodes = Column(Dict)
-    node_instances = Column(Dict)
-    status = Column(Enum(*STATES, name='deployment_modification_status'))
-
-    @declared_attr
-    def deployment_fk(cls):
-        return cls.foreign_key(DeploymentBase)
-
-    @declared_attr
-    def deployment(cls):
-        return cls.one_to_many_relationship('deployment_fk', backreference='modifications')
-
-    @declared_attr
-    def deployment_name(cls):
-        return association_proxy('deployment', cls.name_column_name())
-
-
-class NodeBase(ModelMixin):
-    """
-    Node model representation.
-    """
-    __tablename__ = 'nodes'
-
-    # See base class for an explanation on these properties
-    is_id_unique = False
-
-    _private_fields = ['blueprint_fk', 'host_fk']
-
-    @declared_attr
-    def host_fk(cls):
-        return cls.foreign_key(NodeBase, nullable=True)
-
-    @declared_attr
-    def host(cls):
-        return cls.relationship_to_self('host_fk')
-
-    @declared_attr
-    def host_name(cls):
-        return association_proxy('host', cls.name_column_name())
-
-    @declared_attr
-    def deployment_fk(cls):
-        return cls.foreign_key(DeploymentBase)
-
-    @declared_attr
-    def deployment(cls):
-        return cls.one_to_many_relationship('deployment_fk')
-
-    @declared_attr
-    def deployment_name(cls):
-        return association_proxy('deployment', cls.name_column_name())
-
-    @declared_attr
-    def blueprint_name(cls):
-        return association_proxy('deployment', 'blueprint_{0}'.format(cls.name_column_name()))
-
-    deploy_number_of_instances = Column(Integer, nullable=False)
-    max_number_of_instances = Column(Integer, nullable=False)
-    min_number_of_instances = Column(Integer, nullable=False)
-    number_of_instances = Column(Integer, nullable=False)
-    planned_number_of_instances = Column(Integer, nullable=False)
-    plugins = Column(List)
-    properties = Column(Dict)
-    operations = Column(Dict)
-    type = Column(Text, nullable=False, index=True)
-    type_hierarchy = Column(List)
-
-
-class RelationshipBase(ModelMixin):
-    """
-    Relationship model representation.
-    """
-    __tablename__ = 'relationships'
-
-    _private_fields = ['source_node_fk', 'target_node_fk', 'source_position', 'target_position']
-
-    source_position = Column(Integer)
-    target_position = Column(Integer)
-
-    @declared_attr
-    def deployment_id(self):
-        return association_proxy('source_node', 'deployment_id')
-
-    @declared_attr
-    def source_node_fk(cls):
-        return cls.foreign_key(NodeBase)
-
-    @declared_attr
-    def source_node(cls):
-        return cls.one_to_many_relationship(
-            'source_node_fk',
-            backreference='outbound_relationships',
-            backref_kwargs=dict(
-                order_by=cls.source_position,
-                collection_class=ordering_list('source_position', count_from=0)
-            )
-        )
-
-    @declared_attr
-    def source_name(cls):
-        return association_proxy('source_node', cls.name_column_name())
-
-    @declared_attr
-    def target_node_fk(cls):
-        return cls.foreign_key(NodeBase, nullable=True)
-
-    @declared_attr
-    def target_node(cls):
-        return cls.one_to_many_relationship(
-            'target_node_fk',
-            backreference='inbound_relationships',
-            backref_kwargs=dict(
-                order_by=cls.target_position,
-                collection_class=ordering_list('target_position', count_from=0)
-            )
-        )
-
-    @declared_attr
-    def target_name(cls):
-        return association_proxy('target_node', cls.name_column_name())
-
-    source_interfaces = Column(Dict)
-    source_operations = Column(Dict, nullable=False)
-    target_interfaces = Column(Dict)
-    target_operations = Column(Dict, nullable=False)
-    type = Column(String, nullable=False)
-    type_hierarchy = Column(List)
-    properties = Column(Dict)
-
-
-class NodeInstanceBase(ModelMixin):
-    """
-    Node instance model representation.
-    """
-    __tablename__ = 'node_instances'
-    _private_fields = ['node_fk', 'host_fk']
-
-    runtime_properties = Column(Dict)
-    scaling_groups = Column(List)
-    state = Column(Text, nullable=False)
-    version = Column(Integer, default=1)
-
-    @declared_attr
-    def host_fk(cls):
-        return cls.foreign_key(NodeInstanceBase, nullable=True)
-
-    @declared_attr
-    def host(cls):
-        return cls.relationship_to_self('host_fk')
-
-    @declared_attr
-    def host_name(cls):
-        return association_proxy('host', cls.name_column_name())
-
-    @declared_attr
-    def deployment(cls):
-        return association_proxy('node', 'deployment')
-
-    @declared_attr
-    def deployment_name(cls):
-        return association_proxy('node', 'deployment_name')
-
-    @declared_attr
-    def node_fk(cls):
-        return cls.foreign_key(NodeBase, nullable=True)
-
-    @declared_attr
-    def node(cls):
-        return cls.one_to_many_relationship('node_fk')
-
-    @declared_attr
-    def node_name(cls):
-        return association_proxy('node', cls.name_column_name())
-
-    @property
-    def ip(self):
-        if not self.host_fk:
-            return None
-        host_node_instance = self.host
-        if 'ip' in host_node_instance.runtime_properties:  # pylint: disable=no-member
-            return host_node_instance.runtime_properties['ip']  # pylint: disable=no-member
-        host_node = host_node_instance.node  # pylint: disable=no-member
-        if 'ip' in host_node.properties:
-            return host_node.properties['ip']
-        return None
-
-
-class RelationshipInstanceBase(ModelMixin):
-    """
-    Relationship instance model representation.
-    """
-    __tablename__ = 'relationship_instances'
-    _private_fields = ['relationship_storage_fk',
-                       'source_node_instance_fk',
-                       'target_node_instance_fk',
-                       'source_position',
-                       'target_position']
-
-    source_position = Column(Integer)
-    target_position = Column(Integer)
-
-    @declared_attr
-    def source_node_instance_fk(cls):
-        return cls.foreign_key(NodeInstanceBase, nullable=True)
-
-    @declared_attr
-    def source_node_instance(cls):
-        return cls.one_to_many_relationship(
-            'source_node_instance_fk',
-            backreference='outbound_relationship_instances',
-            backref_kwargs=dict(
-                order_by=cls.source_position,
-                collection_class=ordering_list('source_position', count_from=0)
-            )
-        )
-
-    @declared_attr
-    def source_node_instance_name(cls):
-        return association_proxy('source_node_instance', 'node_{0}'.format(cls.name_column_name()))
-
-    @declared_attr
-    def source_node_name(cls):
-        return association_proxy('source_node_instance', cls.name_column_name())
-
-    @declared_attr
-    def target_node_instance_fk(cls):
-        return cls.foreign_key(NodeInstanceBase, nullable=True)
-
-    @declared_attr
-    def target_node_instance(cls):
-        return cls.one_to_many_relationship(
-            'target_node_instance_fk',
-            backreference='inbound_relationship_instances',
-            backref_kwargs=dict(
-                order_by=cls.target_position,
-                collection_class=ordering_list('target_position', count_from=0)
-            )
-        )
-
-    @declared_attr
-    def target_node_instance_name(cls):
-        return association_proxy('target_node_instance', cls.name_column_name())
-
-    @declared_attr
-    def target_node_name(cls):
-        return association_proxy('target_node_instance', 'node_{0}'.format(cls.name_column_name()))
-
-    @declared_attr
-    def relationship_fk(cls):
-        return cls.foreign_key(RelationshipBase)
-
-    @declared_attr
-    def relationship(cls):
-        return cls.one_to_many_relationship('relationship_fk')
-
-    @declared_attr
-    def relationship_name(cls):
-        return association_proxy('relationship', cls.name_column_name())
-
-
-
-class PluginBase(ModelMixin):
-    """
-    Plugin model representation.
-    """
-    __tablename__ = 'plugins'
-
-    archive_name = Column(Text, nullable=False, index=True)
-    distribution = Column(Text)
-    distribution_release = Column(Text)
-    distribution_version = Column(Text)
-    package_name = Column(Text, nullable=False, index=True)
-    package_source = Column(Text)
-    package_version = Column(Text)
-    supported_platform = Column(Text)
-    supported_py_versions = Column(List)
-    uploaded_at = Column(DateTime, nullable=False, index=True)
-    wheels = Column(List, nullable=False)
-
-
-class TaskBase(ModelMixin):
-    """
-    A Model which represents an task
-    """
-    __tablename__ = 'tasks'
-    _private_fields = ['node_instance_fk', 'relationship_instance_fk', 'execution_fk']
-
-    @declared_attr
-    def node_instance_fk(cls):
-        return cls.foreign_key(NodeInstanceBase, nullable=True)
-
-    @declared_attr
-    def node_instance_name(cls):
-        return association_proxy('node_instance', cls.name_column_name())
-
-    @declared_attr
-    def node_instance(cls):
-        return cls.one_to_many_relationship('node_instance_fk')
-
-    @declared_attr
-    def relationship_instance_fk(cls):
-        return cls.foreign_key(RelationshipInstanceBase, nullable=True)
-
-    @declared_attr
-    def relationship_instance_name(cls):
-        return association_proxy('relationship_instance', cls.name_column_name())
-
-    @declared_attr
-    def relationship_instance(cls):
-        return cls.one_to_many_relationship('relationship_instance_fk')
-
-    @declared_attr
-    def plugin_fk(cls):
-        return cls.foreign_key(PluginBase, nullable=True)
-
-    @declared_attr
-    def plugin(cls):
-        return cls.one_to_many_relationship('plugin_fk')
-
-    @declared_attr
-    def execution_fk(cls):
-        return cls.foreign_key(ExecutionBase, nullable=True)
-
-    @declared_attr
-    def execution(cls):
-        return cls.one_to_many_relationship('execution_fk')
-
-    @declared_attr
-    def execution_name(cls):
-        return association_proxy('execution', cls.name_column_name())
-
-    PENDING = 'pending'
-    RETRYING = 'retrying'
-    SENT = 'sent'
-    STARTED = 'started'
-    SUCCESS = 'success'
-    FAILED = 'failed'
-    STATES = (
-        PENDING,
-        RETRYING,
-        SENT,
-        STARTED,
-        SUCCESS,
-        FAILED,
-    )
-
-    WAIT_STATES = [PENDING, RETRYING]
-    END_STATES = [SUCCESS, FAILED]
-
-    RUNS_ON_SOURCE = 'source'
-    RUNS_ON_TARGET = 'target'
-    RUNS_ON_NODE_INSTANCE = 'node_instance'
-    RUNS_ON = (RUNS_ON_NODE_INSTANCE, RUNS_ON_SOURCE, RUNS_ON_TARGET)
-
-    @orm.validates('max_attempts')
-    def validate_max_attempts(self, _, value):  # pylint: disable=no-self-use
-        """Validates that max attempts is either -1 or a positive number"""
-        if value < 1 and value != TaskBase.INFINITE_RETRIES:
-            raise ValueError('Max attempts can be either -1 (infinite) or any positive number. '
-                             'Got {value}'.format(value=value))
-        return value
-
-    INFINITE_RETRIES = -1
-
-    status = Column(Enum(*STATES, name='status'), default=PENDING)
-
-    due_at = Column(DateTime, default=datetime.utcnow)
-    started_at = Column(DateTime, default=None)
-    ended_at = Column(DateTime, default=None)
-    max_attempts = Column(Integer, default=1)
-    retry_count = Column(Integer, default=0)
-    retry_interval = Column(Float, default=0)
-    ignore_failure = Column(Boolean, default=False)
-
-    # Operation specific fields
-    operation_mapping = Column(String)
-    inputs = Column(Dict)
-    plugin_name = Column(String)
-    _runs_on = Column(Enum(*RUNS_ON, name='runs_on'), name='runs_on')
-
-    @property
-    def actor(self):
-        """
-        Return the actor of the task
-        :return:
-        """
-        return self.node_instance or self.relationship_instance
-
-    @property
-    def runs_on(self):
-        if self._runs_on == self.RUNS_ON_NODE_INSTANCE:
-            return self.node_instance
-        elif self._runs_on == self.RUNS_ON_SOURCE:
-            return self.relationship_instance.source_node_instance  # pylint: disable=no-member
-        elif self._runs_on == self.RUNS_ON_TARGET:
-            return self.relationship_instance.target_node_instance  # pylint: disable=no-member
-        return None
-
-    @classmethod
-    def as_node_instance(cls, instance, runs_on, **kwargs):
-        return cls(node_instance=instance, _runs_on=runs_on, **kwargs)
-
-    @classmethod
-    def as_relationship_instance(cls, instance, runs_on, **kwargs):
-        return cls(relationship_instance=instance, _runs_on=runs_on, **kwargs)
-
-    @staticmethod
-    def abort(message=None):
-        raise TaskAbortException(message)
-
-    @staticmethod
-    def retry(message=None, retry_interval=None):
-        raise TaskRetryException(message, retry_interval=retry_interval)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index 3878dca..0e189e6 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -39,10 +39,7 @@ API:
 """
 
 from aria.logger import LoggerMixin
-from . import (
-    api as storage_api,
-    sql_mapi
-)
+from . import sql_mapi
 
 __all__ = (
     'Storage',
@@ -146,7 +143,7 @@ class ModelStorage(Storage):
         :param model_cls: the model to register.
         :return:
         """
-        model_name = storage_api.generate_lower_name(model_cls)
+        model_name = model_cls.__modelname__
         if model_name in self.registered:
             self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
                                                                           self=self))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/instrumentation.py
----------------------------------------------------------------------
diff --git a/aria/storage/instrumentation.py b/aria/storage/instrumentation.py
index 537dbb5..57fe9bd 100644
--- a/aria/storage/instrumentation.py
+++ b/aria/storage/instrumentation.py
@@ -17,12 +17,11 @@ import copy
 
 import sqlalchemy.event
 
-from . import api
-from . import model as _model
+from .modeling import model as _model
 
 _STUB = object()
 _INSTRUMENTED = {
-    _model.NodeInstance.runtime_properties: dict
+    _model.Node.runtime_properties: dict
 }
 
 
@@ -75,7 +74,7 @@ class _Instrumentation(object):
 
     def _register_set_attribute_listener(self, instrumented_attribute, attribute_type):
         def listener(target, value, *_):
-            mapi_name = self._mapi_name(target.__class__)
+            mapi_name = target.__modelname__
             tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
             tracked_attributes = tracked_instances.setdefault(target.id, {})
             if value is None:
@@ -90,7 +89,7 @@ class _Instrumentation(object):
 
     def _register_instance_listeners(self, instrumented_class, instrumented_attributes):
         def listener(target, *_):
-            mapi_name = self._mapi_name(instrumented_class)
+            mapi_name = instrumented_class.__modelname__
             tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
             tracked_attributes = tracked_instances.setdefault(target.id, {})
             for attribute_name, attribute_type in instrumented_attributes.items():
@@ -110,7 +109,7 @@ class _Instrumentation(object):
 
     def clear(self, target=None):
         if target:
-            mapi_name = self._mapi_name(target.__class__)
+            mapi_name = target.__modelname__
             tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
             tracked_instances.pop(target.id, None)
         else:
@@ -128,10 +127,6 @@ class _Instrumentation(object):
     def __exit__(self, exc_type, exc_val, exc_tb):
         self.restore()
 
-    @staticmethod
-    def _mapi_name(instrumented_class):
-        return api.generate_lower_name(instrumented_class)
-
 
 class _Value(object):
     # You may wonder why is this a full blown class and not a named tuple. The reason is that

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/model.py b/aria/storage/model.py
deleted file mode 100644
index afca3e4..0000000
--- a/aria/storage/model.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Aria's storage.models module
-Path: aria.storage.models
-
-models module holds aria's models.
-
-classes:
-    * Field - represents a single field.
-    * IterField - represents an iterable field.
-    * Model - abstract model implementation.
-    * Snapshot - snapshots implementation model.
-    * Deployment - deployment implementation model.
-    * DeploymentUpdateStep - deployment update step implementation model.
-    * DeploymentUpdate - deployment update implementation model.
-    * DeploymentModification - deployment modification implementation model.
-    * Execution - execution implementation model.
-    * Node - node implementation model.
-    * Relationship - relationship implementation model.
-    * NodeInstance - node instance implementation model.
-    * RelationshipInstance - relationship instance implementation model.
-    * ProviderContext - provider context implementation model.
-    * Plugin - plugin implementation model.
-"""
-from sqlalchemy.ext.declarative import declarative_base
-
-from . import structure
-from . import base_model as base
-
-__all__ = (
-    'Blueprint',
-    'Deployment',
-    'DeploymentUpdateStep',
-    'DeploymentUpdate',
-    'DeploymentModification',
-    'Execution',
-    'Node',
-    'Relationship',
-    'NodeInstance',
-    'RelationshipInstance',
-    'Plugin',
-)
-
-
-#pylint: disable=abstract-method
-# The required abstract method implementation are implemented in the ModelIDMixin, which is used as
-# a base to the DeclerativeBase.
-DeclarativeBase = declarative_base(cls=structure.ModelIDMixin)
-
-
-class Blueprint(DeclarativeBase, base.BlueprintBase):
-    pass
-
-
-class Deployment(DeclarativeBase, base.DeploymentBase):
-    pass
-
-
-class Execution(DeclarativeBase, base.ExecutionBase):
-    pass
-
-
-class DeploymentUpdate(DeclarativeBase, base.DeploymentUpdateBase):
-    pass
-
-
-class DeploymentUpdateStep(DeclarativeBase, base.DeploymentUpdateStepBase):
-    pass
-
-
-class DeploymentModification(DeclarativeBase, base.DeploymentModificationBase):
-    pass
-
-
-class Node(DeclarativeBase, base.NodeBase):
-    pass
-
-
-class Relationship(DeclarativeBase, base.RelationshipBase):
-    pass
-
-
-class NodeInstance(DeclarativeBase, base.NodeInstanceBase):
-    pass
-
-
-class RelationshipInstance(DeclarativeBase, base.RelationshipInstanceBase):
-    pass
-
-
-class Plugin(DeclarativeBase, base.PluginBase):
-    pass
-
-
-class Task(DeclarativeBase, base.TaskBase):
-    pass

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/__init__.py b/aria/storage/modeling/__init__.py
new file mode 100644
index 0000000..697ed09
--- /dev/null
+++ b/aria/storage/modeling/__init__.py
@@ -0,0 +1,35 @@
+# 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 collections import namedtuple
+
+from . import (
+    model,
+    instance_elements as _instance_base,
+    orchestrator_elements as _orchestrator_base,
+    template_elements as _template_base,
+)
+
+_ModelBaseCls = namedtuple('ModelBase', 'instance_elements,'
+                                        'orchestrator_elements,'
+                                        'template_elements')
+model_base = _ModelBaseCls(instance_elements=_instance_base,
+                           orchestrator_elements=_orchestrator_base,
+                           template_elements=_template_base)
+
+__all__ = (
+    'model',
+    'model_base',
+)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/elements.py b/aria/storage/modeling/elements.py
new file mode 100644
index 0000000..8c720b9
--- /dev/null
+++ b/aria/storage/modeling/elements.py
@@ -0,0 +1,106 @@
+# 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
+)
+
+from ...parser.modeling import utils
+from ...utils.collections import OrderedDict
+from ...utils.console import puts
+from .. import exceptions
+
+from . import structure
+from . import type
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+class ParameterBase(structure.ModelMixin):
+    """
+    Represents a typed value.
+
+    This class is used by both service model and service instance elements.
+    """
+    __tablename__ = 'parameter'
+    name = Column(Text, nullable=False)
+    type = Column(Text, nullable=False)
+
+    # Check: value type
+    str_value = Column(Text)
+    description = Column(Text)
+
+    @property
+    def as_raw(self):
+        return OrderedDict((
+            ('name', self.name),
+            ('type_name', self.type),
+            ('value', self.value),
+            ('description', self.description)))
+
+    @property
+    def value(self):
+        if self.type is None:
+            return
+        try:
+            if self.type.lower() in ['str', 'unicode']:
+                return self.str_value.decode('utf-8')
+            elif self.type.lower() == 'int':
+                return int(self.str_value)
+            elif self.type.lower() == 'bool':
+                return bool(self.str_value)
+            elif self.type.lower() == 'float':
+                return float(self.str_value)
+            else:
+                raise exceptions.StorageError('No supported type_name was provided')
+        except ValueError:
+            raise exceptions.StorageError('Trying to cast {0} to {1} failed'.format(self.str_value,
+                                                                                    self.type))
+
+    def instantiate(self, context, container):
+        return ParameterBase(self.type, self.str_value, self.description)
+
+    def coerce_values(self, context, container, report_issues):
+        if self.str_value is not None:
+            self.str_value = utils.coerce_value(context, container, self.str_value, report_issues)
+
+
+class MetadataBase(structure.ModelMixin):
+    """
+    Custom values associated with the deployment template and its plans.
+
+    This class is used by both service model and service instance elements.
+
+    Properties:
+
+    * :code:`values`: Dict of custom values
+    """
+    values = Column(type.StrictDict(key_cls=basestring))
+
+    @property
+    def as_raw(self):
+        return self.values
+
+    def instantiate(self, context, container):
+        metadata = MetadataBase()
+        metadata.values.update(self.values)
+        return metadata
+
+    def dump(self, context):
+        puts('Metadata:')
+        with context.style.indent:
+            for name, value in self.values.iteritems():
+                puts('%s: %s' % (name, context.style.meta(value)))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/instance_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/instance_elements.py b/aria/storage/modeling/instance_elements.py
new file mode 100644
index 0000000..0666c8a
--- /dev/null
+++ b/aria/storage/modeling/instance_elements.py
@@ -0,0 +1,1286 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from sqlalchemy import (
+    Column,
+    Text,
+    Integer,
+    Boolean,
+)
+from sqlalchemy import DateTime
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.ext.orderinglist import ordering_list
+
+from aria.parser import validation
+from aria.utils import collections, formatting, console
+
+from . import (
+    utils,
+    structure,
+    type as aria_types
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+# region Element instances
+
+
+class ServiceInstanceBase(structure.ModelMixin):
+    __tablename__ = 'service_instance'
+
+    __private_fields__ = ['substituion_fk',
+                          'service_template_fk']
+
+    description = Column(Text)
+    _metadata = Column(Text)
+
+    # region orchestrator required columns
+
+    created_at = Column(DateTime, nullable=False, index=True)
+    permalink = Column(Text)
+    policy_triggers = Column(aria_types.Dict)
+    policy_types = Column(aria_types.Dict)
+    scaling_groups = Column(aria_types.Dict)
+    updated_at = Column(DateTime)
+    workflows = Column(aria_types.Dict)
+
+    @declared_attr
+    def service_template_name(cls):
+        return association_proxy('service_template', 'name')
+
+    # endregion
+
+    # region foreign keys
+    @declared_attr
+    def substitution_fk(cls):
+        return cls.foreign_key('substitution', nullable=True)
+
+    @declared_attr
+    def service_template_fk(cls):
+        return cls.foreign_key('service_template')
+
+    # endregion
+
+    # region one-to-one relationships
+    @declared_attr
+    def substitution(cls):
+        return cls.one_to_one_relationship('substitution')
+    # endregion
+
+    # region many-to-one relationships
+    @declared_attr
+    def service_template(cls):
+        return cls.many_to_one_relationship('service_template')
+
+    # endregion
+
+    # region many-to-many relationships
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    @declared_attr
+    def outputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='outputs')
+
+    # endregion
+
+    # association proxies
+
+    def satisfy_requirements(self, context):
+        satisfied = True
+        for node in self.nodes.all():
+            if not node.satisfy_requirements(context):
+                satisfied = False
+        return satisfied
+
+    def validate_capabilities(self, context):
+        satisfied = True
+        for node in self.nodes.all():
+            if not node.validate_capabilities(context):
+                satisfied = False
+        return satisfied
+
+    def find_nodes(self, node_template_name):
+        nodes = []
+        for node in self.nodes.all():
+            if node.template_name == node_template_name:
+                nodes.append(node)
+        return collections.FrozenList(nodes)
+
+    def get_node_ids(self, node_template_name):
+        return collections.FrozenList((node.id for node in self.find_nodes(node_template_name)))
+
+    def find_groups(self, group_template_name):
+        groups = []
+        for group in self.groups.all():
+            if group.template_name == group_template_name:
+                groups.append(group)
+        return collections.FrozenList(groups)
+
+    def get_group_ids(self, group_template_name):
+        return collections.FrozenList((group.id for group in self.find_groups(group_template_name)))
+
+    def is_node_a_target(self, context, target_node):
+        for node in self.nodes.all():
+            if self._is_node_a_target(context, node, target_node):
+                return True
+        return False
+
+    def _is_node_a_target(self, context, source_node, target_node):
+        if source_node.relationships:
+            for relationship in source_node.relationships:
+                if relationship.target_node_id == target_node.id:
+                    return True
+                else:
+                    node = context.modeling.instance.nodes.get(relationship.target_node_id)
+                    if node is not None:
+                        if self._is_node_a_target(context, node, target_node):
+                            return True
+        return False
+
+
+class OperationBase(structure.ModelMixin):
+    """
+    An operation in a :class:`Interface`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+    * :code:`dependencies`: List of strings (interpreted by the orchestrator)
+    * :code:`executor`: Executor string (interpreted by the orchestrator)
+    * :code:`max_retries`: Maximum number of retries allowed in case of failure
+    * :code:`retry_interval`: Interval between retries
+    * :code:`inputs`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'operation'
+
+    __private_fields__ = ['service_template_fk',
+                          'interface_instance_fk']
+
+    # region foreign_keys
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance', nullable=True)
+
+    @declared_attr
+    def interface_instance_fk(cls):
+        return cls.foreign_key('interface', nullable=True)
+
+    # endregion
+    description = Column(Text)
+    implementation = Column(Text)
+    dependencies = Column(aria_types.StrictList(item_cls=basestring))
+
+    executor = Column(Text)
+    max_retries = Column(Integer, default=None)
+    retry_interval = Column(Integer, default=None)
+    plugin = Column(Text)
+    operation = Column(Boolean)
+
+    # region many-to-one relationships
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance')
+
+    @declared_attr
+    def interface(cls):
+        return cls.many_to_one_relationship('interface')
+    # region many-to-many relationships
+
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('implementation', self.implementation),
+            ('dependencies', self.dependencies),
+            ('executor', self.executor),
+            ('max_retries', self.max_retries),
+            ('retry_interval', self.retry_interval),
+            ('inputs', formatting.as_raw_dict(self.inputs))))
+
+    def validate(self, context):
+        utils.validate_dict_values(context, self.inputs)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.inputs, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            if self.implementation is not None:
+                console.puts('Implementation: %s' % context.style.literal(self.implementation))
+            if self.dependencies:
+                console.puts(
+                    'Dependencies: %s'
+                    % ', '.join((str(context.style.literal(v)) for v in self.dependencies)))
+            if self.executor is not None:
+                console.puts('Executor: %s' % context.style.literal(self.executor))
+            if self.max_retries is not None:
+                console.puts('Max retries: %s' % context.style.literal(self.max_retries))
+            if self.retry_interval is not None:
+                console.puts('Retry interval: %s' % context.style.literal(self.retry_interval))
+            utils.dump_parameters(context, self.inputs, 'Inputs')
+
+
+class InterfaceBase(structure.ModelMixin):
+    """
+    A typed set of :class:`Operation`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`inputs`: Dict of :class:`Parameter`
+    * :code:`operations`: Dict of :class:`Operation`
+    """
+    __tablename__ = 'interface'
+
+    __private_fields__ = ['group_fk',
+                          'node_fk',
+                          'relationship_fk']
+
+
+    # region foreign_keys
+    @declared_attr
+    def group_fk(cls):
+        return cls.foreign_key('group', nullable=True)
+
+    @declared_attr
+    def node_fk(cls):
+        return cls.foreign_key('node', nullable=True)
+
+    @declared_attr
+    def relationship_fk(cls):
+        return cls.foreign_key('relationship', nullable=True)
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    edge = Column(Text)
+
+    # region many-to-one relationships
+
+    @declared_attr
+    def node(cls):
+        return cls.many_to_one_relationship('node')
+
+    @declared_attr
+    def relationship(cls):
+        return cls.many_to_one_relationship('relationship')
+
+    @declared_attr
+    def group(cls):
+        return cls.many_to_one_relationship('group')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def inputs(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('inputs', formatting.as_raw_dict(self.inputs)),
+            ('operations', formatting.as_raw_list(self.operations))))
+
+    def validate(self, context):
+        if self.type_name:
+            if context.modeling.interface_types.get_descendant(self.type_name) is None:
+                context.validation.report('interface "%s" has an unknown type: %s'
+                                          % (self.name,
+                                             formatting.safe_repr(self.type_name)),
+                                          level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.inputs)
+        utils.validate_dict_values(context, self.operations)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.inputs, report_issues)
+        utils.coerce_dict_values(context, container, self.operations, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Interface type: %s' % context.style.type(self.type_name))
+            utils.dump_parameters(context, self.inputs, 'Inputs')
+            utils.dump_dict_values(context, self.operations, 'Operations')
+
+
+class CapabilityBase(structure.ModelMixin):
+    """
+    A capability of a :class:`Node`.
+
+    An instance of a :class:`CapabilityTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`min_occurrences`: Minimum number of requirement matches required
+    * :code:`max_occurrences`: Maximum number of requirement matches allowed
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'capability'
+
+    __private_fields__ = ['node_fk']
+
+    # region foreign_keys
+    @declared_attr
+    def node_fk(cls):
+        return cls.foreign_key('node')
+
+    # endregion
+    type_name = Column(Text)
+
+    min_occurrences = Column(Integer, default=None) # optional
+    max_occurrences = Column(Integer, default=None) # optional
+    occurrences = Column(Integer, default=0)
+
+    # region many-to-one relationships
+    @declared_attr
+    def node(cls):
+        return cls.many_to_one_relationship('node')
+
+    # endregion
+
+
+    # region many-to-many relationships
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def has_enough_relationships(self):
+        if self.min_occurrences is not None:
+            return self.occurrences >= self.min_occurrences
+        return True
+
+    def relate(self):
+        if self.max_occurrences is not None:
+            if self.occurrences == self.max_occurrences:
+                return False
+        self.occurrences += 1
+        return True
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties))))
+
+    def validate(self, context):
+        if context.modeling.capability_types.get_descendant(self.type_name) is None:
+            context.validation.report('capability "%s" has an unknown type: %s'
+                                      % (self.name,
+                                         formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            console.puts('Occurrences: %s (%s%s)'
+                         % (self.occurrences,
+                            self.min_occurrences or 0,
+                            (' to %d' % self.max_occurrences)
+                            if self.max_occurrences is not None
+                            else ' or more'))
+            utils.dump_parameters(context, self.properties)
+
+
+class ArtifactBase(structure.ModelMixin):
+    """
+    A file associated with a :class:`Node`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`source_path`: Source path (CSAR or repository)
+    * :code:`target_path`: Path at destination machine
+    * :code:`repository_url`: Repository URL
+    * :code:`repository_credential`: Dict of string
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'artifact'
+
+    __private_fields__ = ['node_fk']
+
+    # region foreign_keys
+
+    @declared_attr
+    def node_fk(cls):
+        return cls.foreign_key('node')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+    source_path = Column(Text)
+    target_path = Column(Text)
+    repository_url = Column(Text)
+    repository_credential = Column(aria_types.StrictDict(basestring, basestring))
+
+    # region many-to-one relationships
+    @declared_attr
+    def node(cls):
+        return cls.many_to_one_relationship('node')
+
+    # endregion
+
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('source_path', self.source_path),
+            ('target_path', self.target_path),
+            ('repository_url', self.repository_url),
+            ('repository_credential', formatting.as_agnostic(self.repository_credential)),
+            ('properties', formatting.as_raw_dict(self.properties))))
+
+    def validate(self, context):
+        if context.modeling.artifact_types.get_descendant(self.type_name) is None:
+            context.validation.report('artifact "%s" has an unknown type: %s'
+                                      % (self.name,
+                                         formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Artifact type: %s' % context.style.type(self.type_name))
+            console.puts('Source path: %s' % context.style.literal(self.source_path))
+            if self.target_path is not None:
+                console.puts('Target path: %s' % context.style.literal(self.target_path))
+            if self.repository_url is not None:
+                console.puts('Repository URL: %s' % context.style.literal(self.repository_url))
+            if self.repository_credential:
+                console.puts('Repository credential: %s'
+                             % context.style.literal(self.repository_credential))
+            utils.dump_parameters(context, self.properties)
+
+
+class PolicyBase(structure.ModelMixin):
+    """
+    An instance of a :class:`PolicyTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`target_node_ids`: Must be represented in the :class:`ServiceInstance`
+    * :code:`target_group_ids`: Must be represented in the :class:`ServiceInstance`
+    """
+    __tablename__ = 'policy'
+
+    __private_fields__ = ['service_instance_fk']
+
+    # region foreign_keys
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    # endregion
+    type_name = Column(Text)
+    target_node_ids = Column(aria_types.StrictList(basestring))
+    target_group_ids = Column(aria_types.StrictList(basestring))
+
+    # region many-to-one relationships
+    @declared_attr
+    def service_instnce(cls):
+        return cls.many_to_one_relationship('service_instance')
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('target_node_ids', self.target_node_ids),
+            ('target_group_ids', self.target_group_ids)))
+
+    def validate(self, context):
+        if context.modeling.policy_types.get_descendant(self.type_name) is None:
+            context.validation.report('policy "%s" has an unknown type: %s'
+                                      % (self.name, utils.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts('Policy: %s' % context.style.node(self.name))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            utils.dump_parameters(context, self.properties)
+            if self.target_node_ids:
+                console.puts('Target nodes:')
+                with context.style.indent:
+                    for node_id in self.target_node_ids:
+                        console.puts(context.style.node(node_id))
+            if self.target_group_ids:
+                console.puts('Target groups:')
+                with context.style.indent:
+                    for group_id in self.target_group_ids:
+                        console.puts(context.style.node(group_id))
+
+
+class GroupPolicyBase(structure.ModelMixin):
+    """
+    Policies applied to groups.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`triggers`: Dict of :class:`GroupPolicyTrigger`
+    """
+    __tablename__ = 'group_policy'
+
+    __private_fields__ = ['group_fk']
+
+    # region foreign_keys
+
+    @declared_attr
+    def group_fk(cls):
+        return cls.foreign_key('group')
+
+    # endregion
+
+    description = Column(Text)
+    type_name = Column(Text)
+
+    # region many-to-one relationships
+    @declared_attr
+    def group(cls):
+        return cls.many_to_one_relationship('group')
+
+    # end region
+
+    # region many-to-many relationships
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('type_name', self.type_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('triggers', formatting.as_raw_list(self.triggers))))
+
+    def validate(self, context):
+        if context.modeling.policy_types.get_descendant(self.type_name) is None:
+            context.validation.report(
+                'group policy "%s" has an unknown type: %s'
+                % (self.name,
+                   formatting.safe_repr(self.type_name)),
+                level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.triggers)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+        utils.coerce_dict_values(context, container, self.triggers, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Group policy type: %s' % context.style.type(self.type_name))
+            utils.dump_parameters(context, self.properties)
+            utils.dump_dict_values(context, self.triggers, 'Triggers')
+
+
+class GroupPolicyTriggerBase(structure.ModelMixin):
+    """
+    Triggers for :class:`GroupPolicy`.
+
+    Properties:
+
+    * :code:`name`: Name
+    * :code:`description`: Description
+    * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+    * :code:`properties`: Dict of :class:`Parameter`
+    """
+    __tablename__ = 'group_policy_trigger'
+
+    __private_fields__ = ['group_policy_fk']
+
+    # region foreign keys
+
+    @declared_attr
+    def group_policy_fk(cls):
+        return cls.foreign_key('group_policy')
+
+    # endregion
+
+    description = Column(Text)
+    implementation = Column(Text)
+
+    # region many-to-one relationships
+
+    @declared_attr
+    def group_policy(cls):
+        return cls.many_to_one_relationship('group_policy')
+
+    # endregion
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('description', self.description),
+            ('implementation', self.implementation),
+            ('properties', formatting.as_raw_dict(self.properties))))
+
+    def validate(self, context):
+        utils.validate_dict_values(context, self.properties)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+    def dump(self, context):
+        console.puts(context.style.node(self.name))
+        if self.description:
+            console.puts(context.style.meta(self.description))
+        with context.style.indent:
+            console.puts('Implementation: %s' % context.style.literal(self.implementation))
+            utils.dump_parameters(context, self.properties)
+
+
+class MappingBase(structure.ModelMixin):
+    """
+    An instance of a :class:`MappingTemplate`.
+
+    Properties:
+
+    * :code:`mapped_name`: Exposed capability or requirement name
+    * :code:`node_id`: Must be represented in the :class:`ServiceInstance`
+    * :code:`name`: Name of capability or requirement at the node
+    """
+    __tablename__ = 'mapping'
+
+    mapped_name = Column(Text)
+    node_id = Column(Text)
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('mapped_name', self.mapped_name),
+            ('node_id', self.node_id),
+            ('name', self.name)))
+
+    def dump(self, context):
+        console.puts('%s -> %s.%s'
+                     % (context.style.node(self.mapped_name),
+                        context.style.node(self.node_id),
+                        context.style.node(self.name)))
+
+
+class SubstitutionBase(structure.ModelMixin):
+    """
+    An instance of a :class:`SubstitutionTemplate`.
+
+    Properties:
+
+    * :code:`node_type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`capabilities`: Dict of :class:`Mapping`
+    * :code:`requirements`: Dict of :class:`Mapping`
+    """
+    __tablename__ = 'substitution'
+
+    node_type_name = Column(Text)
+
+    # region many-to-many relationships
+
+    @declared_attr
+    def capabilities(cls):
+        return cls.many_to_many_relationship('mapping', table_prefix='capabilities')
+
+    @declared_attr
+    def requirements(cls):
+        return cls.many_to_many_relationship('mapping',
+                                             table_prefix='requirements',
+                                             relationship_kwargs=dict(lazy='dynamic'))
+
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('node_type_name', self.node_type_name),
+            ('capabilities', formatting.as_raw_list(self.capabilities)),
+            ('requirements', formatting.as_raw_list(self.requirements))))
+
+    def validate(self, context):
+        if context.modeling.node_types.get_descendant(self.node_type_name) is None:
+            context.validation.report('substitution "%s" has an unknown type: %s'
+                                      % (self.name,  # pylint: disable=no-member
+                                         # TODO fix self.name reference
+                                         formatting.safe_repr(self.node_type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.capabilities)
+        utils.validate_dict_values(context, self.requirements)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.capabilities, report_issues)
+        utils.coerce_dict_values(context, container, self.requirements, report_issues)
+
+    def dump(self, context):
+        console.puts('Substitution:')
+        with context.style.indent:
+            console.puts('Node type: %s' % context.style.type(self.node_type_name))
+            utils.dump_dict_values(context, self.capabilities, 'Capability mappings')
+            utils.dump_dict_values(context, self.requirements, 'Requirement mappings')
+
+
+# endregion
+
+# region Node instances
+
+class NodeBase(structure.ModelMixin):
+    """
+    An instance of a :class:`NodeTemplate`.
+
+    Nodes may have zero or more :class:`Relationship` instances to other nodes.
+
+    Properties:
+
+    * :code:`id`: Unique ID (prefixed with the template name)
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`interfaces`: Dict of :class:`Interface`
+    * :code:`artifacts`: Dict of :class:`Artifact`
+    * :code:`capabilities`: Dict of :class:`CapabilityTemplate`
+    * :code:`relationships`: List of :class:`Relationship`
+    """
+    __tablename__ = 'node'
+
+    __private_fields__ = ['service_instance_fk',
+                          'host_fk',
+                          'node_template_fk']
+
+    # region foreign_keys
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    @declared_attr
+    def host_fk(cls):
+        return cls.foreign_key('node', nullable=True)
+
+    @declared_attr
+    def node_template_fk(cls):
+        return cls.foreign_key('node_template')
+
+    # endregion
+
+    type_name = Column(Text)
+    template_name = Column(Text)
+
+    # region orchestrator required columns
+    runtime_properties = Column(aria_types.Dict)
+    scaling_groups = Column(aria_types.List)
+    state = Column(Text, nullable=False)
+    version = Column(Integer, default=1)
+
+    @declared_attr
+    def plugins(cls):
+        return association_proxy('node_template', 'plugins')
+
+    @declared_attr
+    def host(cls):
+        return cls.relationship_to_self('host_fk')
+
+    @declared_attr
+    def service_instance_name(cls):
+        return association_proxy('service_instance', 'name')
+
+    @property
+    def ip(self):
+        if not self.host_fk:
+            return None
+        host_node = self.host
+        if 'ip' in host_node.runtime_properties:  # pylint: disable=no-member
+            return host_node.runtime_properties['ip']  # pylint: disable=no-member
+        host_node = host_node.node_template  # pylint: disable=no-member
+        host_ip_property = [prop for prop in host_node.properties if prop.name == 'ip']
+        if host_ip_property:
+            return host_ip_property[0].value
+        return None
+
+    @declared_attr
+    def node_template(cls):
+        return cls.many_to_one_relationship('node_template')
+
+    @declared_attr
+    def service_template(cls):
+        return association_proxy('service_instance', 'service_template')
+    # endregion
+
+    # region 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')
+
+    # endregion
+
+    def satisfy_requirements(self, context):
+        node_template = context.modeling.model.node_templates.get(self.template_name)
+        satisfied = True
+        for i in range(len(node_template.requirement_templates)):
+            requirement_template = node_template.requirement_templates[i]
+
+            # Find target template
+            target_node_template, target_node_capability = \
+                requirement_template.find_target(context, node_template)
+            if target_node_template is not None:
+                satisfied = self._satisfy_capability(context,
+                                                     target_node_capability,
+                                                     target_node_template,
+                                                     requirement_template,
+                                                     requirement_template_index=i)
+            else:
+                context.validation.report('requirement "%s" of node "%s" has no target node '
+                                          'template' % (requirement_template.name,
+                                                        self.id),
+                                          level=validation.Issue.BETWEEN_INSTANCES)
+                satisfied = False
+        return satisfied
+
+    def _satisfy_capability(self, context, target_node_capability, target_node_template,
+                            requirement_template, requirement_template_index):
+        # Find target nodes
+        target_nodes = context.modeling.instance.find_nodes(target_node_template.name)
+        if target_nodes:
+            target_node = None
+            target_capability = None
+
+            if target_node_capability is not None:
+                # Relate to the first target node that has capacity
+                for node in target_nodes:
+                    target_capability = node.capabilities.get(target_node_capability.name)
+                    if target_capability.relate():
+                        target_node = node
+                        break
+            else:
+                # Use first target node
+                target_node = target_nodes[0]
+
+            if target_node is not None:
+                relationship = RelationshipBase(
+                    name=requirement_template.name,
+                    source_requirement_index=requirement_template_index,
+                    target_node_id=target_node.id,
+                    target_capability_name=target_capability.name
+                )
+                self.relationships.append(relationship)
+            else:
+                context.validation.report('requirement "%s" of node "%s" targets node '
+                                          'template "%s" but its instantiated nodes do not '
+                                          'have enough capacity'
+                                          % (requirement_template.name,
+                                             self.id,
+                                             target_node_template.name),
+                                          level=validation.Issue.BETWEEN_INSTANCES)
+                return False
+        else:
+            context.validation.report('requirement "%s" of node "%s" targets node template '
+                                      '"%s" but it has no instantiated nodes'
+                                      % (requirement_template.name,
+                                         self.id,
+                                         target_node_template.name),
+                                      level=validation.Issue.BETWEEN_INSTANCES)
+            return False
+
+    def validate_capabilities(self, context):
+        satisfied = False
+        for capability in self.capabilities.itervalues():
+            if not capability.has_enough_relationships:
+                context.validation.report('capability "%s" of node "%s" requires at least %d '
+                                          'relationships but has %d'
+                                          % (capability.name,
+                                             self.id,
+                                             capability.min_occurrences,
+                                             capability.occurrences),
+                                          level=validation.Issue.BETWEEN_INSTANCES)
+                satisfied = False
+        return satisfied
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('id', self.id),
+            ('type_name', self.type_name),
+            ('template_name', self.template_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('interfaces', formatting.as_raw_list(self.interfaces)),
+            ('artifacts', formatting.as_raw_list(self.artifacts)),
+            ('capabilities', formatting.as_raw_list(self.capabilities)),
+            ('relationships', formatting.as_raw_list(self.relationships))))
+
+    def validate(self, context):
+        if len(self.id) > context.modeling.id_max_length:
+            context.validation.report('"%s" has an ID longer than the limit of %d characters: %d'
+                                      % (self.id,
+                                         context.modeling.id_max_length,
+                                         len(self.id)),
+                                      level=validation.Issue.BETWEEN_INSTANCES)
+
+        # TODO: validate that node template is of type?
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.interfaces)
+        utils.validate_dict_values(context, self.artifacts)
+        utils.validate_dict_values(context, self.capabilities)
+        utils.validate_list_values(context, self.relationships)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, self, self.properties, report_issues)
+        utils.coerce_dict_values(context, self, self.interfaces, report_issues)
+        utils.coerce_dict_values(context, self, self.artifacts, report_issues)
+        utils.coerce_dict_values(context, self, self.capabilities, report_issues)
+        utils.coerce_list_values(context, self, self.relationships, report_issues)
+
+    def dump(self, context):
+        console.puts('Node: %s' % context.style.node(self.id))
+        with context.style.indent:
+            console.puts('Template: %s' % context.style.node(self.template_name))
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            utils.dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.interfaces)
+            utils.dump_dict_values(context, self.artifacts, 'Artifacts')
+            utils.dump_dict_values(context, self.capabilities, 'Capabilities')
+            utils.dump_list_values(context, self.relationships, 'Relationships')
+
+
+class GroupBase(structure.ModelMixin):
+    """
+    An instance of a :class:`GroupTemplate`.
+
+    Properties:
+
+    * :code:`id`: Unique ID (prefixed with the template name)
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`interfaces`: Dict of :class:`Interface`
+    * :code:`policies`: Dict of :class:`GroupPolicy`
+    * :code:`member_node_ids`: Must be represented in the :class:`ServiceInstance`
+    * :code:`member_group_ids`: Must be represented in the :class:`ServiceInstance`
+    """
+    __tablename__ = 'group'
+
+    __private_fields__ = ['service_instance_fk']
+
+    # region foreign_keys
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    # endregion
+
+    type_name = Column(Text)
+    template_name = Column(Text)
+    member_node_ids = Column(aria_types.StrictList(basestring))
+    member_group_ids = Column(aria_types.StrictList(basestring))
+
+    # region many-to-one relationships
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance')
+
+    # region many-to-many relationships
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('id', self.id),
+            ('type_name', self.type_name),
+            ('template_name', self.template_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('interfaces', formatting.as_raw_list(self.interfaces)),
+            ('policies', formatting.as_raw_list(self.policies)),
+            ('member_node_ids', self.member_node_ids),
+            ('member_group_ids', self.member_group_ids)))
+
+    def validate(self, context):
+        if context.modeling.group_types.get_descendant(self.type_name) is None:
+            context.validation.report('group "%s" has an unknown type: %s'
+                                      % (self.name,  # pylint: disable=no-member
+                                         # TODO fix self.name reference
+                                         formatting.safe_repr(self.type_name)),
+                                      level=validation.Issue.BETWEEN_TYPES)
+
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.interfaces)
+        utils.validate_dict_values(context, self.policies)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+        utils.coerce_dict_values(context, container, self.interfaces, report_issues)
+        utils.coerce_dict_values(context, container, self.policies, report_issues)
+
+    def dump(self, context):
+        console.puts('Group: %s' % context.style.node(self.id))
+        with context.style.indent:
+            console.puts('Type: %s' % context.style.type(self.type_name))
+            console.puts('Template: %s' % context.style.type(self.template_name))
+            utils.dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.interfaces)
+            utils.dump_dict_values(context, self.policies, 'Policies')
+            if self.member_node_ids:
+                console.puts('Member nodes:')
+                with context.style.indent:
+                    for node_id in self.member_node_ids:
+                        console.puts(context.style.node(node_id))
+
+# endregion
+
+# region Relationship instances
+
+
+class RelationshipBase(structure.ModelMixin):
+    """
+    Connects :class:`Node` to another node.
+
+    An instance of a :class:`RelationshipTemplate`.
+
+    Properties:
+
+    * :code:`name`: Name (usually the name of the requirement at the source node template)
+    * :code:`source_requirement_index`: Must be represented in the source node template
+    * :code:`target_node_id`: Must be represented in the :class:`ServiceInstance`
+    * :code:`target_capability_name`: Matches the capability at the target node
+    * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+    * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+    * :code:`properties`: Dict of :class:`Parameter`
+    * :code:`source_interfaces`: Dict of :class:`Interface`
+    * :code:`target_interfaces`: Dict of :class:`Interface`
+    """
+    __tablename__ = 'relationship'
+
+    __private_fields__ = ['source_node_fk',
+                          'target_node_fk']
+
+    source_requirement_index = Column(Integer)
+    target_node_id = Column(Text)
+    target_capability_name = Column(Text)
+    type_name = Column(Text)
+    template_name = Column(Text)
+
+    # # region orchestrator required columns
+    source_position = Column(Integer)
+    target_position = Column(Integer)
+
+    @declared_attr
+    def source_node_fk(cls):
+        return cls.foreign_key('node', nullable=True)
+
+    @declared_attr
+    def source_node(cls):
+        return cls.many_to_one_relationship(
+            'node',
+            'source_node_fk',
+            backreference='outbound_relationships',
+            backref_kwargs=dict(
+                order_by=cls.source_position,
+                collection_class=ordering_list('source_position', count_from=0),
+            )
+        )
+
+    @declared_attr
+    def source_node_name(cls):
+        return association_proxy('source_node', cls.name_column_name())
+
+    @declared_attr
+    def target_node_fk(cls):
+        return cls.foreign_key('node', nullable=True)
+
+    @declared_attr
+    def target_node(cls):
+        return cls.many_to_one_relationship(
+            'node',
+            'target_node_fk',
+            backreference='inbound_relationships',
+            backref_kwargs=dict(
+                order_by=cls.target_position,
+                collection_class=ordering_list('target_position', count_from=0),
+            )
+        )
+
+    @declared_attr
+    def target_node_name(cls):
+        return association_proxy('target_node', cls.name_column_name())
+    # endregion
+
+    # region many-to-many relationship
+
+    @declared_attr
+    def properties(cls):
+        return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+    # endregion
+
+    @property
+    def as_raw(self):
+        return collections.OrderedDict((
+            ('name', self.name),
+            ('source_requirement_index', self.source_requirement_index),
+            ('target_node_id', self.target_node_id),
+            ('target_capability_name', self.target_capability_name),
+            ('type_name', self.type_name),
+            ('template_name', self.template_name),
+            ('properties', formatting.as_raw_dict(self.properties)),
+            ('source_interfaces', formatting.as_raw_list(self.source_interfaces)),
+            ('target_interfaces', formatting.as_raw_list(self.target_interfaces))))
+
+    def validate(self, context):
+        if self.type_name:
+            if context.modeling.relationship_types.get_descendant(self.type_name) is None:
+                context.validation.report('relationship "%s" has an unknown type: %s'
+                                          % (self.name,
+                                             formatting.safe_repr(self.type_name)),
+                                          level=validation.Issue.BETWEEN_TYPES)
+        utils.validate_dict_values(context, self.properties)
+        utils.validate_dict_values(context, self.source_interfaces)
+        utils.validate_dict_values(context, self.target_interfaces)
+
+    def coerce_values(self, context, container, report_issues):
+        utils.coerce_dict_values(context, container, self.properties, report_issues)
+        utils.coerce_dict_values(context, container, self.source_interfaces, report_issues)
+        utils.coerce_dict_values(context, container, self.target_interfaces, report_issues)
+
+    def dump(self, context):
+        if self.name:
+            if self.source_requirement_index is not None:
+                console.puts('%s (%d) ->' % (
+                    context.style.node(self.name),
+                    self.source_requirement_index))
+            else:
+                console.puts('%s ->' % context.style.node(self.name))
+        else:
+            console.puts('->')
+        with context.style.indent:
+            console.puts('Node: %s' % context.style.node(self.target_node_id))
+            if self.target_capability_name is not None:
+                console.puts('Capability: %s' % context.style.node(self.target_capability_name))
+            if self.type_name is not None:
+                console.puts('Relationship type: %s' % context.style.type(self.type_name))
+            if self.template_name is not None:
+                console.puts('Relationship template: %s' % context.style.node(self.template_name))
+            utils.dump_parameters(context, self.properties)
+            utils.dump_interfaces(context, self.source_interfaces, 'Source interfaces')
+            utils.dump_interfaces(context, self.target_interfaces, 'Target interfaces')
+
+# endregion


[4/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model

Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/model.py b/aria/storage/modeling/model.py
new file mode 100644
index 0000000..62b90b3
--- /dev/null
+++ b/aria/storage/modeling/model.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.
+
+from sqlalchemy.ext.declarative import declarative_base
+
+from . import (
+    template_elements,
+    instance_elements,
+    orchestrator_elements,
+    elements,
+    structure,
+)
+
+__all__ = (
+    'aria_declarative_base',
+
+    'Parameter',
+
+    'MappingTemplate',
+    'InterfaceTemplate',
+    'OperationTemplate',
+    'ServiceTemplate',
+    'NodeTemplate',
+    'GroupTemplate',
+    'ArtifactTemplate',
+    'PolicyTemplate',
+    'GroupPolicyTemplate',
+    'GroupPolicyTriggerTemplate',
+    'RequirementTemplate',
+    'CapabilityTemplate',
+
+    'Mapping',
+    'Substitution',
+    'ServiceInstance',
+    'Node',
+    'Relationship',
+    'Artifact',
+    'Group',
+    'Interface',
+    'Operation',
+    'Capability',
+    'Policy',
+    'GroupPolicy',
+    'GroupPolicyTrigger',
+
+    'Execution',
+    'ServiceInstanceUpdate',
+    'ServiceInstanceUpdateStep',
+    'ServiceInstanceModification',
+    'Plugin',
+    'Task'
+)
+
+aria_declarative_base = declarative_base(cls=structure.ModelIDMixin)
+
+# pylint: disable=abstract-method
+
+# region elements
+
+
+class Parameter(aria_declarative_base, elements.ParameterBase):
+    pass
+
+# endregion
+
+# region template models
+
+
+class MappingTemplate(aria_declarative_base, template_elements.MappingTemplateBase):
+    pass
+
+
+class SubstitutionTemplate(aria_declarative_base, template_elements.SubstitutionTemplateBase):
+    pass
+
+
+class InterfaceTemplate(aria_declarative_base, template_elements.InterfaceTemplateBase):
+    pass
+
+
+class OperationTemplate(aria_declarative_base, template_elements.OperationTemplateBase):
+    pass
+
+
+class ServiceTemplate(aria_declarative_base, template_elements.ServiceTemplateBase):
+    pass
+
+
+class NodeTemplate(aria_declarative_base, template_elements.NodeTemplateBase):
+    pass
+
+
+class GroupTemplate(aria_declarative_base, template_elements.GroupTemplateBase):
+    pass
+
+
+class ArtifactTemplate(aria_declarative_base, template_elements.ArtifactTemplateBase):
+    pass
+
+
+class PolicyTemplate(aria_declarative_base, template_elements.PolicyTemplateBase):
+    pass
+
+
+class GroupPolicyTemplate(aria_declarative_base, template_elements.GroupPolicyTemplateBase):
+    pass
+
+
+class GroupPolicyTriggerTemplate(aria_declarative_base,
+                                 template_elements.GroupPolicyTriggerTemplateBase):
+    pass
+
+
+class RequirementTemplate(aria_declarative_base, template_elements.RequirementTemplateBase):
+    pass
+
+
+class CapabilityTemplate(aria_declarative_base, template_elements.CapabilityTemplateBase):
+    pass
+
+
+# endregion
+
+# region instance models
+
+class Mapping(aria_declarative_base, instance_elements.MappingBase):
+    pass
+
+
+class Substitution(aria_declarative_base, instance_elements.SubstitutionBase):
+    pass
+
+
+class ServiceInstance(aria_declarative_base, instance_elements.ServiceInstanceBase):
+    pass
+
+
+class Node(aria_declarative_base, instance_elements.NodeBase):
+    pass
+
+
+class Relationship(aria_declarative_base, instance_elements.RelationshipBase):
+    pass
+
+
+class Artifact(aria_declarative_base, instance_elements.ArtifactBase):
+    pass
+
+
+class Group(aria_declarative_base, instance_elements.GroupBase):
+    pass
+
+
+class Interface(aria_declarative_base, instance_elements.InterfaceBase):
+    pass
+
+
+class Operation(aria_declarative_base, instance_elements.OperationBase):
+    pass
+
+
+class Capability(aria_declarative_base, instance_elements.CapabilityBase):
+    pass
+
+
+class Policy(aria_declarative_base, instance_elements.PolicyBase):
+    pass
+
+
+class GroupPolicy(aria_declarative_base, instance_elements.GroupPolicyBase):
+    pass
+
+
+class GroupPolicyTrigger(aria_declarative_base, instance_elements.GroupPolicyTriggerBase):
+    pass
+
+
+# endregion
+
+# region orchestrator models
+
+class Execution(aria_declarative_base, orchestrator_elements.Execution):
+    pass
+
+
+class ServiceInstanceUpdate(aria_declarative_base,
+                            orchestrator_elements.ServiceInstanceUpdateBase):
+    pass
+
+
+class ServiceInstanceUpdateStep(aria_declarative_base,
+                                orchestrator_elements.ServiceInstanceUpdateStepBase):
+    pass
+
+
+class ServiceInstanceModification(aria_declarative_base,
+                                  orchestrator_elements.ServiceInstanceModificationBase):
+    pass
+
+
+class Plugin(aria_declarative_base, orchestrator_elements.PluginBase):
+    pass
+
+
+class Task(aria_declarative_base, orchestrator_elements.TaskBase):
+    pass
+# endregion

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/orchestrator_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/orchestrator_elements.py b/aria/storage/modeling/orchestrator_elements.py
new file mode 100644
index 0000000..5f7a3f2
--- /dev/null
+++ b/aria/storage/modeling/orchestrator_elements.py
@@ -0,0 +1,468 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Aria's storage.models module
+Path: aria.storage.models
+
+models module holds aria's models.
+
+classes:
+    * Field - represents a single field.
+    * IterField - represents an iterable field.
+    * Model - abstract model implementation.
+    * Snapshot - snapshots implementation model.
+    * Deployment - deployment implementation model.
+    * DeploymentUpdateStep - deployment update step implementation model.
+    * DeploymentUpdate - deployment update implementation model.
+    * DeploymentModification - deployment modification implementation model.
+    * Execution - execution implementation model.
+    * Node - node implementation model.
+    * Relationship - relationship implementation model.
+    * NodeInstance - node instance implementation model.
+    * RelationshipInstance - relationship instance implementation model.
+    * Plugin - plugin implementation model.
+"""
+from collections import namedtuple
+from datetime import datetime
+
+from sqlalchemy import (
+    Column,
+    Integer,
+    Text,
+    DateTime,
+    Boolean,
+    Enum,
+    String,
+    Float,
+    orm,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from aria.orchestrator.exceptions import TaskAbortException, TaskRetryException
+
+from .type import List, Dict
+from .structure import ModelMixin
+
+__all__ = (
+    'ServiceInstanceUpdateStepBase',
+    'ServiceInstanceUpdateBase',
+    'ServiceInstanceModificationBase',
+    'Execution',
+    'PluginBase',
+    'TaskBase'
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+class Execution(ModelMixin):
+    """
+    Execution model representation.
+    """
+    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+    __tablename__ = 'execution'
+
+    __private_fields__ = ['service_instance_fk']
+
+    TERMINATED = 'terminated'
+    FAILED = 'failed'
+    CANCELLED = 'cancelled'
+    PENDING = 'pending'
+    STARTED = 'started'
+    CANCELLING = 'cancelling'
+    FORCE_CANCELLING = 'force_cancelling'
+
+    STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING]
+    END_STATES = [TERMINATED, FAILED, CANCELLED]
+    ACTIVE_STATES = [state for state in STATES if state not in END_STATES]
+
+    VALID_TRANSITIONS = {
+        PENDING: [STARTED, CANCELLED],
+        STARTED: END_STATES + [CANCELLING],
+        CANCELLING: END_STATES + [FORCE_CANCELLING]
+    }
+
+    @orm.validates('status')
+    def validate_status(self, key, value):
+        """Validation function that verifies execution status transitions are OK"""
+        try:
+            current_status = getattr(self, key)
+        except AttributeError:
+            return
+        valid_transitions = self.VALID_TRANSITIONS.get(current_status, [])
+        if all([current_status is not None,
+                current_status != value,
+                value not in valid_transitions]):
+            raise ValueError('Cannot change execution status from {current} to {new}'.format(
+                current=current_status,
+                new=value))
+        return value
+
+    created_at = Column(DateTime, index=True)
+    started_at = Column(DateTime, nullable=True, index=True)
+    ended_at = Column(DateTime, nullable=True, index=True)
+    error = Column(Text, nullable=True)
+    is_system_workflow = Column(Boolean, nullable=False, default=False)
+    parameters = Column(Dict)
+    status = Column(Enum(*STATES, name='execution_status'), default=PENDING)
+    workflow_name = Column(Text)
+
+    @declared_attr
+    def service_template(cls):
+        return association_proxy('service_instance', 'service_template')
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance')
+
+    @declared_attr
+    def service_instance_name(cls):
+        return association_proxy('service_instance', cls.name_column_name())
+
+    @declared_attr
+    def service_template_name(cls):
+        return association_proxy('service_instance', 'service_template_name')
+
+    def __str__(self):
+        return '<{0} id=`{1}` (status={2})>'.format(
+            self.__class__.__name__,
+            getattr(self, self.name_column_name()),
+            self.status
+        )
+
+
+class ServiceInstanceUpdateBase(ModelMixin):
+    """
+    Deployment update model representation.
+    """
+    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+    steps = None
+
+    __tablename__ = 'service_instance_update'
+    __private_fields__ = ['service_instance_fk',
+                          'execution_fk']
+
+    _private_fields = ['execution_fk', 'deployment_fk']
+
+    created_at = Column(DateTime, nullable=False, index=True)
+    service_instance_plan = Column(Dict, nullable=False)
+    service_instance_update_node_instances = Column(Dict)
+    service_instance_update_service_instance = Column(Dict)
+    service_instance_update_nodes = Column(List)
+    modified_entity_ids = Column(Dict)
+    state = Column(Text)
+
+    @declared_attr
+    def execution_fk(cls):
+        return cls.foreign_key('execution', nullable=True)
+
+    @declared_attr
+    def execution(cls):
+        return cls.many_to_one_relationship('execution')
+
+    @declared_attr
+    def execution_name(cls):
+        return association_proxy('execution', cls.name_column_name())
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance')
+
+    @declared_attr
+    def service_instance_name(cls):
+        return association_proxy('service_instance', cls.name_column_name())
+
+    def to_dict(self, suppress_error=False, **kwargs):
+        dep_update_dict = super(ServiceInstanceUpdateBase, self).to_dict(suppress_error)     #pylint: disable=no-member
+        # Taking care of the fact the DeploymentSteps are _BaseModels
+        dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
+        return dep_update_dict
+
+
+class ServiceInstanceUpdateStepBase(ModelMixin):
+    """
+    Deployment update step model representation.
+    """
+    # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+    __tablename__ = 'service_instance_update_step'
+    __private_fields__ = ['service_instance_update_fk']
+
+    _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
+    ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
+    _entity_types = namedtuple(
+        'ENTITY_TYPES',
+        'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, '
+        'POLICY_TYPE, POLICY_TRIGGER, PLUGIN')
+    ENTITY_TYPES = _entity_types(
+        NODE='node',
+        RELATIONSHIP='relationship',
+        PROPERTY='property',
+        OPERATION='operation',
+        WORKFLOW='workflow',
+        OUTPUT='output',
+        DESCRIPTION='description',
+        GROUP='group',
+        POLICY_TYPE='policy_type',
+        POLICY_TRIGGER='policy_trigger',
+        PLUGIN='plugin'
+    )
+
+    action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False)
+    entity_id = Column(Text, nullable=False)
+    entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
+
+    @declared_attr
+    def service_instance_update_fk(cls):
+        return cls.foreign_key('service_instance_update')
+
+    @declared_attr
+    def service_instance_update(cls):
+        return cls.many_to_one_relationship('service_instance_update',
+                                            backreference='steps')
+
+    @declared_attr
+    def deployment_update_name(cls):
+        return association_proxy('deployment_update', cls.name_column_name())
+
+    def __hash__(self):
+        return hash((getattr(self, self.id_column_name()), self.entity_id))
+
+    def __lt__(self, other):
+        """
+        the order is 'remove' < 'modify' < 'add'
+        :param other:
+        :return:
+        """
+        if not isinstance(other, self.__class__):
+            return not self >= other
+
+        if self.action != other.action:
+            if self.action == 'remove':
+                return_value = True
+            elif self.action == 'add':
+                return_value = False
+            else:
+                return_value = other.action == 'add'
+            return return_value
+
+        if self.action == 'add':
+            return self.entity_type == 'node' and other.entity_type == 'relationship'
+        if self.action == 'remove':
+            return self.entity_type == 'relationship' and other.entity_type == 'node'
+        return False
+
+
+class ServiceInstanceModificationBase(ModelMixin):
+    """
+    Deployment modification model representation.
+    """
+    __tablename__ = 'service_instance_modification'
+    __private_fields__ = ['service_instance_fk']
+
+    STARTED = 'started'
+    FINISHED = 'finished'
+    ROLLEDBACK = 'rolledback'
+
+    STATES = [STARTED, FINISHED, ROLLEDBACK]
+    END_STATES = [FINISHED, ROLLEDBACK]
+
+    context = Column(Dict)
+    created_at = Column(DateTime, nullable=False, index=True)
+    ended_at = Column(DateTime, index=True)
+    modified_nodes = Column(Dict)
+    node_instances = Column(Dict)
+    status = Column(Enum(*STATES, name='deployment_modification_status'))
+
+    @declared_attr
+    def service_instance_fk(cls):
+        return cls.foreign_key('service_instance')
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance',
+                                            backreference='modifications')
+
+    @declared_attr
+    def service_instance_name(cls):
+        return association_proxy('service_instance', cls.name_column_name())
+
+
+class PluginBase(ModelMixin):
+    """
+    Plugin model representation.
+    """
+    __tablename__ = 'plugin'
+
+    archive_name = Column(Text, nullable=False, index=True)
+    distribution = Column(Text)
+    distribution_release = Column(Text)
+    distribution_version = Column(Text)
+    package_name = Column(Text, nullable=False, index=True)
+    package_source = Column(Text)
+    package_version = Column(Text)
+    supported_platform = Column(Text)
+    supported_py_versions = Column(List)
+    uploaded_at = Column(DateTime, nullable=False, index=True)
+    wheels = Column(List, nullable=False)
+
+
+class TaskBase(ModelMixin):
+    """
+    A Model which represents an task
+    """
+    __tablename__ = 'task'
+    __private_fields__ = ['node_fk',
+                          'relationship_fk',
+                          'execution_fk',
+                          'plugin_fk']
+
+    @declared_attr
+    def node_fk(cls):
+        return cls.foreign_key('node', nullable=True)
+
+    @declared_attr
+    def node_name(cls):
+        return association_proxy('node', cls.name_column_name())
+
+    @declared_attr
+    def node(cls):
+        return cls.many_to_one_relationship('node')
+
+    @declared_attr
+    def relationship_fk(cls):
+        return cls.foreign_key('relationship', nullable=True)
+
+    @declared_attr
+    def relationship_name(cls):
+        return association_proxy('relationships', cls.name_column_name())
+
+    @declared_attr
+    def relationship(cls):
+        return cls.many_to_one_relationship('relationship')
+
+    @declared_attr
+    def plugin_fk(cls):
+        return cls.foreign_key('plugin', nullable=True)
+
+    @declared_attr
+    def plugin(cls):
+        return cls.many_to_one_relationship('plugin')
+
+    @declared_attr
+    def execution_fk(cls):
+        return cls.foreign_key('execution', nullable=True)
+
+    @declared_attr
+    def execution(cls):
+        return cls.many_to_one_relationship('execution')
+
+    @declared_attr
+    def execution_name(cls):
+        return association_proxy('execution', cls.name_column_name())
+
+    PENDING = 'pending'
+    RETRYING = 'retrying'
+    SENT = 'sent'
+    STARTED = 'started'
+    SUCCESS = 'success'
+    FAILED = 'failed'
+    STATES = (
+        PENDING,
+        RETRYING,
+        SENT,
+        STARTED,
+        SUCCESS,
+        FAILED,
+    )
+
+    WAIT_STATES = [PENDING, RETRYING]
+    END_STATES = [SUCCESS, FAILED]
+
+    RUNS_ON_SOURCE = 'source'
+    RUNS_ON_TARGET = 'target'
+    RUNS_ON_NODE_INSTANCE = 'node_instance'
+    RUNS_ON = (RUNS_ON_NODE_INSTANCE, RUNS_ON_SOURCE, RUNS_ON_TARGET)
+
+    @orm.validates('max_attempts')
+    def validate_max_attempts(self, _, value):                                  # pylint: disable=no-self-use
+        """Validates that max attempts is either -1 or a positive number"""
+        if value < 1 and value != TaskBase.INFINITE_RETRIES:
+            raise ValueError('Max attempts can be either -1 (infinite) or any positive number. '
+                             'Got {value}'.format(value=value))
+        return value
+
+    INFINITE_RETRIES = -1
+
+    status = Column(Enum(*STATES, name='status'), default=PENDING)
+
+    due_at = Column(DateTime, default=datetime.utcnow)
+    started_at = Column(DateTime, default=None)
+    ended_at = Column(DateTime, default=None)
+    max_attempts = Column(Integer, default=1)
+    retry_count = Column(Integer, default=0)
+    retry_interval = Column(Float, default=0)
+    ignore_failure = Column(Boolean, default=False)
+
+    # Operation specific fields
+    implementation = Column(String)
+    inputs = Column(Dict)
+    # This is unrelated to the plugin of the task. This field is related to the plugin name
+    # received from the blueprint.
+    plugin_name = Column(String)
+    _runs_on = Column(Enum(*RUNS_ON, name='runs_on'), name='runs_on')
+
+    @property
+    def runs_on(self):
+        if self._runs_on == self.RUNS_ON_NODE_INSTANCE:
+            return self.node
+        elif self._runs_on == self.RUNS_ON_SOURCE:
+            return self.relationship.source_node  # pylint: disable=no-member
+        elif self._runs_on == self.RUNS_ON_TARGET:
+            return self.relationship.target_node  # pylint: disable=no-member
+        return None
+
+    @property
+    def actor(self):
+        """
+        Return the actor of the task
+        :return:
+        """
+        return self.node or self.relationship
+
+    @classmethod
+    def as_node_instance(cls, instance, runs_on, **kwargs):
+        return cls(node=instance, _runs_on=runs_on, **kwargs)
+
+    @classmethod
+    def as_relationship_instance(cls, instance, runs_on, **kwargs):
+        return cls(relationship=instance, _runs_on=runs_on, **kwargs)
+
+    @staticmethod
+    def abort(message=None):
+        raise TaskAbortException(message)
+
+    @staticmethod
+    def retry(message=None, retry_interval=None):
+        raise TaskRetryException(message, retry_interval=retry_interval)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/structure.py b/aria/storage/modeling/structure.py
new file mode 100644
index 0000000..eacdb44
--- /dev/null
+++ b/aria/storage/modeling/structure.py
@@ -0,0 +1,320 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Aria's storage.structures module
+Path: aria.storage.structures
+
+models module holds aria's models.
+
+classes:
+    * Field - represents a single field.
+    * IterField - represents an iterable field.
+    * PointerField - represents a single pointer field.
+    * IterPointerField - represents an iterable pointers field.
+    * Model - abstract model implementation.
+"""
+
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.ext import associationproxy
+from sqlalchemy import (
+    Column,
+    ForeignKey,
+    Integer,
+    Text,
+    Table,
+)
+
+from . import utils
+
+
+class Function(object):
+    """
+    An intrinsic function.
+
+    Serves as a placeholder for a value that should eventually be derived
+    by calling the function.
+    """
+
+    @property
+    def as_raw(self):
+        raise NotImplementedError
+
+    def _evaluate(self, context, container):
+        raise NotImplementedError
+
+    def __deepcopy__(self, memo):
+        # Circumvent cloning in order to maintain our state
+        return self
+
+
+class ElementBase(object):
+    """
+    Base class for :class:`ServiceInstance` elements.
+
+    All elements support validation, diagnostic dumping, and representation as
+    raw data (which can be translated into JSON or YAML) via :code:`as_raw`.
+    """
+
+    @property
+    def as_raw(self):
+        raise NotImplementedError
+
+    def validate(self, context):
+        pass
+
+    def coerce_values(self, context, container, report_issues):
+        pass
+
+    def dump(self, context):
+        pass
+
+
+class ModelElementBase(ElementBase):
+    """
+    Base class for :class:`ServiceModel` elements.
+
+    All model elements can be instantiated into :class:`ServiceInstance` elements.
+    """
+
+    def instantiate(self, context, container):
+        raise NotImplementedError
+
+
+class ModelMixin(ModelElementBase):
+
+    @utils.classproperty
+    def __modelname__(cls):                                                                         # pylint: disable=no-self-argument
+        return getattr(cls, '__mapiname__', cls.__tablename__)
+
+    @classmethod
+    def id_column_name(cls):
+        raise NotImplementedError
+
+    @classmethod
+    def name_column_name(cls):
+        raise NotImplementedError
+
+    @classmethod
+    def _get_cls_by_tablename(cls, tablename):
+        """Return class reference mapped to table.
+
+         :param tablename: String with name of table.
+         :return: Class reference or None.
+         """
+        if tablename in (cls.__name__, cls.__tablename__):
+            return cls
+
+        for table_cls in cls._decl_class_registry.values():
+            if tablename == getattr(table_cls, '__tablename__', None):
+                return table_cls
+
+    @classmethod
+    def foreign_key(cls, table_name, nullable=False):
+        """Return a ForeignKey object with the relevant
+
+        :param table: Unique id column in the parent table
+        :param nullable: Should the column be allowed to remain empty
+        """
+        return Column(Integer,
+                      ForeignKey('{tablename}.id'.format(tablename=table_name), ondelete='CASCADE'),
+                      nullable=nullable)
+
+    @classmethod
+    def one_to_one_relationship(cls, table_name, backreference=None):
+        return relationship(lambda: cls._get_cls_by_tablename(table_name),
+                            backref=backref(backreference or cls.__tablename__, uselist=False))
+
+    @classmethod
+    def many_to_one_relationship(cls,
+                                 parent_table_name,
+                                 foreign_key_column=None,
+                                 backreference=None,
+                                 backref_kwargs=None,
+                                 **kwargs):
+        """Return a one-to-many SQL relationship object
+        Meant to be used from inside the *child* object
+
+        :param parent_class: Class of the parent table
+        :param cls: Class of the child table
+        :param foreign_key_column: The column of the foreign key (from the child table)
+        :param backreference: The name to give to the reference to the child (on the parent table)
+        """
+        relationship_kwargs = kwargs
+        if foreign_key_column:
+            relationship_kwargs.setdefault('foreign_keys', getattr(cls, foreign_key_column))
+
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('lazy', 'dynamic')
+        # The following line make sure that when the *parent* is
+        #  deleted, all its connected children are deleted as well
+        backref_kwargs.setdefault('cascade', 'all')
+
+        return relationship(lambda: cls._get_cls_by_tablename(parent_table_name),
+                            backref=backref(backreference or utils.pluralize(cls.__tablename__),
+                                            **backref_kwargs or {}),
+                            **relationship_kwargs)
+
+    @classmethod
+    def relationship_to_self(cls, local_column):
+
+        remote_side_str = '{cls.__name__}.{remote_column}'.format(
+            cls=cls,
+            remote_column=cls.id_column_name()
+        )
+        primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format(
+            remote_side_str=remote_side_str,
+            cls=cls,
+            local_column=local_column)
+        return relationship(cls._get_cls_by_tablename(cls.__tablename__).__name__,
+                            primaryjoin=primaryjoin_str,
+                            remote_side=remote_side_str,
+                            post_update=True)
+
+    @classmethod
+    def many_to_many_relationship(cls, other_table_name, table_prefix, relationship_kwargs=None):
+        """Return a many-to-many SQL relationship object
+
+        Notes:
+        1. The backreference name is the current table's table name
+        2. This method creates a new helper table in the DB
+
+        :param cls: The class of the table we're connecting from
+        :param other_table_name: The class of the table we're connecting to
+        :param table_prefix: Custom prefix for the helper table name and the
+        backreference name
+        """
+        current_table_name = cls.__tablename__
+        current_column_name = '{0}_id'.format(current_table_name)
+        current_foreign_key = '{0}.id'.format(current_table_name)
+
+        other_column_name = '{0}_id'.format(other_table_name)
+        other_foreign_key = '{0}.id'.format(other_table_name)
+
+        helper_table_name = '{0}_{1}'.format(current_table_name, other_table_name)
+
+        backref_name = current_table_name
+        if table_prefix:
+            helper_table_name = '{0}_{1}'.format(table_prefix, helper_table_name)
+            backref_name = '{0}_{1}'.format(table_prefix, backref_name)
+
+        secondary_table = cls.get_secondary_table(
+            cls.metadata,
+            helper_table_name,
+            current_column_name,
+            other_column_name,
+            current_foreign_key,
+            other_foreign_key
+        )
+
+        return relationship(
+            lambda: cls._get_cls_by_tablename(other_table_name),
+            secondary=secondary_table,
+            backref=backref(backref_name),
+            **(relationship_kwargs or {})
+        )
+
+    @staticmethod
+    def get_secondary_table(metadata,
+                            helper_table_name,
+                            first_column_name,
+                            second_column_name,
+                            first_foreign_key,
+                            second_foreign_key):
+        """Create a helper table for a many-to-many relationship
+
+        :param helper_table_name: The name of the table
+        :param first_column_name: The name of the first column in the table
+        :param second_column_name: The name of the second column in the table
+        :param first_foreign_key: The string representing the first foreign key,
+        for example `blueprint.storage_id`, or `tenants.id`
+        :param second_foreign_key: The string representing the second foreign key
+        :return: A Table object
+        """
+        return Table(
+            helper_table_name,
+            metadata,
+            Column(
+                first_column_name,
+                Integer,
+                ForeignKey(first_foreign_key)
+            ),
+            Column(
+                second_column_name,
+                Integer,
+                ForeignKey(second_foreign_key)
+            )
+        )
+
+    def to_dict(self, fields=None, suppress_error=False):
+        """Return a dict representation of the model
+
+        :param suppress_error: If set to True, sets `None` to attributes that
+        it's unable to retrieve (e.g., if a relationship wasn't established
+        yet, and so it's impossible to access a property through it)
+        """
+        res = dict()
+        fields = fields or self.fields()
+        for field in fields:
+            try:
+                field_value = getattr(self, field)
+            except AttributeError:
+                if suppress_error:
+                    field_value = None
+                else:
+                    raise
+            if isinstance(field_value, list):
+                field_value = list(field_value)
+            elif isinstance(field_value, dict):
+                field_value = dict(field_value)
+            elif isinstance(field_value, ModelMixin):
+                field_value = field_value.to_dict()
+            res[field] = field_value
+
+        return res
+
+    @classmethod
+    def _association_proxies(cls):
+        for col, value in vars(cls).items():
+            if isinstance(value, associationproxy.AssociationProxy):
+                yield col
+
+    @classmethod
+    def fields(cls):
+        """Return the list of field names for this table
+
+        Mostly for backwards compatibility in the code (that uses `fields`)
+        """
+        fields = set(cls._association_proxies())
+        fields.update(cls.__table__.columns.keys())
+        return fields - set(getattr(cls, '__private_fields__', []))
+
+    def __repr__(self):
+        return '<{__class__.__name__} id=`{id}`>'.format(
+            __class__=self.__class__,
+            id=getattr(self, self.name_column_name()))
+
+
+class ModelIDMixin(object):
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    name = Column(Text, nullable=True, index=True)
+
+    @classmethod
+    def id_column_name(cls):
+        return 'id'
+
+    @classmethod
+    def name_column_name(cls):
+        return 'name'


[7/7] incubator-ariatosca git commit: wip

Posted by mx...@apache.org.
wip


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

Branch: refs/heads/ARIA-106-Create-sqla-logging-handler
Commit: 67b754254760db0f1276bc10a293c80c1f5f8a54
Parents: b619335
Author: mxmrlv <mx...@gmail.com>
Authored: Mon Feb 13 12:28:27 2017 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Mon Feb 13 20:09:21 2017 +0200

----------------------------------------------------------------------
 aria/__init__.py                                |  3 +-
 aria/logger.py                                  | 42 ++++++++++-
 aria/orchestrator/context/common.py             | 22 +++++-
 aria/orchestrator/workflows/core/engine.py      |  5 ++
 aria/orchestrator/workflows/executor/process.py |  1 -
 aria/storage/__init__.py                        |  6 --
 aria/storage/core.py                            | 16 +++--
 aria/storage/modeling/model.py                  |  4 ++
 aria/storage/modeling/orchestrator_elements.py  | 12 ++++
 tests/mock/topology.py                          |  2 +-
 tests/orchestrator/context/test_operation.py    | 74 ++++++++++++++++++--
 tests/storage/__init__.py                       | 23 +++---
 tests/storage/test_instrumentation.py           |  6 +-
 13 files changed, 176 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/__init__.py
----------------------------------------------------------------------
diff --git a/aria/__init__.py b/aria/__init__.py
index 18eaa56..43529f0 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -97,7 +97,8 @@ def application_model_storage(api, api_kwargs=None, initiator=None, initiator_kw
         storage.modeling.model.ServiceInstanceUpdateStep,
         storage.modeling.model.ServiceInstanceModification,
         storage.modeling.model.Plugin,
-        storage.modeling.model.Task
+        storage.modeling.model.Task,
+        storage.modeling.model.Log
     ]
     return storage.ModelStorage(api_cls=api,
                                 api_kwargs=api_kwargs,

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/logger.py
----------------------------------------------------------------------
diff --git a/aria/logger.py b/aria/logger.py
index 0002cb5..f19d286 100644
--- a/aria/logger.py
+++ b/aria/logger.py
@@ -17,8 +17,10 @@
 Logging related mixins and functions
 """
 
+
 import logging
-from logging.handlers import RotatingFileHandler
+from logging import handlers as logging_handlers
+from datetime import datetime
 
 _base_logger = logging.getLogger('aria')
 
@@ -124,7 +126,7 @@ def create_file_log_handler(
     """
     Create a logging.handlers.RotatingFileHandler
     """
-    rotating_file = RotatingFileHandler(
+    rotating_file = logging_handlers.RotatingFileHandler(
         filename=file_path,
         maxBytes=max_bytes,
         backupCount=backup_count,
@@ -135,5 +137,41 @@ def create_file_log_handler(
     return rotating_file
 
 
+class SQLAlchemyHandler(logging.Handler):
+    def __init__(self, session, engine, log_cls, **kwargs):
+        self._session = session
+        self._engine = engine
+        self._cls = log_cls
+        super(SQLAlchemyHandler, self).__init__(**kwargs)
+
+    def emit(self, record):
+        log = self._cls(
+            logger=record.name,
+            level=record.levelname,
+            msg=record.msg,
+            created_at=datetime.utcnow()
+        )
+        self._session.add(log)
+        self._session.commit()
+
+
+class _SQLAlchemyHandlerFactory(object):
+    from aria.storage.model import Log
+
+    def __init__(self):
+        self._handler = None
+
+    def __call__(self, session, engine, model_cls=Log, level=logging.DEBUG):
+        if self._handler is None or not self._is_eq(session, engine, model_cls):
+            self._handler = SQLAlchemyHandler(session, engine, model_cls, level=level)
+        return self._handler
+
+    def _is_eq(self, session, engine, model_cls):
+        return all([self._handler._session == session,
+                    self._handler._engine == engine,
+                    self._handler._cls == model_cls])
+
+create_sqla_log_handler = _SQLAlchemyHandlerFactory()
+
 _default_file_formatter = logging.Formatter(
     '%(asctime)s [%(name)s:%(levelname)s] %(message)s <%(pathname)s:%(lineno)d>')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 37482cf..d5ef42c 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -17,13 +17,15 @@ A common context for both workflow and operation
 """
 from uuid import uuid4
 
+import logging
+
 import jinja2
 
 from aria import logger
 from aria.storage import exceptions
 
 
-class BaseContext(logger.LoggerMixin):
+class BaseContext(object):
     """
     Base context object for workflow and operation
     """
@@ -34,6 +36,7 @@ class BaseContext(logger.LoggerMixin):
             service_instance_id,
             model_storage,
             resource_storage,
+            ctx_logger=None,
             workdir=None,
             **kwargs):
         super(BaseContext, self).__init__(**kwargs)
@@ -42,8 +45,21 @@ class BaseContext(logger.LoggerMixin):
         self._model = model_storage
         self._resource = resource_storage
         self._service_instance_id = service_instance_id
+        self._logger = self._init_logger(ctx_logger)
         self._workdir = workdir
 
+    def _init_logger(self, ctx_logger=None):
+        ctx_logger = ctx_logger or logging.getLogger('aria_ctx')
+
+        # A handler should be registered only once.
+        sqla_handler = logger.create_sqla_log_handler(**self._model.all_api_kwargs)
+        if sqla_handler not in ctx_logger.handlers:
+            ctx_logger.addHandler(sqla_handler)
+
+        ctx_logger.setLevel(logging.DEBUG)
+
+        return ctx_logger
+
     def __repr__(self):
         return (
             '{name}(name={self.name}, '
@@ -51,6 +67,10 @@ class BaseContext(logger.LoggerMixin):
             .format(name=self.__class__.__name__, self=self))
 
     @property
+    def logger(self):
+        return self._logger
+
+    @property
     def model(self):
         """
         Access to the model storage

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/workflows/core/engine.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index 55b4159..7148dd1 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -71,6 +71,11 @@ class Engine(logger.LoggerMixin):
         except BaseException as e:
             events.on_failure_workflow_signal.send(self._workflow_context, exception=e)
             raise
+        finally:
+            # Each context creates its own handlers an assign them to the logger.
+            # This enables easy serialization. In order the handlers would not overlap, we
+            # need to clear them each execution.
+            self._workflow_context.logger.handlers = []
 
     def cancel_execution(self):
         """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index 560ac43..acc0828 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -324,7 +324,6 @@ def _main():
     # This is required for the instrumentation work properly.
     # See docstring of `remove_mutable_association_listener` for further details
     storage_type.remove_mutable_association_listener()
-
     with instrumentation.track_changes() as instrument:
         try:
             ctx = context_dict['context_cls'].deserialize_from_dict(**context_dict['context'])

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/__init__.py b/aria/storage/__init__.py
index eaadc7e..b76bdf2 100644
--- a/aria/storage/__init__.py
+++ b/aria/storage/__init__.py
@@ -42,12 +42,6 @@ from .core import (
     ModelStorage,
     ResourceStorage,
 )
-from .modeling import (
-    structure,
-    model,
-    model_base,
-    type
-)
 from . import (
     exceptions,
     api,

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index 0e189e6..7d70070 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -84,6 +84,12 @@ class Storage(LoggerMixin):
         self.logger.debug('{name} object is ready: {0!r}'.format(
             self, name=self.__class__.__name__))
 
+    @property
+    def all_api_kwargs(self):
+        kwargs = self._api_kwargs.copy()
+        kwargs.update(self._additional_api_kwargs)
+        return kwargs
+
     def __repr__(self):
         return '{name}(api={self.api})'.format(name=self.__class__.__name__, self=self)
 
@@ -121,9 +127,7 @@ class ResourceStorage(Storage):
         :param name:
         :return:
         """
-        kwargs = self._api_kwargs.copy()
-        kwargs.update(self._additional_api_kwargs)
-        self.registered[name] = self.api(name=name, **kwargs)
+        self.registered[name] = self.api(name=name, **self.all_api_kwargs)
         self.registered[name].create()
         self.logger.debug('setup {name} in storage {self!r}'.format(name=name, self=self))
 
@@ -148,9 +152,9 @@ class ModelStorage(Storage):
             self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
                                                                           self=self))
             return
-        kwargs = self._api_kwargs.copy()
-        kwargs.update(self._additional_api_kwargs)
-        self.registered[model_name] = self.api(name=model_name, model_cls=model_cls, **kwargs)
+        self.registered[model_name] = self.api(name=model_name,
+                                               model_cls=model_cls,
+                                               **self.all_api_kwargs)
         self.registered[model_name].create()
         self.logger.debug('setup {name} in storage {self!r}'.format(name=model_name, self=self))
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/model.py b/aria/storage/modeling/model.py
index 62b90b3..cf7d933 100644
--- a/aria/storage/modeling/model.py
+++ b/aria/storage/modeling/model.py
@@ -216,4 +216,8 @@ class Plugin(aria_declarative_base, orchestrator_elements.PluginBase):
 
 class Task(aria_declarative_base, orchestrator_elements.TaskBase):
     pass
+
+
+class Log(aria_declarative_base, orchestrator_elements.LogBase):
+    pass
 # endregion

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/modeling/orchestrator_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/orchestrator_elements.py b/aria/storage/modeling/orchestrator_elements.py
index 5f7a3f2..8efc147 100644
--- a/aria/storage/modeling/orchestrator_elements.py
+++ b/aria/storage/modeling/orchestrator_elements.py
@@ -466,3 +466,15 @@ class TaskBase(ModelMixin):
     @staticmethod
     def retry(message=None, retry_interval=None):
         raise TaskRetryException(message, retry_interval=retry_interval)
+
+
+class LogBase(ModelMixin):
+    __tablename__ = 'log'
+
+    logger = Column(String)
+    level = Column(String)
+    msg = Column(String)
+    created_at = Column(DateTime, index=True)
+
+    def __repr__(self):
+        return "<Log: {0} - {1}>".format(self.created_at, self.msg[:50])

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/mock/topology.py
----------------------------------------------------------------------
diff --git a/tests/mock/topology.py b/tests/mock/topology.py
index b04fb46..d3e8b7b 100644
--- a/tests/mock/topology.py
+++ b/tests/mock/topology.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from aria.storage import model
+from aria.storage.modeling import model
 
 from . import models
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index 3f39979..08d360c 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -38,8 +38,7 @@ global_test_holder = {}
 @pytest.fixture
 def ctx(tmpdir):
     context = mock.context.simple(
-        str(tmpdir.join('workdir')),
-        inmemory=True,
+        str(tmpdir),
         context_kwargs=dict(workdir=str(tmpdir.join('workdir')))
     )
     yield context
@@ -61,7 +60,7 @@ def test_node_operation_task_execution(ctx, executor):
     node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
     interface = mock.models.get_interface(
         operation_name,
-        operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__))
+        operation_kwargs=dict(implementation=op_path(basic_operation, module_path=__name__))
     )
     node.interfaces = [interface]
     ctx.model.node.update(node)
@@ -102,7 +101,7 @@ def test_relationship_operation_task_execution(ctx, executor):
 
     interface = mock.models.get_interface(
         operation_name=operation_name,
-        operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__)),
+        operation_kwargs=dict(implementation=op_path(basic_operation, module_path=__name__)),
         edge='source'
     )
 
@@ -210,8 +209,73 @@ def test_plugin_workdir(ctx, executor, tmpdir):
     assert expected_file.read() == content
 
 
+def test_operation_logging(ctx, executor):
+    operation_name = 'aria.interfaces.lifecycle.create'
+
+    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    interface = mock.models.get_interface(
+        operation_name,
+        operation_kwargs=dict(implementation=op_path(logged_operation, module_path=__name__))
+    )
+    node.interfaces = [interface]
+    ctx.model.node.update(node)
+
+    wf_start = 'wf_start'
+    wf_end = 'wf_end'
+
+    inputs = {
+        'op_start': 'op_start',
+        'op_end': 'op_end',
+    }
+
+    @workflow
+    def basic_workflow(graph, ctx, **_):
+        ctx.logger.info(wf_start)
+        graph.add_tasks(
+            api.task.OperationTask.node(
+                name=operation_name,
+                instance=node,
+                inputs=inputs
+            )
+        )
+        ctx.logger.debug(wf_end)
+
+    execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
+
+    op_start_log = ctx.model.log.list(filters=dict(msg=inputs['op_start']))
+    assert len(op_start_log) == 1
+    op_start_log = op_start_log[0]
+    assert op_start_log.level.lower() == 'info'
+
+    op_end_log = ctx.model.log.list(filters=dict(msg=inputs['op_end']))
+    assert len(op_end_log) == 1
+    op_end_log = op_end_log[0]
+    assert op_end_log.level.lower() == 'debug'
+
+    wf_start_log = ctx.model.log.list(filters=dict(msg=wf_start))
+    assert len(wf_start_log) == 1
+    wf_start_log = wf_start_log[0]
+    assert wf_start_log.level.lower() == 'info'
+
+    wf_end_log = ctx.model.log.list(filters=dict(msg=wf_end))
+    assert len(wf_end_log) == 1
+    wf_end_log = wf_end_log[0]
+    assert wf_end_log.level.lower() == 'debug'
+
+    assert (wf_start_log.created_at <
+            wf_end_log.created_at <
+            op_start_log.created_at <
+            op_end_log.created_at)
+
+
+@operation
+def logged_operation(ctx, **_):
+    ctx.logger.info(ctx.task.inputs['op_start'])
+    ctx.logger.debug(ctx.task.inputs['op_end'])
+
+
 @operation
-def my_operation(ctx, **_):
+def basic_operation(ctx, **_):
     global_test_holder[ctx.name] = ctx
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index 4278831..5323d01 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -12,8 +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.
-import os
-import platform
+
 from shutil import rmtree
 from tempfile import mkdtemp
 
@@ -23,19 +22,19 @@ from sqlalchemy import (
     Column,
     Text,
     Integer,
-    pool
+    pool,
+    MetaData
 )
 
 
-from aria.storage import (
+from aria.storage.modeling import (
     model,
-    type as aria_type,
     structure,
-    modeling
+    type as aria_type
 )
 
 
-class MockModel(model.aria_declarative_base, structure.ModelMixin): #pylint: disable=abstract-method
+class MockModel(structure.ModelMixin, model.aria_declarative_base): #pylint: disable=abstract-method
     __tablename__ = 'mock_model'
     model_dict = Column(aria_type.Dict)
     model_list = Column(aria_type.List)
@@ -58,14 +57,8 @@ def release_sqlite_storage(storage):
     :param storage:
     :return:
     """
-    mapis = storage.registered.values()
-
-    if mapis:
-        for session in set(mapi._session for mapi in mapis):
-            session.rollback()
-            session.close()
-        for engine in set(mapi._engine for mapi in mapis):
-            model.aria_declarative_base.metadata.drop_all(engine)
+    storage.all_api_kwargs['session'].close()
+    MetaData(bind=storage.all_api_kwargs['engine']).drop_all()
 
 
 def init_inmemory_model_storage():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
index 08d5ae0..7f0eb02 100644
--- a/tests/storage/test_instrumentation.py
+++ b/tests/storage/test_instrumentation.py
@@ -17,13 +17,15 @@ import pytest
 from sqlalchemy import Column, Text, Integer, event
 
 from aria.storage import (
-    structure,
     ModelStorage,
     sql_mapi,
     instrumentation,
     exceptions,
+)
+from aria.storage.modeling import (
+    model,
     type as aria_type,
-    model
+    structure,
 )
 from ..storage import release_sqlite_storage, init_inmemory_model_storage
 


[3/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model

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

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/type.py b/aria/storage/modeling/type.py
new file mode 100644
index 0000000..9e3de3d
--- /dev/null
+++ b/aria/storage/modeling/type.py
@@ -0,0 +1,302 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+from collections import namedtuple
+
+from sqlalchemy import (
+    TypeDecorator,
+    VARCHAR,
+    event
+)
+from sqlalchemy.ext import mutable
+
+from .. import exceptions
+
+
+class _MutableType(TypeDecorator):
+    """
+    Dict representation of type.
+    """
+    @property
+    def python_type(self):
+        raise NotImplementedError
+
+    def process_literal_param(self, value, dialect):
+        pass
+
+    impl = VARCHAR
+
+    def process_bind_param(self, value, dialect):
+        if value is not None:
+            value = json.dumps(value)
+        return value
+
+    def process_result_value(self, value, dialect):
+        if value is not None:
+            value = json.loads(value)
+        return value
+
+
+class Dict(_MutableType):
+    @property
+    def python_type(self):
+        return dict
+
+
+class List(_MutableType):
+    @property
+    def python_type(self):
+        return list
+
+
+class _StrictDictMixin(object):
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, dict):
+                    for k, v in value.items():
+                        cls._assert_strict_key(k)
+                        cls._assert_strict_value(v)
+                    return cls(value)
+                return mutable.MutableDict.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).__setitem__(key, value)
+
+    def setdefault(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).setdefault(key, value)
+
+    def update(self, *args, **kwargs):
+        for k, v in kwargs.items():
+            self._assert_strict_key(k)
+            self._assert_strict_value(v)
+        super(_StrictDictMixin, self).update(*args, **kwargs)
+
+    @classmethod
+    def _assert_strict_key(cls, key):
+        if cls._key_cls is not None and not isinstance(key, cls._key_cls):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._key_cls, type(key)
+            ))
+
+    @classmethod
+    def _assert_strict_value(cls, value):
+        if cls._value_cls is not None and not isinstance(value, cls._value_cls):
+            raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
+                cls._value_cls, type(value)
+            ))
+
+
+class _MutableDict(mutable.MutableDict):
+    """
+    Enables tracking for dict values.
+    """
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            return mutable.MutableDict.coerce(key, value)
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+
+class _StrictListMixin(object):
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, list):
+                    for item in value:
+                        cls._assert_item(item)
+                    return cls(value)
+                return mutable.MutableList.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, index, value):
+        """Detect list set events and emit change events."""
+        self._assert_item(value)
+        super(_StrictListMixin, self).__setitem__(index, value)
+
+    def append(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).append(item)
+
+    def extend(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).extend(item)
+
+    def insert(self, index, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).insert(index, item)
+
+    @classmethod
+    def _assert_item(cls, item):
+        if cls._item_cls is not None and not isinstance(item, cls._item_cls):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._item_cls, type(item)
+            ))
+
+
+class _MutableList(mutable.MutableList):
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            return mutable.MutableList.coerce(key, value)
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+_StrictDictID = namedtuple('_StrictDictID', 'key_cls, value_cls')
+_StrictValue = namedtuple('_StrictValue', 'type_cls, listener_cls')
+
+
+class _StrictDict(object):
+    """
+    This entire class functions as a factory for strict dicts and their listeners.
+    No type class, and no listener type class is created more than once. If a relevant type class
+    exists it is returned.
+    """
+    _strict_map = {}
+
+    def __call__(self, key_cls=None, value_cls=None):
+        strict_dict_map_key = _StrictDictID(key_cls=key_cls, value_cls=value_cls)
+        if strict_dict_map_key not in self._strict_map:
+            key_cls_name = getattr(key_cls, '__name__', str(key_cls))
+            value_cls_name = getattr(value_cls, '__name__', str(value_cls))
+            # Creating the type class itself. this class would be returned (used by the sqlalchemy
+            # Column).
+            strict_dict_cls = type(
+                'StrictDict_{0}_{1}'.format(key_cls_name, value_cls_name),
+                (Dict, ),
+                {}
+            )
+            # Creating the type listening class.
+            # The new class inherits from both the _MutableDict class and the _StrictDictMixin,
+            # while setting the necessary _key_cls and _value_cls as class attributes.
+            listener_cls = type(
+                'StrictMutableDict_{0}_{1}'.format(key_cls_name, value_cls_name),
+                (_StrictDictMixin, _MutableDict),
+                {'_key_cls': key_cls, '_value_cls': value_cls}
+            )
+            self._strict_map[strict_dict_map_key] = _StrictValue(type_cls=strict_dict_cls,
+                                                                 listener_cls=listener_cls)
+
+        return self._strict_map[strict_dict_map_key].type_cls
+
+StrictDict = _StrictDict()
+
+
+class _StrictList(object):
+    """
+    This entire class functions as a factory for strict lists and their listeners.
+    No type class, and no listener type class is created more than once. If a relevant type class
+    exists it is returned.
+    """
+    _strict_map = {}
+
+    def __call__(self, item_cls=None):
+
+        if item_cls not in self._strict_map:
+            item_cls_name = getattr(item_cls, '__name__', str(item_cls))
+            # Creating the type class itself. this class would be returned (used by the sqlalchemy
+            # Column).
+            strict_list_cls = type(
+                'StrictList_{0}'.format(item_cls_name),
+                (List, ),
+                {}
+            )
+            # Creating the type listening class.
+            # The new class inherits from both the _MutableList class and the _StrictListMixin,
+            # while setting the necessary _item_cls as class attribute.
+            listener_cls = type(
+                'StrictMutableList_{0}'.format(item_cls_name),
+                (_StrictListMixin, _MutableList),
+                {'_item_cls': item_cls}
+            )
+            self._strict_map[item_cls] = _StrictValue(type_cls=strict_list_cls,
+                                                      listener_cls=listener_cls)
+
+        return self._strict_map[item_cls].type_cls
+
+StrictList = _StrictList()
+
+
+def _mutable_association_listener(mapper, cls):
+    strict_dict_type_to_listener = \
+        dict((v.type_cls, v.listener_cls) for v in _StrictDict._strict_map.values())
+
+    strict_list_type_to_listener = \
+        dict((v.type_cls, v.listener_cls) for v in _StrictList._strict_map.values())
+
+    for prop in mapper.column_attrs:
+        column_type = prop.columns[0].type
+        # Dict Listeners
+        if type(column_type) in strict_dict_type_to_listener:                                       # pylint: disable=unidiomatic-typecheck
+            strict_dict_type_to_listener[type(column_type)].associate_with_attribute(
+                getattr(cls, prop.key))
+        elif isinstance(column_type, Dict):
+            _MutableDict.associate_with_attribute(getattr(cls, prop.key))
+
+        # List Listeners
+        if type(column_type) in strict_list_type_to_listener:                                       # pylint: disable=unidiomatic-typecheck
+            strict_list_type_to_listener[type(column_type)].associate_with_attribute(
+                getattr(cls, prop.key))
+        elif isinstance(column_type, List):
+            _MutableList.associate_with_attribute(getattr(cls, prop.key))
+_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
+
+
+def _register_mutable_association_listener():
+    event.listen(*_LISTENER_ARGS)
+
+
+def remove_mutable_association_listener():
+    """
+    Remove the event listener that associates ``Dict`` and ``List`` column types with
+    ``MutableDict`` and ``MutableList``, respectively.
+
+    This call must happen before any model instance is instantiated.
+    This is because once it does, that would trigger the listener we are trying to remove.
+    Once it is triggered, many other listeners will then be registered.
+    At that point, it is too late.
+
+    The reason this function exists is that the association listener, interferes with ARIA change
+    tracking instrumentation, so a way to disable it is required.
+
+    Note that the event listener this call removes is registered by default.
+    """
+    if event.contains(*_LISTENER_ARGS):
+        event.remove(*_LISTENER_ARGS)
+
+_register_mutable_association_listener()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/utils.py b/aria/storage/modeling/utils.py
new file mode 100644
index 0000000..75e34f5
--- /dev/null
+++ b/aria/storage/modeling/utils.py
@@ -0,0 +1,139 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from random import randrange
+
+from shortuuid import ShortUUID
+
+from ...utils.console import puts
+
+
+# UUID = ShortUUID() # default alphabet is base57, which is alphanumeric without visually ambiguous
+# characters; ID length is 22
+UUID = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123456789')  # alphanumeric; ID length is 25
+
+
+def generate_id_string(length=None):
+    """
+    A random string with a strong guarantee of universal uniqueness (uses UUID).
+
+    The default length is 25 characters.
+    """
+
+    the_id = UUID.uuid()
+    if length is not None:
+        the_id = the_id[:length]
+    return the_id
+
+
+def generate_hex_string():
+    """
+    A random string of 5 hex digits with no guarantee of universal uniqueness.
+    """
+
+    return '%05x' % randrange(16 ** 5)
+
+
+def validate_dict_values(context, the_dict):
+    if not the_dict:
+        return
+    validate_list_values(context, the_dict.itervalues())
+
+
+def validate_list_values(context, the_list):
+    if not the_list:
+        return
+    for value in the_list:
+        value.validate(context)
+
+
+def coerce_dict_values(context, container, the_dict, report_issues=False):
+    if not the_dict:
+        return
+    coerce_list_values(context, container, the_dict.itervalues(), report_issues)
+
+
+def coerce_list_values(context, container, the_list, report_issues=False):
+    if not the_list:
+        return
+    for value in the_list:
+        value.coerce_values(context, container, report_issues)
+
+
+def instantiate_dict(context, container, the_dict, from_dict):
+    if not from_dict:
+        return
+    for name, value in from_dict.iteritems():
+        value = value.instantiate(context, container)
+        if value is not None:
+            the_dict[name] = value
+
+
+def dump_list_values(context, the_list, name):
+    if not the_list:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for value in the_list:
+            value.dump(context)
+
+
+def dump_dict_values(context, the_dict, name):
+    if not the_dict:
+        return
+    dump_list_values(context, the_dict.itervalues(), name)
+
+
+def dump_parameters(context, parameters, name='Properties'):
+    if not parameters:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for parameter_name, parameter in parameters.iteritems():
+            if parameter.type_name is not None:
+                puts('%s = %s (%s)' % (context.style.property(parameter_name),
+                                       context.style.literal(parameter.value),
+                                       context.style.type(parameter.type_name)))
+            else:
+                puts('%s = %s' % (context.style.property(parameter_name),
+                                  context.style.literal(parameter.value)))
+            if parameter.description:
+                puts(context.style.meta(parameter.description))
+
+
+def dump_interfaces(context, interfaces, name='Interfaces'):
+    if not interfaces:
+        return
+    puts('%s:' % name)
+    with context.style.indent:
+        for interface in interfaces.itervalues():
+            interface.dump(context)
+
+
+def pluralize(noun):
+    if noun.endswith('s'):
+        return '{0}es'.format(noun)
+    elif noun.endswith('y'):
+        return '{0}ies'.format(noun[:-1])
+    else:
+        return '{0}s'.format(noun)
+
+
+class classproperty(object):                                                                        # pylint: disable=invalid-name
+    def __init__(self, f):
+        self._func = f
+
+    def __get__(self, instance, owner):
+        return self._func(owner)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/sql_mapi.py
----------------------------------------------------------------------
diff --git a/aria/storage/sql_mapi.py b/aria/storage/sql_mapi.py
index 9dae08a..b80ac8e 100644
--- a/aria/storage/sql_mapi.py
+++ b/aria/storage/sql_mapi.py
@@ -142,8 +142,12 @@ class SQLAlchemyModelAPI(api.ModelAPI):
     def _establish_connection(self):
         pass
 
-    def create(self, checkfirst=True, **kwargs):
+    def create(self, checkfirst=True, create_all=True, **kwargs):
         self.model_cls.__table__.create(self._engine, checkfirst=checkfirst)
+        if create_all:
+            # In order to create any models created dynamically (e.g. many-to-many helper tables are
+            # created at runtime).
+            self.model_cls.metadata.create_all(bind=self._engine, checkfirst=checkfirst)
 
     def drop(self):
         """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/structure.py b/aria/storage/structure.py
deleted file mode 100644
index fa592ac..0000000
--- a/aria/storage/structure.py
+++ /dev/null
@@ -1,190 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""
-Aria's storage.structures module
-Path: aria.storage.structures
-
-models module holds aria's models.
-
-classes:
-    * Field - represents a single field.
-    * IterField - represents an iterable field.
-    * PointerField - represents a single pointer field.
-    * IterPointerField - represents an iterable pointers field.
-    * Model - abstract model implementation.
-"""
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.ext import associationproxy
-from sqlalchemy import (
-    Column,
-    ForeignKey,
-    Integer,
-    Text
-)
-
-
-class ModelMixin(object):
-
-    @classmethod
-    def id_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def name_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def _get_cls_by_tablename(cls, tablename):
-        """Return class reference mapped to table.
-
-         :param tablename: String with name of table.
-         :return: Class reference or None.
-         """
-        if tablename in (cls.__name__, cls.__tablename__):
-            return cls
-
-        for table_cls in cls._decl_class_registry.values():
-            if tablename in (getattr(table_cls, '__name__', None),
-                             getattr(table_cls, '__tablename__', None)):
-                return table_cls
-
-    @classmethod
-    def foreign_key(cls, table, nullable=False):
-        """Return a ForeignKey object with the relevant
-
-        :param table: Unique id column in the parent table
-        :param nullable: Should the column be allowed to remain empty
-        """
-        table_cls = cls._get_cls_by_tablename(table.__tablename__)
-        foreign_key_str = '{tablename}.{unique_id}'.format(tablename=table_cls.__tablename__,
-                                                           unique_id=table_cls.id_column_name())
-        column = Column(ForeignKey(foreign_key_str, ondelete='CASCADE'),
-                        nullable=nullable)
-        column.__remote_table_name = table_cls.__name__
-        return column
-
-    @classmethod
-    def one_to_many_relationship(cls,
-                                 foreign_key_column,
-                                 backreference=None,
-                                 backref_kwargs=None,
-                                 **kwargs):
-        """Return a one-to-many SQL relationship object
-        Meant to be used from inside the *child* object
-
-        :param parent_class: Class of the parent table
-        :param cls: Class of the child table
-        :param foreign_key_column: The column of the foreign key (from the child table)
-        :param backreference: The name to give to the reference to the child (on the parent table)
-        """
-        backref_kwargs = backref_kwargs or {}
-        parent_table = cls._get_cls_by_tablename(
-            getattr(cls, foreign_key_column).__remote_table_name)
-        primaryjoin_str = '{parent_class_name}.{parent_unique_id} == ' \
-                          '{child_class.__name__}.{foreign_key_column}'\
-            .format(
-                parent_class_name=parent_table.__name__,
-                parent_unique_id=parent_table.id_column_name(),
-                child_class=cls,
-                foreign_key_column=foreign_key_column
-            )
-        return relationship(
-            parent_table.__name__,
-            primaryjoin=primaryjoin_str,
-            foreign_keys=[getattr(cls, foreign_key_column)],
-            # The following line make sure that when the *parent* is
-            # deleted, all its connected children are deleted as well
-            backref=backref(backreference or cls.__tablename__, cascade='all', **backref_kwargs),
-            **kwargs
-        )
-
-    @classmethod
-    def relationship_to_self(cls, local_column):
-
-        remote_side_str = '{cls.__name__}.{remote_column}'.format(
-            cls=cls,
-            remote_column=cls.id_column_name()
-        )
-        primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format(
-            remote_side_str=remote_side_str,
-            cls=cls,
-            local_column=local_column)
-        return relationship(cls.__name__,
-                            primaryjoin=primaryjoin_str,
-                            remote_side=remote_side_str,
-                            post_update=True)
-
-    def to_dict(self, fields=None, suppress_error=False):
-        """Return a dict representation of the model
-
-        :param suppress_error: If set to True, sets `None` to attributes that
-        it's unable to retrieve (e.g., if a relationship wasn't established
-        yet, and so it's impossible to access a property through it)
-        """
-        res = dict()
-        fields = fields or self.fields()
-        for field in fields:
-            try:
-                field_value = getattr(self, field)
-            except AttributeError:
-                if suppress_error:
-                    field_value = None
-                else:
-                    raise
-            if isinstance(field_value, list):
-                field_value = list(field_value)
-            elif isinstance(field_value, dict):
-                field_value = dict(field_value)
-            elif isinstance(field_value, ModelMixin):
-                field_value = field_value.to_dict()
-            res[field] = field_value
-
-        return res
-
-    @classmethod
-    def _association_proxies(cls):
-        for col, value in vars(cls).items():
-            if isinstance(value, associationproxy.AssociationProxy):
-                yield col
-
-    @classmethod
-    def fields(cls):
-        """Return the list of field names for this table
-
-        Mostly for backwards compatibility in the code (that uses `fields`)
-        """
-        fields = set(cls._association_proxies())
-        fields.update(cls.__table__.columns.keys())
-        return fields - set(getattr(cls, '_private_fields', []))
-
-    def __repr__(self):
-        return '<{__class__.__name__} id=`{id}`>'.format(
-            __class__=self.__class__,
-            id=getattr(self, self.name_column_name()))
-
-
-class ModelIDMixin(object):
-    id = Column(Integer, primary_key=True, autoincrement=True)
-    name = Column(Text, nullable=True, index=True)
-
-    @classmethod
-    def id_column_name(cls):
-        return 'id'
-
-    @classmethod
-    def name_column_name(cls):
-        return 'name'


[2/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model

Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/type.py b/aria/storage/type.py
deleted file mode 100644
index ac695b1..0000000
--- a/aria/storage/type.py
+++ /dev/null
@@ -1,299 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-from collections import namedtuple
-
-from sqlalchemy import (
-    TypeDecorator,
-    VARCHAR,
-    event
-)
-from sqlalchemy.ext import mutable
-
-from . import exceptions
-
-
-class _MutableType(TypeDecorator):
-    """
-    Dict representation of type.
-    """
-    @property
-    def python_type(self):
-        raise NotImplementedError
-
-    def process_literal_param(self, value, dialect):
-        pass
-
-    impl = VARCHAR
-
-    def process_bind_param(self, value, dialect):
-        if value is not None:
-            value = json.dumps(value)
-        return value
-
-    def process_result_value(self, value, dialect):
-        if value is not None:
-            value = json.loads(value)
-        return value
-
-
-class Dict(_MutableType):
-    @property
-    def python_type(self):
-        return dict
-
-
-class List(_MutableType):
-    @property
-    def python_type(self):
-        return list
-
-
-class _StrictDictMixin(object):
-
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            if not isinstance(value, cls):
-                if isinstance(value, dict):
-                    for k, v in value.items():
-                        cls._assert_strict_key(k)
-                        cls._assert_strict_value(v)
-                    return cls(value)
-                return mutable.MutableDict.coerce(key, value)
-            else:
-                return value
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-    def __setitem__(self, key, value):
-        self._assert_strict_key(key)
-        self._assert_strict_value(value)
-        super(_StrictDictMixin, self).__setitem__(key, value)
-
-    def setdefault(self, key, value):
-        self._assert_strict_key(key)
-        self._assert_strict_value(value)
-        super(_StrictDictMixin, self).setdefault(key, value)
-
-    def update(self, *args, **kwargs):
-        for k, v in kwargs.items():
-            self._assert_strict_key(k)
-            self._assert_strict_value(v)
-        super(_StrictDictMixin, self).update(*args, **kwargs)
-
-    @classmethod
-    def _assert_strict_key(cls, key):
-        if cls._key_cls is not None and not isinstance(key, cls._key_cls):
-            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
-                cls._key_cls, type(key)
-            ))
-
-    @classmethod
-    def _assert_strict_value(cls, value):
-        if cls._value_cls is not None and not isinstance(value, cls._value_cls):
-            raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
-                cls._value_cls, type(value)
-            ))
-
-
-class _MutableDict(mutable.MutableDict):
-    """
-    Enables tracking for dict values.
-    """
-
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            return mutable.MutableDict.coerce(key, value)
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-
-class _StrictListMixin(object):
-
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            if not isinstance(value, cls):
-                if isinstance(value, list):
-                    for item in value:
-                        cls._assert_item(item)
-                    return cls(value)
-                return mutable.MutableList.coerce(key, value)
-            else:
-                return value
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-    def __setitem__(self, index, value):
-        """Detect list set events and emit change events."""
-        self._assert_item(value)
-        super(_StrictListMixin, self).__setitem__(index, value)
-
-    def append(self, item):
-        self._assert_item(item)
-        super(_StrictListMixin, self).append(item)
-
-    def extend(self, item):
-        self._assert_item(item)
-        super(_StrictListMixin, self).extend(item)
-
-    def insert(self, index, item):
-        self._assert_item(item)
-        super(_StrictListMixin, self).insert(index, item)
-
-    @classmethod
-    def _assert_item(cls, item):
-        if cls._item_cls is not None and not isinstance(item, cls._item_cls):
-            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
-                cls._item_cls, type(item)
-            ))
-
-
-class _MutableList(mutable.MutableList):
-
-    @classmethod
-    def coerce(cls, key, value):
-        "Convert plain dictionaries to MutableDict."
-        try:
-            return mutable.MutableList.coerce(key, value)
-        except ValueError as e:
-            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-_StrictDictID = namedtuple('_StrictDictID', 'key_cls, value_cls')
-_StrictValue = namedtuple('_StrictValue', 'type_cls, listener_cls')
-
-
-class _StrictDict(object):
-    """
-    This entire class functions as a factory for strict dicts and their listeners.
-    No type class, and no listener type class is created more than once. If a relevant type class
-    exists it is returned.
-    """
-    _strict_map = {}
-
-    def __call__(self, key_cls=None, value_cls=None):
-        strict_dict_map_key = _StrictDictID(key_cls=key_cls, value_cls=value_cls)
-        if strict_dict_map_key not in self._strict_map:
-            # Creating the type class itself. this class would be returned (used by the sqlalchemy
-            # Column).
-            strict_dict_cls = type(
-                'StrictDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
-                (Dict, ),
-                {}
-            )
-            # Creating the type listening class.
-            # The new class inherits from both the _MutableDict class and the _StrictDictMixin,
-            # while setting the necessary _key_cls and _value_cls as class attributes.
-            listener_cls = type(
-                'StrictMutableDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
-                (_StrictDictMixin, _MutableDict),
-                {'_key_cls': key_cls, '_value_cls': value_cls}
-            )
-            self._strict_map[strict_dict_map_key] = _StrictValue(type_cls=strict_dict_cls,
-                                                                 listener_cls=listener_cls)
-
-        return self._strict_map[strict_dict_map_key].type_cls
-
-StrictDict = _StrictDict()
-
-
-class _StrictList(object):
-    """
-    This entire class functions as a factory for strict lists and their listeners.
-    No type class, and no listener type class is created more than once. If a relevant type class
-    exists it is returned.
-    """
-    _strict_map = {}
-
-    def __call__(self, item_cls=None):
-
-        if item_cls not in self._strict_map:
-            # Creating the type class itself. this class would be returned (used by the sqlalchemy
-            # Column).
-            strict_list_cls = type(
-                'StrictList_{0}'.format(item_cls.__name__),
-                (List, ),
-                {}
-            )
-            # Creating the type listening class.
-            # The new class inherits from both the _MutableList class and the _StrictListMixin,
-            # while setting the necessary _item_cls as class attribute.
-            listener_cls = type(
-                'StrictMutableList_{0}'.format(item_cls.__name__),
-                (_StrictListMixin, _MutableList),
-                {'_item_cls': item_cls}
-            )
-            self._strict_map[item_cls] = _StrictValue(type_cls=strict_list_cls,
-                                                      listener_cls=listener_cls)
-
-        return self._strict_map[item_cls].type_cls
-
-StrictList = _StrictList()
-
-
-def _mutable_association_listener(mapper, cls):
-    strict_dict_type_to_listener = \
-        dict((v.type_cls, v.listener_cls) for v in _StrictDict._strict_map.values())
-
-    strict_list_type_to_listener = \
-        dict((v.type_cls, v.listener_cls) for v in _StrictList._strict_map.values())
-
-    for prop in mapper.column_attrs:
-        column_type = prop.columns[0].type
-        # Dict Listeners
-        if type(column_type) in strict_dict_type_to_listener:                                       # pylint: disable=unidiomatic-typecheck
-            strict_dict_type_to_listener[type(column_type)].associate_with_attribute(
-                getattr(cls, prop.key))
-        elif isinstance(column_type, Dict):
-            _MutableDict.associate_with_attribute(getattr(cls, prop.key))
-
-        # List Listeners
-        if type(column_type) in strict_list_type_to_listener:                                       # pylint: disable=unidiomatic-typecheck
-            strict_list_type_to_listener[type(column_type)].associate_with_attribute(
-                getattr(cls, prop.key))
-        elif isinstance(column_type, List):
-            _MutableList.associate_with_attribute(getattr(cls, prop.key))
-_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
-
-
-def _register_mutable_association_listener():
-    event.listen(*_LISTENER_ARGS)
-
-
-def remove_mutable_association_listener():
-    """
-    Remove the event listener that associates ``Dict`` and ``List`` column types with
-    ``MutableDict`` and ``MutableList``, respectively.
-
-    This call must happen before any model instance is instantiated.
-    This is because once it does, that would trigger the listener we are trying to remove.
-    Once it is triggered, many other listeners will then be registered.
-    At that point, it is too late.
-
-    The reason this function exists is that the association listener, interferes with ARIA change
-    tracking instrumentation, so a way to disable it is required.
-
-    Note that the event listener this call removes is registered by default.
-    """
-    if event.contains(*_LISTENER_ARGS):
-        event.remove(*_LISTENER_ARGS)
-
-_register_mutable_association_listener()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage_initializer.py
----------------------------------------------------------------------
diff --git a/aria/storage_initializer.py b/aria/storage_initializer.py
new file mode 100644
index 0000000..aea5ec8
--- /dev/null
+++ b/aria/storage_initializer.py
@@ -0,0 +1,135 @@
+# 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 datetime import datetime
+from threading import RLock
+
+from .storage import model
+from .orchestrator import operation
+from .utils.formatting import safe_repr
+from .utils.console import puts, Colored
+
+
+def initialize_storage(context, model_storage, deployment_id):
+    blueprint = _create_blueprint(context)
+    model_storage.blueprint.put(blueprint)
+
+    deployment = _create_deployment(context, blueprint, deployment_id)
+    model_storage.deployment.put(deployment)
+
+    # Create nodes and node instances
+    for node_template in context.modeling.model.node_templates.values():
+        model_storage.node_template.put(node_template)
+
+        for a_node in context.modeling.instance.find_nodes(node_template.name):
+            node = _create_node_instance(deployment, node_template, a_node)
+            model_storage.node.put(node)
+
+    # Create relationships
+    for node_template in context.modeling.model.node_templates.values():
+        for index, requirement_template in enumerate(node_template.requirement_templates):
+            # We are currently limited only to requirements for specific node templates!
+            if requirement_template.target_node_template_name:
+                model_storage.requirement_template.put(requirement_template)
+
+                for node in context.modeling.instance.find_nodes(node_template.name):
+                    for relationship_model in node.relationships:
+                        if relationship_model.source_requirement_index == index:
+                            source_instance = \
+                                model_storage.node.get_by_name(node.id)
+                            target_instance = \
+                                model_storage.node.get_by_name(
+                                    relationship_model.target_node_id)
+                            relationship = \
+                                _create_relationship_instance(source_instance, target_instance)
+                            model_storage.relationship.put(relationship)
+
+
+def _create_blueprint(context):
+    now = datetime.utcnow()
+    main_file_name = unicode(context.presentation.location)
+    try:
+        name = context.modeling.model.metadata.values.get('template_name')
+    except AttributeError:
+        name = None
+
+    return model.ServiceTemplate(
+        plan={},
+        name=name or main_file_name,
+        description=context.modeling.model.description or '',
+        created_at=now,
+        updated_at=now,
+        main_file_name=main_file_name
+    )
+
+
+def _create_deployment(context, service_template, service_instance_id):
+    now = datetime.utcnow()
+    return model.ServiceInstance(
+        name='{0}_{1}'.format(service_template.name, service_instance_id),
+        service_template=service_template,
+        description=context.modeling.instance.description or '',
+        created_at=now,
+        updated_at=now,
+        workflows={},
+        permalink='',
+        policy_triggers={},
+        scaling_groups={}
+    )
+
+
+def _create_node_instance(service_instance, node, node_model):
+    return model.Node(
+        service_instance=service_instance,
+        name=node_model.id,
+        runtime_properties={},
+        version=None,
+        node_template=node,
+        state='',
+        scaling_groups=[]
+    )
+
+
+def _create_relationship_instance(source_instance, target_instance):
+    return model.Relationship(
+        source_node=source_instance,
+        target_node=target_instance
+    )
+
+
+_TERMINAL_LOCK = RLock()
+
+
+@operation
+def _dry_node(ctx, _plugin, _implementation, **kwargs):
+    with _TERMINAL_LOCK:
+        print '> node instance: %s' % Colored.red(ctx.node.name)
+        _dump_implementation(_plugin, _implementation)
+
+
+@operation
+def _dry_relationship(ctx, _plugin, _implementation, **kwargs):
+    with _TERMINAL_LOCK:
+        puts('> relationship instance: %s -> %s' % (
+            Colored.red(ctx.relationship.source_node_instance.name),
+            Colored.red(ctx.relationship.target_node_instance.name)))
+        _dump_implementation(_plugin, _implementation)
+
+
+def _dump_implementation(plugin, implementation):
+    if plugin:
+        print '  plugin: %s' % Colored.magenta(plugin)
+    if implementation:
+        print '  implementation: %s' % Colored.yellow(safe_repr(implementation))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/utils/application.py
----------------------------------------------------------------------
diff --git a/aria/utils/application.py b/aria/utils/application.py
index 113e054..2f40825 100644
--- a/aria/utils/application.py
+++ b/aria/utils/application.py
@@ -102,14 +102,14 @@ class StorageManager(LoggerMixin):
         assert hasattr(self.model_storage, 'blueprint')
 
         self.logger.debug('creating blueprint resource storage entry')
-        self.resource_storage.blueprint.upload(
+        self.resource_storage.service_template.upload(
             entry_id=self.blueprint_id,
             source=os.path.dirname(source))
         self.logger.debug('created blueprint resource storage entry')
 
         self.logger.debug('creating blueprint model storage entry')
         now = datetime.utcnow()
-        blueprint = self.model_storage.blueprint.model_cls(
+        blueprint = self.model_storage.service_template.model_cls(
             plan=self.blueprint_plan,
             id=self.blueprint_id,
             description=self.blueprint_plan.get('description'),
@@ -117,7 +117,7 @@ class StorageManager(LoggerMixin):
             updated_at=now,
             main_file_name=main_file_name,
         )
-        self.model_storage.blueprint.put(blueprint)
+        self.model_storage.service_template.put(blueprint)
         self.logger.debug('created blueprint model storage entry')
 
     def create_nodes_storage(self):
@@ -164,10 +164,10 @@ class StorageManager(LoggerMixin):
         self.logger.debug('creating deployment resource storage entry')
         temp_dir = tempfile.mkdtemp()
         try:
-            self.resource_storage.blueprint.download(
+            self.resource_storage.service_template.download(
                 entry_id=self.blueprint_id,
                 destination=temp_dir)
-            self.resource_storage.deployment.upload(
+            self.resource_storage.service_instance.upload(
                 entry_id=self.deployment_id,
                 source=temp_dir)
         finally:
@@ -176,7 +176,7 @@ class StorageManager(LoggerMixin):
 
         self.logger.debug('creating deployment model storage entry')
         now = datetime.utcnow()
-        deployment = self.model_storage.deployment.model_cls(
+        deployment = self.model_storage.service_instance.model_cls(
             id=self.deployment_id,
             blueprint_id=self.blueprint_id,
             description=self.deployment_plan['description'],
@@ -190,7 +190,7 @@ class StorageManager(LoggerMixin):
             created_at=now,
             updated_at=now
         )
-        self.model_storage.deployment.put(deployment)
+        self.model_storage.service_instance.put(deployment)
         self.logger.debug('created deployment model storage entry')
 
     def create_node_instances_storage(self):
@@ -207,24 +207,24 @@ class StorageManager(LoggerMixin):
             relationship_instances = []
 
             for index, relationship_instance in enumerate(node_instance['relationships']):
-                relationship_instance_model = self.model_storage.relationship_instance.model_cls(
+                relationship_instance_model = self.model_storage.relationship.model_cls(
                     relationship=node_model.relationships[index],
                     target_name=relationship_instance['target_name'],
                     type=relationship_instance['type'],
                     target_id=relationship_instance['target_id'])
                 relationship_instances.append(relationship_instance_model)
-                self.model_storage.relationship_instance.put(relationship_instance_model)
+                self.model_storage.relationship.put(relationship_instance_model)
 
-            node_instance_model = self.model_storage.node_instance.model_cls(
+            node_instance_model = self.model_storage.node.model_cls(
                 node=node_model,
                 id=node_instance['id'],
                 runtime_properties={},
-                state=self.model_storage.node_instance.model_cls.UNINITIALIZED,
+                state=self.model_storage.node.model_cls.UNINITIALIZED,
                 deployment_id=self.deployment_id,
                 version='1.0',
                 relationship_instances=relationship_instances)
 
-            self.model_storage.node_instance.put(node_instance_model)
+            self.model_storage.node.put(node_instance_model)
         self.logger.debug('created node-instances model storage entries')
 
     def create_plugin_storage(self, plugin_id, source):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
index 08da2f3..a252a7c 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
@@ -24,5 +24,5 @@ group_types:
     description: >-
       This is the default (root) TOSCA Group Type definition that all other TOSCA base Group Types derive from.
     interfaces:
-      standard:
+      Standard:
         type: tosca.interfaces.node.lifecycle.Standard

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/context.py
----------------------------------------------------------------------
diff --git a/tests/mock/context.py b/tests/mock/context.py
index cb040ae..cf1a32d 100644
--- a/tests/mock/context.py
+++ b/tests/mock/context.py
@@ -38,13 +38,13 @@ def simple(tmpdir, inmemory=False, context_kwargs=None):
         api_kwargs=dict(directory=os.path.join(tmpdir, 'resources'))
     )
 
-    deployment_id = create_simple_topology_two_nodes(model_storage)
+    service_instance_id = create_simple_topology_two_nodes(model_storage)
 
     final_kwargs = dict(
         name='simple_context',
         model_storage=model_storage,
         resource_storage=resource_storage,
-        deployment_id=deployment_id,
+        service_instance_id=service_instance_id,
         workflow_name=models.WORKFLOW_NAME,
         task_max_attempts=models.TASK_MAX_ATTEMPTS,
         task_retry_interval=models.TASK_RETRY_INTERVAL

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 8229038..047526a 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -15,8 +15,7 @@
 
 from datetime import datetime
 
-from aria.storage import model
-
+from aria.storage.modeling import model
 from . import operations
 
 DEPLOYMENT_NAME = 'test_deployment_id'
@@ -35,84 +34,78 @@ RELATIONSHIP_INSTANCE_NAME = 'relationship_instance'
 
 
 def get_dependency_node(deployment):
-    return model.Node(
+    return model.NodeTemplate(
         name=DEPENDENCY_NODE_NAME,
-        type='test_node_type',
+        type_name='test_node_type',
         type_hierarchy=[],
-        number_of_instances=1,
-        planned_number_of_instances=1,
-        deploy_number_of_instances=1,
-        properties={},
-        operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
-        min_number_of_instances=1,
-        max_number_of_instances=1,
-        deployment_fk=deployment.id
+        default_instances=1,
+        min_instances=1,
+        max_instances=1,
+        service_template=deployment.service_template,
     )
 
 
-def get_dependency_node_instance(dependency_node):
-    return model.NodeInstance(
+def get_dependency_node_instance(dependency_node, deployment):
+    return model.Node(
         name=DEPENDENCY_NODE_INSTANCE_NAME,
+        service_instance=deployment,
         runtime_properties={'ip': '1.1.1.1'},
         version=None,
-        node_fk=dependency_node.id,
+        node_template=dependency_node,
         state='',
         scaling_groups=[]
     )
 
 
-def get_relationship(source=None, target=None):
-    return model.Relationship(
-        source_node_fk=source.id,
-        target_node_fk=target.id,
-        source_interfaces={},
-        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        target_interfaces={},
-        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        type='rel_type',
-        type_hierarchy=[],
-        properties={},
-    )
+def get_relationship(target):
+    requirement_template = model.RequirementTemplate(target_node_template_name=target.name)
+    capability_template = model.CapabilityTemplate()
+
+    return requirement_template, capability_template
 
 
-def get_relationship_instance(source_instance, target_instance, relationship):
-    return model.RelationshipInstance(
-        relationship_fk=relationship.id,
-        target_node_instance_fk=target_instance.id,
-        source_node_instance_fk=source_instance.id,
+def get_relationship_instance(source_instance, target_instance):
+    return model.Relationship(
+        target_node=target_instance,
+        source_node=source_instance,
     )
 
 
-def get_dependent_node(deployment):
-    return model.Node(
+def get_dependent_node(deployment, requirement_template, capability_template):
+    operation_templates = [model.OperationTemplate(implementation=op,
+                                                   service_template=deployment.service_template)
+                           for op in operations.NODE_OPERATIONS]
+    interface_template = model.InterfaceTemplate(operation_templates=operation_templates)
+
+    return model.NodeTemplate(
         name=DEPENDENT_NODE_NAME,
-        deployment_fk=deployment.id,
-        type='test_node_type',
+        type_name='test_node_type',
         type_hierarchy=[],
-        number_of_instances=1,
-        planned_number_of_instances=1,
-        deploy_number_of_instances=1,
-        properties={},
-        operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
-        min_number_of_instances=1,
-        max_number_of_instances=1,
+        default_instances=1,
+        min_instances=1,
+        max_instances=1,
+        service_template=deployment.service_template,
+        interface_templates=[interface_template],
+        requirement_templates=[requirement_template],
+        capability_templates=[capability_template],
     )
 
 
-def get_dependent_node_instance(dependent_node):
-    return model.NodeInstance(
+def get_dependent_node_instance(dependent_node, deployment):
+    return model.Node(
         name=DEPENDENT_NODE_INSTANCE_NAME,
+        service_instance=deployment,
         runtime_properties={},
         version=None,
-        node_fk=dependent_node.id,
+        node_template=dependent_node,
         state='',
-        scaling_groups=[]
+        scaling_groups=[],
     )
 
 
 def get_blueprint():
     now = datetime.now()
-    return model.Blueprint(
+    return model.ServiceTemplate(
         plan={},
         name=BLUEPRINT_NAME,
         description=None,
@@ -124,7 +117,7 @@ def get_blueprint():
 
 def get_execution(deployment):
     return model.Execution(
-        deployment_fk=deployment.id,
+        service_instance=deployment,
         status=model.Execution.STARTED,
         workflow_name=WORKFLOW_NAME,
         started_at=datetime.utcnow(),
@@ -134,19 +127,16 @@ def get_execution(deployment):
 
 def get_deployment(blueprint):
     now = datetime.utcnow()
-    return model.Deployment(
+    return model.ServiceInstance(
         name=DEPLOYMENT_NAME,
-        blueprint_fk=blueprint.id,
+        service_template=blueprint,
         description='',
         created_at=now,
         updated_at=now,
         workflows={},
-        inputs={},
-        groups={},
         permalink='',
         policy_triggers={},
         policy_types={},
-        outputs={},
         scaling_groups={},
     )
 
@@ -165,3 +155,28 @@ def get_plugin(package_name='package', package_version='0.1'):
         uploaded_at=datetime.now(),
         wheels=[],
     )
+
+
+def get_interface_template(operation_name, operation_kwargs=None, interface_kwargs=None):
+    operation_template = model.OperationTemplate(
+        name=operation_name,
+        **(operation_kwargs or {})
+
+    )
+    return model.InterfaceTemplate(
+        operation_templates=[operation_template],
+        name=operation_name.rsplit('.', 1)[0],
+        **(interface_kwargs or {})
+    )
+
+
+def get_interface(operation_name,
+                  operation_kwargs=None,
+                  interface_kwargs=None,
+                  edge=None):
+    operation = model.Operation(name=operation_name, **(operation_kwargs or {}))
+    interface_name = operation_name.rsplit('.', 1)[0]
+    return model.Interface(operations=[operation],
+                           name=interface_name,
+                           edge=edge,
+                           **(interface_kwargs or {}))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/topology.py
----------------------------------------------------------------------
diff --git a/tests/mock/topology.py b/tests/mock/topology.py
index e219c33..b04fb46 100644
--- a/tests/mock/topology.py
+++ b/tests/mock/topology.py
@@ -13,84 +13,74 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from datetime import datetime
-
 from aria.storage import model
 
 from . import models
 
 
-def create_simple_topology_single_node(model_storage, deployment_id, create_operation):
-    now = datetime.utcnow()
-
-    blueprint = model.Blueprint(name='mock-blueprint',
-                                created_at=now,
-                                updated_at=now,
-                                plan={},
-                                main_file_name='mock-file')
-    model_storage.blueprint.put(blueprint)
-
-    deployment = model.Deployment(name='mock-deployment-%d' % deployment_id,
-                                  blueprint_fk=blueprint.id,
-                                  created_at=now,
-                                  updated_at=now)
-    model_storage.deployment.put(deployment)
-
-    node = model.Node(name='mock-node',
-                      type='tosca.nodes.Compute',
-                      operations={
-                          'tosca.interfaces.node.lifecycle.Standard.create': {
-                              'operation': create_operation,
-                              'inputs': {
-                                  'key': 'create',
-                                  'value': True}}},
-                      number_of_instances=1,
-                      planned_number_of_instances=1,
-                      deploy_number_of_instances=1,
-                      min_number_of_instances=1,
-                      max_number_of_instances=1,
-                      deployment_fk=deployment.id)
+def create_simple_topology_single_node(model_storage, create_operation):
+    service_template = models.get_blueprint()
+    model_storage.service_template.put(service_template)
+
+    service_instance = models.get_deployment(service_template)
+    model_storage.service_instance.put(service_instance)
+
+    node_template = models.get_dependency_node(service_instance)
+    node_template.interface_templates = [models.get_interface_template(
+        'tosca.interfaces.node.lifecycle.Standard.create',
+        operation_kwargs=dict(
+            implementation=create_operation,
+            inputs=[model.Parameter(name='key', str_value='create', type='str'),
+                    model.Parameter(name='value', str_value=str(True), type='bool')]
+        )
+    )]
+    model_storage.node_template.put(node_template)
+
+    node = models.get_dependency_node_instance(node_template, service_instance)
+    node.interfaces = [models.get_interface(
+        'tosca.interfaces.node.lifecycle.Standard.create',
+        operation_kwargs=dict(
+            implementation=create_operation,
+            inputs=[model.Parameter(name='key', str_value='create', type='str'),
+                    model.Parameter(name='value', str_value=str(True), type='bool')])
+    )]
     model_storage.node.put(node)
 
-    node_instance = model.NodeInstance(name='mock-node-instance',
-                                       state='',
-                                       node_fk=node.id)
-    model_storage.node_instance.put(node_instance)
-
 
 def create_simple_topology_two_nodes(model_storage):
     blueprint = models.get_blueprint()
-    model_storage.blueprint.put(blueprint)
+    model_storage.service_template.put(blueprint)
     deployment = models.get_deployment(blueprint)
-    model_storage.deployment.put(deployment)
+    model_storage.service_instance.put(deployment)
 
     #################################################################################
     # Creating a simple deployment with node -> node as a graph
 
     dependency_node = models.get_dependency_node(deployment)
-    model_storage.node.put(dependency_node)
-    storage_dependency_node = model_storage.node.get(dependency_node.id)
+    model_storage.node_template.put(dependency_node)
+    storage_dependency_node = model_storage.node_template.get(dependency_node.id)
+
+    dependency_node_instance = models.get_dependency_node_instance(storage_dependency_node,
+                                                                   deployment)
+    model_storage.node.put(dependency_node_instance)
+    storage_dependency_node_instance = model_storage.node.get(dependency_node_instance.id)
 
-    dependency_node_instance = models.get_dependency_node_instance(storage_dependency_node)
-    model_storage.node_instance.put(dependency_node_instance)
-    storage_dependency_node_instance = model_storage.node_instance.get(dependency_node_instance.id)
+    req_template, cap_template = models.get_relationship(storage_dependency_node)
+    model_storage.requirement_template.put(req_template)
+    model_storage.capability_template.put(cap_template)
 
-    dependent_node = models.get_dependent_node(deployment)
-    model_storage.node.put(dependent_node)
-    storage_dependent_node = model_storage.node.get(dependent_node.id)
+    dependent_node = models.get_dependent_node(deployment, req_template, cap_template)
+    model_storage.node_template.put(dependent_node)
+    storage_dependent_node = model_storage.node_template.get(dependent_node.id)
 
-    dependent_node_instance = models.get_dependent_node_instance(storage_dependent_node)
-    model_storage.node_instance.put(dependent_node_instance)
-    storage_dependent_node_instance = model_storage.node_instance.get(dependent_node_instance.id)
+    dependent_node_instance = models.get_dependent_node_instance(storage_dependent_node, deployment)
+    model_storage.node.put(dependent_node_instance)
+    storage_dependent_node_instance = model_storage.node.get(dependent_node_instance.id)
 
-    relationship = models.get_relationship(storage_dependent_node, storage_dependency_node)
-    model_storage.relationship.put(relationship)
-    storage_relationship = model_storage.relationship.get(relationship.id)
     relationship_instance = models.get_relationship_instance(
-        relationship=storage_relationship,
         target_instance=storage_dependency_node_instance,
         source_instance=storage_dependent_node_instance
     )
-    model_storage.relationship_instance.put(relationship_instance)
+    model_storage.relationship.put(relationship_instance)
 
     return deployment.id

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index e8c7cca..3f39979 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -58,92 +58,94 @@ def executor():
 def test_node_operation_task_execution(ctx, executor):
     operation_name = 'aria.interfaces.lifecycle.create'
 
-    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-    node.operations[operation_name] = {
-        'operation': op_path(my_operation, module_path=__name__)
-
-    }
+    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    interface = mock.models.get_interface(
+        operation_name,
+        operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__))
+    )
+    node.interfaces = [interface]
     ctx.model.node.update(node)
-    node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-
     inputs = {'putput': True}
 
     @workflow
     def basic_workflow(graph, **_):
         graph.add_tasks(
-            api.task.OperationTask.node_instance(
+            api.task.OperationTask.node(
                 name=operation_name,
-                instance=node_instance,
+                instance=node,
                 inputs=inputs
             )
         )
 
     execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
 
-    operation_context = global_test_holder[op_name(node_instance, operation_name)]
+    operation_context = global_test_holder[op_name(node, operation_name)]
 
     assert isinstance(operation_context, context.operation.NodeOperationContext)
 
     # Task bases assertions
-    assert operation_context.task.actor == node_instance
-    assert operation_context.task.name == op_name(node_instance, operation_name)
-    assert operation_context.task.operation_mapping == node.operations[operation_name]['operation']
+    assert operation_context.task.actor == node
+    assert operation_context.task.name == op_name(node, operation_name)
+    operations = interface.operations.filter_by(name=operation_name)                                # pylint: disable=no-member
+    assert operations.count() == 1
+    assert operation_context.task.implementation == operations[0].implementation
     assert operation_context.task.inputs == inputs
 
     # Context based attributes (sugaring)
-    assert operation_context.node == node_instance.node
-    assert operation_context.node_instance == node_instance
+    assert operation_context.node_template == node.node_template
+    assert operation_context.node == node
 
 
 def test_relationship_operation_task_execution(ctx, executor):
-    operation_name = 'aria.interfaces.relationship_lifecycle.postconfigure'
+    operation_name = 'aria.interfaces.relationship_lifecycle.post_configure'
     relationship = ctx.model.relationship.list()[0]
-    relationship.source_operations[operation_name] = {
-        'operation': op_path(my_operation, module_path=__name__)
-    }
-    ctx.model.relationship.update(relationship)
-    relationship_instance = ctx.model.relationship_instance.list()[0]
 
-    dependency_node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-    dependency_node_instance = \
-        ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-    dependent_node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
-    dependent_node_instance = \
-        ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+    interface = mock.models.get_interface(
+        operation_name=operation_name,
+        operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__)),
+        edge='source'
+    )
 
+    relationship.interfaces = [interface]
+    ctx.model.relationship.update(relationship)
     inputs = {'putput': True}
 
     @workflow
     def basic_workflow(graph, **_):
         graph.add_tasks(
-            api.task.OperationTask.relationship_instance(
-                instance=relationship_instance,
+            api.task.OperationTask.relationship(
+                instance=relationship,
                 name=operation_name,
-                operation_end=api.task.OperationTask.SOURCE_OPERATION,
-                inputs=inputs
+                inputs=inputs,
+                edge='source'
             )
         )
 
     execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
 
-    operation_context = global_test_holder[op_name(relationship_instance, operation_name)]
+    operation_context = global_test_holder[op_name(relationship,
+                                                   operation_name)]
 
     assert isinstance(operation_context, context.operation.RelationshipOperationContext)
 
     # Task bases assertions
-    assert operation_context.task.actor == relationship_instance
-    assert operation_context.task.name == op_name(relationship_instance, operation_name)
-    assert operation_context.task.operation_mapping == \
-           relationship.source_operations[operation_name]['operation']
+    assert operation_context.task.actor == relationship
+    assert operation_context.task.name.startswith(operation_name)
+    operation = interface.operations.filter_by(name=operation_name)                                 # pylint: disable=no-member
+    assert operation_context.task.implementation == operation.all()[0].implementation
     assert operation_context.task.inputs == inputs
 
     # Context based attributes (sugaring)
+    dependency_node_template = ctx.model.node_template.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+    dependency_node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    dependent_node_template = ctx.model.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+    dependent_node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+
+    assert operation_context.target_node_template == dependency_node_template
     assert operation_context.target_node == dependency_node
-    assert operation_context.target_node_instance == dependency_node_instance
     assert operation_context.relationship == relationship
-    assert operation_context.relationship_instance == relationship_instance
+    assert operation_context.source_node_template == dependent_node_template
     assert operation_context.source_node == dependent_node
-    assert operation_context.source_node_instance == dependent_node_instance
 
 
 def test_invalid_task_operation_id(ctx, executor):
@@ -155,39 +157,42 @@ def test_invalid_task_operation_id(ctx, executor):
     :return:
     """
     operation_name = 'aria.interfaces.lifecycle.create'
-    other_node_instance, node_instance = ctx.model.node_instance.list()
-    assert other_node_instance.id == 1
-    assert node_instance.id == 2
+    other_node, node = ctx.model.node.list()
+    assert other_node.id == 1
+    assert node.id == 2
 
-    node = node_instance.node
-    node.operations[operation_name] = {
-        'operation': op_path(get_node_instance_id, module_path=__name__)
-
-    }
+    interface = mock.models.get_interface(
+        operation_name=operation_name,
+        operation_kwargs=dict(implementation=op_path(get_node_instance_id, module_path=__name__))
+    )
+    node.interfaces = [interface]
     ctx.model.node.update(node)
 
     @workflow
     def basic_workflow(graph, **_):
         graph.add_tasks(
-            api.task.OperationTask.node_instance(name=operation_name, instance=node_instance)
+            api.task.OperationTask.node(name=operation_name, instance=node)
         )
 
     execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
 
-    op_node_instance_id = global_test_holder[op_name(node_instance, operation_name)]
-    assert op_node_instance_id == node_instance.id
-    assert op_node_instance_id != other_node_instance.id
+    op_node_instance_id = global_test_holder[op_name(node, operation_name)]
+    assert op_node_instance_id == node.id
+    assert op_node_instance_id != other_node.id
 
 
 def test_plugin_workdir(ctx, executor, tmpdir):
     op = 'test.op'
     plugin_name = 'mock_plugin'
-    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-    node.operations[op] = {'operation': '{0}.{1}'.format(__name__, _test_plugin_workdir.__name__),
-                           'plugin': plugin_name}
+    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    node.interfaces = [mock.models.get_interface(
+        op,
+        operation_kwargs=dict(
+            implementation='{0}.{1}'.format(__name__, _test_plugin_workdir.__name__),
+            plugin=plugin_name)
+    )]
     node.plugins = [{'name': plugin_name}]
     ctx.model.node.update(node)
-    node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
 
     filename = 'test_file'
     content = 'file content'
@@ -195,11 +200,13 @@ def test_plugin_workdir(ctx, executor, tmpdir):
 
     @workflow
     def basic_workflow(graph, **_):
-        graph.add_tasks(api.task.OperationTask.node_instance(
-            name=op, instance=node_instance, inputs=inputs))
+        graph.add_tasks(api.task.OperationTask.node(
+            name=op, instance=node, inputs=inputs))
 
     execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
-    expected_file = tmpdir.join('workdir', 'plugins', str(ctx.deployment.id), plugin_name, filename)
+    expected_file = tmpdir.join('workdir', 'plugins', str(ctx.service_instance.id),
+                                plugin_name,
+                                filename)
     assert expected_file.read() == content
 
 
@@ -210,7 +217,7 @@ def my_operation(ctx, **_):
 
 @operation
 def get_node_instance_id(ctx, **_):
-    global_test_holder[ctx.name] = ctx.node_instance.id
+    global_test_holder[ctx.name] = ctx.node.id
 
 
 @operation

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_resource_render.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_resource_render.py b/tests/orchestrator/context/test_resource_render.py
index ded18c8..aef439c 100644
--- a/tests/orchestrator/context/test_resource_render.py
+++ b/tests/orchestrator/context/test_resource_render.py
@@ -17,7 +17,7 @@ import pytest
 
 from tests import mock, storage
 
-_IMPLICIT_CTX_TEMPLATE = '{{ctx.deployment.name}}'
+_IMPLICIT_CTX_TEMPLATE = '{{ctx.service_instance.name}}'
 _IMPLICIT_CTX_TEMPLATE_PATH = 'implicit-ctx.template'
 _VARIABLES_TEMPLATE = '{{variable}}'
 _VARIABLES_TEMPLATE_PATH = 'variables.template'

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_serialize.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_serialize.py b/tests/orchestrator/context/test_serialize.py
index ee123a7..1fdcb1a 100644
--- a/tests/orchestrator/context/test_serialize.py
+++ b/tests/orchestrator/context/test_serialize.py
@@ -40,16 +40,16 @@ def test_serialize_operation_context(context, executor, tmpdir):
     eng.execute()
 
 
-
 @workflow
 def _mock_workflow(ctx, graph):
-    op = 'test.op'
+    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
     plugin_name = 'mock_plugin'
-    node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-    node = node_instance.node
-    node.operations[op] = {'operation': _operation_mapping(), 'plugin': plugin_name}
+    node.interfaces = [mock.models.get_interface(
+        'test.op',
+        operation_kwargs=dict(implementation=_operation_mapping(), plugin=plugin_name)
+    )]
     node.plugins = [{'name': plugin_name}]
-    task = api.task.OperationTask.node_instance(instance=node_instance, name=op)
+    task = api.task.OperationTask.node(instance=node, name='test.op')
     graph.add_tasks(task)
     return graph
 
@@ -59,14 +59,14 @@ def _mock_operation(ctx):
     # We test several things in this operation
     # ctx.task, ctx.node, etc... tell us that the model storage was properly re-created
     # a correct ctx.task.operation_mapping tells us we kept the correct task_id
-    assert ctx.task.operation_mapping == _operation_mapping()
+    assert ctx.task.implementation == _operation_mapping()
     # a correct ctx.node.name tells us we kept the correct actor_id
-    assert ctx.node.name == mock.models.DEPENDENCY_NODE_NAME
+    assert ctx.node.name == mock.models.DEPENDENCY_NODE_INSTANCE_NAME
     # a correct ctx.name tells us we kept the correct name
     assert ctx.name is not None
     assert ctx.name == ctx.task.name
     # a correct ctx.deployment.name tells us we kept the correct deployment_id
-    assert ctx.deployment.name == mock.models.DEPLOYMENT_NAME
+    assert ctx.service_instance.name == mock.models.DEPLOYMENT_NAME
     # Here we test that the resource storage was properly re-created
     test_file_content = ctx.resource.blueprint.read(TEST_FILE_ENTRY_ID, TEST_FILE_NAME)
     assert test_file_content == TEST_FILE_CONTENT

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_toolbelt.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_toolbelt.py b/tests/orchestrator/context/test_toolbelt.py
index beb5730..28bd3d3 100644
--- a/tests/orchestrator/context/test_toolbelt.py
+++ b/tests/orchestrator/context/test_toolbelt.py
@@ -48,45 +48,46 @@ def executor():
 
 
 def _get_elements(workflow_context):
-    dependency_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-    dependency_node.host = dependency_node
-    workflow_context.model.node.update(dependency_node)
+    dependency_node_template = workflow_context.model.node_template.get_by_name(
+        mock.models.DEPENDENCY_NODE_NAME)
+    dependency_node_template.host = dependency_node_template
+    workflow_context.model.node.update(dependency_node_template)
 
-    dependency_node_instance = workflow_context.model.node_instance.get_by_name(
+    dependency_node = workflow_context.model.node.get_by_name(
         mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-    dependency_node_instance.host_fk = dependency_node_instance.id
-    workflow_context.model.node_instance.update(dependency_node_instance)
+    dependency_node.host_fk = dependency_node.id
+    workflow_context.model.node.update(dependency_node)
 
-    dependent_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
-    dependent_node.host_fk = dependency_node.id
-    workflow_context.model.node.update(dependent_node)
+    dependent_node_template = workflow_context.model.node_template.get_by_name(
+        mock.models.DEPENDENT_NODE_NAME)
+    dependent_node_template.host = dependency_node_template
+    workflow_context.model.node_template.update(dependent_node_template)
 
-    dependent_node_instance = workflow_context.model.node_instance.get_by_name(
+    dependent_node = workflow_context.model.node.get_by_name(
         mock.models.DEPENDENT_NODE_INSTANCE_NAME)
-    dependent_node_instance.host_fk = dependent_node_instance.id
-    workflow_context.model.node_instance.update(dependent_node_instance)
+    dependent_node.host = dependent_node
+    workflow_context.model.node.update(dependent_node)
 
     relationship = workflow_context.model.relationship.list()[0]
-    relationship_instance = workflow_context.model.relationship_instance.list()[0]
-    return dependency_node, dependency_node_instance, dependent_node, dependent_node_instance, \
-        relationship, relationship_instance
+    return dependency_node_template, dependency_node, dependent_node_template, dependent_node, \
+        relationship
 
 
 def test_host_ip(workflow_context, executor):
     operation_name = 'aria.interfaces.lifecycle.create'
-    dependency_node, dependency_node_instance, _, _, _, _ = _get_elements(workflow_context)
-    dependency_node.operations[operation_name] = {
-        'operation': op_path(host_ip, module_path=__name__)
-
-    }
-    workflow_context.model.node.put(dependency_node)
+    _, dependency_node, _, _, _ = _get_elements(workflow_context)
+    dependency_node.interfaces = [mock.models.get_interface(
+        operation_name,
+        operation_kwargs=dict(implementation=op_path(host_ip, module_path=__name__))
+    )]
+    workflow_context.model.node.update(dependency_node)
     inputs = {'putput': True}
 
     @workflow
     def basic_workflow(graph, **_):
         graph.add_tasks(
-            api.task.OperationTask.node_instance(
-                instance=dependency_node_instance,
+            api.task.OperationTask.node(
+                instance=dependency_node,
                 name=operation_name,
                 inputs=inputs
             )
@@ -94,35 +95,37 @@ def test_host_ip(workflow_context, executor):
 
     execute(workflow_func=basic_workflow, workflow_context=workflow_context, executor=executor)
 
-    assert global_test_holder.get('host_ip') == \
-           dependency_node_instance.runtime_properties.get('ip')
+    assert global_test_holder.get('host_ip') == dependency_node.runtime_properties.get('ip')
 
 
 def test_relationship_tool_belt(workflow_context, executor):
-    operation_name = 'aria.interfaces.relationship_lifecycle.postconfigure'
-    _, _, _, _, relationship, relationship_instance = \
-        _get_elements(workflow_context)
-    relationship.source_operations[operation_name] = {
-        'operation': op_path(relationship_operation, module_path=__name__)
-    }
-    workflow_context.model.relationship.put(relationship)
+    operation_name = 'aria.interfaces.relationship_lifecycle.post_configure'
+    _, _, _, _, relationship = _get_elements(workflow_context)
+    relationship.interfaces = [
+        mock.models.get_interface(
+            operation_name,
+            operation_kwargs=dict(
+                implementation=op_path(relationship_operation, module_path=__name__)),
+            edge='source')
+    ]
+    workflow_context.model.relationship.update(relationship)
 
     inputs = {'putput': True}
 
     @workflow
     def basic_workflow(graph, **_):
         graph.add_tasks(
-            api.task.OperationTask.relationship_instance(
-                instance=relationship_instance,
+            api.task.OperationTask.relationship(
+                instance=relationship,
                 name=operation_name,
-                operation_end=api.task.OperationTask.SOURCE_OPERATION,
+                edge='source',
                 inputs=inputs
             )
         )
 
     execute(workflow_func=basic_workflow, workflow_context=workflow_context, executor=executor)
 
-    assert isinstance(global_test_holder.get(op_name(relationship_instance, operation_name)),
+    assert isinstance(global_test_holder.get(op_name(relationship, operation_name)),
                       RelationshipToolBelt)
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_workflow.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_workflow.py b/tests/orchestrator/context/test_workflow.py
index bb54037..4e8eca4 100644
--- a/tests/orchestrator/context/test_workflow.py
+++ b/tests/orchestrator/context/test_workflow.py
@@ -29,9 +29,11 @@ class TestWorkflowContext(object):
     def test_execution_creation_on_workflow_context_creation(self, storage):
         ctx = self._create_ctx(storage)
         execution = storage.execution.get(ctx.execution.id)             # pylint: disable=no-member
-        assert execution.deployment == storage.deployment.get_by_name(models.DEPLOYMENT_NAME)
+        assert execution.service_instance == storage.service_instance.get_by_name(
+            models.DEPLOYMENT_NAME)
         assert execution.workflow_name == models.WORKFLOW_NAME
-        assert execution.blueprint == storage.blueprint.get_by_name(models.BLUEPRINT_NAME)
+        assert execution.service_template == storage.service_template.get_by_name(
+            models.BLUEPRINT_NAME)
         assert execution.status == storage.execution.model_cls.PENDING
         assert execution.parameters == {}
         assert execution.created_at <= datetime.utcnow()
@@ -51,7 +53,7 @@ class TestWorkflowContext(object):
             name='simple_context',
             model_storage=storage,
             resource_storage=None,
-            deployment_id=storage.deployment.get_by_name(models.DEPLOYMENT_NAME).id,
+            service_instance_id=storage.service_instance.get_by_name(models.DEPLOYMENT_NAME).id,
             workflow_name=models.WORKFLOW_NAME,
             task_max_attempts=models.TASK_MAX_ATTEMPTS,
             task_retry_interval=models.TASK_RETRY_INTERVAL
@@ -62,8 +64,8 @@ class TestWorkflowContext(object):
 def storage():
     workflow_storage = application_model_storage(
         sql_mapi.SQLAlchemyModelAPI, initiator=test_storage.init_inmemory_model_storage)
-    workflow_storage.blueprint.put(models.get_blueprint())
-    blueprint = workflow_storage.blueprint.get_by_name(models.BLUEPRINT_NAME)
-    workflow_storage.deployment.put(models.get_deployment(blueprint))
+    workflow_storage.service_template.put(models.get_blueprint())
+    blueprint = workflow_storage.service_template.get_by_name(models.BLUEPRINT_NAME)
+    workflow_storage.service_instance.put(models.get_deployment(blueprint))
     yield workflow_storage
     test_storage.release_sqlite_storage(workflow_storage)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/execution_plugin/test_local.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_local.py b/tests/orchestrator/execution_plugin/test_local.py
index 86f2aa7..9e9540f 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -42,10 +42,10 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties map.key value
+            ctx node runtime-properties map.key value
             ''',
             windows_script='''
-            ctx node-instance runtime-properties map.key value
+            ctx node runtime-properties map.key value
         ''')
         props = self._run(
             executor, workflow_context,
@@ -56,12 +56,12 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties map.key1 $key1
-            ctx node-instance runtime-properties map.key2 $key2
+            ctx node runtime-properties map.key1 $key1
+            ctx node runtime-properties map.key2 $key2
             ''',
             windows_script='''
-            ctx node-instance runtime-properties map.key1 %key1%
-            ctx node-instance runtime-properties map.key2 %key2%
+            ctx node runtime-properties map.key1 %key1%
+            ctx node runtime-properties map.key2 %key2%
         ''')
         props = self._run(
             executor, workflow_context,
@@ -80,10 +80,10 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties map.cwd $PWD
+            ctx node runtime-properties map.cwd $PWD
             ''',
             windows_script='''
-            ctx node-instance runtime-properties map.cwd %CD%
+            ctx node runtime-properties map.cwd %CD%
             ''')
         tmpdir = str(tmpdir)
         props = self._run(
@@ -96,7 +96,7 @@ class TestLocalRunScript(object):
         assert p_map['cwd'] == tmpdir
 
     def test_process_command_prefix(self, executor, workflow_context, tmpdir):
-        use_ctx = 'ctx node-instance runtime-properties map.key value'
+        use_ctx = 'ctx node runtime-properties map.key value'
         python_script = ['import subprocess',
                          'subprocess.Popen("{0}".split(' ')).communicate()[0]'.format(use_ctx)]
         python_script = '\n'.join(python_script)
@@ -120,12 +120,12 @@ class TestLocalRunScript(object):
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties map.arg1 "$1"
-            ctx node-instance runtime-properties map.arg2 $2
+            ctx node runtime-properties map.arg1 "$1"
+            ctx node runtime-properties map.arg2 $2
             ''',
             windows_script='''
-            ctx node-instance runtime-properties map.arg1 %1
-            ctx node-instance runtime-properties map.arg2 %2
+            ctx node runtime-properties map.arg1 %1
+            ctx node runtime-properties map.arg2 %2
             ''')
         props = self._run(
             executor, workflow_context,
@@ -186,7 +186,7 @@ class TestLocalRunScript(object):
         script = '''
 from aria.orchestrator.execution_plugin import ctx, inputs
 if __name__ == '__main__':
-    ctx.node_instance.runtime_properties['key'] = inputs['key']
+    ctx.node.runtime_properties['key'] = inputs['key']
 '''
         suffix = '.py'
         script_path = self._create_script(
@@ -208,10 +208,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties key "${input_as_env_var}"
+            ctx node runtime-properties key "${input_as_env_var}"
             ''',
             windows_script='''
-            ctx node-instance runtime-properties key "%input_as_env_var%"
+            ctx node runtime-properties key "%input_as_env_var%"
         ''')
         props = self._run(
             executor, workflow_context,
@@ -226,10 +226,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties key "${input_as_env_var}"
+            ctx node runtime-properties key "${input_as_env_var}"
             ''',
             windows_script='''
-            ctx node-instance runtime-properties key "%input_as_env_var%"
+            ctx node runtime-properties key "%input_as_env_var%"
         ''')
 
         props = self._run(
@@ -248,10 +248,10 @@ if __name__ == '__main__':
         script_path = self._create_script(
             tmpdir,
             linux_script='''#! /bin/bash -e
-            ctx node-instance runtime-properties nonexistent
+            ctx node runtime-properties nonexistent
             ''',
             windows_script='''
-            ctx node-instance runtime-properties nonexistent
+            ctx node runtime-properties nonexistent
         ''')
         exception = self._run_and_get_task_exception(
             executor, workflow_context,
@@ -462,7 +462,7 @@ if __name__ == '__main__':
         script_path = os.path.basename(local_script_path) if local_script_path else None
         if script_path:
             workflow_context.resource.deployment.upload(
-                entry_id=str(workflow_context.deployment.id),
+                entry_id=str(workflow_context.service_instance.id),
                 source=local_script_path,
                 path=script_path)
 
@@ -476,13 +476,18 @@ if __name__ == '__main__':
         @workflow
         def mock_workflow(ctx, graph):
             op = 'test.op'
-            node_instance = ctx.model.node_instance.get_by_name(
-                mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-            node_instance.node.operations[op] = {
-                'operation': '{0}.{1}'.format(operations.__name__,
-                                              operations.run_script_locally.__name__)}
-            graph.add_tasks(api.task.OperationTask.node_instance(
-                instance=node_instance,
+            node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+            node.interfaces = [mock.models.get_interface(
+                op,
+                operation_kwargs=dict(implementation='{0}.{1}'.format(
+                    operations.__name__,
+                    operations.run_script_locally.__name__))
+            )]
+            # node.operations[op] = {
+            #     'operation': '{0}.{1}'.format(operations.__name__,
+            #                                   operations.run_script_locally.__name__)}
+            graph.add_tasks(api.task.OperationTask.node(
+                instance=node,
                 name=op,
                 inputs=inputs))
             return graph
@@ -492,7 +497,7 @@ if __name__ == '__main__':
             workflow_context=workflow_context,
             tasks_graph=tasks_graph)
         eng.execute()
-        return workflow_context.model.node_instance.get_by_name(
+        return workflow_context.model.node.get_by_name(
             mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
 
     @pytest.fixture

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/execution_plugin/test_ssh.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_ssh.py b/tests/orchestrator/execution_plugin/test_ssh.py
index 65195c8..2e270bb 100644
--- a/tests/orchestrator/execution_plugin/test_ssh.py
+++ b/tests/orchestrator/execution_plugin/test_ssh.py
@@ -124,10 +124,10 @@ class TestWithActualSSHServer(object):
 
     def test_run_script_download_resource_and_render(self, tmpdir):
         resource = tmpdir.join('resource')
-        resource.write('{{ctx.deployment.name}}')
+        resource.write('{{ctx.service_instance.name}}')
         self._upload(str(resource), 'test_resource')
         props = self._execute()
-        assert props['test_value'] == self._workflow_context.deployment.name
+        assert props['test_value'] == self._workflow_context.service_instance.name
 
     @pytest.mark.parametrize('value', ['string-value', [1, 2, 3], {'key': 'value'}])
     def test_run_script_inputs_as_env_variables_no_override(self, value):
@@ -217,12 +217,13 @@ class TestWithActualSSHServer(object):
         @workflow
         def mock_workflow(ctx, graph):
             op = 'test.op'
-            node_instance = ctx.model.node_instance.get_by_name(
-                mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-            node_instance.node.operations[op] = {
-                'operation': '{0}.{1}'.format(operations.__name__, operation.__name__)}
-            graph.sequence(*[api.task.OperationTask.node_instance(
-                instance=node_instance,
+            node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+            node.interfaces = [mock.models.get_interface(
+                op,
+                dict(implementation='{0}.{1}'.format(operations.__name__, operation.__name__))
+            )]
+            graph.sequence(*[api.task.OperationTask.node(
+                instance=node,
                 name=op,
                 inputs={
                     'script_path': script_path,
@@ -241,7 +242,7 @@ class TestWithActualSSHServer(object):
             workflow_context=self._workflow_context,
             tasks_graph=tasks_graph)
         eng.execute()
-        return self._workflow_context.model.node_instance.get_by_name(
+        return self._workflow_context.model.node.get_by_name(
             mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
 
     def _execute_and_get_task_exception(self, *args, **kwargs):
@@ -253,7 +254,7 @@ class TestWithActualSSHServer(object):
 
     def _upload(self, source, path):
         self._workflow_context.resource.deployment.upload(
-            entry_id=str(self._workflow_context.deployment.id),
+            entry_id=str(self._workflow_context.service_instance.id),
             source=source,
             path=path)
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/test_runner.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/test_runner.py b/tests/orchestrator/test_runner.py
index 1d46e91..17230df 100644
--- a/tests/orchestrator/test_runner.py
+++ b/tests/orchestrator/test_runner.py
@@ -47,10 +47,10 @@ def test_runner_no_tasks():
 def test_runner_tasks():
     @workflow
     def workflow_fn(ctx, graph):
-        for node_instance in ctx.model.node_instance.iter():
+        for node in ctx.model.node:
             graph.add_tasks(
-                OperationTask.node_instance(instance=node_instance,
-                                            name='tosca.interfaces.node.lifecycle.Standard.create'))
+                OperationTask.node(instance=node,
+                                   name='tosca.interfaces.node.lifecycle.Standard.create'))
 
     _test_runner(workflow_fn)
 
@@ -60,8 +60,7 @@ def test_runner_tasks():
 def _initialize_model_storage_fn(model_storage):
     mock.topology.create_simple_topology_single_node(
         model_storage,
-        1,
-        '%s.%s' % (__name__, mock_create_operation.__name__)
+        '{0}.{1}'.format(__name__, mock_create_operation.__name__)
     )
 
 
@@ -70,5 +69,5 @@ def _test_runner(workflow_fn):
                     workflow_fn=workflow_fn,
                     inputs={},
                     initialize_model_storage_fn=_initialize_model_storage_fn,
-                    deployment_id=1)
+                    service_instance_id=1)
     runner.run()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/__init__.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/__init__.py b/tests/orchestrator/workflows/__init__.py
index fe04b2f..7f0fd56 100644
--- a/tests/orchestrator/workflows/__init__.py
+++ b/tests/orchestrator/workflows/__init__.py
@@ -13,4 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from . import api, builtin, core
+from . import api, core

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/api/test_task.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/api/test_task.py b/tests/orchestrator/workflows/api/test_task.py
index bb629ef..79618c1 100644
--- a/tests/orchestrator/workflows/api/test_task.py
+++ b/tests/orchestrator/workflows/api/test_task.py
@@ -16,10 +16,9 @@
 
 import pytest
 
-from aria.storage import model
 from aria.orchestrator import context
 from aria.orchestrator.workflows import api
-
+from aria.storage.modeling import model
 from tests import mock, storage
 
 
@@ -31,7 +30,7 @@ def ctx(tmpdir):
     :return:
     """
     simple_context = mock.context.simple(str(tmpdir), inmemory=False)
-    simple_context.model.execution.put(mock.models.get_execution(simple_context.deployment))
+    simple_context.model.execution.put(mock.models.get_execution(simple_context.service_instance))
     yield simple_context
     storage.release_sqlite_storage(simple_context.model)
 
@@ -40,32 +39,33 @@ class TestOperationTask(object):
 
     def test_node_operation_task_creation(self, ctx):
         operation_name = 'aria.interfaces.lifecycle.create'
-        op_details = {'operation': True, 'plugin': 'plugin'}
-        node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
-        node.operations[operation_name] = op_details
+        interface = mock.models.get_interface(
+            operation_name,
+            operation_kwargs=dict(plugin='plugin', implementation='op_path'))
+
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+        node.interfaces = [interface]
         node.plugins = [{'name': 'plugin',
                          'package_name': 'package',
                          'package_version': '0.1'}]
-        ctx.model.node.update(node)
-        node_instance = \
-            ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
-        inputs = {'inputs': True}
+        ctx.model.node_template.update(node)
+        inputs = {'name': True}
         max_attempts = 10
         retry_interval = 10
         ignore_failure = True
 
         with context.workflow.current.push(ctx):
-            api_task = api.task.OperationTask.node_instance(
+            api_task = api.task.OperationTask.node(
                 name=operation_name,
-                instance=node_instance,
+                instance=node,
                 inputs=inputs,
                 max_attempts=max_attempts,
                 retry_interval=retry_interval,
                 ignore_failure=ignore_failure)
 
-        assert api_task.name == '{0}.{1}'.format(operation_name, node_instance.id)
-        assert api_task.operation_mapping is True
-        assert api_task.actor == node_instance
+        assert api_task.name == '{0}.{1}'.format(operation_name, node.id)
+        assert api_task.implementation == 'op_path'
+        assert api_task.actor == node
         assert api_task.inputs == inputs
         assert api_task.retry_interval == retry_interval
         assert api_task.max_attempts == max_attempts
@@ -77,29 +77,34 @@ class TestOperationTask(object):
 
     def test_source_relationship_operation_task_creation(self, ctx):
         operation_name = 'aria.interfaces.relationship_lifecycle.preconfigure'
-        op_details = {'operation': True, 'plugin': 'plugin'}
+
+        interface = mock.models.get_interface(
+            operation_name,
+            operation_kwargs=dict(implementation='op_path', plugin='plugin'),
+            edge='source'
+        )
+
         relationship = ctx.model.relationship.list()[0]
-        relationship.source_operations[operation_name] = op_details
+        relationship.interfaces = [interface]
         relationship.source_node.plugins = [{'name': 'plugin',
                                              'package_name': 'package',
                                              'package_version': '0.1'}]
-        relationship_instance = ctx.model.relationship_instance.list()[0]
-        inputs = {'inputs': True}
+        inputs = {'name': True}
         max_attempts = 10
         retry_interval = 10
 
         with context.workflow.current.push(ctx):
-            api_task = api.task.OperationTask.relationship_instance(
+            api_task = api.task.OperationTask.relationship(
                 name=operation_name,
-                instance=relationship_instance,
-                operation_end=api.task.OperationTask.SOURCE_OPERATION,
+                instance=relationship,
+                edge='source',
                 inputs=inputs,
                 max_attempts=max_attempts,
                 retry_interval=retry_interval)
 
-        assert api_task.name == '{0}.{1}'.format(operation_name, relationship_instance.id)
-        assert api_task.operation_mapping is True
-        assert api_task.actor == relationship_instance
+        assert api_task.name == '{0}.{1}'.format(operation_name, relationship.id)
+        assert api_task.implementation == 'op_path'
+        assert api_task.actor == relationship
         assert api_task.inputs == inputs
         assert api_task.retry_interval == retry_interval
         assert api_task.max_attempts == max_attempts
@@ -110,29 +115,33 @@ class TestOperationTask(object):
 
     def test_target_relationship_operation_task_creation(self, ctx):
         operation_name = 'aria.interfaces.relationship_lifecycle.preconfigure'
-        op_details = {'operation': True, 'plugin': 'plugin'}
+        interface = mock.models.get_interface(
+            operation_name,
+            operation_kwargs=dict(implementation='op_path', plugin='plugin'),
+            edge='target'
+        )
+
         relationship = ctx.model.relationship.list()[0]
-        relationship.target_operations[operation_name] = op_details
+        relationship.interfaces = [interface]
         relationship.target_node.plugins = [{'name': 'plugin',
                                              'package_name': 'package',
                                              'package_version': '0.1'}]
-        relationship_instance = ctx.model.relationship_instance.list()[0]
-        inputs = {'inputs': True}
+        inputs = {'name': True}
         max_attempts = 10
         retry_interval = 10
 
         with context.workflow.current.push(ctx):
-            api_task = api.task.OperationTask.relationship_instance(
+            api_task = api.task.OperationTask.relationship(
                 name=operation_name,
-                instance=relationship_instance,
-                operation_end=api.task.OperationTask.TARGET_OPERATION,
+                instance=relationship,
+                edge='target',
                 inputs=inputs,
                 max_attempts=max_attempts,
                 retry_interval=retry_interval)
 
-        assert api_task.name == '{0}.{1}'.format(operation_name, relationship_instance.id)
-        assert api_task.operation_mapping is True
-        assert api_task.actor == relationship_instance
+        assert api_task.name == '{0}.{1}'.format(operation_name, relationship.id)
+        assert api_task.implementation == 'op_path'
+        assert api_task.actor == relationship
         assert api_task.inputs == inputs
         assert api_task.retry_interval == retry_interval
         assert api_task.max_attempts == max_attempts
@@ -142,12 +151,12 @@ class TestOperationTask(object):
         assert api_task.runs_on == model.Task.RUNS_ON_TARGET
 
     def test_operation_task_default_values(self, ctx):
-        dependency_node_instance = ctx.model.node_instance.get_by_name(
+        dependency_node_instance = ctx.model.node.get_by_name(
             mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
         with context.workflow.current.push(ctx):
             task = api.task.OperationTask(
                 name='stub',
-                operation_mapping='',
+                implementation='',
                 actor=dependency_node_instance)
 
         assert task.inputs == {}

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/builtin/test_execute_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/builtin/test_execute_operation.py b/tests/orchestrator/workflows/builtin/test_execute_operation.py
index 87e3425..cc9a8a8 100644
--- a/tests/orchestrator/workflows/builtin/test_execute_operation.py
+++ b/tests/orchestrator/workflows/builtin/test_execute_operation.py
@@ -30,8 +30,9 @@ def ctx(tmpdir):
 
 
 def test_execute_operation(ctx):
-    node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-
+    node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+    node.interfaces = [mock.models.get_interface(mock.operations.NODE_OPERATIONS_INSTALL[0])]
+    ctx.model.node.update(node)
     operation_name = mock.operations.NODE_OPERATIONS_INSTALL[0]
 
     execute_tasks = list(
@@ -43,13 +44,13 @@ def test_execute_operation(ctx):
             allow_kwargs_override=False,
             run_by_dependency_order=False,
             type_names=[],
-            node_ids=[],
-            node_instance_ids=[node_instance.id]
+            node_template_ids=[],
+            node_ids=[node.id]
         ).topological_order()
     )
 
     assert len(execute_tasks) == 1
-    assert execute_tasks[0].name == '{0}.{1}'.format(operation_name, node_instance.id)
+    assert execute_tasks[0].name == '{0}.{1}'.format(operation_name, node.id)
 
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/builtin/test_heal.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/builtin/test_heal.py b/tests/orchestrator/workflows/builtin/test_heal.py
index 3e4498f..b553049 100644
--- a/tests/orchestrator/workflows/builtin/test_heal.py
+++ b/tests/orchestrator/workflows/builtin/test_heal.py
@@ -34,9 +34,9 @@ def ctx(tmpdir):
 @pytest.mark.skip(reason='heal is not implemented for now')
 def test_heal_dependent_node(ctx):
     dependent_node_instance = \
-        ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+        ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
     dependent_node_instance.host_fk = dependent_node_instance.id
-    ctx.model.node_instance.update(dependent_node_instance)
+    ctx.model.node.update(dependent_node_instance)
     heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependent_node_instance.id)
 
     assert len(list(heal_graph.tasks)) == 2
@@ -64,9 +64,9 @@ def test_heal_dependent_node(ctx):
 @pytest.mark.skip(reason='heal is not implemented for now')
 def test_heal_dependency_node(ctx):
     dependency_node_instance = \
-        ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
     dependency_node_instance.host_fk = dependency_node_instance.id
-    ctx.model.node_instance.update(dependency_node_instance)
+    ctx.model.node.update(dependency_node_instance)
     heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependency_node_instance.id)
     # both subgraphs should contain un\install for both the dependent and the dependency
     assert len(list(heal_graph.tasks)) == 2

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/core/test_engine.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_engine.py b/tests/orchestrator/workflows/core/test_engine.py
index 05a3d90..b2b67fc 100644
--- a/tests/orchestrator/workflows/core/test_engine.py
+++ b/tests/orchestrator/workflows/core/test_engine.py
@@ -23,7 +23,7 @@ from aria.orchestrator import (
     workflow,
     operation,
 )
-from aria.storage import model
+from aria.storage.modeling import model
 from aria.orchestrator.workflows import (
     api,
     exceptions,
@@ -60,13 +60,14 @@ class BaseTest(object):
             max_attempts=None,
             retry_interval=None,
             ignore_failure=None):
-        node_instance = \
-            ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-        node_instance.node.operations['aria.interfaces.lifecycle.create'] = {
-            'operation': '{name}.{func.__name__}'.format(name=__name__, func=func)
-        }
-        return api.task.OperationTask.node_instance(
-            instance=node_instance,
+        node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+        node.interfaces = [mock.models.get_interface(
+            'aria.interfaces.lifecycle.create',
+            operation_kwargs=dict(implementation='{name}.{func.__name__}'.format(name=__name__,
+                                                                                 func=func))
+        )]
+        return api.task.OperationTask.node(
+            instance=node,
             name='aria.interfaces.lifecycle.create',
             inputs=inputs,
             max_attempts=max_attempts,
@@ -219,8 +220,12 @@ class TestCancel(BaseTest):
 
         @workflow
         def mock_workflow(ctx, graph):
-            return graph.sequence(*(self._op(mock_sleep_task, ctx, inputs={'seconds': 0.1})
-                                    for _ in range(number_of_tasks)))
+            operations = (
+                self._op(mock_sleep_task, ctx, inputs=dict(seconds=0.1))
+                for _ in range(number_of_tasks)
+            )
+            return graph.sequence(*operations)
+
         eng = self._engine(workflow_func=mock_workflow,
                            workflow_context=workflow_context,
                            executor=executor)


[6/7] incubator-ariatosca git commit: ARIA-44 Merge parser and storage model

Posted by mx...@apache.org.
ARIA-44 Merge parser and storage model

Additional Changes:
  * As a part of this task the name to address the model has been changes to the __tablename__ (or __mapiname__ if specified)
  * All of the relationships return a query, so accessing the entire list should be done via all()


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

Branch: refs/heads/ARIA-106-Create-sqla-logging-handler
Commit: b6193359239ec673f3d7b313dd04122302e75ba6
Parents: 1498ad3
Author: mxmrlv <mx...@gmail.com>
Authored: Thu Jan 19 11:39:36 2017 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Sun Feb 12 18:12:13 2017 +0200

----------------------------------------------------------------------
 aria/__init__.py                                |   49 +-
 aria/cli/args_parser.py                         |    4 +-
 aria/cli/commands.py                            |   15 +-
 aria/orchestrator/context/common.py             |   22 +-
 aria/orchestrator/context/operation.py          |   43 +-
 aria/orchestrator/context/toolbelt.py           |    2 +-
 aria/orchestrator/context/workflow.py           |   22 +-
 aria/orchestrator/runner.py                     |   11 +-
 aria/orchestrator/workflows/api/task.py         |  149 +-
 .../workflows/builtin/execute_operation.py      |   54 +-
 aria/orchestrator/workflows/builtin/heal.py     |   14 +-
 aria/orchestrator/workflows/builtin/install.py  |   16 +-
 aria/orchestrator/workflows/builtin/start.py    |    6 +-
 aria/orchestrator/workflows/builtin/stop.py     |    6 +-
 .../orchestrator/workflows/builtin/uninstall.py |   18 +-
 aria/orchestrator/workflows/builtin/utils.py    |   52 +-
 .../orchestrator/workflows/builtin/workflows.py |  175 ++-
 aria/orchestrator/workflows/core/engine.py      |    2 +-
 aria/orchestrator/workflows/core/task.py        |   26 +-
 aria/orchestrator/workflows/executor/process.py |   14 +-
 aria/orchestrator/workflows/executor/thread.py  |    2 +-
 aria/parser/modeling/__init__.py                |    8 +-
 aria/parser/modeling/storage.py                 |  224 ++-
 aria/parser/modeling/utils.py                   |    4 +-
 aria/storage/__init__.py                        |   13 +-
 aria/storage/api.py                             |    6 +-
 aria/storage/base_model.py                      |  757 ----------
 aria/storage/core.py                            |    7 +-
 aria/storage/instrumentation.py                 |   15 +-
 aria/storage/model.py                           |  110 --
 aria/storage/modeling/__init__.py               |   35 +
 aria/storage/modeling/elements.py               |  106 ++
 aria/storage/modeling/instance_elements.py      | 1286 ++++++++++++++++
 aria/storage/modeling/model.py                  |  219 +++
 aria/storage/modeling/orchestrator_elements.py  |  468 ++++++
 aria/storage/modeling/structure.py              |  320 ++++
 aria/storage/modeling/template_elements.py      | 1387 ++++++++++++++++++
 aria/storage/modeling/type.py                   |  302 ++++
 aria/storage/modeling/utils.py                  |  139 ++
 aria/storage/sql_mapi.py                        |    6 +-
 aria/storage/structure.py                       |  190 ---
 aria/storage/type.py                            |  299 ----
 aria/storage_initializer.py                     |  135 ++
 aria/utils/application.py                       |   24 +-
 .../profiles/tosca-simple-1.0/groups.yaml       |    2 +-
 tests/mock/context.py                           |    4 +-
 tests/mock/models.py                            |  123 +-
 tests/mock/topology.py                          |  100 +-
 tests/orchestrator/context/test_operation.py    |  125 +-
 .../context/test_resource_render.py             |    2 +-
 tests/orchestrator/context/test_serialize.py    |   18 +-
 tests/orchestrator/context/test_toolbelt.py     |   75 +-
 tests/orchestrator/context/test_workflow.py     |   14 +-
 .../orchestrator/execution_plugin/test_local.py |   63 +-
 tests/orchestrator/execution_plugin/test_ssh.py |   21 +-
 tests/orchestrator/test_runner.py               |   11 +-
 tests/orchestrator/workflows/__init__.py        |    2 +-
 tests/orchestrator/workflows/api/test_task.py   |   83 +-
 .../workflows/builtin/test_execute_operation.py |   11 +-
 .../orchestrator/workflows/builtin/test_heal.py |    8 +-
 .../orchestrator/workflows/core/test_engine.py  |   25 +-
 tests/orchestrator/workflows/core/test_task.py  |   80 +-
 .../test_task_graph_into_exececution_graph.py   |   20 +-
 .../workflows/executor/test_executor.py         |    9 +-
 .../workflows/executor/test_process_executor.py |    8 +-
 .../executor/test_process_executor_extension.py |   15 +-
 .../test_process_executor_tracked_changes.py    |   51 +-
 tests/resources/scripts/test_ssh.sh             |   30 +-
 .../service_templates/node-cellar/workflows.py  |    9 +-
 tests/storage/__init__.py                       |   11 +-
 tests/storage/test_instrumentation.py           |   17 +-
 tests/storage/test_model_storage.py             |   45 +-
 tests/storage/test_models.py                    |  643 ++++----
 tests/storage/test_structures.py                |  135 +-
 74 files changed, 5797 insertions(+), 2725 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/__init__.py
----------------------------------------------------------------------
diff --git a/aria/__init__.py b/aria/__init__.py
index 8b87473..18eaa56 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -61,26 +61,47 @@ def application_model_storage(api, api_kwargs=None, initiator=None, initiator_kw
     """
     Initiate model storage
     """
-    models = [
-        storage.model.Plugin,
+    models_to_register = [
+        storage.modeling.model.Parameter,
 
-        storage.model.Blueprint,
-        storage.model.Deployment,
-        storage.model.DeploymentUpdate,
-        storage.model.DeploymentUpdateStep,
-        storage.model.DeploymentModification,
+        storage.modeling.model.MappingTemplate,
+        storage.modeling.model.SubstitutionTemplate,
+        storage.modeling.model.ServiceTemplate,
+        storage.modeling.model.NodeTemplate,
+        storage.modeling.model.GroupTemplate,
+        storage.modeling.model.InterfaceTemplate,
+        storage.modeling.model.OperationTemplate,
+        storage.modeling.model.ArtifactTemplate,
+        storage.modeling.model.PolicyTemplate,
+        storage.modeling.model.GroupPolicyTemplate,
+        storage.modeling.model.GroupPolicyTriggerTemplate,
+        storage.modeling.model.RequirementTemplate,
+        storage.modeling.model.CapabilityTemplate,
 
-        storage.model.Node,
-        storage.model.NodeInstance,
-        storage.model.Relationship,
-        storage.model.RelationshipInstance,
+        storage.modeling.model.Mapping,
+        storage.modeling.model.Substitution,
+        storage.modeling.model.ServiceInstance,
+        storage.modeling.model.Node,
+        storage.modeling.model.Group,
+        storage.modeling.model.Interface,
+        storage.modeling.model.Operation,
+        storage.modeling.model.Capability,
+        storage.modeling.model.Artifact,
+        storage.modeling.model.Policy,
+        storage.modeling.model.GroupPolicy,
+        storage.modeling.model.GroupPolicyTrigger,
+        storage.modeling.model.Relationship,
 
-        storage.model.Execution,
-        storage.model.Task,
+        storage.modeling.model.Execution,
+        storage.modeling.model.ServiceInstanceUpdate,
+        storage.modeling.model.ServiceInstanceUpdateStep,
+        storage.modeling.model.ServiceInstanceModification,
+        storage.modeling.model.Plugin,
+        storage.modeling.model.Task
     ]
     return storage.ModelStorage(api_cls=api,
                                 api_kwargs=api_kwargs,
-                                items=models,
+                                items=models_to_register,
                                 initiator=initiator,
                                 initiator_kwargs=initiator_kwargs or {})
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/cli/args_parser.py
----------------------------------------------------------------------
diff --git a/aria/cli/args_parser.py b/aria/cli/args_parser.py
index e661620..50fec39 100644
--- a/aria/cli/args_parser.py
+++ b/aria/cli/args_parser.py
@@ -138,9 +138,9 @@ def add_workflow_parser(workflow):
         default='install',
         help='The workflow name')
     workflow.add_argument(
-        '-d', '--deployment-id',
+        '-i', '--service-instance-id',
         required=False,
-        help='A unique ID for the deployment')
+        help='A unique ID for the service instance')
 
 
 @sub_parser_decorator(

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index 0890cd1..91d748f 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -42,7 +42,7 @@ from ..parser.consumption import (
     Instance
 )
 from ..parser.loading import LiteralLocation, UriLocation
-from ..parser.modeling import initialize_storage
+from ..parser.modeling.storage import initialize_storage
 from ..utils.application import StorageManager
 from ..utils.caching import cachedmethod
 from ..utils.console import (puts, Colored, indent)
@@ -211,10 +211,10 @@ class WorkflowCommand(BaseCommand):
     def __call__(self, args_namespace, unknown_args):
         super(WorkflowCommand, self).__call__(args_namespace, unknown_args)
 
-        deployment_id = args_namespace.deployment_id or 1 
+        service_instance_id = args_namespace.service_instance_id or 1 
         context = self._parse(args_namespace.uri)
         workflow_fn, inputs = self._get_workflow(context, args_namespace.workflow)
-        self._run(context, args_namespace.workflow, workflow_fn, inputs, deployment_id)
+        self._run(context, args_namespace.workflow, workflow_fn, inputs, service_instance_id)
     
     def _parse(self, uri):
         # Parse
@@ -259,13 +259,14 @@ class WorkflowCommand(BaseCommand):
         
         return workflow_fn, inputs
     
-    def _run(self, context, workflow_name, workflow_fn, inputs, deployment_id):
+    def _run(self, context, workflow_name, workflow_fn, inputs, service_instance_id):
         # Storage
         def _initialize_storage(model_storage):
-            initialize_storage(context, model_storage, deployment_id)
+            initialize_storage(context, model_storage, service_instance_id)
 
         # Create runner
-        runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage, deployment_id)
+        runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage,
+                        service_instance_id)
         
         # Run
         runner.run()
@@ -366,7 +367,7 @@ class ExecuteCommand(BaseCommand):
             FileSystemResourceDriver(local_resource_storage()))
         model_storage = application_model_storage(
             FileSystemModelDriver(local_model_storage()))
-        deployment = model_storage.deployment.get(args_namespace.deployment_id)
+        deployment = model_storage.service_instance.get(args_namespace.deployment_id)
 
         try:
             workflow = deployment.workflows[args_namespace.workflow_id]

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 6ab27ef..37482cf 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -31,7 +31,7 @@ class BaseContext(logger.LoggerMixin):
     def __init__(
             self,
             name,
-            deployment_id,
+            service_instance_id,
             model_storage,
             resource_storage,
             workdir=None,
@@ -41,13 +41,13 @@ class BaseContext(logger.LoggerMixin):
         self._id = str(uuid4())
         self._model = model_storage
         self._resource = resource_storage
-        self._deployment_id = deployment_id
+        self._service_instance_id = service_instance_id
         self._workdir = workdir
 
     def __repr__(self):
         return (
             '{name}(name={self.name}, '
-            'deployment_id={self._deployment_id}, '
+            'deployment_id={self._service_instance_id}, '
             .format(name=self.__class__.__name__, self=self))
 
     @property
@@ -67,18 +67,18 @@ class BaseContext(logger.LoggerMixin):
         return self._resource
 
     @property
-    def blueprint(self):
+    def service_template(self):
         """
         The blueprint model
         """
-        return self.deployment.blueprint
+        return self.service_instance.service_template
 
     @property
-    def deployment(self):
+    def service_instance(self):
         """
         The deployment model
         """
-        return self.model.deployment.get(self._deployment_id)
+        return self.model.service_instance.get(self._service_instance_id)
 
     @property
     def name(self):
@@ -101,11 +101,11 @@ class BaseContext(logger.LoggerMixin):
         Download a blueprint resource from the resource storage
         """
         try:
-            self.resource.deployment.download(entry_id=str(self.deployment.id),
+            self.resource.deployment.download(entry_id=str(self.service_instance.id),
                                               destination=destination,
                                               path=path)
         except exceptions.StorageError:
-            self.resource.blueprint.download(entry_id=str(self.blueprint.id),
+            self.resource.blueprint.download(entry_id=str(self.service_template.id),
                                              destination=destination,
                                              path=path)
 
@@ -126,9 +126,9 @@ class BaseContext(logger.LoggerMixin):
         Read a deployment resource as string from the resource storage
         """
         try:
-            return self.resource.deployment.read(entry_id=str(self.deployment.id), path=path)
+            return self.resource.deployment.read(entry_id=str(self.service_instance.id), path=path)
         except exceptions.StorageError:
-            return self.resource.blueprint.read(entry_id=str(self.blueprint.id), path=path)
+            return self.resource.deployment.read(entry_id=str(self.service_template.id), path=path)
 
     def get_resource_and_render(self, path=None, variables=None):
         """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py
index d1f61b2..c5ac8f0 100644
--- a/aria/orchestrator/context/operation.py
+++ b/aria/orchestrator/context/operation.py
@@ -31,7 +31,7 @@ class BaseOperationContext(BaseContext):
                  name,
                  model_storage,
                  resource_storage,
-                 deployment_id,
+                 service_instance_id,
                  task_id,
                  actor_id,
                  **kwargs):
@@ -39,7 +39,7 @@ class BaseOperationContext(BaseContext):
             name=name,
             model_storage=model_storage,
             resource_storage=resource_storage,
-            deployment_id=deployment_id,
+            service_instance_id=service_instance_id,
             **kwargs)
         self._task_id = task_id
         self._actor_id = actor_id
@@ -69,7 +69,7 @@ class BaseOperationContext(BaseContext):
         if not self.task.plugin_name:
             return None
         plugin_workdir = '{0}/plugins/{1}/{2}'.format(self._workdir,
-                                                      self.deployment.id,
+                                                      self.service_instance.id,
                                                       self.task.plugin_name)
         file.makedirs(plugin_workdir)
         return plugin_workdir
@@ -79,7 +79,7 @@ class BaseOperationContext(BaseContext):
         context_cls = self.__class__
         context_dict = {
             'name': self.name,
-            'deployment_id': self._deployment_id,
+            'service_instance_id': self._service_instance_id,
             'task_id': self._task_id,
             'actor_id': self._actor_id,
             'workdir': self._workdir,
@@ -106,20 +106,20 @@ class NodeOperationContext(BaseOperationContext):
     Context for node based operations.
     """
     @property
-    def node(self):
+    def node_template(self):
         """
         the node of the current operation
         :return:
         """
-        return self.node_instance.node
+        return self.node.node_template
 
     @property
-    def node_instance(self):
+    def node(self):
         """
         The node instance of the current operation
         :return:
         """
-        return self.model.node_instance.get(self._actor_id)
+        return self.model.node.get(self._actor_id)
 
 
 class RelationshipOperationContext(BaseOperationContext):
@@ -127,50 +127,41 @@ class RelationshipOperationContext(BaseOperationContext):
     Context for relationship based operations.
     """
     @property
-    def source_node(self):
+    def source_node_template(self):
         """
         The source node
         :return:
         """
-        return self.relationship.source_node
+        return self.source_node.node_template
 
     @property
-    def source_node_instance(self):
+    def source_node(self):
         """
         The source node instance
         :return:
         """
-        return self.relationship_instance.source_node_instance
+        return self.relationship.source_node
 
     @property
-    def target_node(self):
+    def target_node_template(self):
         """
         The target node
         :return:
         """
-        return self.relationship.target_node
+        return self.target_node.node_template
 
     @property
-    def target_node_instance(self):
+    def target_node(self):
         """
         The target node instance
         :return:
         """
-        return self.relationship_instance.target_node_instance
+        return self.relationship.target_node
 
     @property
     def relationship(self):
         """
-        The relationship of the current operation
-        :return:
-        """
-
-        return self.relationship_instance.relationship
-
-    @property
-    def relationship_instance(self):
-        """
         The relationship instance of the current operation
         :return:
         """
-        return self.model.relationship_instance.get(self._actor_id)
+        return self.model.relationship.get(self._actor_id)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/toolbelt.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/toolbelt.py b/aria/orchestrator/context/toolbelt.py
index 301b013..def7d42 100644
--- a/aria/orchestrator/context/toolbelt.py
+++ b/aria/orchestrator/context/toolbelt.py
@@ -33,7 +33,7 @@ class NodeToolBelt(object):
         :return:
         """
         assert isinstance(self._op_context, operation.NodeOperationContext)
-        host = self._op_context.node_instance.host
+        host = self._op_context.node.host
         return host.runtime_properties.get('ip')
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/workflow.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/workflow.py b/aria/orchestrator/context/workflow.py
index a15790e..00ed974 100644
--- a/aria/orchestrator/context/workflow.py
+++ b/aria/orchestrator/context/workflow.py
@@ -49,18 +49,16 @@ class WorkflowContext(BaseContext):
 
     def __repr__(self):
         return (
-            '{name}(deployment_id={self._deployment_id}, '
+            '{name}(deployment_id={self._service_instance_id}, '
             'workflow_name={self._workflow_name}'.format(
                 name=self.__class__.__name__, self=self))
 
     def _create_execution(self):
-        execution_cls = self.model.execution.model_cls
         now = datetime.utcnow()
         execution = self.model.execution.model_cls(
-            deployment=self.deployment,
+            service_instance=self.service_instance,
             workflow_name=self._workflow_name,
             created_at=now,
-            status=execution_cls.PENDING,
             parameters=self.parameters,
         )
         self.model.execution.put(execution)
@@ -81,27 +79,27 @@ class WorkflowContext(BaseContext):
         self.model.execution.put(value)
 
     @property
-    def nodes(self):
+    def node_templates(self):
         """
         Iterator over nodes
         """
-        key = 'deployment_{0}'.format(self.model.node.model_cls.name_column_name())
+        key = 'service_instance_{0}'.format(self.model.node_template.model_cls.name_column_name())
 
-        return self.model.node.iter(
+        return self.model.node_template.iter(
             filters={
-                key: getattr(self.deployment, self.deployment.name_column_name())
+                key: getattr(self.service_instance, self.service_instance.name_column_name())
             }
         )
 
     @property
-    def node_instances(self):
+    def nodes(self):
         """
         Iterator over node instances
         """
-        key = 'deployment_{0}'.format(self.model.node_instance.model_cls.name_column_name())
-        return self.model.node_instance.iter(
+        key = 'service_instance_{0}'.format(self.model.node.model_cls.name_column_name())
+        return self.model.node.iter(
             filters={
-                key: getattr(self.deployment, self.deployment.name_column_name())
+                key: getattr(self.service_instance, self.service_instance.name_column_name())
             }
         )
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/runner.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/runner.py b/aria/orchestrator/runner.py
index 5950dc5..8927de9 100644
--- a/aria/orchestrator/runner.py
+++ b/aria/orchestrator/runner.py
@@ -50,7 +50,7 @@ class Runner(object):
     """
 
     def __init__(self, workflow_name, workflow_fn, inputs, initialize_model_storage_fn,
-                 deployment_id, storage_path='', is_storage_temporary=True):
+                 service_instance_id, storage_path='', is_storage_temporary=True):
         if storage_path == '':
             # Temporary file storage
             the_file, storage_path = tempfile.mkstemp(suffix='.db', prefix='aria-')
@@ -61,7 +61,7 @@ class Runner(object):
         self._storage_name = os.path.basename(storage_path)
         self._is_storage_temporary = is_storage_temporary
 
-        workflow_context = self.create_workflow_context(workflow_name, deployment_id,
+        workflow_context = self.create_workflow_context(workflow_name, service_instance_id,
                                                         initialize_model_storage_fn)
 
         tasks_graph = workflow_fn(ctx=workflow_context, **inputs)
@@ -77,7 +77,10 @@ class Runner(object):
         finally:
             self.cleanup()
 
-    def create_workflow_context(self, workflow_name, deployment_id, initialize_model_storage_fn):
+    def create_workflow_context(self,
+                                workflow_name,
+                                service_instance_id,
+                                initialize_model_storage_fn):
         self.cleanup()
         model_storage = application_model_storage(
             sql_mapi.SQLAlchemyModelAPI,
@@ -89,7 +92,7 @@ class Runner(object):
             name=workflow_name,
             model_storage=model_storage,
             resource_storage=resource_storage,
-            deployment_id=deployment_id,
+            service_instance_id=service_instance_id,
             workflow_name=self.__class__.__name__,
             task_max_attempts=1,
             task_retry_interval=1)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 44715c1..6a00844 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -18,7 +18,7 @@ Provides the tasks to be entered into the task graph
 """
 from uuid import uuid4
 
-from aria.storage import model
+from aria.storage.modeling import model
 
 from ... import context
 from .. import exceptions
@@ -57,13 +57,13 @@ class OperationTask(BaseTask):
     Represents an operation task in the task_graph
     """
 
-    SOURCE_OPERATION = 'source_operations'
-    TARGET_OPERATION = 'target_operations'
+    SOURCE_OPERATION = 'source'
+    TARGET_OPERATION = 'target'
 
     def __init__(self,
                  name,
                  actor,
-                 operation_mapping,
+                 implementation,
                  max_attempts=None,
                  retry_interval=None,
                  ignore_failure=None,
@@ -76,12 +76,12 @@ class OperationTask(BaseTask):
         :param actor: the operation host on which this operation is registered.
         :param inputs: operation inputs.
         """
-        assert isinstance(actor, (model.NodeInstance,
-                                  model.RelationshipInstance))
+        assert isinstance(actor, (model.Node,
+                                  model.Relationship))
         super(OperationTask, self).__init__()
         self.actor = actor
         self.name = '{name}.{actor.id}'.format(name=name, actor=actor)
-        self.operation_mapping = operation_mapping
+        self.implementation = implementation
         self.inputs = inputs or {}
         self.plugin = plugin or {}
         self.max_attempts = (self.workflow_context._task_max_attempts
@@ -93,70 +93,133 @@ class OperationTask(BaseTask):
         self.runs_on = runs_on
 
     @classmethod
-    def node_instance(cls, instance, name, inputs=None, *args, **kwargs):
+    def _merge_inputs(cls, operation_inputs, additional_inputs=None):
+        final_inputs = dict((p.name, p.as_raw['value']) for p in operation_inputs)
+        final_inputs.update(additional_inputs or {})
+        return final_inputs
+
+    @classmethod
+    def node(cls, instance, name, inputs=None, *args, **kwargs):
         """
         Represents a node based operation
 
         :param instance: the node of which this operation belongs to.
         :param name: the name of the operation.
         """
-        assert isinstance(instance, model.NodeInstance)
-        return cls._instance(instance=instance,
-                             name=name,
-                             operation_details=instance.node.operations[name],
-                             inputs=inputs,
-                             plugins=instance.node.plugins or [],
-                             runs_on=model.Task.RUNS_ON_NODE_INSTANCE,
-                             *args,
-                             **kwargs)
+        assert isinstance(instance, model.Node)
+        interface_name = _get_interface_name(name)
+        interfaces = instance.interfaces.filter_by(name=interface_name)
+        if interfaces.count() > 1:
+            raise exceptions.TaskException(
+                "More than one interface with the same name `{0}` found".format(name)
+            )
+        elif interfaces.count() == 0:
+            raise exceptions.TaskException(
+                "No Interface with the name `{interface_name}` found".format(
+                    interface_name=interface_name)
+            )
+
+        operation_templates = interfaces[0].operations.filter_by(name=name)
+        if operation_templates.count() > 1:
+            raise exceptions.TaskException(
+                "More than one operation with the same name `{0}` were found".format(name)
+            )
+
+        elif operation_templates.count() == 0:
+            raise exceptions.TaskException(
+                "No interface with the name `{operation_name}` found".format(
+                    operation_name=name)
+            )
+
+        return cls._instance(
+            instance=instance,
+            name=name,
+            operation_template=operation_templates[0],
+            plugins=instance.plugins or [],
+            runs_on=model.Task.RUNS_ON_NODE_INSTANCE,
+            inputs=cls._merge_inputs(operation_templates[0].inputs, inputs),
+            *args,
+            **kwargs)
 
     @classmethod
-    def relationship_instance(cls, instance, name, operation_end, inputs=None, *args, **kwargs):
+    def relationship(cls, instance, name, edge, runs_on=None, inputs=None, *args,
+                     **kwargs):
         """
         Represents a relationship based operation
 
         :param instance: the relationship of which this operation belongs to.
         :param name: the name of the operation.
-        :param operation_end: source or target end of the relationship, this corresponds directly
-        with 'source_operations' and 'target_operations'
+        :param edge: the edge of the interface ("source" or "target").
+        :param runs_on: where to run the operation ("source" or "target"); if None defaults to the
+                        interface edge.
         :param inputs any additional inputs to the operation
         """
-        assert isinstance(instance, model.RelationshipInstance)
-        if operation_end not in [cls.TARGET_OPERATION, cls.SOURCE_OPERATION]:
-            raise exceptions.TaskException('The operation end should be {0} or {1}'.format(
-                cls.TARGET_OPERATION, cls.SOURCE_OPERATION
-            ))
-        operation_details = getattr(instance.relationship, operation_end)[name]
-        if operation_end == cls.SOURCE_OPERATION:
-            plugins = instance.relationship.source_node.plugins
-            runs_on = model.Task.RUNS_ON_SOURCE
+        assert isinstance(instance, model.Relationship)
+        interface_name = _get_interface_name(name)
+        interfaces = instance.interfaces.filter_by(name=interface_name, edge=edge)
+        count = interfaces.count()
+        if count > 1:
+            raise exceptions.TaskException(
+                "More than one interface with the same name `{interface_name}` found at `{edge}`"
+                + " edge".format(
+                    interface_name=interface_name, edge=edge)
+            )
+        elif count == 0:
+            raise exceptions.TaskException(
+                "No interface with the name `{interface_name}` found at `{edge}` edge".format(
+                    interface_name=interface_name, edge=edge)
+            )
+
+        operations = interfaces.all()[0].operations.filter_by(name=name)
+        count = operations.count()
+        if count > 1:
+            raise exceptions.TaskException(
+                "More than one operation with the same name `{0}` found".format(name)
+            )
+        elif count == 0:
+            raise exceptions.TaskException(
+                "No operation with the name `{operation_name}` found".format(
+                    operation_name=name)
+            )
+
+        if not runs_on:
+            if edge == cls.SOURCE_OPERATION:
+                runs_on = model.Task.RUNS_ON_SOURCE
+            else:
+                runs_on = model.Task.RUNS_ON_TARGET
+
+        if runs_on == model.Task.RUNS_ON_SOURCE:
+            plugins = instance.source_node.plugins
         else:
-            plugins = instance.relationship.target_node.plugins
-            runs_on = model.Task.RUNS_ON_TARGET
+            plugins = instance.target_node.plugins
+
         return cls._instance(instance=instance,
                              name=name,
-                             operation_details=operation_details,
-                             inputs=inputs,
+                             operation_template=operations[0],
                              plugins=plugins or [],
                              runs_on=runs_on,
+                             inputs=cls._merge_inputs(operations[0].inputs, inputs),
                              *args,
                              **kwargs)
 
     @classmethod
-    def _instance(cls, instance, name, operation_details, inputs, plugins, runs_on, *args,
+    def _instance(cls,
+                  instance,
+                  name,
+                  operation_template,
+                  inputs,
+                  plugins,
+                  runs_on,
+                  *args,
                   **kwargs):
-        operation_mapping = operation_details.get('operation')
-        operation_inputs = operation_details.get('inputs', {})
-        operation_inputs.update(inputs or {})
-        plugin_name = operation_details.get('plugin')
-        matching_plugins = [p for p in plugins if p['name'] == plugin_name]
+        matching_plugins = [p for p in plugins if p['name'] == operation_template.plugin]
         # All matching plugins should have identical package_name/package_version, so it's safe to
         # take the first found.
         plugin = matching_plugins[0] if matching_plugins else {}
         return cls(actor=instance,
                    name=name,
-                   operation_mapping=operation_mapping,
-                   inputs=operation_inputs,
+                   implementation=operation_template.implementation,
+                   inputs=inputs,
                    plugin=plugin,
                    runs_on=runs_on,
                    *args,
@@ -197,3 +260,7 @@ class StubTask(BaseTask):
     Enables creating empty tasks.
     """
     pass
+
+
+def _get_interface_name(operation_name):
+    return operation_name.rsplit('.', 1)[0]

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 e76993d..5a7f6ce 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -30,8 +30,8 @@ def execute_operation(
         allow_kwargs_override,
         run_by_dependency_order,
         type_names,
+        node_template_ids,
         node_ids,
-        node_instance_ids,
         **kwargs):
     """
     The execute_operation workflow
@@ -43,32 +43,32 @@ def execute_operation(
     :param bool allow_kwargs_override:
     :param bool run_by_dependency_order:
     :param type_names:
+    :param node_template_ids:
     :param node_ids:
-    :param node_instance_ids:
     :param kwargs:
     :return:
     """
     subgraphs = {}
     # filtering node instances
-    filtered_node_instances = list(_filter_node_instances(
+    filtered_nodes = list(_filter_node_instances(
         context=ctx,
+        node_template_ids=node_template_ids,
         node_ids=node_ids,
-        node_instance_ids=node_instance_ids,
         type_names=type_names))
 
     if run_by_dependency_order:
         filtered_node_instances_ids = set(node_instance.id
-                                          for node_instance in filtered_node_instances)
-        for node_instance in ctx.node_instances:
-            if node_instance.id not in filtered_node_instances_ids:
-                subgraphs[node_instance.id] = ctx.task_graph(
-                    name='execute_operation_stub_{0}'.format(node_instance.id))
+                                          for node_instance in filtered_nodes)
+        for node in ctx.node_instances:
+            if node.id not in filtered_node_instances_ids:
+                subgraphs[node.id] = ctx.task_graph(
+                    name='execute_operation_stub_{0}'.format(node.id))
 
     # registering actual tasks to sequences
-    for node_instance in filtered_node_instances:
+    for node in filtered_nodes:
         graph.add_tasks(
             _create_node_instance_task(
-                node_instance=node_instance,
+                nodes=node,
                 operation=operation,
                 operation_kwargs=operation_kwargs,
                 allow_kwargs_override=allow_kwargs_override
@@ -80,37 +80,37 @@ def execute_operation(
 
     # adding tasks dependencies if required
     if run_by_dependency_order:
-        for node_instance in ctx.node_instances:
-            for relationship_instance in node_instance.relationship_instances:
-                graph.add_dependency(source_task=subgraphs[node_instance.id],
-                                     after=[subgraphs[relationship_instance.target_id]])
+        for node in ctx.nodes:
+            for relationship in node.relationships:
+                graph.add_dependency(
+                    source_task=subgraphs[node.id], after=[subgraphs[relationship.target_id]])
 
 
-def _filter_node_instances(context, node_ids=(), node_instance_ids=(), type_names=()):
+def _filter_node_instances(context, node_template_ids=(), node_ids=(), type_names=()):
     def _is_node_by_id(node_id):
-        return not node_ids or node_id in node_ids
+        return not node_template_ids or node_id in node_template_ids
 
     def _is_node_instance_by_id(node_instance_id):
-        return not node_instance_ids or node_instance_id in node_instance_ids
+        return not node_ids or node_instance_id in node_ids
 
     def _is_node_by_type(node_type_hierarchy):
         return not type_names or node_type_hierarchy in type_names
 
-    for node_instance in context.node_instances:
-        if all((_is_node_by_id(node_instance.node.id),
-                _is_node_instance_by_id(node_instance.id),
-                _is_node_by_type(node_instance.node.type_hierarchy))):
-            yield node_instance
+    for node in context.nodes:
+        if all((_is_node_by_id(node.node_template.id),
+                _is_node_instance_by_id(node.id),
+                _is_node_by_type(node.node_template.type_hierarchy))):
+            yield node
 
 
 def _create_node_instance_task(
-        node_instance,
+        nodes,
         operation,
         operation_kwargs,
         allow_kwargs_override):
     """
     A workflow which executes a single operation
-    :param node_instance: the node instance to install
+    :param nodes: the node instance to install
     :param basestring operation: the operation name
     :param dict operation_kwargs:
     :param bool allow_kwargs_override:
@@ -120,7 +120,7 @@ def _create_node_instance_task(
     if allow_kwargs_override is not None:
         operation_kwargs['allow_kwargs_override'] = allow_kwargs_override
 
-    return OperationTask.node_instance(
-        instance=node_instance,
+    return OperationTask.node(
+        instance=nodes,
         name=operation,
         inputs=operation_kwargs)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/heal.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/heal.py b/aria/orchestrator/workflows/builtin/heal.py
index 87ac492..2592323 100644
--- a/aria/orchestrator/workflows/builtin/heal.py
+++ b/aria/orchestrator/workflows/builtin/heal.py
@@ -21,7 +21,7 @@ Builtin heal workflow
 
 from aria import workflow
 
-from .workflows import (install_node_instance, uninstall_node_instance)
+from .workflows import (install_node, uninstall_node)
 from ..api import task
 
 
@@ -35,8 +35,8 @@ def heal(ctx, graph, node_instance_id):
     :param node_instance_id: the id of the node instance to heal
     :return:
     """
-    failing_node = ctx.model.node_instance.get(node_instance_id)
-    host_node = ctx.model.node_instance.get(failing_node.host.id)
+    failing_node = ctx.model.node.get(node_instance_id)
+    host_node = ctx.model.node.get(failing_node.host.id)
     failed_node_instance_subgraph = _get_contained_subgraph(ctx, host_node)
     failed_node_instance_ids = list(n.id for n in failed_node_instance_subgraph)
 
@@ -78,7 +78,7 @@ def heal_uninstall(ctx, graph, failing_node_instances, targeted_node_instances):
 
     # create install sub workflow for every node instance
     for node_instance in failing_node_instances:
-        node_instance_sub_workflow = task.WorkflowTask(uninstall_node_instance,
+        node_instance_sub_workflow = task.WorkflowTask(uninstall_node,
                                                        node_instance=node_instance)
         node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
         graph.add_tasks(node_instance_sub_workflow)
@@ -98,7 +98,7 @@ def heal_uninstall(ctx, graph, failing_node_instances, targeted_node_instances):
         for relationship_instance in reversed(node_instance.outbound_relationship_instances):
 
             target_node_instance = \
-                ctx.model.node_instance.get(relationship_instance.target_node_instance.id)
+                ctx.model.node.get(relationship_instance.target_node_instance.id)
             target_node_instance_subgraph = node_instance_sub_workflows[target_node_instance.id]
             graph.add_dependency(target_node_instance_subgraph, node_instance_sub_workflow)
 
@@ -131,7 +131,7 @@ def heal_install(ctx, graph, failing_node_instances, targeted_node_instances):
 
     # create install sub workflow for every node instance
     for node_instance in failing_node_instances:
-        node_instance_sub_workflow = task.WorkflowTask(install_node_instance,
+        node_instance_sub_workflow = task.WorkflowTask(install_node,
                                                        node_instance=node_instance)
         node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
         graph.add_tasks(node_instance_sub_workflow)
@@ -151,7 +151,7 @@ def heal_install(ctx, graph, failing_node_instances, targeted_node_instances):
         node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
 
         for relationship_instance in node_instance.outbound_relationship_instances:
-            target_node_instance = ctx.model.node_instance.get(
+            target_node_instance = ctx.model.node.get(
                 relationship_instance.target_node_instance.id)
             target_node_instance_subworkflow = node_instance_sub_workflows[target_node_instance.id]
             graph.add_dependency(node_instance_sub_workflow, target_node_instance_subworkflow)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/install.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/install.py b/aria/orchestrator/workflows/builtin/install.py
index 81d1da2..2b9ec66 100644
--- a/aria/orchestrator/workflows/builtin/install.py
+++ b/aria/orchestrator/workflows/builtin/install.py
@@ -17,18 +17,16 @@
 Builtin install workflow
 """
 
-from .workflows import install_node_instance
-from .utils import create_node_instance_task_dependencies
+from .workflows import install_node
+from .utils import create_node_task_dependencies
 from ..api.task import WorkflowTask
 from ... import workflow
 
 
 @workflow
 def install(ctx, graph):
-    tasks_and_node_instances = []
-    for node_instance in ctx.model.node_instance.iter():
-        tasks_and_node_instances.append((
-            WorkflowTask(install_node_instance, node_instance=node_instance),
-            node_instance))
-    graph.add_tasks([task for task, _ in tasks_and_node_instances])
-    create_node_instance_task_dependencies(graph, tasks_and_node_instances)
+    tasks_and_nodes = []
+    for node in ctx.nodes:
+        tasks_and_nodes.append((WorkflowTask(install_node, node=node), node))
+    graph.add_tasks([task for task, _ in tasks_and_nodes])
+    create_node_task_dependencies(graph, tasks_and_nodes)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/start.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/start.py b/aria/orchestrator/workflows/builtin/start.py
index a044d73..ad67554 100644
--- a/aria/orchestrator/workflows/builtin/start.py
+++ b/aria/orchestrator/workflows/builtin/start.py
@@ -17,12 +17,12 @@
 Builtin start workflow
 """
 
-from .workflows import start_node_instance
+from .workflows import start_node
 from ..api.task import WorkflowTask
 from ... import workflow
 
 
 @workflow
 def start(ctx, graph):
-    for node_instance in ctx.model.node_instance.iter():
-        graph.add_tasks(WorkflowTask(start_node_instance, node_instance=node_instance))
+    for node in ctx.model.node.iter():
+        graph.add_tasks(WorkflowTask(start_node, node=node))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/stop.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/stop.py b/aria/orchestrator/workflows/builtin/stop.py
index 584c0d3..23ac366 100644
--- a/aria/orchestrator/workflows/builtin/stop.py
+++ b/aria/orchestrator/workflows/builtin/stop.py
@@ -17,12 +17,12 @@
 Builtin stop workflow
 """
 
-from .workflows import stop_node_instance
+from .workflows import stop_node
 from ..api.task import WorkflowTask
 from ... import workflow
 
 
 @workflow
 def stop(ctx, graph):
-    for node_instance in ctx.model.node_instance.iter():
-        graph.add_tasks(WorkflowTask(stop_node_instance, node_instance=node_instance))
+    for node in ctx.model.node.iter():
+        graph.add_tasks(WorkflowTask(stop_node, node=node))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/uninstall.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/uninstall.py b/aria/orchestrator/workflows/builtin/uninstall.py
index bfcc9ec..e4afcd9 100644
--- a/aria/orchestrator/workflows/builtin/uninstall.py
+++ b/aria/orchestrator/workflows/builtin/uninstall.py
@@ -17,18 +17,18 @@
 Builtin uninstall workflow
 """
 
-from .workflows import uninstall_node_instance
-from .utils import create_node_instance_task_dependencies
+from .workflows import uninstall_node
+from .utils import create_node_task_dependencies
 from ..api.task import WorkflowTask
 from ... import workflow
 
 
 @workflow
 def uninstall(ctx, graph):
-    tasks_and_node_instances = []
-    for node_instance in ctx.model.node_instance.iter():
-        tasks_and_node_instances.append((
-            WorkflowTask(uninstall_node_instance, node_instance=node_instance),
-            node_instance))
-    graph.add_tasks([task for task, _ in tasks_and_node_instances])
-    create_node_instance_task_dependencies(graph, tasks_and_node_instances, reverse=True)
+    tasks_and_nodes = []
+    for node in ctx.nodes:
+        tasks_and_nodes.append((
+            WorkflowTask(uninstall_node, node=node),
+            node))
+    graph.add_tasks([task for task, _ in tasks_and_nodes])
+    create_node_task_dependencies(graph, tasks_and_nodes, reverse=True)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/utils.py b/aria/orchestrator/workflows/builtin/utils.py
index 8b7a8bc..c9dbc6b 100644
--- a/aria/orchestrator/workflows/builtin/utils.py
+++ b/aria/orchestrator/workflows/builtin/utils.py
@@ -16,50 +16,49 @@
 from ..api.task import OperationTask
 
 
-def create_node_instance_task(operation_name, node_instance):
+def create_node_task(operation_name, node):
     """
-    Returns a new operation task if the operation exists in the node instance, otherwise returns
-    None.
+    Returns a new operation task if the operation exists in the node, otherwise returns None.
     """
 
-    if operation_name in node_instance.node.operations:
-        return OperationTask.node_instance(instance=node_instance,
-                                           name=operation_name)
+    if _has_operation(node.interfaces, operation_name):
+        return OperationTask.node(instance=node,
+                                  name=operation_name)
     return None
 
 
-def create_relationship_instance_tasks(operation_name, operations_attr, node_instance):
+def create_relationship_tasks(operation_name, runs_on, node):
     """
-    Returns a list of operation tasks for each outbound relationship of the node instance if
-    the operation exists there.
+    Returns a list of operation tasks for each outbound relationship of the node if the operation
+    exists there.
     """
 
     sequence = []
-    for relationship_instance in node_instance.outbound_relationship_instances:
-        if operation_name in getattr(relationship_instance.relationship, operations_attr):
+    for relationship in node.outbound_relationships:
+        if _has_operation(relationship.interfaces, operation_name):
             sequence.append(
-                OperationTask.relationship_instance(instance=relationship_instance,
-                                                    name=operation_name,
-                                                    operation_end=operations_attr))
+                OperationTask.relationship(instance=relationship,
+                                           name=operation_name,
+                                           edge='source',
+                                           runs_on=runs_on))
     return sequence
 
 
-def create_node_instance_task_dependencies(graph, tasks_and_node_instances, reverse=False):
+def create_node_task_dependencies(graph, tasks_and_nodes, reverse=False):
     """
-    Creates dependencies between tasks if there is an outbound relationship between their node
-    instances.
+    Creates dependencies between tasks if there is a relationship (outbound) between their nodes.
     """
 
-    def get_task(node_instance_id):
-        for task, node_instance in tasks_and_node_instances:
-            if node_instance.id == node_instance_id:
+    def get_task(node_id):
+        for task, node in tasks_and_nodes:
+            if node.id == node_id:
                 return task
         return None
 
-    for task, node_instance in tasks_and_node_instances:
+    for task, node in tasks_and_nodes:
         dependencies = []
-        for relationship_instance in node_instance.outbound_relationship_instances:
-            dependency = get_task(relationship_instance.target_node_instance.id)
+        for relationship in node.outbound_relationships:
+            dependency = get_task(relationship.target_node.id)
             if dependency:
                 dependencies.append(dependency)
         if dependencies:
@@ -68,3 +67,10 @@ def create_node_instance_task_dependencies(graph, tasks_and_node_instances, reve
                     graph.add_dependency(dependency, task)
             else:
                 graph.add_dependency(task, dependencies)
+
+
+def _has_operation(interfaces, operation_name):
+    for interface in interfaces:
+        if interface.operations.filter_by(name=operation_name).count() == 1:
+            return True
+    return False

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/workflows.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py
index c2fbded..180b4e9 100644
--- a/aria/orchestrator/workflows/builtin/workflows.py
+++ b/aria/orchestrator/workflows/builtin/workflows.py
@@ -17,122 +17,157 @@
 A set of builtin workflows.
 """
 
-from .utils import (create_node_instance_task, create_relationship_instance_tasks)
+from .utils import (create_node_task, create_relationship_tasks)
 from ... import workflow
 
 
+NORMATIVE_STANDARD_INTERFACE = 'Standard' # 'tosca.interfaces.node.lifecycle.Standard'
+NORMATIVE_CONFIGURE_INTERFACE = 'Configure' # 'tosca.interfaces.relationship.Configure'
+
+NORMATIVE_CREATE = NORMATIVE_STANDARD_INTERFACE + '.create'
+NORMATIVE_START = NORMATIVE_STANDARD_INTERFACE + '.start'
+NORMATIVE_STOP = NORMATIVE_STANDARD_INTERFACE + '.stop'
+NORMATIVE_DELETE = NORMATIVE_STANDARD_INTERFACE + '.delete'
+
+NORMATIVE_CONFIGURE = NORMATIVE_STANDARD_INTERFACE + '.configure'
+NORMATIVE_PRE_CONFIGURE_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.pre_configure_source'
+NORMATIVE_PRE_CONFIGURE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.pre_configure_target'
+NORMATIVE_POST_CONFIGURE_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.post_configure_source'
+NORMATIVE_POST_CONFIGURE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.post_configure_target'
+
+NORMATIVE_ADD_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.add_source'
+NORMATIVE_ADD_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.add_target'
+NORMATIVE_REMOVE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.remove_target'
+NORMATIVE_TARGET_CHANGED = NORMATIVE_CONFIGURE_INTERFACE + '.target_changed'
+
+
 __all__ = (
-    'install_node_instance',
-    'uninstall_node_instance',
-    'start_node_instance',
-    'stop_node_instance',
+    'NORMATIVE_STANDARD_INTERFACE',
+    'NORMATIVE_CONFIGURE_INTERFACE',
+    'NORMATIVE_CREATE',
+    'NORMATIVE_START',
+    'NORMATIVE_STOP',
+    'NORMATIVE_DELETE',
+    'NORMATIVE_CONFIGURE',
+    'NORMATIVE_PRE_CONFIGURE_SOURCE',
+    'NORMATIVE_PRE_CONFIGURE_TARGET',
+    'NORMATIVE_POST_CONFIGURE_SOURCE',
+    'NORMATIVE_POST_CONFIGURE_TARGET',
+    'NORMATIVE_ADD_SOURCE',
+    'NORMATIVE_ADD_TARGET',
+    'NORMATIVE_REMOVE_TARGET',
+    'NORMATIVE_TARGET_CHANGED',
+    'install_node',
+    'uninstall_node',
+    'start_node',
+    'stop_node',
 )
 
 
-@workflow(suffix_template='{node_instance.id}')
-def install_node_instance(graph, node_instance, **kwargs):
+@workflow(suffix_template='{node.id}')
+def install_node(graph, node, **kwargs):
     sequence = []
 
     # Create
     sequence.append(
-        create_node_instance_task(
-            'tosca.interfaces.node.lifecycle.Standard.create',
-            node_instance))
+        create_node_task(
+            NORMATIVE_CREATE,
+            node))
 
     # Configure
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.pre_configure_source',
-            'source_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_PRE_CONFIGURE_SOURCE,
+            'source',
+            node)
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.pre_configure_target',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_PRE_CONFIGURE_TARGET,
+            'target',
+            node)
     sequence.append(
-        create_node_instance_task(
-            'tosca.interfaces.node.lifecycle.Standard.configure',
-            node_instance))
+        create_node_task(
+            NORMATIVE_CONFIGURE,
+            node))
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.post_configure_source',
-            'source_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_POST_CONFIGURE_SOURCE,
+            'source',
+            node)
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.post_configure_target',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_POST_CONFIGURE_TARGET,
+            'target',
+            node)
 
     # Start
-    sequence += _create_start_tasks(node_instance)
+    sequence += _create_start_tasks(node)
 
     graph.sequence(*sequence)
 
 
-@workflow(suffix_template='{node_instance.id}')
-def uninstall_node_instance(graph, node_instance, **kwargs):
+@workflow(suffix_template='{node.id}')
+def uninstall_node(graph, node, **kwargs):
     # Stop
-    sequence = _create_stop_tasks(node_instance)
+    sequence = _create_stop_tasks(node)
 
     # Delete
     sequence.append(
-        create_node_instance_task(
-            'tosca.interfaces.node.lifecycle.Standard.delete',
-            node_instance))
+        create_node_task(
+            NORMATIVE_DELETE,
+            node))
 
     graph.sequence(*sequence)
 
 
-@workflow(suffix_template='{node_instance.id}')
-def start_node_instance(graph, node_instance, **kwargs):
-    graph.sequence(*_create_start_tasks(node_instance))
+@workflow(suffix_template='{node.id}')
+def start_node(graph, node, **kwargs):
+    graph.sequence(*_create_start_tasks(node))
 
 
-@workflow(suffix_template='{node_instance.id}')
-def stop_node_instance(graph, node_instance, **kwargs):
-    graph.sequence(*_create_stop_tasks(node_instance))
+@workflow(suffix_template='{node.id}')
+def stop_node(graph, node, **kwargs):
+    graph.sequence(*_create_stop_tasks(node))
 
 
-def _create_start_tasks(node_instance):
+def _create_start_tasks(node):
     sequence = []
     sequence.append(
-        create_node_instance_task(
-            'tosca.interfaces.node.lifecycle.Standard.start',
-            node_instance))
+        create_node_task(
+            NORMATIVE_START,
+            node))
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.add_source',
-            'source_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_ADD_SOURCE,
+            'source',
+            node)
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.add_target',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_ADD_TARGET,
+            'target',
+            node)
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.target_changed',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_TARGET_CHANGED,
+            'target',
+            node)
     return sequence
 
 
-def _create_stop_tasks(node_instance):
+def _create_stop_tasks(node):
     sequence = []
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.remove_target',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_REMOVE_TARGET,
+            'target',
+            node)
     sequence += \
-        create_relationship_instance_tasks(
-            'tosca.interfaces.relationship.Configure.target_changed',
-            'target_operations',
-            node_instance)
+        create_relationship_tasks(
+            NORMATIVE_TARGET_CHANGED,
+            'target',
+            node)
     sequence.append(
-        create_node_instance_task(
-            'tosca.interfaces.node.lifecycle.Standard.stop',
-            node_instance))
+        create_node_task(
+            NORMATIVE_STOP,
+            node))
     return sequence

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/core/engine.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index fd83614..55b4159 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -23,7 +23,7 @@ from datetime import datetime
 import networkx
 
 from aria import logger
-from aria.storage import model
+from aria.storage.modeling import model
 from aria.orchestrator import events
 
 from .. import exceptions

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/core/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py
index f65fc0d..d0e1363 100644
--- a/aria/orchestrator/workflows/core/task.py
+++ b/aria/orchestrator/workflows/core/task.py
@@ -24,7 +24,7 @@ from functools import (
 )
 
 from aria import logger
-from aria.storage import model
+from aria.storage.modeling import model
 from aria.orchestrator.context import operation as operation_context
 
 from .. import exceptions
@@ -109,11 +109,11 @@ class OperationTask(BaseTask):
         model_storage = api_task._workflow_context.model
 
         base_task_model = model_storage.task.model_cls
-        if isinstance(api_task.actor, model.NodeInstance):
-            context_class = operation_context.NodeOperationContext
+        if isinstance(api_task.actor, model.Node):
+            context_cls = operation_context.NodeOperationContext
             task_model_cls = base_task_model.as_node_instance
-        elif isinstance(api_task.actor, model.RelationshipInstance):
-            context_class = operation_context.RelationshipOperationContext
+        elif isinstance(api_task.actor, model.Relationship):
+            context_cls = operation_context.RelationshipOperationContext
             task_model_cls = base_task_model.as_relationship_instance
         else:
             raise RuntimeError('No operation context could be created for {actor.model_cls}'
@@ -127,7 +127,7 @@ class OperationTask(BaseTask):
         # package_name and package_version
         operation_task = task_model_cls(
             name=api_task.name,
-            operation_mapping=api_task.operation_mapping,
+            implementation=api_task.implementation,
             instance=api_task.actor,
             inputs=api_task.inputs,
             status=base_task_model.PENDING,
@@ -141,13 +141,13 @@ class OperationTask(BaseTask):
         )
         self._workflow_context.model.task.put(operation_task)
 
-        self._ctx = context_class(name=api_task.name,
-                                  model_storage=self._workflow_context.model,
-                                  resource_storage=self._workflow_context.resource,
-                                  deployment_id=self._workflow_context._deployment_id,
-                                  task_id=operation_task.id,
-                                  actor_id=api_task.actor.id,
-                                  workdir=self._workflow_context._workdir)
+        self._ctx = context_cls(name=api_task.name,
+                                model_storage=self._workflow_context.model,
+                                resource_storage=self._workflow_context.resource,
+                                service_instance_id=self._workflow_context._service_instance_id,
+                                task_id=operation_task.id,
+                                actor_id=api_task.actor.id,
+                                workdir=self._workflow_context._workdir)
         self._task_id = operation_task.id
         self._update_fields = None
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index c4b8ba1..560ac43 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -19,8 +19,8 @@ Subprocess based executor
 
 # pylint: disable=wrong-import-position
 
-import sys
 import os
+import sys
 
 # As part of the process executor implementation, subprocess are started with this module as their
 # entry point. We thus remove this module's directory from the python path if it happens to be
@@ -47,7 +47,7 @@ from aria.utils import imports
 from aria.utils import exceptions
 from aria.orchestrator.workflows.executor import base
 from aria.storage import instrumentation
-from aria.storage import type as storage_type
+from aria.storage.modeling import type as storage_type
 
 _IS_WIN = os.name == 'nt'
 
@@ -190,7 +190,7 @@ class ProcessExecutor(base.BaseExecutor):
     def _create_arguments_dict(self, task):
         return {
             'task_id': task.id,
-            'operation_mapping': task.operation_mapping,
+            'implementation': task.implementation,
             'operation_inputs': task.inputs,
             'port': self._server_port,
             'context': task.context.serialization_dict,
@@ -281,9 +281,9 @@ def _patch_session(ctx, messenger, instrument):
     if not ctx.model:
         return
 
-    # We arbitrarily select the ``node_instance`` mapi to extract the session from it.
+    # We arbitrarily select the ``node`` mapi to extract the session from it.
     # could have been any other mapi just as well
-    session = ctx.model.node_instance._session
+    session = ctx.model.node._session
     original_refresh = session.refresh
 
     def patched_refresh(target):
@@ -317,7 +317,7 @@ def _main():
     messenger = _Messenger(task_id=task_id, port=port)
     messenger.started()
 
-    operation_mapping = arguments['operation_mapping']
+    implementation = arguments['implementation']
     operation_inputs = arguments['operation_inputs']
     context_dict = arguments['context']
 
@@ -329,7 +329,7 @@ def _main():
         try:
             ctx = context_dict['context_cls'].deserialize_from_dict(**context_dict['context'])
             _patch_session(ctx=ctx, messenger=messenger, instrument=instrument)
-            task_func = imports.load_attribute(operation_mapping)
+            task_func = imports.load_attribute(implementation)
             aria.install_aria_extensions()
             for decorate in process_executor.decorate():
                 task_func = decorate(task_func)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/executor/thread.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/thread.py b/aria/orchestrator/workflows/executor/thread.py
index 1a6ad9f..7ae0217 100644
--- a/aria/orchestrator/workflows/executor/thread.py
+++ b/aria/orchestrator/workflows/executor/thread.py
@@ -57,7 +57,7 @@ class ThreadExecutor(BaseExecutor):
                 task = self._queue.get(timeout=1)
                 self._task_started(task)
                 try:
-                    task_func = imports.load_attribute(task.operation_mapping)
+                    task_func = imports.load_attribute(task.implementation)
                     task_func(ctx=task.context, **task.inputs)
                     self._task_succeeded(task)
                 except BaseException as e:

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/__init__.py b/aria/parser/modeling/__init__.py
index a1efd9f..cad25ca 100644
--- a/aria/parser/modeling/__init__.py
+++ b/aria/parser/modeling/__init__.py
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from .exceptions import CannotEvaluateFunctionException
 from .context import IdType, ModelingContext
 from .elements import Element, ModelElement, Function, Parameter, Metadata
 from .instance_elements import (ServiceInstance, Node, Capability, Relationship, Artifact, Group,
@@ -24,10 +23,10 @@ from .model_elements import (ServiceModel, NodeTemplate, RequirementTemplate, Ca
                              GroupPolicyTemplate, GroupPolicyTriggerTemplate, MappingTemplate,
                              SubstitutionTemplate, InterfaceTemplate, OperationTemplate)
 from .types import TypeHierarchy, Type, RelationshipType, PolicyType, PolicyTriggerType
-from .storage import initialize_storage
+from .exceptions import CannotEvaluateFunctionException
+
 
 __all__ = (
-    'CannotEvaluateFunctionException',
     'IdType',
     'ModelingContext',
     'Element',
@@ -67,4 +66,5 @@ __all__ = (
     'RelationshipType',
     'PolicyType',
     'PolicyTriggerType',
-    'initialize_storage')
+    'CannotEvaluateFunctionException',
+)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/storage.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/storage.py b/aria/parser/modeling/storage.py
index 46c3a7c..ff1e536 100644
--- a/aria/parser/modeling/storage.py
+++ b/aria/parser/modeling/storage.py
@@ -13,167 +13,129 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+"""
+This solution is temporary, as we plan to combine aria.parser.modeling and aria.storage.modeling
+into one package (aria.modeling?).
+"""
+
 from datetime import datetime
 from threading import RLock
 
-from ...storage import model
-from ...orchestrator import operation
+from ...storage.modeling import model
+from ...orchestrator.decorators import operation
 from ...utils.console import puts, Colored
 from ...utils.formatting import safe_repr
 
 
-def initialize_storage(context, model_storage, deployment_id):
-    blueprint = create_blueprint(context)
-    model_storage.blueprint.put(blueprint)
-
-    deployment = create_deployment(context, blueprint, deployment_id)
-    model_storage.deployment.put(deployment)
-
-    # Create nodes and node instances
-    for node_template in context.modeling.model.node_templates.itervalues():
-        node = create_node(context, deployment, node_template)
-        model_storage.node.put(node)
+def initialize_storage(context, model_storage, service_instance_id):
+    s_service_template = create_service_template(context)
+    model_storage.service_template.put(s_service_template)
 
-        for a_node in context.modeling.instance.find_nodes(node_template.name):
-            node_instance = create_node_instance(node, a_node)
-            model_storage.node_instance.put(node_instance)
+    s_service_instance = create_service_instance(context, s_service_template, service_instance_id)
+    model_storage.service_instance.put(s_service_instance)
 
-    # Create relationships
+    # Create node templates and nodes
     for node_template in context.modeling.model.node_templates.itervalues():
-        for index, requirement_template in enumerate(node_template.requirement_templates):
-            # We are currently limited only to requirements for specific node templates!
-            if requirement_template.target_node_template_name:
-                source = model_storage.node.get_by_name(node_template.name)
-                target = model_storage.node.get_by_name(
-                    requirement_template.target_node_template_name)
-                relationship = create_relationship(context, source, target,
-                                                   requirement_template.relationship_template)
-                model_storage.relationship.put(relationship)
-
-                for node in context.modeling.instance.find_nodes(node_template.name):
-                    for relationship_model in node.relationships:
-                        if relationship_model.source_requirement_index == index:
-                            source_instance = \
-                                model_storage.node_instance.get_by_name(node.id)
-                            target_instance = \
-                                model_storage.node_instance.get_by_name(
-                                    relationship_model.target_node_id)
-                            relationship_instance = \
-                                create_relationship_instance(relationship, source_instance,
-                                                             target_instance)
-                            model_storage.relationship_instance.put(relationship_instance)
-
-
-def create_blueprint(context):
+        s_node_template = create_node_template(s_service_template, node_template)
+        model_storage.node_template.put(s_node_template)
+
+        for node in context.modeling.instance.find_nodes(node_template.name):
+            s_node = create_node(s_service_instance, s_node_template, node)
+            model_storage.node.put(s_node)
+            create_interfaces(context, model_storage, node.interfaces,
+                              s_node, 'node', None, '_dry_node')
+
+    # Create relationships between nodes
+    for source_node in context.modeling.instance.nodes.itervalues():
+        for relationship in source_node.relationships:
+            s_source_node = model_storage.node.get_by_name(source_node.id)
+            s_target_node = model_storage.node.get_by_name(relationship.target_node_id)
+            s_relationship = create_relationship(s_source_node, s_target_node)
+            model_storage.relationship.put(s_relationship)
+            # TOSCA always uses the "source" edge
+            create_interfaces(context, model_storage, relationship.source_interfaces,
+                              s_relationship, 'relationship', 'source', '_dry_relationship')
+
+
+def create_service_template(context):
     now = datetime.utcnow()
     main_file_name = unicode(context.presentation.location)
     try:
         name = context.modeling.model.metadata.values.get('template_name')
     except AttributeError:
         name = None
-    return model.Blueprint(
-        plan={},
+    return model.ServiceTemplate(
         name=name or main_file_name,
         description=context.modeling.model.description or '',
         created_at=now,
         updated_at=now,
-        main_file_name=main_file_name)
+        main_file_name=main_file_name,
+        plan={}
+    )
 
 
-def create_deployment(context, blueprint, deployment_id):
+def create_service_instance(context, service_template, service_instance_id):
     now = datetime.utcnow()
-    return model.Deployment(
-        name='%s_%s' % (blueprint.name, deployment_id),
-        blueprint_fk=blueprint.id,
+    return model.ServiceInstance(
+        name='{0}_{1}'.format(service_template.name, service_instance_id),
+        service_template=service_template,
         description=context.modeling.instance.description or '',
         created_at=now,
-        updated_at=now,
-        workflows={},
-        inputs={},
-        groups={},
-        permalink='',
-        policy_triggers={},
-        policy_types={},
-        outputs={},
-        scaling_groups={})
-
-
-def create_node(context, deployment, node_template):
-    operations = create_operations(context, node_template.interface_templates, '_dry_node')
-    return model.Node(
-        name=node_template.name,
-        type=node_template.type_name,
-        type_hierarchy=[],
-        number_of_instances=node_template.default_instances,
-        planned_number_of_instances=node_template.default_instances,
-        deploy_number_of_instances=node_template.default_instances,
-        properties={},
-        operations=operations,
-        min_number_of_instances=node_template.min_instances,
-        max_number_of_instances=node_template.max_instances or 100,
-        deployment_fk=deployment.id)
-
-
-def create_relationship(context, source, target, relationship_template):
-    if relationship_template:
-        source_operations = create_operations(context,
-                                              relationship_template.source_interface_templates,
-                                              '_dry_relationship')
-        target_operations = create_operations(context,
-                                              relationship_template.target_interface_templates,
-                                              '_dry_relationship')
-    else:
-        source_operations = {}
-        target_operations = {}
-    return model.Relationship(
-        source_node_fk=source.id,
-        target_node_fk=target.id,
-        source_interfaces={},
-        source_operations=source_operations,
-        target_interfaces={},
-        target_operations=target_operations,
-        type='rel_type',
-        type_hierarchy=[],
-        properties={})
-
-
-def create_node_instance(node, node_model):
-    return model.NodeInstance(
-        name=node_model.id,
-        runtime_properties={},
-        version=None,
-        node_fk=node.id,
-        state='',
-        scaling_groups=[])
+        updated_at=now)
 
 
-def create_relationship_instance(relationship, source_instance, target_instance):
-    return model.RelationshipInstance(
-        relationship_fk=relationship.id,
-        source_node_instance_fk=source_instance.id,
-        target_node_instance_fk=target_instance.id)
+def create_node_template(service_template, node_template):
+    return model.NodeTemplate(
+        name=node_template.name,
+        type_name=node_template.type_name,
+        default_instances=node_template.default_instances,
+        min_instances=node_template.min_instances,
+        max_instances=node_template.max_instances or 100,
+        service_template=service_template)
 
 
-def create_operations(context, interfaces, fn_name):
-    operations = {}
-    for interface in interfaces.itervalues():
-        operations[interface.type_name] = {}
-        for oper in interface.operation_templates.itervalues():
-            name = '%s.%s' % (interface.type_name, oper.name)
-            operations[name] = {
-                'operation': '%s.%s' % (__name__, fn_name),
-                'inputs': {
-                    '_plugin': None,
-                    '_implementation': None}}
-            if oper.implementation:
-                plugin, implementation = _parse_implementation(context, oper.implementation)
-                operations[name]['inputs']['_plugin'] = plugin
-                operations[name]['inputs']['_implementation'] = implementation
+def create_node(service_instance, node_template, node):
+    return model.Node(
+        name=node.id,
+        state='',
+        node_template=node_template,
+        service_instance=service_instance)
 
-    return operations
+
+def create_relationship(source_node, target_node):
+    return model.Relationship(
+        source_node=source_node,
+        target_node=target_node)
+
+
+def create_interfaces(context, model_storage, interfaces, node_or_relationship, type_name, edge,
+                      fn_name):
+    for interface_name, interface in interfaces.iteritems():
+        s_interface = model.Interface(name=interface_name,
+                                      type_name=interface.type_name,
+                                      edge=edge)
+        setattr(s_interface, type_name, node_or_relationship)
+        model_storage.interface.put(s_interface)
+        for operation_name, oper in interface.operations.iteritems():
+            operation_name = '{0}.{1}'.format(interface_name, operation_name)
+            s_operation = model.Operation(name=operation_name,
+                                          implementation='{0}.{1}'.format(__name__, fn_name),
+                                          interface=s_interface)
+            plugin, implementation = _parse_implementation(context, oper.implementation)
+            # TODO: operation's user inputs
+            s_operation.inputs.append(model.Parameter(name='_plugin', # pylint: disable=no-member
+                                                      str_value=str(plugin),
+                                                      type='str'))
+            s_operation.inputs.append(model.Parameter(name='_implementation', # pylint: disable=no-member
+                                                      str_value=str(implementation),
+                                                      type='str'))
+            model_storage.operation.put(s_operation)
 
 
 def _parse_implementation(context, implementation):
+    if not implementation:
+        return '', ''
+
     index = implementation.find('>')
     if index == -1:
         return 'execution', implementation
@@ -204,7 +166,7 @@ _TERMINAL_LOCK = RLock()
 @operation
 def _dry_node(ctx, _plugin, _implementation, **kwargs):
     with _TERMINAL_LOCK:
-        print '> node instance: %s' % Colored.red(ctx.node_instance.name)
+        print '> node instance: %s' % Colored.red(ctx.node.name)
         _dump_implementation(_plugin, _implementation)
 
 
@@ -212,8 +174,8 @@ def _dry_node(ctx, _plugin, _implementation, **kwargs):
 def _dry_relationship(ctx, _plugin, _implementation, **kwargs):
     with _TERMINAL_LOCK:
         puts('> relationship instance: %s -> %s' % (
-            Colored.red(ctx.relationship_instance.source_node_instance.name),
-            Colored.red(ctx.relationship_instance.target_node_instance.name)))
+            Colored.red(ctx.relationship.source_node.name),
+            Colored.red(ctx.relationship.target_node.name)))
         _dump_implementation(_plugin, _implementation)
 
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/utils.py b/aria/parser/modeling/utils.py
index 906106e..21db433 100644
--- a/aria/parser/modeling/utils.py
+++ b/aria/parser/modeling/utils.py
@@ -57,7 +57,7 @@ def coerce_value(context, container, value, report_issues=False):
         return [coerce_value(context, container, v, report_issues) for v in value]
     elif isinstance(value, dict):
         return OrderedDict((k, coerce_value(context, container, v, report_issues))
-                           for k, v in value.iteritems())
+                           for k, v in value.items())
     elif hasattr(value, '_evaluate'):
         try:
             value = value._evaluate(context, container)
@@ -73,7 +73,7 @@ def coerce_value(context, container, value, report_issues=False):
 def validate_dict_values(context, the_dict):
     if not the_dict:
         return
-    validate_list_values(context, the_dict.itervalues())
+    validate_list_values(context, the_dict.values())
 
 
 def validate_list_values(context, the_list):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/__init__.py b/aria/storage/__init__.py
index a1c07d7..eaadc7e 100644
--- a/aria/storage/__init__.py
+++ b/aria/storage/__init__.py
@@ -42,14 +42,18 @@ from .core import (
     ModelStorage,
     ResourceStorage,
 )
+from .modeling import (
+    structure,
+    model,
+    model_base,
+    type
+)
 from . import (
     exceptions,
     api,
-    structure,
     core,
     filesystem_rapi,
     sql_mapi,
-    model
 )
 
 __all__ = (
@@ -60,5 +64,8 @@ __all__ = (
     'ResourceStorage',
     'filesystem_rapi',
     'sql_mapi',
-    'api'
+    'api',
+    'model',
+    'model_base',
+    'type',
 )

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/api.py
----------------------------------------------------------------------
diff --git a/aria/storage/api.py b/aria/storage/api.py
index f6da6de..09a4dd9 100644
--- a/aria/storage/api.py
+++ b/aria/storage/api.py
@@ -44,7 +44,7 @@ class ModelAPI(StorageAPI):
         """
         super(ModelAPI, self).__init__(**kwargs)
         self._model_cls = model_cls
-        self._name = name or generate_lower_name(model_cls)
+        self._name = name or model_cls.__modelname__
 
     @property
     def name(self):
@@ -178,6 +178,4 @@ def generate_lower_name(model_cls):
     :return: lower name
     :rtype: basestring
     """
-    return ''.join(
-        character if character.islower() else '_{0}'.format(character.lower())
-        for character in model_cls.__name__)[1:]
+    return getattr(model_cls, '__mapiname__', model_cls.__tablename__)