ariatosca-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mxm...@apache.org
Subject incubator-ariatosca git commit: Phase 2: added orchestrator based models and merged existing models
Date Mon, 23 Jan 2017 13:26:40 GMT
Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-44-Merge-parser-and-storage-models 5dac33458 -> 5e5ff9cea


Phase 2: added orchestrator based models and merged existing models


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

Branch: refs/heads/ARIA-44-Merge-parser-and-storage-models
Commit: 5e5ff9cead9880be2c38b4d6749ac573bfd33e66
Parents: 5dac334
Author: mxmrlv <mxmrlv@gmail.com>
Authored: Mon Jan 23 15:26:26 2017 +0200
Committer: mxmrlv <mxmrlv@gmail.com>
Committed: Mon Jan 23 15:26:26 2017 +0200

----------------------------------------------------------------------
 aria/modeling/instance_elements.py     | 164 ++++++++--
 aria/modeling/model_elements.py        |  72 ++++-
 aria/modeling/models.py                |  48 ++-
 aria/modeling/orchestrator_elements.py | 459 ++++++++++++++++++++++++++++
 aria/storage/base_model.py             |   1 -
 aria/storage/structure.py              |   5 +
 tests/storage/test_new_modelling.py    |  19 +-
 7 files changed, 715 insertions(+), 53 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/aria/modeling/instance_elements.py
----------------------------------------------------------------------
diff --git a/aria/modeling/instance_elements.py b/aria/modeling/instance_elements.py
index 5af244f..dda0320 100644
--- a/aria/modeling/instance_elements.py
+++ b/aria/modeling/instance_elements.py
@@ -4,10 +4,16 @@ from sqlalchemy import (
     Text,
     Integer,
 )
+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 . import elements, utils
-from ..storage import structure, type
+from ..storage import (
+    structure,
+    type as aria_types,
+)
 from ..utils import collections, formatting, console
 from ..parser import validation
 
@@ -17,9 +23,46 @@ from ..parser import validation
 class ServiceInstance(structure.ModelMixin):
     __tablename__ = 'service_instance'
     description = Column(Text)
-    # Check: type
     metadata = Column(Text)
-    substitution = Column(Text)
+
+    # region orchestrator required columns
+
+    created_at = Column(DateTime, nullable=False, index=True)
+    groups = Column(aria_types.Dict)
+    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(cls):
+        return cls.many_to_one_relationship('service_template_fk')
+
+    @declared_attr
+    def service_template_name(cls):
+        from . import model_elements
+        return association_proxy('service_template', model_elements.ServiceTemplate)
+
+    # endregion
+
+    # region foregin keys
+    @declared_attr
+    def substitution_fk(cls):
+        return cls.foreign_key(Substitution)
+
+    @declared_attr
+    def service_template_fk(cls):
+        from . import model_elements
+        return cls.foreign_key(model_elements.ServiceTemplate)
+    # endregion
+
+    # region one-to-one relationships
+    @declared_attr
+    def substitution(cls):
+        return cls.one_to_one_relationship(Substitution)
+    # endregion
 
     # region one-to-many relationships
     @declared_attr
@@ -51,6 +94,12 @@ class ServiceInstance(structure.ModelMixin):
 
     # endregion
 
+    # association proxies
+
+    @declared_attr
+    def service_template_name(cls):
+        return association_proxy('service_template', cls.name_column_name())
+
     def satisfy_requirements(self, context):
         satisfied = True
         for node in self.nodes.itervalues():
@@ -131,9 +180,8 @@ class Operation(structure.ModelMixin):
 
     # endregion
     description = Column(Text)
-    # Check: what's this for?
     implementation = Column(Text)
-    dependencies = Column(type.StrictList(item_cls=basestring))
+    dependencies = Column(aria_types.StrictList(item_cls=basestring))
 
     executor = Column(Text)
     max_retries = Column(Integer, default=None)
@@ -365,11 +413,10 @@ class Artifact(structure.ModelMixin):
 
     description = Column(Text)
     type_name = Column(Text)
-    # Check: what's that?
     source_path = Column(Text)
     target_path = Column(Text)
     repository_url = Column(Text)
-    repository_credential = Column(type.StrictDict(basestring, basestring))
+    repository_credential = Column(aria_types.StrictDict(basestring, basestring))
 
     # region many-to-many relationships
 
@@ -439,8 +486,8 @@ class Policy(structure.ModelMixin):
 
     # endregion
     type_name = Column(Text)
