Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-52-Support-order-management-in-relationships 238e2757e -> 17d07ed97 (forced update)
ARIA-52-Support-order-management-in-relationships Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/17d07ed9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/17d07ed9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/17d07ed9 Branch: refs/heads/ARIA-52-Support-order-management-in-relationships Commit: 17d07ed972341dd2780067316732f4d789e846ed Parents: 3caf177 Author: mxmrlv <[email protected]> Authored: Tue Dec 27 16:42:57 2016 +0200 Committer: mxmrlv <[email protected]> Committed: Sun Jan 8 15:34:46 2017 +0200 ---------------------------------------------------------------------- aria/storage/base_model.py | 73 ++++++++++++++---- aria/storage/sql_mapi.py | 2 +- aria/storage/structure.py | 10 ++- tests/storage/test_model_storage.py | 128 ++++++++++++++++++++++++++++++- 4 files changed, 194 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/17d07ed9/aria/storage/base_model.py ---------------------------------------------------------------------- diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py index 418d3b6..a2bbb60 100644 --- a/aria/storage/base_model.py +++ b/aria/storage/base_model.py @@ -51,6 +51,7 @@ from sqlalchemy import ( Float, orm, ) +from sqlalchemy.ext.orderinglist import ordering_list from ..orchestrator.exceptions import TaskAbortException, TaskRetryException from .structure import ModelMixin @@ -415,7 +416,14 @@ class RelationshipBase(ModelMixin): """ __tablename__ = 'relationships' - _private_fields = ['source_node_fk', 'target_node_fk'] + _private_fields = ['source_node_fk', 'target_node_fk', 'source_position', 'target_position'] + + source_position = Column(Integer) + target_position = Column(Integer) + + @declared_attr + def deployment_id(self): + return association_proxy('source_node', 'deployment_id') @declared_attr def source_node_fk(cls): @@ -423,8 +431,14 @@ class RelationshipBase(ModelMixin): @declared_attr def source_node(cls): - return cls.one_to_many_relationship('source_node_fk', - backreference='outbound_relationships') + return cls.one_to_many_relationship( + 'source_node_fk', + backreference='outbound_relationships', + backref_kwargs=dict( + order_by=cls.source_position, + collection_class=ordering_list('source_position', count_from=0) + ) + ) @declared_attr def source_name(cls): @@ -432,11 +446,18 @@ class RelationshipBase(ModelMixin): @declared_attr def target_node_fk(cls): - return cls.foreign_key(NodeBase) + return cls.foreign_key(NodeBase, nullable=True) @declared_attr def target_node(cls): - return cls.one_to_many_relationship('target_node_fk', backreference='inbound_relationships') + return cls.one_to_many_relationship( + 'target_node_fk', + backreference='inbound_relationships', + backref_kwargs=dict( + order_by=cls.target_position, + collection_class=ordering_list('target_position', count_from=0) + ) + ) @declared_attr def target_name(cls): @@ -515,35 +536,60 @@ class RelationshipInstanceBase(ModelMixin): __tablename__ = 'relationship_instances' _private_fields = ['relationship_storage_fk', 'source_node_instance_fk', - 'target_node_instance_fk'] + 'target_node_instance_fk', + 'source_position', + 'target_position'] + + source_position = Column(Integer) + target_position = Column(Integer) @declared_attr def source_node_instance_fk(cls): - return cls.foreign_key(NodeInstanceBase) + return cls.foreign_key(NodeInstanceBase, nullable=True) @declared_attr def source_node_instance(cls): - return cls.one_to_many_relationship('source_node_instance_fk', - backreference='outbound_relationship_instances') + return cls.one_to_many_relationship( + 'source_node_instance_fk', + backreference='outbound_relationship_instances', + backref_kwargs=dict( + order_by=cls.source_position, + collection_class=ordering_list('source_position', count_from=0) + ) + ) @declared_attr def source_node_instance_name(cls): + return association_proxy('source_node_instance', 'node_{0}'.format(cls.name_column_name())) + + @declared_attr + def source_node_name(cls): return association_proxy('source_node_instance', cls.name_column_name()) @declared_attr def target_node_instance_fk(cls): - return cls.foreign_key(NodeInstanceBase) + return cls.foreign_key(NodeInstanceBase, nullable=True) @declared_attr def target_node_instance(cls): - return cls.one_to_many_relationship('target_node_instance_fk', - backreference='inbound_relationship_instances') + return cls.one_to_many_relationship( + 'target_node_instance_fk', + backreference='inbound_relationship_instances', + backref_kwargs=dict( + order_by=cls.target_position, + collection_class=ordering_list('target_position', count_from=0) + ) + ) @declared_attr def target_node_instance_name(cls): return association_proxy('target_node_instance', cls.name_column_name()) @declared_attr + def target_node_name(cls): + return association_proxy('target_node_instance', 'node_{0}'.format(cls.name_column_name())) + + @declared_attr def relationship_fk(cls): return cls.foreign_key(RelationshipBase) @@ -556,6 +602,7 @@ class RelationshipInstanceBase(ModelMixin): return association_proxy('relationship', cls.name_column_name()) + class PluginBase(ModelMixin): """ Plugin model representation. @@ -658,7 +705,7 @@ class TaskBase(ModelMixin): INFINITE_RETRIES = -1 - status = Column(Enum(*STATES), name='status', default=PENDING) + status = Column(Enum(*STATES, name='status'), default=PENDING) due_at = Column(DateTime, default=datetime.utcnow) started_at = Column(DateTime, default=None) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/17d07ed9/aria/storage/sql_mapi.py ---------------------------------------------------------------------- diff --git a/aria/storage/sql_mapi.py b/aria/storage/sql_mapi.py index cde40c2..809f677 100644 --- a/aria/storage/sql_mapi.py +++ b/aria/storage/sql_mapi.py @@ -46,7 +46,7 @@ class SQLAlchemyModelAPI(api.ModelAPI): if not result: raise exceptions.StorageError( - 'Requested {0} with ID `{1}` was not found' + 'Requested `{0}` with ID `{1}` was not found' .format(self.model_cls.__name__, entry_id) ) return result http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/17d07ed9/aria/storage/structure.py ---------------------------------------------------------------------- diff --git a/aria/storage/structure.py b/aria/storage/structure.py index 431633b..fa592ac 100644 --- a/aria/storage/structure.py +++ b/aria/storage/structure.py @@ -80,7 +80,9 @@ class ModelMixin(object): @classmethod def one_to_many_relationship(cls, foreign_key_column, - backreference=None): + backreference=None, + backref_kwargs=None, + **kwargs): """Return a one-to-many SQL relationship object Meant to be used from inside the *child* object @@ -89,6 +91,7 @@ class ModelMixin(object): :param foreign_key_column: The column of the foreign key (from the child table) :param backreference: The name to give to the reference to the child (on the parent table) """ + backref_kwargs = backref_kwargs or {} parent_table = cls._get_cls_by_tablename( getattr(cls, foreign_key_column).__remote_table_name) primaryjoin_str = '{parent_class_name}.{parent_unique_id} == ' \ @@ -105,7 +108,8 @@ class ModelMixin(object): foreign_keys=[getattr(cls, foreign_key_column)], # The following line make sure that when the *parent* is # deleted, all its connected children are deleted as well - backref=backref(backreference or cls.__tablename__, cascade='all'), + backref=backref(backreference or cls.__tablename__, cascade='all', **backref_kwargs), + **kwargs ) @classmethod @@ -145,6 +149,8 @@ class ModelMixin(object): 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 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/17d07ed9/tests/storage/test_model_storage.py ---------------------------------------------------------------------- diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py index 6f9527e..0e8d1a0 100644 --- a/tests/storage/test_model_storage.py +++ b/tests/storage/test_model_storage.py @@ -27,7 +27,11 @@ from aria.storage import ( ) from aria import application_model_storage from ..storage import get_sqlite_api_kwargs, release_sqlite_storage -from ..mock import context as mock_context +from ..mock import ( + context as mock_context, + models, + operations +) class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method @@ -46,6 +50,11 @@ def storage(): release_sqlite_storage(base_storage) [email protected] +def context(): + return mock_context.simple(get_sqlite_api_kwargs()) + + @pytest.fixture(scope='module', autouse=True) def module_cleanup(): model.DeclarativeBase.metadata.remove(MockModel.__table__) #pylint: disable=no-member @@ -104,8 +113,7 @@ def test_inner_list_update(storage): assert storage_mm.model_list[0] == 'new_value' -def test_model_to_dict(): - context = mock_context.simple(get_sqlite_api_kwargs()) +def test_model_to_dict(context): deployment = context.deployment deployment_dict = deployment.to_dict() @@ -144,3 +152,117 @@ def test_application_storage_factory(): assert storage.execution release_sqlite_storage(storage) + + +def test_relationship_model_ordering(context): + deployment = context.model.deployment.get_by_name(models.DEPLOYMENT_NAME) + source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME) + source_node_instance = context.model.node_instance.get_by_name( + models.DEPENDENT_NODE_INSTANCE_NAME) + target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME) + target_node_instance = context.model.node_instance.get_by_name( + models.DEPENDENCY_NODE_INSTANCE_NAME) + new_node = model.Node( + name='new_node', + type='test_node_type', + type_hierarchy=[], + number_of_instances=1, + planned_number_of_instances=1, + deploy_number_of_instances=1, + properties={}, + operations=dict((key, {}) for key in operations.NODE_OPERATIONS), + min_number_of_instances=1, + max_number_of_instances=1, + deployment=deployment + ) + source_to_new_relationship = model.Relationship( + source_node=source_node, + target_node=new_node, + source_interfaces={}, + source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS), + target_interfaces={}, + target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS), + type='rel_type', + type_hierarchy=[], + properties={}, + ) + new_node_instance = model.NodeInstance( + name='new_node_instance', + runtime_properties={}, + version=None, + node=new_node, + state='', + scaling_groups=[] + ) + source_to_new_relationship_instance = model.RelationshipInstance( + relationship=source_to_new_relationship, + source_node_instance=source_node_instance, + target_node_instance=new_node_instance, + ) + + new_to_target_relationship = model.Relationship( + source_node=new_node, + target_node=target_node, + source_interfaces={}, + source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS), + target_interfaces={}, + target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS), + type='rel_type', + type_hierarchy=[], + properties={}, + ) + new_to_target_relationship_instance = model.RelationshipInstance( + relationship=new_to_target_relationship, + source_node_instance=new_node_instance, + target_node_instance=target_node_instance, + ) + + + context.model.node.put(new_node) + context.model.node_instance.put(new_node_instance) + context.model.relationship.put(source_to_new_relationship) + context.model.relationship.put(new_to_target_relationship) + context.model.relationship_instance.put(source_to_new_relationship_instance) + context.model.relationship_instance.put(new_to_target_relationship_instance) + + def flip_and_assert(node_instance, direction): + """ + Reversed the order of relationships and assert effects took place. + :param node_instance: the node instance to operatate on + :param direction: the type of relationships to flip (inbound/outbount) + :return: + """ + assert direction in ('inbound', 'outbound') + + relationships = getattr(node_instance.node, direction + '_relationships') + relationship_instances = getattr(node_instance, direction + '_relationship_instances') + assert len(relationships) == 2 + assert len(relationship_instances) == 2 + + first_rel, second_rel = relationships + first_rel_instance, second_rel_instance = relationship_instances + assert getattr(first_rel, relationships.ordering_attr) == 0 + assert getattr(second_rel, relationships.ordering_attr) == 1 + assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 0 + assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 1 + + reversed_relationships = list(reversed(relationships)) + reversed_relationship_instances = list(reversed(relationship_instances)) + + assert relationships != reversed_relationships + assert relationship_instances != reversed_relationship_instances + + relationships[:] = reversed_relationships + relationship_instances[:] = reversed_relationship_instances + context.model.node_instance.update(node_instance) + + assert relationships == reversed_relationships + assert relationship_instances == reversed_relationship_instances + + assert getattr(first_rel, relationships.ordering_attr) == 1 + assert getattr(second_rel, relationships.ordering_attr) == 0 + assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 1 + assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 0 + + flip_and_assert(source_node_instance, 'outbound') + flip_and_assert(target_node_instance, 'inbound')
