ariatosca-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mxm...@apache.org
Subject [7/8] incubator-ariatosca git commit: Separate plugin specification form plugin; move dry support to CLI; various renames and refactorings
Date Sun, 12 Mar 2017 09:45:57 GMT
Separate plugin specification form plugin; move dry support to CLI; various renames and refactorings


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

Branch: refs/heads/Unified_coerce
Commit: dd5bfa930dede8a81ce382e65cad8fe924d4e99d
Parents: aa01cd4
Author: Tal Liron <tal.liron@gmail.com>
Authored: Fri Mar 10 16:57:17 2017 -0600
Committer: Tal Liron <tal.liron@gmail.com>
Committed: Fri Mar 10 16:57:17 2017 -0600

----------------------------------------------------------------------
 aria/cli/args_parser.py                         |    5 +
 aria/cli/commands.py                            |    4 +
 aria/cli/dry.py                                 |   75 +
 aria/modeling/__init__.py                       |   18 +-
 aria/modeling/bases.py                          |  405 -----
 aria/modeling/misc.py                           |  234 ---
 aria/modeling/mixins.py                         |  405 +++++
 aria/modeling/models.py                         |   91 +-
 aria/modeling/orchestration.py                  |  235 +--
 aria/modeling/service.py                        | 1529 ------------------
 aria/modeling/service_changes.py                |  219 +++
 aria/modeling/service_common.py                 |  270 ++++
 aria/modeling/service_instance.py               | 1529 ++++++++++++++++++
 aria/modeling/service_template.py               |  260 +--
 aria/orchestrator/workflows/api/task.py         |   44 +-
 .../workflows/builtin/execute_operation.py      |    3 +-
 aria/orchestrator/workflows/builtin/utils.py    |   13 +-
 .../orchestrator/workflows/builtin/workflows.py |   60 +-
 aria/orchestrator/workflows/core/task.py        |   13 +-
 aria/orchestrator/workflows/dry.py              |   53 -
 .../simple_v1_0/modeling/__init__.py            |   47 +-
 tests/end2end/test_orchestrator.py              |    3 +
 tests/mock/models.py                            |   16 +
 tests/modeling/__init__.py                      |   34 +
 tests/modeling/test_mixins.py                   |  219 +++
 tests/modeling/test_model_storage.py            |  102 ++
 tests/modeling/test_models.py                   |  835 ++++++++++
 tests/orchestrator/context/test_operation.py    |   15 +-
 tests/orchestrator/context/test_serialize.py    |    7 +-
 tests/orchestrator/context/test_toolbelt.py     |    2 +-
 tests/orchestrator/workflows/api/test_task.py   |   33 +-
 .../workflows/builtin/test_execute_operation.py |    2 +-
 tests/orchestrator/workflows/core/test_task.py  |   15 +-
 .../tosca-simple-1.0/node-cellar/workflows.py   |    3 +-
 tests/storage/__init__.py                       |   22 +-
 tests/storage/test_instrumentation.py           |    8 +-
 tests/storage/test_model_storage.py             |  102 --
 tests/storage/test_models.py                    |  833 ----------
 tests/storage/test_structures.py                |  220 ---
 39 files changed, 4069 insertions(+), 3914 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/args_parser.py
----------------------------------------------------------------------
diff --git a/aria/cli/args_parser.py b/aria/cli/args_parser.py
index e092ee6..1d18145 100644
--- a/aria/cli/args_parser.py
+++ b/aria/cli/args_parser.py
@@ -137,6 +137,11 @@ def add_workflow_parser(workflow):
         '-w', '--workflow',
         default='install',
         help='The workflow name')