-    target_node_ids = Column(type.StrictList(basestring))
-    target_group_ids = Column(type.StrictList(basestring))
+    target_node_ids = Column(aria_types.StrictList(basestring))
+    target_group_ids = Column(aria_types.StrictList(basestring))
 
     # region many-to-many relationships
 
@@ -581,7 +628,6 @@ class GroupPolicyTrigger(structure.ModelMixin):
     # endregion
 
     description = Column(Text)
-    # Check: what's that?
     implementation = Column(Text)
 
     # region many-to-many relationships
@@ -661,11 +707,11 @@ class Substitution(structure.ModelMixin):
 
     @declared_attr
     def capabilities(cls):
-        return cls.many_to_many_relationship(elements.Parameter, table_prefix='capabilities')
+        return cls.many_to_many_relationship(Mapping, table_prefix='capabilities')
 
     @declared_attr
     def requirements(cls):
-        return cls.many_to_many_relationship(elements.Parameter, table_prefix='requirements')
+        return cls.many_to_many_relationship(Mapping, table_prefix='requirements')
 
     # endregion
 
@@ -727,11 +773,51 @@ class Node(structure.ModelMixin):
     def service_instance_fk(cls):
         return cls.foreign_key(ServiceInstance)
 
+    @declared_attr
+    def host_fk(cls):
+        return cls.foreign_key(Node)
+
+    @declared_attr
+    def node_template_fk(cls):
+        from . import model_elements
+        return cls.foreign_key(model_elements.NodeTemplate)
+
     # 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 host(cls):
+        return cls.relationship_to_self('host_fk')
+
+    @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
+
+    @declared_attr
+    def node_template(cls):
+        return cls.many_to_one_relationship('node_template_fk')
+
+    @declared_attr
+    def service_template(cls):
+        return association_proxy('service_instance', 'service_template')
+    # endregion
+
     # region one-to-many relationships
     @declared_attr
     def interfaces(cls):
@@ -745,10 +831,6 @@ class Node(structure.ModelMixin):
     def capabilities(cls):
         return cls.one_to_many_relationship(Capability)
 
-    @declared_attr
-    def relationships(cls):
-        return cls.one_to_many_relationship(Relationship)
-
     # endregion
 
     # region many-to-many relationships
@@ -917,8 +999,8 @@ class Group(structure.ModelMixin):
 
     type_name = Column(Text)
     template_name = Column(Text)
-    member_node_ids = Column(type.StrictList(basestring))
-    member_group_ids = Column(type.StrictList(basestring))
+    member_node_ids = Column(aria_types.StrictList(basestring))
+    member_group_ids = Column(aria_types.StrictList(basestring))
 
     # region one-to-many relationships
     @declared_attr
@@ -986,6 +1068,7 @@ class Group(structure.ModelMixin):
 
 # region Relationship instances
 