+    workflow.add_flag_argument(
+        'dry',
+        default=True,
+        help_true='dry run',
+        help_false='wet run')
 
 
 @sub_parser_decorator(

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index 52d4f14..a9079c5 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -50,6 +50,7 @@ from ..utils.collections import OrderedDict
 from ..orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS
 from ..orchestrator.runner import Runner
 from ..orchestrator.workflows.builtin import BUILTIN_WORKFLOWS
+from .dry import convert_to_dry
 
 from .exceptions import (
     AriaCliFormatInputsError,
@@ -212,6 +213,7 @@ class WorkflowCommand(BaseCommand):
 
         context = self._parse(args_namespace.uri)
         workflow_fn, inputs = self._get_workflow(context, args_namespace.workflow)
+        self._dry = args_namespace.dry
         self._run(context, args_namespace.workflow, workflow_fn, inputs)
     
     def _parse(self, uri):
@@ -265,6 +267,8 @@ class WorkflowCommand(BaseCommand):
     def _run(self, context, workflow_name, workflow_fn, inputs):
         # Storage
         def _initialize_storage(model_storage):
+            if self._dry:
+                convert_to_dry(context.modeling.instance)
             context.modeling.store(model_storage)
 
         # Create runner

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/dry.py
----------------------------------------------------------------------
diff --git a/aria/cli/dry.py b/aria/cli/dry.py
new file mode 100644
index 0000000..98b7217
--- /dev/null
+++ b/aria/cli/dry.py
@@ -0,0 +1,75 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from threading import RLock
+
+from ..modeling import models
+from ..orchestrator.decorators import operation
+from ..utils.collections import OrderedDict
+from ..utils.console import puts, Colored
+from ..utils.formatting import safe_repr
+
+
+_TERMINAL_LOCK = RLock()
+
+
+def convert_to_dry(service):
+    for workflow in service.workflows:
+        convert_operation_to_dry(workflow)
+
+    for node in service.nodes:
+        for interface in node.interfaces.itervalues():
+            for oper in interface.operations.itervalues():
+                convert_operation_to_dry(oper)
+        for relationship in node.outbound_relationships:
+            for interface in relationship.interfaces.itervalues():
+                for oper in interface.operations.itervalues():
+                    convert_operation_to_dry(oper)
+
+
+def convert_operation_to_dry(oper):
+    plugin = oper.plugin_specification.name \
+        if oper.plugin_specification is not None else None
+    if oper.inputs is None:
+        oper.inputs = OrderedDict()
+    oper.inputs['_implementation'] = models.Parameter(name='_implementation',
+                                                      type_name='string',
+                                                      value=oper.implementation)
+    oper.inputs['_plugin'] = models.Parameter(name='_plugin',
+                                              type_name='string',
+                                              value=plugin)
+    oper.implementation = '{0}.{1}'.format(__name__, 'dry_operation')
+    oper.plugin_specification = None
+
+
+@operation
+def dry_operation(ctx, _plugin, _implementation, **kwargs):
+    with _TERMINAL_LOCK:
+        print ctx.name
+        if hasattr(ctx, 'relationship'):
+            puts('> Relationship: {0} -> {1}'.format(
+                Colored.red(ctx.relationship.source_node.name),
+                Colored.red(ctx.relationship.target_node.name)))
+        else:
+            puts('> Node: {0}'.format(Colored.red(ctx.node.name)))
+        puts('  Operation: {0}'.format(Colored.green(ctx.name)))
+        _dump_implementation(_plugin, _implementation)
+
+
+def _dump_implementation(plugin, implementation):
+    if plugin:
+        puts('  Plugin: {0}'.format(Colored.magenta(plugin, bold=True)))
+    if implementation:
+        puts('  Implementation: {0}'.format(Colored.magenta(safe_repr(implementation))))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/modeling/__init__.py b/aria/modeling/__init__.py
index 67b5703..4dfc39d 100644
--- a/aria/modeling/__init__.py
+++ b/aria/modeling/__init__.py
@@ -16,26 +16,32 @@
 from collections import namedtuple
 
 from . import (
-    bases,
+    mixins,
     types,
     models,
     service_template as _service_template_bases,
-    service as _service_bases,
-    orchestration as _orchestration_bases,
+    service_instance as _service_instance_bases,
+    service_changes as _service_changes_bases,
+    service_common as _service_common_bases,
+    orchestration as _orchestration_bases
 )
 
 
 _ModelBasesCls = namedtuple('ModelBase', 'service_template,'
-                                         'service,'
+                                         'service_instance,'
+                                         'service_changes,'
+                                         'service_common,'
                                          'orchestration')
 
 model_bases = _ModelBasesCls(service_template=_service_template_bases,
-                             service=_service_bases,
+                             service_instance=_service_instance_bases,
+                             service_changes=_service_changes_bases,
+                             service_common=_service_common_bases,
                              orchestration=_orchestration_bases)
 
 
 __all__ = (
-    'bases',
+    'mixins',
     'types',
     'models',
     'model_bases',

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/bases.py
----------------------------------------------------------------------
diff --git a/aria/modeling/bases.py b/aria/modeling/bases.py
deleted file mode 100644
index efcb968..0000000
--- a/aria/modeling/bases.py
+++ /dev/null
@@ -1,405 +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:
-    * ModelMixin - abstract model implementation.
-    * ModelIDMixin - abstract model implementation with IDs.
-"""
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext import associationproxy
-from sqlalchemy import (
-    Column,
-    ForeignKey,
-    Integer,
-    Text,
-    Table,
-)
-
-from . import utils
-from ..utils import formatting
-
-
-class ModelMixin(object):
-
-    @utils.classproperty
-    def __modelname__(cls):                                                                         # pylint: disable=no-self-argument
-        return getattr(cls, '__mapiname__', cls.__tablename__)
-
-    @classmethod
-    def id_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def name_column_name(cls):
-        raise NotImplementedError
-
-    @classmethod
-    def foreign_key(cls, parent_table, nullable=False):
-        """
-        Return a ForeignKey object.
-
-        :param parent_table: Parent table name
-        :param nullable: Should the column be allowed to remain empty
-        """
-        return Column(Integer,
-                      ForeignKey('{table}.id'.format(table=parent_table),
-                                 ondelete='CASCADE'),
-                      nullable=nullable)
-
-    @classmethod
-    def relationship_to_self(cls,
-                             column_name,
-                             relationship_kwargs=None):
-        relationship_kwargs = relationship_kwargs or {}
-
-        remote_side = '{cls}.{remote_column}'.format(
-            cls=cls.__name__,
-            remote_column=cls.id_column_name()
-        )
-
-        primaryjoin = '{remote_side} == {cls}.{column}'.format(
-            remote_side=remote_side,
-            cls=cls.__name__,
-            column=column_name
-        )
-
-        return relationship(
-            cls._get_cls_by_tablename(cls.__tablename__).__name__,
-            primaryjoin=primaryjoin,
-            remote_side=remote_side,
-            post_update=True,
-            **relationship_kwargs
-        )
-
-    @classmethod
-    def one_to_many_relationship_to_self(cls,
-                                         key,
-                                         dict_key=None,
-                                         relationship_kwargs=None):
-        relationship_kwargs = relationship_kwargs or {}
-
-        relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
-            cls=cls.__name__,
-            remote_column=key
-        ))
-
-        return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
-                                        backreference='', dict_key=dict_key)
-
-    @classmethod
-    def one_to_one_relationship(cls,
-                                other_table,
-                                key=None,
-                                foreign_key=None,
-                                backreference=None,
-                                backref_kwargs=None,
-                                relationship_kwargs=None):
-        backref_kwargs = backref_kwargs or {}
-        backref_kwargs.setdefault('uselist', False)
-
-        return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, foreign_key=foreign_key)
-
-    @classmethod
-    def one_to_many_relationship(cls,
-                                 child_table,
-                                 key=None,
-                                 foreign_key=None,
-                                 dict_key=None,
-                                 backreference=None,
-                                 backref_kwargs=None,
-                                 relationship_kwargs=None):
-        backref_kwargs = backref_kwargs or {}
-        backref_kwargs.setdefault('uselist', False)
-
-        return cls._create_relationship(child_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, foreign_key=foreign_key,
-                                        dict_key=dict_key)
-
-    @classmethod
-    def many_to_one_relationship(cls,
-                                 parent_table,
-                                 key=None,
-                                 foreign_key=None,
-                                 backreference=None,
-                                 backref_kwargs=None,
-                                 relationship_kwargs=None):
-        """
-        Return a one-to-many SQL relationship object
-        Meant to be used from inside the *child* object
-
-        :param parent_table: Name of the parent table
-        :param foreign_key: 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)
-        """
-
-        if backreference is None:
-            backreference = formatting.pluralize(cls.__tablename__)
-
-        backref_kwargs = backref_kwargs or {}
-        backref_kwargs.setdefault('uselist', True)
-        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 cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, foreign_key=foreign_key)
-
-    @classmethod
-    def many_to_many_relationship(cls,
-                                  other_table,
-                                  table_prefix=None,
-                                  key=None,
-                                  dict_key=None,
-                                  backreference=None,
-                                  backref_kwargs=None,
-                                  relationship_kwargs=None):
-        """
-        Return a many-to-many SQL relationship object
-
-        Notes:
-
-        1. The backreference name is the current table's table name
-        2. This method creates a new helper table in the DB
-
-        :param cls: The class of the table we're connecting from
-        :param other_table: The class of the table we're connecting to
-        :param table_prefix: Custom prefix for the helper table name and the backreference name
-        :param dict_key: If provided, will use a dict class with this column as the key
-        """
-
-        this_table = cls.__tablename__
-        this_column_name = '{0}_id'.format(this_table)
-        this_foreign_key = '{0}.id'.format(this_table)
-
-        other_column_name = '{0}_id'.format(other_table)
-        other_foreign_key = '{0}.id'.format(other_table)
-
-        helper_table = '{0}_{1}'.format(this_table, other_table)
-
-        if backreference is None:
-            backreference = formatting.pluralize(this_table)
-            if table_prefix:
-                helper_table = '{0}_{1}'.format(table_prefix, helper_table)
-                backreference = '{0}_{1}'.format(table_prefix, backreference)
-
-        backref_kwargs = backref_kwargs or {}
-        backref_kwargs.setdefault('uselist', True)
-
-        relationship_kwargs = relationship_kwargs or {}
-        relationship_kwargs.setdefault('secondary', cls._get_secondary_table(
-            cls.metadata,
-            helper_table,
-            this_column_name,
-            other_column_name,
-            this_foreign_key,
-            other_foreign_key
-        ))
-
-        return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
-                                        backreference, key=key, dict_key=dict_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 _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
-                             key=None, foreign_key=None, dict_key=None):
-        relationship_kwargs = relationship_kwargs or {}
-
-        if key:
-            relationship_kwargs.setdefault('foreign_keys',
-                                           lambda: getattr(
-                                               cls._get_cls_by_tablename(cls.__tablename__),
-                                               key))
-
-        elif foreign_key:
-            relationship_kwargs.setdefault('foreign_keys',
-                                           lambda: getattr(
-                                               cls._get_cls_by_tablename(table),
-                                               foreign_key))
-
-        if dict_key:
-            relationship_kwargs.setdefault('collection_class',
-                                           attribute_mapped_collection(dict_key))
-
-        if backreference == '':
-            return relationship(
-                lambda: cls._get_cls_by_tablename(table),
-                **relationship_kwargs
-            )
-        else:
-            if backreference is None:
-                backreference = cls.__tablename__
-            backref_kwargs = backref_kwargs or {}
-            return relationship(
-                lambda: cls._get_cls_by_tablename(table),
-                backref=backref(backreference, **backref_kwargs),
-                **relationship_kwargs
-            )
-
-    @staticmethod
-    def _get_secondary_table(metadata,
-                             helper_table,
-                             first_column,
-                             second_column,
-                             first_foreign_key,
-                             second_foreign_key):
-        """
-        Create a helper table for a many-to-many relationship
-
-        :param helper_table: 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,
-            metadata,
-            Column(
-                first_column,
-                Integer,
-                ForeignKey(first_foreign_key)
-            ),
-            Column(
-                second_column,
-                Integer,
-                ForeignKey(second_foreign_key)
-            )
-        )
-
-    @classmethod
-    def _association_proxies(cls):
-        for col, value in vars(cls).items():
-            if isinstance(value, associationproxy.AssociationProxy):
-                yield col
-
-    @classmethod
-    def fields(cls):
-        """
-        Return the list of field names for this table
-
-        Mostly for backwards compatibility in the code (that uses `fields`)
-        """
-
-        fields = set(cls._association_proxies())
-        fields.update(cls.__table__.columns.keys())
-        return fields - set(getattr(cls, '__private_fields__', []))
-
-    @classmethod
-    def _get_cls_by_tablename(cls, tablename):
-        """
-        Return class reference mapped to table.
-
-        :param tablename: String with name of table.
-        :return: Class reference or None.
-        """
-
-        if tablename in (cls.__name__, cls.__tablename__):
-            return cls
-
-        for table_cls in cls._decl_class_registry.values():
-            if tablename == getattr(table_cls, '__tablename__', None):
-                return table_cls
-
-    def __repr__(self):
-        return '<{cls} id=`{id}`>'.format(
-            cls=self.__class__.__name__,
-            id=getattr(self, self.name_column_name()))
-
-
-class ModelIDMixin(object):
-    id = Column(Integer, primary_key=True, autoincrement=True)
-    name = Column(Text, index=True)
-
-    @classmethod
-    def id_column_name(cls):
-        return 'id'
-
-    @classmethod
-    def name_column_name(cls):
-        return 'name'
-
-
-class InstanceModelMixin(ModelMixin):
-    """
-    Mixin for :class:`ServiceInstance` models.
-
-    All models 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 TemplateModelMixin(InstanceModelMixin):
-    """
-    Mixin for :class:`ServiceTemplate` models.
-
-    All model models can be instantiated into :class:`ServiceInstance` models.
-    """
-
-    def instantiate(self, context, container):
-        raise NotImplementedError

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/misc.py
----------------------------------------------------------------------
diff --git a/aria/modeling/misc.py b/aria/modeling/misc.py
deleted file mode 100644
index 105876a..0000000
--- a/aria/modeling/misc.py
+++ /dev/null
@@ -1,234 +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.
-
-# pylint: disable=no-self-argument, no-member, abstract-method
-
-import cPickle as pickle
-import logging
-
-from sqlalchemy import (
-    Column,
-    Text,
-    Binary
-)
-from sqlalchemy.ext.declarative import declared_attr
-
-from ..storage import exceptions
-from ..utils import collections, formatting, console
-from .bases import InstanceModelMixin, TemplateModelMixin
-from . import utils
-
-
-class ParameterBase(TemplateModelMixin):
-    """
-    Represents a typed value.
-
-    This class is used by both service template and service instance elements.
-
-    :ivar name: Name
-    :ivar type_name: Type name
-    :ivar value: Value
-    :ivar description: Description
-    """
-
-    __tablename__ = 'parameter'
-
-    name = Column(Text)
-    type_name = Column(Text)
-
-    # Check: value type
-    _value = Column(Binary, name='value')
-    description = Column(Text)
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('type_name', self.type_name),
-            ('value', self.value),
-            ('description', self.description)))
-
-    @property
-    def value(self):
-        if self._value is None:
-            return None
-        try:
-            return pickle.loads(self._value)
-        except BaseException:
-            raise exceptions.StorageError('bad format for parameter of type "{0}": {1}'.format(
-                self.type_name, self._value))
-
-    @value.setter
-    def value(self, value):
-        if value is None:
-            self._value = None
-        else:
-            try:
-                self._value = pickle.dumps(value)
-            except (pickle.PicklingError, TypeError):
-                logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
-                                               .format(self.type_name, value))
-                self._value = pickle.dumps(str(value))
-
-    def instantiate(self, context, container):
-        from . import models
-        return models.Parameter(name=self.name,
-                                type_name=self.type_name,
-                                _value=self._value,
-                                description=self.description)
-
-    def coerce_values(self, context, container, report_issues):
-        if self.value is not None:
-            self.value = utils.coerce_value(context, container, self.value,
-                                            report_issues)
-
-    def dump(self, context):
-        if self.type_name is not None:
-            console.puts('{0}: {1} ({2})'.format(
-                context.style.property(self.name),
-                context.style.literal(self.value),
-                context.style.type(self.type_name)))
-        else:
-            console.puts('{0}: {1}'.format(
-                context.style.property(self.name),
-                context.style.literal(self.value)))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-
-
-class TypeBase(InstanceModelMixin):
-    """
-    Represents a type and its children.
-    """
-
-    __tablename__ = 'type'
-
-    variant = Column(Text, nullable=False)
-    description = Column(Text)
-    _role = Column(Text, name='role')
-
-    @declared_attr
-    def parent(cls):
-        return cls.relationship_to_self('parent_type_fk')
-
-    @declared_attr
-    def children(cls):
-        return cls.one_to_many_relationship_to_self('parent_type_fk')
-
-    # region foreign keys
-
-    __private_fields__ = ['parent_type_fk']
-
-    # Type one-to-many to Type
-    @declared_attr
-    def parent_type_fk(cls):
-        return cls.foreign_key('type', nullable=True)
-
-    # endregion
-
-    @property
-    def role(self):
-        def get_role(the_type):
-            if the_type is None:
-                return None
-            elif the_type._role is None:
-                return get_role(the_type.parent)
-            return the_type._role
-
-        return get_role(self)
-
-    @role.setter
-    def role(self, value):
-        self._role = value
-
-    def is_descendant(self, base_name, name):
-        base = self.get_descendant(base_name)
-        if base is not None:
-            if base.get_descendant(name) is not None:
-                return True
-        return False
-
-    def get_descendant(self, name):
-        if self.name == name:
-            return self
-        for child in self.children:
-            found = child.get_descendant(name)
-            if found is not None:
-                return found
-        return None
-
-    def iter_descendants(self):
-        for child in self.children:
-            yield child
-            for descendant in child.iter_descendants():
-                yield descendant
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('description', self.description),
-            ('role', self.role)))
-
-    @property
-    def as_raw_all(self):
-        types = []
-        self._append_raw_children(types)
-        return types
-
-    def dump(self, context):
-        if self.name:
-            console.puts(context.style.type(self.name))
-        with context.style.indent:
-            for child in self.children:
-                child.dump(context)
-
-    def _append_raw_children(self, types):
-        for child in self.children:
-            raw_child = formatting.as_raw(child)
-            raw_child['parent'] = self.name
-            types.append(raw_child)
-            child._append_raw_children(types)
-
-
-class MetadataBase(TemplateModelMixin):
-    """
-    Custom values associated with the service.
-
-    This class is used by both service template and service instance elements.
-
-    :ivar name: Name
-    :ivar value: Value
-    """
-
-    __tablename__ = 'metadata'
-
-    value = Column(Text)
-
-    @property
-    def as_raw(self):
-        return collections.OrderedDict((
-            ('name', self.name),
-            ('value', self.value)))
-
-    def instantiate(self, context, container):
-        from . import models
-        return models.Metadata(name=self.name,
-                               value=self.value)
-
-    def dump(self, context):
-        console.puts('{0}: {1}'.format(
-            context.style.property(self.name),
-            context.style.literal(self.value)))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
new file mode 100644
index 0000000..04497b5
--- /dev/null
+++ b/aria/modeling/mixins.py
@@ -0,0 +1,405 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+ARIA's storage.structures module
+Path: aria.storage.structures
+
+models module holds ARIA's models.
+
+classes:
+    * ModelMixin - abstract model implementation.
+    * ModelIDMixin - abstract model implementation with IDs.
+"""
+
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.orm.collections import attribute_mapped_collection
+from sqlalchemy.ext import associationproxy
+from sqlalchemy import (
+    Column,
+    ForeignKey,
+    Integer,
+    Text,
+    Table,
+)
+
+from . import utils
+from ..utils import formatting
+
+
+class ModelMixin(object):
+
+    @utils.classproperty
+    def __modelname__(cls):                                                                         # pylint: disable=no-self-argument
+        return getattr(cls, '__mapiname__', cls.__tablename__)
+
+    @classmethod
+    def id_column_name(cls):
+        raise NotImplementedError
+
+    @classmethod
+    def name_column_name(cls):
+        raise NotImplementedError
+
+    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 fields(cls):
+        """
+        Return the list of field names for this table
+
+        Mostly for backwards compatibility in the code (that uses `fields`)
+        """
+
+        fields = set(cls._iter_association_proxies())
+        fields.update(cls.__table__.columns.keys())
+        return fields - set(getattr(cls, '__private_fields__', []))
+
+    @classmethod
+    def _create_foreign_key(cls, parent_table, nullable=False):
+        """
+        Return a ForeignKey object.
+
+        :param parent_table: Parent table name
+        :param nullable: Should the column be allowed to remain empty
+        """
+        return Column(Integer,
+                      ForeignKey('{table}.id'.format(table=parent_table),
+                                 ondelete='CASCADE'),
+                      nullable=nullable)
+
+    @classmethod
+    def _create_relationship_to_self(cls,
+                                     column_name,
+                                     relationship_kwargs=None):
+        relationship_kwargs = relationship_kwargs or {}
+
+        remote_side = '{cls}.{remote_column}'.format(
+            cls=cls.__name__,
+            remote_column=cls.id_column_name()
+        )
+
+        primaryjoin = '{remote_side} == {cls}.{column}'.format(
+            remote_side=remote_side,
+            cls=cls.__name__,
+            column=column_name
+        )
+
+        return relationship(
+            cls._get_cls_by_tablename(cls.__tablename__).__name__,
+            primaryjoin=primaryjoin,
+            remote_side=remote_side,
+            post_update=True,
+            **relationship_kwargs
+        )
+
+    @classmethod
+    def _create_one_to_many_relationship_to_self(cls,
+                                                 key,
+                                                 dict_key=None,
+                                                 relationship_kwargs=None):
+        relationship_kwargs = relationship_kwargs or {}
+
+        relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
+            cls=cls.__name__,
+            remote_column=key
+        ))
+
+        return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
+                                        backreference='', dict_key=dict_key)
+
+    @classmethod
+    def _create_one_to_one_relationship(cls,
+                                        other_table,
+                                        key=None,
+                                        foreign_key=None,
+                                        backreference=None,
+                                        backref_kwargs=None,
+                                        relationship_kwargs=None):
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('uselist', False)
+
+        return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
+                                        backreference, key=key, foreign_key=foreign_key)
+
+    @classmethod
+    def _create_one_to_many_relationship(cls,
+                                         child_table,
+                                         key=None,
+                                         foreign_key=None,
+                                         dict_key=None,
+                                         backreference=None,
+                                         backref_kwargs=None,
+                                         relationship_kwargs=None):
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('uselist', False)
+
+        return cls._create_relationship(child_table, backref_kwargs, relationship_kwargs,
+                                        backreference, key=key, foreign_key=foreign_key,
+                                        dict_key=dict_key)
+
+    @classmethod
+    def _create_many_to_one_relationship(cls,
+                                         parent_table,
+                                         key=None,
+                                         foreign_key=None,
+                                         backreference=None,
+                                         backref_kwargs=None,
+                                         relationship_kwargs=None):
+        """
+        Return a one-to-many SQL relationship object
+        Meant to be used from inside the *child* object
+
+        :param parent_table: Name of the parent table
+        :param foreign_key: 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)
+        """
+
+        if backreference is None:
+            backreference = formatting.pluralize(cls.__tablename__)
+
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('uselist', True)
+        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 cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
+                                        backreference, key=key, foreign_key=foreign_key)
+
+    @classmethod
+    def _create_many_to_many_relationship(cls,
+                                          other_table,
+                                          table_prefix=None,
+                                          key=None,
+                                          dict_key=None,
+                                          backreference=None,
+                                          backref_kwargs=None,
+                                          relationship_kwargs=None):
+        """
+        Return a many-to-many SQL relationship object
+
+        Notes:
+
+        1. The backreference name is the current table's table name
+        2. This method creates a new helper table in the DB
+
+        :param cls: The class of the table we're connecting from
+        :param other_table: The class of the table we're connecting to
+        :param table_prefix: Custom prefix for the helper table name and the backreference name
+        :param dict_key: If provided, will use a dict class with this column as the key
+        """
+
+        this_table = cls.__tablename__
+        this_column_name = '{0}_id'.format(this_table)
+        this_foreign_key = '{0}.id'.format(this_table)
+
+        other_column_name = '{0}_id'.format(other_table)
+        other_foreign_key = '{0}.id'.format(other_table)
+
+        helper_table = '{0}_{1}'.format(this_table, other_table)
+
+        if backreference is None:
+            backreference = formatting.pluralize(this_table)
+            if table_prefix:
+                helper_table = '{0}_{1}'.format(table_prefix, helper_table)
+                backreference = '{0}_{1}'.format(table_prefix, backreference)
+
+        backref_kwargs = backref_kwargs or {}
+        backref_kwargs.setdefault('uselist', True)
+
+        relationship_kwargs = relationship_kwargs or {}
+        relationship_kwargs.setdefault('secondary', cls._get_secondary_table(
+            cls.metadata,
+            helper_table,
+            this_column_name,
+            other_column_name,
+            this_foreign_key,
+            other_foreign_key
+        ))
+
+        return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
+                                        backreference, key=key, dict_key=dict_key)
+
+    @classmethod
+    def _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
+                             key=None, foreign_key=None, dict_key=None):
+        relationship_kwargs = relationship_kwargs or {}
+
+        if key:
+            relationship_kwargs.setdefault('foreign_keys',
+                                           lambda: getattr(
+                                               cls._get_cls_by_tablename(cls.__tablename__),
+                                               key))
+
+        elif foreign_key:
+            relationship_kwargs.setdefault('foreign_keys',
+                                           lambda: getattr(
+                                               cls._get_cls_by_tablename(table),
+                                               foreign_key))
+
+        if dict_key:
+            relationship_kwargs.setdefault('collection_class',
+                                           attribute_mapped_collection(dict_key))
+
+        if backreference == '':
+            return relationship(
+                lambda: cls._get_cls_by_tablename(table),
+                **relationship_kwargs
+            )
+        else:
+            if backreference is None:
+                backreference = cls.__tablename__
+            backref_kwargs = backref_kwargs or {}
+            return relationship(
+                lambda: cls._get_cls_by_tablename(table),
+                backref=backref(backreference, **backref_kwargs),
+                **relationship_kwargs
+            )
+
+    @staticmethod
+    def _get_secondary_table(metadata,
+                             helper_table,
+                             first_column,
+                             second_column,
+                             first_foreign_key,
+                             second_foreign_key):
+        """
+        Create a helper table for a many-to-many relationship
+
+        :param helper_table: 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,
+            metadata,
+            Column(
+                first_column,
+                Integer,
+                ForeignKey(first_foreign_key)
+            ),
+            Column(
+                second_column,
+                Integer,
+                ForeignKey(second_foreign_key)
+            )
+        )
+
+    @classmethod
+    def _iter_association_proxies(cls):
+        for col, value in vars(cls).items():
+            if isinstance(value, associationproxy.AssociationProxy):
+                yield col
+
+    @classmethod
+    def _get_cls_by_tablename(cls, tablename):
+        """
+        Return class reference mapped to table.
+
+        :param tablename: String with name of table.
+        :return: Class reference or None.
+        """
+
+        if tablename in (cls.__name__, cls.__tablename__):
+            return cls
+
+        for table_cls in cls._decl_class_registry.values():
+            if tablename == getattr(table_cls, '__tablename__', None):
+                return table_cls
+
+    def __repr__(self):
+        return '<{cls} id=`{id}`>'.format(
+            cls=self.__class__.__name__,
+            id=getattr(self, self.name_column_name()))
+
+
+class ModelIDMixin(object):
+    id = Column(Integer, primary_key=True, autoincrement=True)
+    name = Column(Text, index=True)
+
+    @classmethod
+    def id_column_name(cls):
+        return 'id'
+
+    @classmethod
+    def name_column_name(cls):
+        return 'name'
+
+
+class InstanceModelMixin(ModelMixin):
+    """
+    Mixin for :class:`ServiceInstance` models.
+
+    All models 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 TemplateModelMixin(InstanceModelMixin):
+    """
+    Mixin for :class:`ServiceTemplate` models.
+
+    All model models can be instantiated into :class:`ServiceInstance` models.
+    """
+
+    def instantiate(self, context, container):
+        raise NotImplementedError

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/models.py
----------------------------------------------------------------------
diff --git a/aria/modeling/models.py b/aria/modeling/models.py
index fc2c669..90238e6 100644
--- a/aria/modeling/models.py
+++ b/aria/modeling/models.py
@@ -19,14 +19,15 @@ from sqlalchemy.ext.declarative import declarative_base
 
 from . import (
     service_template,
-    service,
+    service_instance,
+    service_changes,
+    service_common,
     orchestration,
-    misc,
-    bases,
+    mixins,
 )
 
 