+
 class Relationship(structure.ModelMixin):
     """
     Connects :class:`Node` to another node.
@@ -1018,7 +1101,50 @@ class Relationship(structure.ModelMixin):
     type_name = Column(Text)
     template_name = Column(Text)
 
-    # region many-to-many relationsh
+    # 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(
+            'source_node_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_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(
+            'target_node_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_name(cls):
+        return association_proxy('target_node', cls.name_column_name())
+    # endregion
+
+    # region many-to-many relationship
 
     @declared_attr
     def properties(cls):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/aria/modeling/model_elements.py
----------------------------------------------------------------------
diff --git a/aria/modeling/model_elements.py b/aria/modeling/model_elements.py
index 94ba6cb..08f74f8 100644
--- a/aria/modeling/model_elements.py
+++ b/aria/modeling/model_elements.py
@@ -19,11 +19,16 @@ from sqlalchemy import (
     Column,
     Text,
     Integer,
+    DateTime,
 )
+from sqlalchemy.ext.associationproxy import association_proxy
 from sqlalchemy.ext.declarative import declared_attr
 
 from . import elements, instance_elements, utils
-from ..storage import structure, type
+from ..storage import (
+    structure,
+   type as aria_type,
+)
 from ..utils import collections, formatting, console
 from ..parser import validation
 
@@ -39,8 +44,28 @@ class ServiceTemplate(structure.ModelMixin):
 
     description = Column(Text)
     metadata = Column(Text)
-    # Check type
-    substitution_template = Column(Text)
+
+    # region orchestrator required columns
+
+    created_at = Column(DateTime, nullable=False, index=True)
+    main_file_name = Column(Text)
+    plan = Column(aria_type.Dict)
+    updated_at = Column(DateTime)
+
+    # endregion
+
+    # region foreign keys
+    @declared_attr
+    def substitution_template_fk(cls):
+        return cls.foreign_key(SubstitutionTemplate)
+
+    # endregion
+
+    # region one-to-one relationships
+    @declared_attr
+    def substitution_template(cls):
+        return cls.one_to_one_relationship(SubstitutionTemplate)
+    # endregion
 
     # region one-to-many relationships
     @declared_attr
@@ -249,10 +274,8 @@ class OperationTemplate(structure.ModelMixin):
     # endregion
 
     description = Column(Text)
-    # Check: type
     implementation = Column(Text)
-    dependencies = Column(type.StrictList(item_cls=basestring))
-    # Check
+    dependencies = Column(aria_type.StrictList(item_cls=basestring))
     executor = Column(Text)
     max_retries = Column(Integer)
     retry_interval = Column(Integer)
@@ -339,10 +362,9 @@ class ArtifactTemplate(structure.ModelMixin):
     description = Column(Text)
     type_name = Column(Text)
     source_path = Column(Text)
-    # Check: what the hell is target path?
     target_path = Column(Text)
     repository_url = Column(Text)
-    repository_credential = Column(type.StrictDict(basestring, basestring))
+    repository_credential = Column(aria_type.StrictDict(basestring, basestring))
 
     # region many-to-many relationships
 
@@ -429,8 +451,8 @@ class PolicyTemplate(structure.ModelMixin):
 
     description = Column(Text)
     type_name = Column(Text)
-    target_node_template_names = Column(type.StrictList(basestring))
-    target_group_template_names = Column(type.StrictList(basestring))
+    target_node_template_names = Column(aria_type.StrictList(basestring))
+    target_group_template_names = Column(aria_type.StrictList(basestring))
 
     # region many-to-many relationships
 
@@ -585,7 +607,6 @@ class GroupPolicyTriggerTemplate(structure.ModelMixin):
     # endregion
 
     description = Column(Text)
-    # Check: ???
     implementation = Column(Text)
 
     # region many-to-many relationships
@@ -683,7 +704,7 @@ class SubstitutionTemplate(structure.ModelMixin):
     * :code:`capability_templates`: Dict of :class:`MappingTemplate`
     * :code:`requirement_templates`: Dict of :class:`MappingTemplate`
     """
-    __tablename__ = 'substituion_template'
+    __tablename__ = 'substitution_template'
     node_type_name = Column(Text)
 
     # region many-to-many relationships
@@ -748,6 +769,10 @@ class NodeTemplate(structure.ModelMixin):
     def service_template_fk(cls):
         return cls.foreign_key(ServiceTemplate)
 
+    @declared_attr
+    def host_fk(cls):
+        return cls.foreign_key(NodeTemplate, nullable=True)
+
     # endregion
 
     description = Column(Text)
@@ -755,7 +780,22 @@ class NodeTemplate(structure.ModelMixin):
     default_instances = Column(Integer, default=1)
     min_instances = Column(Integer, default=0)
     man_instances = Column(Integer, default=None)
-    target_node_template_constraints = Column(type.StrictList(FunctionType))
+    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_fk', cls.name_column_name())
+
+    # endregion
 
     # region one-to-many relationships
     @declared_attr
@@ -882,8 +922,8 @@ class GroupTemplate(structure.ModelMixin):
 
     description = Column(Text)
     type_name = Column(Text)
-    member_node_template_names = Column(type.StrictList(basestring))
-    member_group_template_names = Column(type.StrictList(basestring))
+    member_node_template_names = Column(aria_type.StrictList(basestring))
+    member_group_template_names = Column(aria_type.StrictList(basestring))
 
     # region one-to-many relationships
     @declared_attr
@@ -992,7 +1032,7 @@ class RequirementTemplate(structure.ModelMixin):
 
     target_node_type_name = Column(Text)
     target_node_template_name = Column(Text)
-    target_node_template_constraints = Column(type.StrictList(FunctionType))
+    target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
     target_capability_type_name = Column(Text)
     target_capability_name = Column(Text)
     # CHECK: ???

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/aria/modeling/models.py
----------------------------------------------------------------------
diff --git a/aria/modeling/models.py b/aria/modeling/models.py
index 95978bb..0f76942 100644
--- a/aria/modeling/models.py
+++ b/aria/modeling/models.py
@@ -1,7 +1,7 @@
 from sqlalchemy.ext.declarative import declarative_base
 
 from ..storage import structure