-aria_declarative_base = declarative_base(cls=bases.ModelIDMixin)
+aria_declarative_base = declarative_base(cls=mixins.ModelIDMixin)
 
 
 # region service template models
@@ -84,91 +85,99 @@ class ArtifactTemplate(aria_declarative_base, service_template.ArtifactTemplateB
 
 # region service instance models
 
-class Service(aria_declarative_base, service.ServiceBase):
+class Service(aria_declarative_base, service_instance.ServiceBase):
     pass
 
 
-class Node(aria_declarative_base, service.NodeBase):
+class Node(aria_declarative_base, service_instance.NodeBase):
     pass
 
 
-class Group(aria_declarative_base, service.GroupBase):
+class Group(aria_declarative_base, service_instance.GroupBase):
     pass
 
 
-class Policy(aria_declarative_base, service.PolicyBase):
+class Policy(aria_declarative_base, service_instance.PolicyBase):
     pass
 
 
-class Substitution(aria_declarative_base, service.SubstitutionBase):
+class Substitution(aria_declarative_base, service_instance.SubstitutionBase):
     pass
 
 
-class SubstitutionMapping(aria_declarative_base, service.SubstitutionMappingBase):
+class SubstitutionMapping(aria_declarative_base, service_instance.SubstitutionMappingBase):
     pass
 
 
-class Relationship(aria_declarative_base, service.RelationshipBase):
+class Relationship(aria_declarative_base, service_instance.RelationshipBase):
     pass
 
 
-class Capability(aria_declarative_base, service.CapabilityBase):
+class Capability(aria_declarative_base, service_instance.CapabilityBase):
     pass
 
 
-class Interface(aria_declarative_base, service.InterfaceBase):
+class Interface(aria_declarative_base, service_instance.InterfaceBase):
     pass
 
 
-class Operation(aria_declarative_base, service.OperationBase):
+class Operation(aria_declarative_base, service_instance.OperationBase):
     pass
 
 
-class Artifact(aria_declarative_base, service.ArtifactBase):
+class Artifact(aria_declarative_base, service_instance.ArtifactBase):
     pass
 
 # endregion
 
 
-# region orchestration models
+# region service changes models
 
-class Execution(aria_declarative_base, orchestration.Execution):
+class ServiceUpdate(aria_declarative_base, service_changes.ServiceUpdateBase):
     pass
 
 
-class ServiceUpdate(aria_declarative_base, orchestration.ServiceUpdateBase):
+class ServiceUpdateStep(aria_declarative_base, service_changes.ServiceUpdateStepBase):
     pass
 
 
-class ServiceUpdateStep(aria_declarative_base, orchestration.ServiceUpdateStepBase):
+class ServiceModification(aria_declarative_base, service_changes.ServiceModificationBase):
     pass
 
+# endregion
+
+
+# region common service models
 
-class ServiceModification(aria_declarative_base, orchestration.ServiceModificationBase):
+class Parameter(aria_declarative_base, service_common.ParameterBase):
     pass
 
 
-class Plugin(aria_declarative_base, orchestration.PluginBase):
+class Type(aria_declarative_base, service_common.TypeBase):
     pass
 
 
-class Task(aria_declarative_base, orchestration.TaskBase):
+class Metadata(aria_declarative_base, service_common.MetadataBase):
+    pass
+
+
+class PluginSpecification(aria_declarative_base, service_common.PluginSpecificationBase):
     pass
 
 # endregion
 
 
-# region misc models
+# region orchestration models
 
-class Parameter(aria_declarative_base, misc.ParameterBase):
+class Execution(aria_declarative_base, orchestration.ExecutionBase):
     pass
 
 
-class Type(aria_declarative_base, misc.TypeBase):
+class Plugin(aria_declarative_base, orchestration.PluginBase):
     pass
 
 
-class Metadata(aria_declarative_base, misc.MetadataBase):
+class Task(aria_declarative_base, orchestration.TaskBase):
     pass
 
 # endregion
@@ -202,18 +211,21 @@ models_to_register = [
     Operation,
     Artifact,
 
-    # Orchestration models
-    Execution,
+    # Service changes models
     ServiceUpdate,
     ServiceUpdateStep,
     ServiceModification,
-    Plugin,
-    Task,
 
-    # Misc models
+    # Common service models
     Parameter,
     Type,
-    Metadata
+    Metadata,
+    PluginSpecification,
+
+    # Orchestration models
+    Execution,
+    Plugin,
+    Task
 ]
 
 __all__ = (
@@ -247,16 +259,19 @@ __all__ = (
     'Operation',
     'Artifact',
 
-    # Orchestration models
-    'Execution',
+    # Service changes models
     'ServiceUpdate',
     'ServiceUpdateStep',
     'ServiceModification',
-    'Plugin',
-    'Task',
 
-    # Misc models
+    # Common service models
     'Parameter',
     'Type',
-    'Metadata'
+    'Metadata',
+    'PluginSpecification',
+
+    # Orchestration models
+    'Execution',
+    'Plugin',
+    'Task'
 )

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/orchestration.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index c842c07..d8bdd3c 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -14,23 +14,14 @@
 # limitations under the License.
 
 """
-ARIA's storage.models module
-Path: aria.storage.models
-
-models module holds ARIA's models.
-
 classes:
     * Execution - execution implementation model.
-    * ServiceUpdate - service update implementation model.
-    * ServiceUpdateStep - service update step implementation model.
-    * ServiceModification - service modification implementation model.
     * Plugin - plugin implementation model.
     * Task - a task
 """
 
 # pylint: disable=no-self-argument, no-member, abstract-method
 
-from collections import namedtuple
 from datetime import datetime
 
 from sqlalchemy import (
@@ -49,19 +40,16 @@ from sqlalchemy.ext.declarative import declared_attr
 
 from ..orchestrator.exceptions import (TaskAbortException, TaskRetryException)
 from .types import (List, Dict)
-from .bases import ModelMixin
+from .mixins import ModelMixin
 
 __all__ = (
-    'Execution',
-    'ServiceUpdateBase',
-    'ServiceUpdateStepBase',
-    'ServiceModificationBase',
+    'ExecutionBase',
     'PluginBase',
     'TaskBase'
 )
 
 
-class Execution(ModelMixin):
+class ExecutionBase(ModelMixin):
     """
     Execution model representation.
     """
@@ -117,7 +105,7 @@ class Execution(ModelMixin):
 
     @declared_attr
     def service(cls):
-        return cls.many_to_one_relationship('service')
+        return cls._create_many_to_one_relationship('service')
 
     @declared_attr
     def service_name(cls):
@@ -133,7 +121,7 @@ class Execution(ModelMixin):
 
     @declared_attr
     def service_fk(cls):
-        return cls.foreign_key('service')
+        return cls._create_foreign_key('service')
 
     # endregion
 
@@ -145,182 +133,6 @@ class Execution(ModelMixin):
         )
 
 
-class ServiceUpdateBase(ModelMixin):
-    """
-    Deployment update model representation.
-    """
-
-    steps = None
-
-    __tablename__ = 'service_update'
-
-    _private_fields = ['execution_fk',
-                       'service_fk']
-
-    created_at = Column(DateTime, nullable=False, index=True)
-    service_plan = Column(Dict, nullable=False)
-    service_update_nodes = Column(Dict)
-    service_update_service = Column(Dict)
-    service_update_node_templates = Column(List)
-    modified_entity_ids = Column(Dict)
-    state = Column(Text)
-
-    @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(cls):
-        return cls.many_to_one_relationship('service',
-                                            backreference='updates')
-
-    @declared_attr
-    def service_name(cls):
-        return association_proxy('service', cls.name_column_name())
-
-    # region foreign keys
-
-    __private_fields__ = ['service_fk',
-                          'execution_fk']
-
-    @declared_attr
-    def execution_fk(cls):
-        return cls.foreign_key('execution', nullable=True)
-
-    @declared_attr
-    def service_fk(cls):
-        return cls.foreign_key('service')
-
-    # endregion
-
-    def to_dict(self, suppress_error=False, **kwargs):
-        dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error)     #pylint: disable=no-member
-        # Taking care of the fact the DeploymentSteps are _BaseModels
-        dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
-        return dep_update_dict
-
-
-class ServiceUpdateStepBase(ModelMixin):
-    """
-    Deployment update step model representation.
-    """
-
-    __tablename__ = 'service_update_step'
-
-    _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, PLUGIN')
-    ENTITY_TYPES = _entity_types(
-        NODE='node',
-        RELATIONSHIP='relationship',
-        PROPERTY='property',
-        OPERATION='operation',
-        WORKFLOW='workflow',
-        OUTPUT='output',
-        DESCRIPTION='description',
-        GROUP='group',
-        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_update(cls):
-        return cls.many_to_one_relationship('service_update',
-                                            backreference='steps')
-
-    @declared_attr
-    def service_update_name(cls):
-        return association_proxy('service_update', cls.name_column_name())
-
-    # region foreign keys
-
-    __private_fields__ = ['service_update_fk']
-
-    @declared_attr
-    def service_update_fk(cls):
-        return cls.foreign_key('service_update')
-
-    # endregion
-
-    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 ServiceModificationBase(ModelMixin):
-    """
-    Deployment modification model representation.
-    """
-
-    __tablename__ = 'service_modification'
-
-    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_node_templates = Column(Dict)
-    nodes = Column(Dict)
-    status = Column(Enum(*STATES, name='service_modification_status'))
-
-    @declared_attr
-    def service(cls):
-        return cls.many_to_one_relationship('service',
-                                            backreference='modifications')
-
-    @declared_attr
-    def service_name(cls):
-        return association_proxy('service', cls.name_column_name())
-
-    # region foreign keys
-
-    __private_fields__ = ['service_fk']
-
-    @declared_attr
-    def service_fk(cls):
-        return cls.foreign_key('service')
-
-    # endregion
-
-
 class PluginBase(ModelMixin):
     """
     Plugin model representation.
@@ -340,16 +152,6 @@ class PluginBase(ModelMixin):
     uploaded_at = Column(DateTime, nullable=False, index=True)
     wheels = Column(List, nullable=False)
 
-    # region foreign keys
-
-    __private_fields__ = ['service_template_fk']
-
-    @declared_attr
-    def service_template_fk(cls):
-        return cls.foreign_key('service_template', nullable=True)
-
-    # endregion
-
 
 class TaskBase(ModelMixin):
     """
@@ -389,7 +191,7 @@ class TaskBase(ModelMixin):
 
     @declared_attr
     def node(cls):
-        return cls.many_to_one_relationship('node')
+        return cls._create_many_to_one_relationship('node')
 
     @declared_attr
     def relationship_name(cls):
@@ -397,15 +199,15 @@ class TaskBase(ModelMixin):
 
     @declared_attr
     def relationship(cls):
-        return cls.many_to_one_relationship('relationship')
+        return cls._create_many_to_one_relationship('relationship')
 
     @declared_attr
     def plugin(cls):
-        return cls.many_to_one_relationship('plugin')
+        return cls._create_many_to_one_relationship('plugin')
 
     @declared_attr
     def execution(cls):
-        return cls.many_to_one_relationship('execution')
+        return cls._create_many_to_one_relationship('execution')
 
     @declared_attr
     def execution_name(cls):
@@ -413,8 +215,8 @@ class TaskBase(ModelMixin):
 
     @declared_attr
     def inputs(cls):
-        return cls.many_to_many_relationship('parameter', table_prefix='inputs',
-                                             dict_key='name')
+        return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+                                                     dict_key='name')
 
     status = Column(Enum(*STATES, name='status'), default=PENDING)
 
@@ -428,9 +230,6 @@ class TaskBase(ModelMixin):
 
     # Operation specific fields
     implementation = Column(String)
-    # 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
@@ -468,28 +267,28 @@ class TaskBase(ModelMixin):
 
     @declared_attr
     def node_fk(cls):
-        return cls.foreign_key('node', nullable=True)
+        return cls._create_foreign_key('node', nullable=True)
 
     @declared_attr
     def relationship_fk(cls):
-        return cls.foreign_key('relationship', nullable=True)
+        return cls._create_foreign_key('relationship', nullable=True)
 
     @declared_attr
     def plugin_fk(cls):
-        return cls.foreign_key('plugin', nullable=True)
+        return cls._create_foreign_key('plugin', nullable=True)
 
     @declared_attr
     def execution_fk(cls):
-        return cls.foreign_key('execution', nullable=True)
+        return cls._create_foreign_key('execution', nullable=True)
 
     # endregion
 
     @classmethod
-    def as_node_task(cls, instance, runs_on, **kwargs):
+    def for_node(cls, instance, runs_on, **kwargs):
         return cls(node=instance, _runs_on=runs_on, **kwargs)
 
     @classmethod
-    def as_relationship_task(cls, instance, runs_on, **kwargs):
+    def for_relationship(cls, instance, runs_on, **kwargs):
         return cls(relationship=instance, _runs_on=runs_on, **kwargs)
 
     @staticmethod


Mime
View raw message