-from . import elements, model_elements, instance_elements
+from . import elements, model_elements, instance_elements, orchestrator_elements
 
 DB = declarative_base(cls=structure.ModelIDMixin)
 
@@ -15,6 +15,12 @@ class Parameter(DB, elements.Parameter):
 
 # region template models
 
+class MappingTemplate(DB, model_elements.MappingTemplate):
+    pass
+
+
+class SubstitutionTemplate(DB, model_elements.SubstitutionTemplate):
+    pass
 
 class ServiceTemplate(DB, model_elements.ServiceTemplate):
     pass
@@ -52,25 +58,25 @@ class GroupPolicyTriggerTemplate(DB, model_elements.GroupPolicyTriggerTemplate):
     pass
 
 
-class MappingTemplate(DB, model_elements.MappingTemplate):
+class RequirementTemplate(DB, model_elements.RequirementTemplate):
     pass
 
 
-class SubstitutionTemplate(DB, model_elements.SubstitutionTemplate):
+class CapabilityTemplate(DB, model_elements.CapabilityTemplate):
     pass
 
 
-class RequirementTemplate(DB, model_elements.RequirementTemplate):
-    pass
+# endregion
 
+# region instance models
 
-class CapabilityTemplate(DB, model_elements.CapabilityTemplate):
+class Mapping(DB, instance_elements.Mapping):
     pass
 
 
-# endregion
+class Substitution(DB, instance_elements.Substitution):
+    pass
 
-# region instance models
 
 class ServiceInstance(DB, instance_elements.ServiceInstance):
     pass
@@ -113,15 +119,33 @@ class GroupPolicyTrigger(DB, instance_elements.GroupPolicyTrigger):
     pass
 
 
-class Mapping(DB, instance_elements.Mapping):
+class Relationship(DB, instance_elements.Relationship):
     pass
 
 
-class Substitution(DB, instance_elements.Substitution):
+# endregion
+
+# region orchestrator models
+class Execution(DB, orchestrator_elements.ExecutionBase):
     pass
 
 
-class Relationship(DB, instance_elements.Relationship):
+class ServiceInstanceUpdate(DB, orchestrator_elements.ServiceInstanceUpdateBase):
+    pass
+
+
+class ServiceInstanceUpdateStep(DB, orchestrator_elements.ServiceInstanceUpdateStepBase):
     pass
 
-# endregion
\ No newline at end of file
+
+class ServiceInstanceModification(DB, orchestrator_elements.ServiceInstanceModificationBase):
+    pass
+
+
+class Plugin(DB, orchestrator_elements.PluginBase):
+    pass
+
+
+class Task(DB, orchestrator_elements.TaskBase):
+    pass
+# endregion

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/aria/modeling/orchestrator_elements.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestrator_elements.py b/aria/modeling/orchestrator_elements.py
new file mode 100644
index 0000000..5348a79
--- /dev/null
+++ b/aria/modeling/orchestrator_elements.py
@@ -0,0 +1,459 @@
+# 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 ..storage.structure import ModelMixin
+from ..storage.type import (
+    List,
+    Dict
+)
+
+__all__ = (
+    'BlueprintBase',
+    'DeploymentBase',
+    'ServiceInstanceUpdateStepBase',
+    'ServiceInstanceUpdateBase',
+    'ServiceInstanceModificationBase',
+    'ExecutionBase',
+    'NodeBase',
+    'RelationshipBase',
+    'NodeInstanceBase',
+    'RelationshipInstanceBase',
+    'PluginBase',
+    'TaskBase'
+)
+
+#pylint: disable=no-self-argument, abstract-method
+
+
+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 service_instance_fk(cls):
+        from . import instance_elements
+        return cls.foreign_key(instance_elements.ServiceInstance, nullable=True)
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance_fk')
+
+    @declared_attr
+    def service_instance_name(cls):
+        return association_proxy('service_instance', cls.name_column_name())
+
+    @declared_attr
+    def blueprint_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__ = '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.many_to_one_relationship('execution_fk')
+
+    @declared_attr
+    def execution_name(cls):
+        return association_proxy('execution', cls.name_column_name())
+
+    @declared_attr
+    def service_instance_fk(cls):
+        from . import instance_elements
+        return cls.foreign_key(instance_elements.ServiceInstance)
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance_fk')
+
+    @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__ = '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(ServiceInstanceUpdateBase)
+
+    @declared_attr
+    def deployment_update(cls):
+        return cls.many_to_one_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 ServiceInstanceModificationBase(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 service_instance_fk(cls):
+        from . import instance_elements
+        return cls.foreign_key(instance_elements.ServiceInstance)
+
+    @declared_attr
+    def service_instance(cls):
+        return cls.many_to_one_relationship('service_instance_fk', backreference='modifications')
+
+    @declared_attr
+    def deployment_name(cls):
+        return association_proxy('service_instance', 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_fk(cls):
+        from . import instance_elements
+        return cls.foreign_key(instance_elements.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_fk')
+
+    @declared_attr
+    def relationship_fk(cls):
+        from . import instance_elements
+        return cls.foreign_key(instance_elements.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_fk')
+
+    @declared_attr
+    def plugin_fk(cls):
+        return cls.foreign_key(PluginBase, nullable=True)
+
+    @declared_attr
+    def plugin(cls):
+        return cls.many_to_one_relationship('plugin_fk')
+
+    @declared_attr
+    def plugin_name(cls):
+        return association_proxy('plugin', 'name')
+
+    @declared_attr
+    def execution_fk(cls):
+        return cls.foreign_key(ExecutionBase, nullable=True)
+
+    @declared_attr
+    def execution(cls):
+        return cls.many_to_one_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]
+
+    @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)
+
+    @property
+    def actor(self):
+        """
+        Return the actor of the task
+        :return:
+        """
+        return self.node_instance or self.relationship_instance
+
+    @classmethod
+    def as_node_instance(cls, instance, **kwargs):
+        return cls(node_instance=instance, **kwargs)
+
+    @classmethod
+    def as_relationship_instance(cls, instance, **kwargs):
+        return cls(relationship_instance=instance, **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/5e5ff9ce/aria/storage/base_model.py
----------------------------------------------------------------------
diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py
index 4ac71d3..65344a6 100644
--- a/aria/storage/base_model.py
+++ b/aria/storage/base_model.py
@@ -602,7 +602,6 @@ class RelationshipInstanceBase(ModelMixin):
         return association_proxy('relationship', cls.name_column_name())
 
 
-
 class PluginBase(ModelMixin):
     """
     Plugin model representation.

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/aria/storage/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/structure.py b/aria/storage/structure.py
index de14747..472b5ba 100644
--- a/aria/storage/structure.py
+++ b/aria/storage/structure.py
@@ -82,6 +82,11 @@ class ModelMixin(Element):
         return column
 
     @classmethod
+    def one_to_one_relationship(cls, table, backreference=None):
+        return relationship(table.__name__,
+                            backref=backref(backreference or cls.__tablename__, uselist=False))
+
+    @classmethod
     def many_to_one_relationship(cls,
                                  foreign_key_column,
                                  backreference=None,

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/5e5ff9ce/tests/storage/test_new_modelling.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_new_modelling.py b/tests/storage/test_new_modelling.py
index 6efeb51..ff96d3d 100644
--- a/tests/storage/test_new_modelling.py
+++ b/tests/storage/test_new_modelling.py
@@ -18,6 +18,9 @@ DB = declarative_base(cls=structure.ModelIDMixin)
 
 tosca_classes = [
     models.Parameter,
+
+    models.MappingTemplate,
+    models.SubstitutionTemplate,
     models.ServiceTemplate,
     models.NodeTemplate,
     models.GroupTemplate,
@@ -27,10 +30,11 @@ tosca_classes = [
     models.PolicyTemplate,
     models.GroupPolicyTemplate,
     models.GroupPolicyTriggerTemplate,
-    models.MappingTemplate,
-    models.SubstitutionTemplate,
     models.RequirementTemplate,
     models.CapabilityTemplate,
+
+    models.Mapping,
+    models.Substitution,
     models.ServiceInstance,
     models.Node,
     models.Group,
@@ -41,9 +45,14 @@ tosca_classes = [
     models.Policy,
     models.GroupPolicy,
     models.GroupPolicyTrigger,
-    models.Mapping,
-    models.Substitution,
-    models.Relationship
+    models.Relationship,
+
+    models.Execution,
+    models.ServiceInstanceUpdate,
+    models.ServiceInstanceUpdateStep,
+    models.ServiceInstanceModification,
+    models.Plugin,
+    models.Task
 ]
 
 


Mime
View raw message