Fixes for code review; make sure to set relationship names correctly
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/7d20a848 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/7d20a848 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/7d20a848 Branch: refs/heads/ARIA-105-integrate-modeling Commit: 7d20a84886fdd90e33b83696edf246346a1cc987 Parents: 09f826a Author: Tal Liron <[email protected]> Authored: Mon Mar 20 14:24:34 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Mon Mar 20 14:24:34 2017 -0500 ---------------------------------------------------------------------- aria/modeling/mixins.py | 12 +- aria/modeling/models.py | 101 ++--- aria/modeling/orchestration.py | 65 +-- aria/modeling/relationship.py | 402 +++++++++++++++++++ aria/modeling/relationships.py | 402 ------------------- aria/modeling/service_changes.py | 41 +- aria/modeling/service_common.py | 27 +- aria/modeling/service_instance.py | 355 ++++++++-------- aria/modeling/service_template.py | 300 +++++++------- aria/modeling/utils.py | 18 +- aria/orchestrator/workflows/api/task.py | 20 +- aria/orchestrator/workflows/builtin/utils.py | 4 +- aria/orchestrator/workflows/exceptions.py | 7 +- docs/requirements.txt | 2 +- .../simple_v1_0/modeling/__init__.py | 8 +- tests/modeling/test_models.py | 2 +- .../workflows/executor/test_executor.py | 1 - 17 files changed, 896 insertions(+), 871 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index 8eb08e8..e6db5a3 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -23,15 +23,15 @@ from sqlalchemy.ext import associationproxy from sqlalchemy import ( Column, Integer, - Text, + Text ) -from .utils import classproperty +from . import utils class ModelMixin(object): - @classproperty + @utils.classproperty def __modelname__(cls): # pylint: disable=no-self-argument return getattr(cls, '__mapiname__', cls.__tablename__) @@ -47,7 +47,7 @@ class ModelMixin(object): """ Return a dict representation of the model - :param suppress_error: If set to True, sets `None` to attributes that it's unable to + :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) """ @@ -77,7 +77,7 @@ class ModelMixin(object): """ Return the list of field names for this table - Mostly for backwards compatibility in the code (that uses `fields`) + Mostly for backwards compatibility in the code (that uses ``fields``) """ fields = set(cls._iter_association_proxies()) @@ -114,7 +114,7 @@ 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`. + raw data (which can be translated into JSON or YAML) via ``as_raw``. """ @property http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/models.py ---------------------------------------------------------------------- diff --git a/aria/modeling/models.py b/aria/modeling/models.py index 0e15273..a01783b 100644 --- a/aria/modeling/models.py +++ b/aria/modeling/models.py @@ -30,6 +30,57 @@ from . import ( aria_declarative_base = declarative_base(cls=mixins.ModelIDMixin) +# See also models_to_register at the bottom of this file +__all__ = ( + 'aria_declarative_base', + 'models_to_register', + + # Service template models + 'ServiceTemplate', + 'NodeTemplate', + 'GroupTemplate', + 'PolicyTemplate', + 'SubstitutionTemplate', + 'SubstitutionTemplateMapping', + 'RequirementTemplate', + 'RelationshipTemplate', + 'CapabilityTemplate', + 'InterfaceTemplate', + 'OperationTemplate', + 'ArtifactTemplate', + + # Service instance models + 'Service', + 'Node', + 'Group', + 'Policy', + 'Substitution', + 'SubstitutionMapping', + 'Relationship', + 'Capability', + 'Interface', + 'Operation', + 'Artifact', + + # Service changes models + 'ServiceUpdate', + 'ServiceUpdateStep', + 'ServiceModification', + + # Common service models + 'Parameter', + 'Type', + 'Metadata', + 'PluginSpecification', + + # Orchestration models + 'Execution', + 'Plugin', + 'Task', + 'Log' +) + + # region service template models class ServiceTemplate(aria_declarative_base, service_template.ServiceTemplateBase): @@ -187,6 +238,7 @@ class Log(aria_declarative_base, orchestration.LogBase): # endregion +# See also __all__ at the top of this file models_to_register = [ # Service template models ServiceTemplate, @@ -232,52 +284,3 @@ models_to_register = [ Task, Log ] - -__all__ = ( - 'aria_declarative_base', - 'models_to_register', - - # Service template models - 'ServiceTemplate', - 'NodeTemplate', - 'GroupTemplate', - 'PolicyTemplate', - 'SubstitutionTemplate', - 'SubstitutionTemplateMapping', - 'RequirementTemplate', - 'RelationshipTemplate', - 'CapabilityTemplate', - 'InterfaceTemplate', - 'OperationTemplate', - 'ArtifactTemplate', - - # Service instance models - 'Service', - 'Node', - 'Group', - 'Policy', - 'Substitution', - 'SubstitutionMapping', - 'Relationship', - 'Capability', - 'Interface', - 'Operation', - 'Artifact', - - # Service changes models - 'ServiceUpdate', - 'ServiceUpdateStep', - 'ServiceModification', - - # Common service models - 'Parameter', - 'Type', - 'Metadata', - 'PluginSpecification', - - # Orchestration models - 'Execution', - 'Plugin', - 'Task', - 'Log' -) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/orchestration.py ---------------------------------------------------------------------- diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py index e8a9986..0277756 100644 --- a/aria/modeling/orchestration.py +++ b/aria/modeling/orchestration.py @@ -41,7 +41,7 @@ from sqlalchemy.ext.declarative import declared_attr from ..orchestrator.exceptions import (TaskAbortException, TaskRetryException) from .types import (List, Dict) from .mixins import ModelMixin -from . import relationships +from . import relationship class ExecutionBase(ModelMixin): @@ -51,6 +51,11 @@ class ExecutionBase(ModelMixin): __tablename__ = 'execution' + __private_fields__ = ['service_fk', + 'service_name', + 'service_template', + 'service_template_name'] + TERMINATED = 'terminated' FAILED = 'failed' CANCELLED = 'cancelled' @@ -96,13 +101,13 @@ class ExecutionBase(ModelMixin): @declared_attr def service(cls): - return relationships.many_to_one(cls, 'service') + return relationship.many_to_one(cls, 'service') # region foreign keys @declared_attr def service_fk(cls): - return relationships.fk('service') + return relationship.foreign_key('service') # endregion @@ -132,11 +137,6 @@ class ExecutionBase(ModelMixin): self.status ) - __private_fields__ = ['service_fk', - 'service_name', - 'service_template', - 'service_template_name'] - class PluginBase(ModelMixin): """ @@ -165,6 +165,14 @@ class TaskBase(ModelMixin): __tablename__ = 'task' + __private_fields__ = ['node_fk', + 'relationship_fk', + 'plugin_fk', + 'execution_fk', + 'node_name', + 'relationship_name', + 'execution_name'] + PENDING = 'pending' RETRYING = 'retrying' SENT = 'sent' @@ -192,23 +200,23 @@ class TaskBase(ModelMixin): @declared_attr def node(cls): - return relationships.many_to_one(cls, 'node') + return relationship.many_to_one(cls, 'node') @declared_attr def relationship(cls): - return relationships.many_to_one(cls, 'relationship') + return relationship.many_to_one(cls, 'relationship') @declared_attr def plugin(cls): - return relationships.many_to_one(cls, 'plugin') + return relationship.many_to_one(cls, 'plugin') @declared_attr def execution(cls): - return relationships.many_to_one(cls, 'execution') + return relationship.many_to_one(cls, 'execution') @declared_attr def inputs(cls): - return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') status = Column(Enum(*STATES, name='status'), default=PENDING) @@ -254,19 +262,19 @@ class TaskBase(ModelMixin): @declared_attr def node_fk(cls): - return relationships.fk('node', nullable=True) + return relationship.foreign_key('node', nullable=True) @declared_attr def relationship_fk(cls): - return relationships.fk('relationship', nullable=True) + return relationship.foreign_key('relationship', nullable=True) @declared_attr def plugin_fk(cls): - return relationships.fk('plugin', nullable=True) + return relationship.foreign_key('plugin', nullable=True) @declared_attr def execution_fk(cls): - return relationships.fk('execution', nullable=True) + return relationship.foreign_key('execution', nullable=True) # endregion @@ -305,25 +313,21 @@ class TaskBase(ModelMixin): def retry(message=None, retry_interval=None): raise TaskRetryException(message, retry_interval=retry_interval) - __private_fields__ = ['node_fk', - 'relationship_fk', - 'plugin_fk', - 'execution_fk', - 'node_name', - 'relationship_name', - 'execution_name'] - class LogBase(ModelMixin): + __tablename__ = 'log' + __private_fields__ = ['execution_fk', + 'task_fk'] + @declared_attr def execution(cls): - return relationships.many_to_one(cls, 'execution') + return relationship.many_to_one(cls, 'execution') @declared_attr def task(cls): - return relationships.many_to_one(cls, 'task') + return relationship.many_to_one(cls, 'task') level = Column(String) msg = Column(String) @@ -334,17 +338,14 @@ class LogBase(ModelMixin): @declared_attr def execution_fk(cls): - return relationships.fk('execution') + return relationship.foreign_key('execution') @declared_attr def task_fk(cls): - return relationships.fk('task', nullable=True) + return relationship.foreign_key('task', nullable=True) # endregion def __repr__(self): return "<{self.created_at}: [{self.level}] @{self.actor}> {msg}".format( self=self, msg=self.msg[:50]) - - __private_fields__ = ['execution_fk', - 'task_fk'] http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/relationship.py ---------------------------------------------------------------------- diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py new file mode 100644 index 0000000..bed1599 --- /dev/null +++ b/aria/modeling/relationship.py @@ -0,0 +1,402 @@ +# 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=invalid-name, redefined-outer-name + +from sqlalchemy.orm import relationship, backref +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Table +) + +from ..utils import formatting + + +def foreign_key(other_table, + nullable=False): + """ + Declare a foreign key property, which will also create a foreign key column in the table with + the name of the property. By convention the property name should end in "_fk". + + You are required to explicitly create foreign keys in order to allow for one-to-one, + one-to-many, and many-to-one relationships (but not for many-to-many relationships). If you do + not do so, SQLAlchemy will fail to create the relationship property and raise an exception with + a clear error message. + + You should normally not have to access this property directly, but instead use the associated + relationship properties. + + *This utility method should only be used during class creation.* + + :param other_table: Other table name + :type other_table: basestring + :param nullable: True to allow null values (meaning that there is no relationship) + :type nullable: bool + """ + + return Column(Integer, + ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'), + nullable=nullable) + + +def one_to_one_self(model_class, + fk, + relationship_kwargs=None): + """ + Declare a one-to-one relationship property. The property value would be an instance of the same + model. + + You will need an associated foreign key to our own table. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param fk: Foreign key name + :type fk: basestring + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + """ + + relationship_kwargs = relationship_kwargs or {} + + remote_side = '{model_class}.{remote_column}'.format( + model_class=model_class.__name__, + remote_column=model_class.id_column_name() + ) + + primaryjoin = '{remote_side} == {model_class}.{column}'.format( + remote_side=remote_side, + model_class=model_class.__name__, + column=fk + ) + + return relationship( + _get_class_for_table(model_class, model_class.__tablename__).__name__, + primaryjoin=primaryjoin, + remote_side=remote_side, + post_update=True, + **relationship_kwargs + ) + + +def one_to_many_self(model_class, + fk, + dict_key=None, + relationship_kwargs=None): + """ + Declare a one-to-many relationship property. The property value would be a list or dict of + instances of the same model. + + You will need an associated foreign key to our own table. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param fk: Foreign key name + :type fk: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + """ + + relationship_kwargs = relationship_kwargs or {} + + relationship_kwargs.setdefault('remote_side', '{model_class}.{remote_column}'.format( + model_class=model_class.__name__, + remote_column=fk + )) + + return _relationship(model_class, model_class.__tablename__, None, relationship_kwargs, + other_property=False, dict_key=dict_key) + + +def one_to_one(model_class, + other_table, + fk=None, + other_fk=None, + other_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a one-to-one relationship property. The property value would be an instance of the other + table's model. + + You have two options for the foreign key. Either this table can have an associated key to the + other table (use the ``fk`` argument) or the other table can have an associated foreign key to + this our table (use the ``other_fk`` argument). + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param other_table: Other table name + :type other_table: basestring + :param fk: Foreign key name at our table (no need specify if there's no ambiguity) + :type fk: basestring + :param other_fk: Foreign key name at the other table (no need specify if there's no ambiguity) + :type other_fk: basestring + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SQLAlchemy ``backref`` + :type backref_kwargs: {} + """ + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', False) + + return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, + other_property, fk=fk, other_fk=other_fk) + + +def one_to_many(model_class, + child_table, + child_fk=None, + dict_key=None, + child_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a one-to-many relationship property. The property value would be a list or dict of + instances of the child table's model. + + The child table will need an associated foreign key to our table. + + The declaration will automatically create a matching many-to-one property at the child model, + named after our table name. Use the ``child_property`` argument to override this name. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param child_table: Child table name + :type child_table: basestring + :param child_fk: Foreign key name at the child table (no need specify if there's no ambiguity) + :type child_fk: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param child_property: Override name of matching many-to-one property at child table; set to + false to disable + :type child_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SQLAlchemy ``backref`` + :type backref_kwargs: {} + """ + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', False) + + return _relationship(model_class, child_table, backref_kwargs, relationship_kwargs, + child_property, other_fk=child_fk, dict_key=dict_key) + + +def many_to_one(model_class, + parent_table, + fk=None, + parent_fk=None, + parent_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a many-to-one relationship property. The property value would be an instance of the + parent table's model. + + You will need an associated foreign key to the parent table. + + The declaration will automatically create a matching one-to-many property at the child model, + named after the plural form of our table name. Use the ``parent_property`` argument to override + this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a + Python collection then use :meth:`one_to_many` at that model. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param parent_table: Parent table name + :type parent_table: basestring + :param fk: Foreign key name at our table (no need specify if there's no ambiguity) + :type fk: basestring + :param parent_property: Override name of matching one-to-many property at parent table; set to + false to disable + :type parent_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SQLAlchemy ``backref`` + :type backref_kwargs: {} + """ + + if parent_property is None: + parent_property = formatting.pluralize(model_class.__tablename__) + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', True) + backref_kwargs.setdefault('lazy', 'dynamic') + backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted + + return _relationship(model_class, parent_table, backref_kwargs, relationship_kwargs, + parent_property, fk=fk, other_fk=parent_fk) + + +def many_to_many(model_class, + other_table, + prefix=None, + dict_key=None, + other_property=None, + relationship_kwargs=None, + backref_kwargs=None): + """ + Declare a many-to-many relationship property. The property value would be a list or dict of + instances of the other table's model. + + You do not need associated foreign keys for this relationship. Instead, an extra table will be + created for you. + + The declaration will automatically create a matching many-to-many property at the other model, + named after the plural form of our table name. Use the ``other_property`` argument to override + this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a + Python collection then use :meth:`many_to_many` again at that model. + + *This utility method should only be used during class creation.* + + :param model_class: The class in which this relationship will be declared + :type model_class: type + :param parent_table: Parent table name + :type parent_table: basestring + :param prefix: Optional prefix for extra table name as well as for ``other_property`` + :type prefix: basestring + :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + :param other_property: Override name of matching many-to-many property at other table; set to + false to disable + :type other_property: basestring|bool + :param relationship_kwargs: Extra kwargs for SQLAlchemy ``relationship`` + :type relationship_kwargs: {} + :param backref_kwargs: Extra kwargs for SQLAlchemy ``backref`` + :type backref_kwargs: {} + """ + + this_table = model_class.__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) + + secondary_table = '{0}_{1}'.format(this_table, other_table) + + if other_property is None: + other_property = formatting.pluralize(this_table) + if prefix is not None: + secondary_table = '{0}_{1}'.format(prefix, secondary_table) + other_property = '{0}_{1}'.format(prefix, other_property) + + backref_kwargs = backref_kwargs or {} + backref_kwargs.setdefault('uselist', True) + + relationship_kwargs = relationship_kwargs or {} + relationship_kwargs.setdefault('secondary', _get_secondary_table( + model_class.metadata, + secondary_table, + this_column_name, + other_column_name, + this_foreign_key, + other_foreign_key + )) + + return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, + other_property, dict_key=dict_key) + + +def _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, other_property, + fk=None, other_fk=None, dict_key=None): + relationship_kwargs = relationship_kwargs or {} + + if fk: + relationship_kwargs.setdefault('foreign_keys', + lambda: getattr( + _get_class_for_table( + model_class, + model_class.__tablename__), + fk)) + + elif other_fk: + relationship_kwargs.setdefault('foreign_keys', + lambda: getattr( + _get_class_for_table( + model_class, + other_table), + other_fk)) + + if dict_key: + relationship_kwargs.setdefault('collection_class', + attribute_mapped_collection(dict_key)) + + if other_property is False: + # No backref + return relationship( + lambda: _get_class_for_table(model_class, other_table), + **relationship_kwargs + ) + else: + if other_property is None: + other_property = model_class.__tablename__ + backref_kwargs = backref_kwargs or {} + return relationship( + lambda: _get_class_for_table(model_class, other_table), + backref=backref(other_property, **backref_kwargs), + **relationship_kwargs + ) + + +def _get_class_for_table(model_class, tablename): + if tablename in (model_class.__name__, model_class.__tablename__): + return model_class + + for table_cls in model_class._decl_class_registry.values(): + if tablename == getattr(table_cls, '__tablename__', None): + return table_cls + + raise ValueError('unknown table: {0}'.format(tablename)) + + +def _get_secondary_table(metadata, + name, + first_column, + second_column, + first_foreign_key, + second_foreign_key): + return Table( + name, + metadata, + Column( + first_column, + Integer, + ForeignKey(first_foreign_key) + ), + Column( + second_column, + Integer, + ForeignKey(second_foreign_key) + ) + ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/relationships.py ---------------------------------------------------------------------- diff --git a/aria/modeling/relationships.py b/aria/modeling/relationships.py deleted file mode 100644 index 76f07f9..0000000 --- a/aria/modeling/relationships.py +++ /dev/null @@ -1,402 +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=invalid-name, redefined-outer-name - -from sqlalchemy.orm import relationship, backref -from sqlalchemy.orm.collections import attribute_mapped_collection -from sqlalchemy import ( - Column, - ForeignKey, - Integer, - Table -) - -from ..utils import formatting - - -def fk(other_table, - nullable=False): - """ - Declare a foreign key property, which will also create a foreign key column in the table with - the name of the property. By convention the property name should end in "_fk". - - You are required to explicitly create foreign keys in order to allow for one-to-one, - one-to-many, and many-to-one relationships (but not for many-to-many relationships). If you do - not do so, SQLAlchemy will fail to create the relationship property and raise an exception with - a clear error message. - - You should normally not have to access this property directly, but instead use the associated - relationship properties. - - *This utility method should only be used during class creation.* - - :param other_table: Other table name - :type other_table: basestring - :param nullable: True to allow null values (meaning that there is no relationship) - :type nullable: bool - """ - - return Column(Integer, - ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'), - nullable=nullable) - - -def one_to_one_self(model_class, - fk, - relationship_kwargs=None): - """ - Declare a one-to-one relationship property. The property value would be an instance of the same - model. - - You will need an associated foreign key to our own table. - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param fk: Foreign key name - :type fk: basestring - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - """ - - relationship_kwargs = relationship_kwargs or {} - - remote_side = '{model_class}.{remote_column}'.format( - model_class=model_class.__name__, - remote_column=model_class.id_column_name() - ) - - primaryjoin = '{remote_side} == {model_class}.{column}'.format( - remote_side=remote_side, - model_class=model_class.__name__, - column=fk - ) - - return relationship( - _get_class_for_table(model_class, model_class.__tablename__).__name__, - primaryjoin=primaryjoin, - remote_side=remote_side, - post_update=True, - **relationship_kwargs - ) - - -def one_to_many_self(model_class, - fk, - dict_key=None, - relationship_kwargs=None): - """ - Declare a one-to-many relationship property. The property value would be a list or dict of - instances of the same model. - - You will need an associated foreign key to our own table. - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param fk: Foreign key name - :type fk: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will - be a list - :type dict_key: basestring - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - """ - - relationship_kwargs = relationship_kwargs or {} - - relationship_kwargs.setdefault('remote_side', '{model_class}.{remote_column}'.format( - model_class=model_class.__name__, - remote_column=fk - )) - - return _relationship(model_class, model_class.__tablename__, None, relationship_kwargs, - other_property=False, dict_key=dict_key) - - -def one_to_one(model_class, - other_table, - fk=None, - other_fk=None, - other_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a one-to-one relationship property. The property value would be an instance of the other - table's model. - - You have two options for the foreign key. Either this table can have an associated key to the - other table (use the `fk` argument) or the other table can have an associated foreign key to - this our table (use the `other_fk` argument). - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param other_table: Other table name - :type other_table: basestring - :param fk: Foreign key name at our table (no need specify if there's no ambiguity) - :type fk: basestring - :param other_fk: Foreign key name at the other table (no need specify if there's no ambiguity) - :type other_fk: basestring - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SQLAlchemy `backref` - :type backref_kwargs: {} - """ - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', False) - - return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, - other_property, fk=fk, other_fk=other_fk) - - -def one_to_many(model_class, - child_table, - child_fk=None, - dict_key=None, - child_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a one-to-many relationship property. The property value would be a list or dict of - instances of the child table's model. - - The child table will need an associated foreign key to our table. - - The declaration will automatically create a matching many-to-one property at the child model, - named after our table name. Use the `child_property` argument to override this name. - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param child_table: Child table name - :type child_table: basestring - :param child_fk: Foreign key name at the child table (no need specify if there's no ambiguity) - :type child_fk: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will - be a list - :type dict_key: basestring - :param child_property: Override name of matching many-to-one property at child table; set to - false to disable - :type child_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SQLAlchemy `backref` - :type backref_kwargs: {} - """ - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', False) - - return _relationship(model_class, child_table, backref_kwargs, relationship_kwargs, - child_property, other_fk=child_fk, dict_key=dict_key) - - -def many_to_one(model_class, - parent_table, - fk=None, - parent_fk=None, - parent_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a many-to-one relationship property. The property value would be an instance of the - parent table's model. - - You will need an associated foreign key to the parent table. - - The declaration will automatically create a matching one-to-many property at the child model, - named after the plural form of our table name. Use the `parent_property` argument to override - this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a - Python collection then use :meth:`one_to_many` at that model. - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param parent_table: Parent table name - :type parent_table: basestring - :param fk: Foreign key name at our table (no need specify if there's no ambiguity) - :type fk: basestring - :param parent_property: Override name of matching one-to-many property at parent table; set to - false to disable - :type parent_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SQLAlchemy `backref` - :type backref_kwargs: {} - """ - - if parent_property is None: - parent_property = formatting.pluralize(model_class.__tablename__) - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', True) - backref_kwargs.setdefault('lazy', 'dynamic') - backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted - - return _relationship(model_class, parent_table, backref_kwargs, relationship_kwargs, - parent_property, fk=fk, other_fk=parent_fk) - - -def many_to_many(model_class, - other_table, - prefix=None, - dict_key=None, - other_property=None, - relationship_kwargs=None, - backref_kwargs=None): - """ - Declare a many-to-many relationship property. The property value would be a list or dict of - instances of the other table's model. - - You do not need associated foreign keys for this relationship. Instead, an extra table will be - created for you. - - The declaration will automatically create a matching many-to-many property at the other model, - named after the plural form of our table name. Use the `other_property` argument to override - this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a - Python collection then use :meth:`many_to_many` again at that model. - - *This utility method should only be used during class creation.* - - :param model_class: The class in which this relationship will be declared - :type model_class: type - :param parent_table: Parent table name - :type parent_table: basestring - :param prefix: Optional prefix for extra table name as well as for `other_property` - :type prefix: basestring - :param dict_key: If set the value will be a dict with this key as the dict key; otherwise will - be a list - :type dict_key: basestring - :param other_property: Override name of matching many-to-many property at other table; set to - false to disable - :type other_property: basestring|bool - :param relationship_kwargs: Extra kwargs for SQLAlchemy `relationship` - :type relationship_kwargs: {} - :param backref_kwargs: Extra kwargs for SQLAlchemy `backref` - :type backref_kwargs: {} - """ - - this_table = model_class.__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) - - secondary_table = '{0}_{1}'.format(this_table, other_table) - - if other_property is None: - other_property = formatting.pluralize(this_table) - if prefix is not None: - secondary_table = '{0}_{1}'.format(prefix, secondary_table) - other_property = '{0}_{1}'.format(prefix, other_property) - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', True) - - relationship_kwargs = relationship_kwargs or {} - relationship_kwargs.setdefault('secondary', _get_secondary_table( - model_class.metadata, - secondary_table, - this_column_name, - other_column_name, - this_foreign_key, - other_foreign_key - )) - - return _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, - other_property, dict_key=dict_key) - - -def _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, other_property, - fk=None, other_fk=None, dict_key=None): - relationship_kwargs = relationship_kwargs or {} - - if fk: - relationship_kwargs.setdefault('foreign_keys', - lambda: getattr( - _get_class_for_table( - model_class, - model_class.__tablename__), - fk)) - - elif other_fk: - relationship_kwargs.setdefault('foreign_keys', - lambda: getattr( - _get_class_for_table( - model_class, - other_table), - other_fk)) - - if dict_key: - relationship_kwargs.setdefault('collection_class', - attribute_mapped_collection(dict_key)) - - if other_property is False: - # No backref - return relationship( - lambda: _get_class_for_table(model_class, other_table), - **relationship_kwargs - ) - else: - if other_property is None: - other_property = model_class.__tablename__ - backref_kwargs = backref_kwargs or {} - return relationship( - lambda: _get_class_for_table(model_class, other_table), - backref=backref(other_property, **backref_kwargs), - **relationship_kwargs - ) - - -def _get_class_for_table(model_class, tablename): - if tablename in (model_class.__name__, model_class.__tablename__): - return model_class - - for table_cls in model_class._decl_class_registry.values(): - if tablename == getattr(table_cls, '__tablename__', None): - return table_cls - - raise ValueError('unknown table: {0}'.format(tablename)) - - -def _get_secondary_table(metadata, - name, - first_column, - second_column, - first_foreign_key, - second_foreign_key): - return Table( - name, - metadata, - Column( - first_column, - Integer, - ForeignKey(first_foreign_key) - ), - Column( - second_column, - Integer, - ForeignKey(second_foreign_key) - ) - ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/service_changes.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py index bd59b35..a33e6ae 100644 --- a/aria/modeling/service_changes.py +++ b/aria/modeling/service_changes.py @@ -35,7 +35,7 @@ from sqlalchemy.ext.declarative import declared_attr from .types import (List, Dict) from .mixins import ModelMixin -from . import relationships +from . import relationship class ServiceUpdateBase(ModelMixin): @@ -47,8 +47,10 @@ class ServiceUpdateBase(ModelMixin): __tablename__ = 'service_update' - _private_fields = ['execution_fk', - 'service_fk'] + __private_fields__ = ['service_fk', + 'execution_fk', + 'execution_name', + 'service_name'] created_at = Column(DateTime, nullable=False, index=True) service_plan = Column(Dict, nullable=False) @@ -60,21 +62,21 @@ class ServiceUpdateBase(ModelMixin): @declared_attr def execution(cls): - return relationships.many_to_one(cls, 'execution') + return relationship.many_to_one(cls, 'execution') @declared_attr def service(cls): - return relationships.many_to_one(cls, 'service', parent_property='updates') + return relationship.many_to_one(cls, 'service', parent_property='updates') # region foreign keys @declared_attr def execution_fk(cls): - return relationships.fk('execution', nullable=True) + return relationship.foreign_key('execution', nullable=True) @declared_attr def service_fk(cls): - return relationships.fk('service') + return relationship.foreign_key('service') # endregion @@ -98,11 +100,6 @@ class ServiceUpdateBase(ModelMixin): dep_update_dict['steps'] = [step.to_dict() for step in self.steps] return dep_update_dict - __private_fields__ = ['service_fk', - 'execution_fk', - 'execution_name', - 'service_name'] - class ServiceUpdateStepBase(ModelMixin): """ @@ -111,6 +108,9 @@ class ServiceUpdateStepBase(ModelMixin): __tablename__ = 'service_update_step' + __private_fields__ = ['service_update_fk', + 'service_update_name'] + _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY') ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify') @@ -135,13 +135,13 @@ class ServiceUpdateStepBase(ModelMixin): @declared_attr def service_update(cls): - return relationships.many_to_one(cls, 'service_update', parent_property='steps') + return relationship.many_to_one(cls, 'service_update', parent_property='steps') # region foreign keys @declared_attr def service_update_fk(cls): - return relationships.fk('service_update') + return relationship.foreign_key('service_update') # endregion @@ -181,9 +181,6 @@ class ServiceUpdateStepBase(ModelMixin): return self.entity_type == 'relationship' and other.entity_type == 'node' return False - __private_fields__ = ['service_update_fk', - 'service_update_name'] - class ServiceModificationBase(ModelMixin): """ @@ -192,6 +189,9 @@ class ServiceModificationBase(ModelMixin): __tablename__ = 'service_modification' + __private_fields__ = ['service_fk', + 'service_name'] + STARTED = 'started' FINISHED = 'finished' ROLLEDBACK = 'rolledback' @@ -208,13 +208,13 @@ class ServiceModificationBase(ModelMixin): @declared_attr def service(cls): - return relationships.many_to_one(cls, 'service', parent_property='modifications') + return relationship.many_to_one(cls, 'service', parent_property='modifications') # region foreign keys @declared_attr def service_fk(cls): - return relationships.fk('service') + return relationship.foreign_key('service') # endregion @@ -226,6 +226,3 @@ class ServiceModificationBase(ModelMixin): return association_proxy('service', cls.name_column_name()) # endregion - - __private_fields__ = ['service_fk', - 'service_name'] http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 3ff0555..dfe4674 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -27,7 +27,7 @@ from ..utils import collections, formatting, console from .mixins import InstanceModelMixin, TemplateModelMixin from .types import List from . import ( - relationships, + relationship, utils ) @@ -111,24 +111,26 @@ class TypeBase(InstanceModelMixin): __tablename__ = 'type' + __private_fields__ = ['parent_type_fk'] + variant = Column(Text, nullable=False) description = Column(Text) _role = Column(Text, name='role') @declared_attr def parent(cls): - return relationships.one_to_one_self(cls, 'parent_type_fk') + return relationship.one_to_one_self(cls, 'parent_type_fk') @declared_attr def children(cls): - return relationships.one_to_many_self(cls, 'parent_type_fk') + return relationship.one_to_many_self(cls, 'parent_type_fk') # region foreign keys @declared_attr def parent_type_fk(cls): """For Type one-to-many to Type""" - return relationships.fk('type', nullable=True) + return relationship.foreign_key('type', nullable=True) # endregion @@ -182,6 +184,9 @@ class TypeBase(InstanceModelMixin): self._append_raw_children(types) return types + def coerce_values(self, container, report_issues): + pass + def dump(self): context = ConsumptionContext.get_thread_local() if self.name: @@ -197,8 +202,6 @@ class TypeBase(InstanceModelMixin): types.append(raw_child) child._append_raw_children(types) - __private_fields__ = ['parent_type_fk'] - class MetadataBase(TemplateModelMixin): """ @@ -220,6 +223,9 @@ class MetadataBase(TemplateModelMixin): ('name', self.name), ('value', self.value))) + def coerce_values(self, container, report_issues): + pass + def instantiate(self, container): from . import models return models.Metadata(name=self.name, @@ -239,6 +245,8 @@ class PluginSpecificationBase(InstanceModelMixin): __tablename__ = 'plugin_specification' + __private_fields__ = ['service_template_fk'] + archive_name = Column(Text, nullable=False, index=True) distribution = Column(Text) distribution_release = Column(Text) @@ -254,15 +262,16 @@ class PluginSpecificationBase(InstanceModelMixin): @declared_attr def service_template_fk(cls): """For ServiceTemplate one-to-many to PluginSpecification""" - return relationships.fk('service_template', nullable=True) + return relationship.foreign_key('service_template', nullable=True) # endregion + def coerce_values(self, container, report_issues): + pass + def find_plugin(self, plugins): # TODO: this should check versions/distribution and other specification for plugin in plugins: if plugin.name == self.name: return plugin return None - - __private_fields__ = ['service_template_fk'] http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/7d20a848/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index 5c2cf54..b97c148 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -29,7 +29,7 @@ from ..parser import validation from ..parser.consumption import ConsumptionContext from ..utils import collections, formatting, console from . import ( - relationships, + relationship, utils, types as modeling_types ) @@ -86,48 +86,52 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods __tablename__ = 'service' + __private_fields__ = ['substitution_fk', + 'service_template_fk', + 'service_template_name'] + @declared_attr def service_template(cls): - return relationships.many_to_one(cls, 'service_template') + return relationship.many_to_one(cls, 'service_template') description = Column(Text) @declared_attr def meta_data(cls): # Warning! We cannot use the attr name "metadata" because it's used by SQLAlchemy! - return relationships.many_to_many(cls, 'metadata', dict_key='name') + return relationship.many_to_many(cls, 'metadata', dict_key='name') @declared_attr def nodes(cls): - return relationships.one_to_many(cls, 'node', dict_key='name') + return relationship.one_to_many(cls, 'node', dict_key='name') @declared_attr def groups(cls): - return relationships.one_to_many(cls, 'group', dict_key='name') + return relationship.one_to_many(cls, 'group', dict_key='name') @declared_attr def policies(cls): - return relationships.one_to_many(cls, 'policy', dict_key='name') + return relationship.one_to_many(cls, 'policy', dict_key='name') @declared_attr def substitution(cls): - return relationships.one_to_one(cls, 'substitution') + return relationship.one_to_one(cls, 'substitution') @declared_attr def inputs(cls): - return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def outputs(cls): - return relationships.many_to_many(cls, 'parameter', prefix='outputs', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='outputs', dict_key='name') @declared_attr def workflows(cls): - return relationships.one_to_many(cls, 'operation', dict_key='name') + return relationship.one_to_many(cls, 'operation', dict_key='name') @declared_attr def plugin_specifications(cls): - return relationships.many_to_many(cls, 'plugin_specification') + return relationship.many_to_many(cls, 'plugin_specification') created_at = Column(DateTime, nullable=False, index=True) updated_at = Column(DateTime) @@ -144,12 +148,12 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def substitution_fk(cls): """Service one-to-one to Substitution""" - return relationships.fk('substitution', nullable=True) + return relationship.foreign_key('substitution', nullable=True) @declared_attr def service_template_fk(cls): """For Service many-to-one to ServiceTemplate""" - return relationships.fk('service_template', nullable=True) + return relationship.foreign_key('service_template', nullable=True) # endregion @@ -184,11 +188,11 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods def _is_node_a_target(self, source_node, target_node): if source_node.outbound_relationships: - for relationship in source_node.outbound_relationships: - if relationship.target_node.name == target_node.name: + for the_relationship in source_node.outbound_relationships: + if the_relationship.target_node.name == target_node.name: return True else: - node = relationship.target_node + node = the_relationship.target_node if node is not None: if self._is_node_a_target(node, target_node): return True @@ -256,30 +260,34 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods console.puts(context.style.node(node.name)) if node.outbound_relationships: with context.style.indent: - for relationship in node.outbound_relationships: - if relationship.relationship_template is not None: - relationship_name = context.style.node( - relationship.relationship_template.name) - elif relationship.type is not None: - relationship_name = context.style.type(relationship.type.name) + for the_relationship in node.outbound_relationships: + relationship_name = context.style.property(the_relationship.name) + if the_relationship.type is not None: + relationship_type = context.style.type(the_relationship.type.name) else: - relationship_name = '?' - if relationship.target_capability is not None: - capability_name = context.style.node(relationship.target_capability.name) + relationship_type = None + if the_relationship.target_capability is not None: + capability_name = \ + context.style.node(the_relationship.target_capability.name) else: capability_name = None if capability_name is not None: - console.puts('-> {0} {1}'.format(relationship_name, capability_name)) + if relationship_type is not None: + console.puts('-> {0} ({1}) {2}'.format(relationship_name, + relationship_type, + capability_name)) + else: + console.puts('-> {0} {1}'.format(relationship_name, capability_name)) else: - console.puts('-> {0}'.format(relationship_name)) - target_node = relationship.target_node + if relationship_type is not None: + console.puts('-> {0} ({1})'.format(relationship_name, + relationship_type)) + else: + console.puts('-> {0}'.format(relationship_name)) + target_node = the_relationship.target_node with console.indent(3): self._dump_graph_node(target_node) - __private_fields__ = ['substitution_fk', - 'service_template_fk', - 'service_template_name'] - class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods """ @@ -335,49 +343,55 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods __tablename__ = 'node' + __private_fields__ = ['type_fk', + 'host_fk', + 'service_fk', + 'node_template_fk', + 'service_name'] + @declared_attr def node_template(cls): - return relationships.many_to_one(cls, 'node_template') + return relationship.many_to_one(cls, 'node_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') description = Column(Text) @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return relationships.one_to_many(cls, 'interface', dict_key='name') + return relationship.one_to_many(cls, 'interface', dict_key='name') @declared_attr def artifacts(cls): - return relationships.one_to_many(cls, 'artifact', dict_key='name') + return relationship.one_to_many(cls, 'artifact', dict_key='name') @declared_attr def capabilities(cls): - return relationships.one_to_many(cls, 'capability', dict_key='name') + return relationship.one_to_many(cls, 'capability', dict_key='name') @declared_attr def outbound_relationships(cls): - return relationships.one_to_many(cls, 'relationship', child_fk='source_node_fk', - child_property='source_node') + return relationship.one_to_many(cls, 'relationship', child_fk='source_node_fk', + child_property='source_node') @declared_attr def inbound_relationships(cls): - return relationships.one_to_many(cls, 'relationship', child_fk='target_node_fk', - child_property='target_node') + return relationship.one_to_many(cls, 'relationship', child_fk='target_node_fk', + child_property='target_node') @declared_attr def plugin_specifications(cls): - return relationships.many_to_many(cls, 'plugin_specification', dict_key='name') + return relationship.many_to_many(cls, 'plugin_specification', dict_key='name') @declared_attr def host(cls): - return relationships.one_to_one_self(cls, 'host_fk') + return relationship.one_to_one_self(cls, 'host_fk') # region orchestration @@ -409,22 +423,22 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def type_fk(cls): """For Node many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def host_fk(cls): """For Node one-to-one to Node""" - return relationships.fk('node', nullable=True) + return relationship.foreign_key('node', nullable=True) @declared_attr def service_fk(cls): """For Service one-to-many to Node""" - return relationships.fk('service') + return relationship.foreign_key('service') @declared_attr def node_template_fk(cls): """For Node many-to-one to NodeTemplate""" - return relationships.fk('node_template', nullable=True) + return relationship.foreign_key('node_template', nullable=True) # endregion @@ -479,14 +493,14 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods if target_node is not None: if requirement_template.relationship_template is not None: - relationship = \ + the_relationship = \ requirement_template.relationship_template.instantiate(self) else: - relationship = models.Relationship(target_capability=target_capability) - relationship.name = requirement_template.name - relationship.requirement_template = requirement_template - relationship.target_node = target_node - self.outbound_relationships.append(relationship) + the_relationship = models.Relationship(target_capability=target_capability) + the_relationship.name = requirement_template.name + the_relationship.requirement_template = requirement_template + the_relationship.target_node = target_node + self.outbound_relationships.append(the_relationship) return True else: context.validation.report('requirement "{0}" of node "{1}" targets node ' @@ -569,12 +583,6 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods utils.dump_dict_values(self.capabilities, 'Capabilities') utils.dump_list_values(self.outbound_relationships, 'Relationships') - __private_fields__ = ['type_fk', - 'host_fk', - 'service_fk', - 'node_template_fk', - 'service_name'] - class GroupBase(InstanceModelMixin): """ Usually an instance of a :class:`GroupTemplate`. @@ -602,44 +610,48 @@ class GroupBase(InstanceModelMixin): __tablename__ = 'group' + __private_fields__ = ['type_fk', + 'service_fk', + 'group_template_fk'] + @declared_attr def group_template(cls): - return relationships.many_to_one(cls, 'group_template') + return relationship.many_to_one(cls, 'group_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') description = Column(Text) @declared_attr def nodes(cls): - return relationships.many_to_many(cls, 'node') + return relationship.many_to_many(cls, 'node') @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return relationships.one_to_many(cls, 'interface', dict_key='name') + return relationship.one_to_many(cls, 'interface', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Group many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def service_fk(cls): """For Service one-to-many to Group""" - return relationships.fk('service') + return relationship.foreign_key('service') @declared_attr def group_template_fk(cls): """For Group many-to-one to GroupTemplate""" - return relationships.fk('group_template', nullable=True) + return relationship.foreign_key('group_template', nullable=True) # endregion @@ -671,10 +683,6 @@ class GroupBase(InstanceModelMixin): for node in self.nodes: console.puts(context.style.node(node.name)) - __private_fields__ = ['type_fk', - 'service_fk', - 'group_template_fk'] - class PolicyBase(InstanceModelMixin): """ @@ -701,44 +709,48 @@ class PolicyBase(InstanceModelMixin): __tablename__ = 'policy' + __private_fields__ = ['type_fk', + 'service_fk', + 'policy_template_fk'] + @declared_attr def policy_template(cls): - return relationships.many_to_one(cls, 'policy_template') + return relationship.many_to_one(cls, 'policy_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') description = Column(Text) @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def nodes(cls): - return relationships.many_to_many(cls, 'node') + return relationship.many_to_many(cls, 'node') @declared_attr def groups(cls): - return relationships.many_to_many(cls, 'group') + return relationship.many_to_many(cls, 'group') # region foreign_keys @declared_attr def type_fk(cls): """For Policy many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def service_fk(cls): """For Service one-to-many to Policy""" - return relationships.fk('service') + return relationship.foreign_key('service') @declared_attr def policy_template_fk(cls): """For Policy many-to-one to PolicyTemplate""" - return relationships.fk('policy_template', nullable=True) + return relationship.foreign_key('policy_template', nullable=True) # endregion @@ -772,10 +784,6 @@ class PolicyBase(InstanceModelMixin): for group in self.groups: console.puts(context.style.node(group.name)) - __private_fields__ = ['type_fk', - 'service_fk', - 'policy_template_fk'] - class SubstitutionBase(InstanceModelMixin): """ @@ -796,29 +804,32 @@ class SubstitutionBase(InstanceModelMixin): __tablename__ = 'substitution' + __private_fields__ = ['node_type_fk', + 'substitution_template_fk'] + @declared_attr def substitution_template(cls): - return relationships.many_to_one(cls, 'substitution_template') + return relationship.many_to_one(cls, 'substitution_template') @declared_attr def node_type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') @declared_attr def mappings(cls): - return relationships.one_to_many(cls, 'substitution_mapping', dict_key='name') + return relationship.one_to_many(cls, 'substitution_mapping', dict_key='name') # region foreign_keys @declared_attr def node_type_fk(cls): """For Substitution many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def substitution_template_fk(cls): """For Substitution many-to-one to SubstitutionTemplate""" - return relationships.fk('substitution_template', nullable=True) + return relationship.foreign_key('substitution_template', nullable=True) # endregion @@ -841,9 +852,6 @@ class SubstitutionBase(InstanceModelMixin): console.puts('Node type: {0}'.format(context.style.type(self.node_type.name))) utils.dump_dict_values(self.mappings, 'Mappings') - __private_fields__ = ['node_type_fk', - 'substitution_template_fk'] - class SubstitutionMappingBase(InstanceModelMixin): """ @@ -868,39 +876,44 @@ class SubstitutionMappingBase(InstanceModelMixin): __tablename__ = 'substitution_mapping' + __private_fields__ = ['substitution_fk', + 'node_fk', + 'capability_fk', + 'requirement_template_fk'] + @declared_attr def node(cls): - return relationships.one_to_one(cls, 'node') + return relationship.one_to_one(cls, 'node') @declared_attr def capability(cls): - return relationships.one_to_one(cls, 'capability') + return relationship.one_to_one(cls, 'capability') @declared_attr def requirement_template(cls): - return relationships.one_to_one(cls, 'requirement_template') + return relationship.one_to_one(cls, 'requirement_template') # region foreign keys @declared_attr def substitution_fk(cls): """For Substitution one-to-many to SubstitutionMapping""" - return relationships.fk('substitution') + return relationship.foreign_key('substitution') @declared_attr def node_fk(cls): """For Substitution one-to-one to NodeTemplate""" - return relationships.fk('node') + return relationship.foreign_key('node') @declared_attr def capability_fk(cls): """For Substitution one-to-one to Capability""" - return relationships.fk('capability', nullable=True) + return relationship.foreign_key('capability', nullable=True) @declared_attr def requirement_template_fk(cls): """For Substitution one-to-one to RequirementTemplate""" - return relationships.fk('requirement_template', nullable=True) + return relationship.foreign_key('requirement_template', nullable=True) # endregion @@ -909,6 +922,9 @@ class SubstitutionMappingBase(InstanceModelMixin): return collections.OrderedDict(( ('name', self.name))) + def coerce_values(self, container, report_issues): + pass + def validate(self): context = ConsumptionContext.get_thread_local() if (self.capability is None) and (self.requirement_template is None): @@ -927,11 +943,6 @@ class SubstitutionMappingBase(InstanceModelMixin): if self.capability else self.requirement_template.name))) - __private_fields__ = ['substitution_fk', - 'node_fk', - 'capability_fk', - 'requirement_template_fk'] - class RelationshipBase(InstanceModelMixin): """ @@ -969,29 +980,38 @@ class RelationshipBase(InstanceModelMixin): __tablename__ = 'relationship' + __private_fields__ = ['type_fk', + 'source_node_fk', + 'target_node_fk', + 'target_capability_fk', + 'requirement_template_fk', + 'relationship_template_fk', + 'source_node_name', + 'target_node_name'] + @declared_attr def relationship_template(cls): - return relationships.many_to_one(cls, 'relationship_template') + return relationship.many_to_one(cls, 'relationship_template') @declared_attr def requirement_template(cls): - return relationships.many_to_one(cls, 'requirement_template') + return relationship.many_to_one(cls, 'requirement_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') @declared_attr def target_capability(cls): - return relationships.one_to_one(cls, 'capability') + return relationship.one_to_one(cls, 'capability') @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr def interfaces(cls): - return relationships.one_to_many(cls, 'interface', dict_key='name') + return relationship.one_to_many(cls, 'interface', dict_key='name') # region orchestration @@ -1005,32 +1025,32 @@ class RelationshipBase(InstanceModelMixin): @declared_attr def type_fk(cls): """For Relationship many-to-one to Type""" - return relationships.fk('type', nullable=True) + return relationship.foreign_key('type', nullable=True) @declared_attr def source_node_fk(cls): """For Node one-to-many to Relationship""" - return relationships.fk('node') + return relationship.foreign_key('node') @declared_attr def target_node_fk(cls): """For Node one-to-many to Relationship""" - return relationships.fk('node') + return relationship.foreign_key('node') @declared_attr def target_capability_fk(cls): """For Relationship one-to-one to Capability""" - return relationships.fk('capability', nullable=True) + return relationship.foreign_key('capability', nullable=True) @declared_attr def requirement_template_fk(cls): """For Relationship many-to-one to RequirementTemplate""" - return relationships.fk('requirement_template', nullable=True) + return relationship.foreign_key('requirement_template', nullable=True) @declared_attr def relationship_template_fk(cls): """For Relationship many-to-one to RelationshipTemplate""" - return relationships.fk('relationship_template', nullable=True) + return relationship.foreign_key('relationship_template', nullable=True) # endregion @@ -1087,15 +1107,6 @@ class RelationshipBase(InstanceModelMixin): utils.dump_dict_values(self.properties, 'Properties') utils.dump_interfaces(self.interfaces, 'Interfaces') - __private_fields__ = ['type_fk', - 'source_node_fk', - 'target_node_fk', - 'target_capability_fk', - 'requirement_template_fk', - 'relationship_template_fk', - 'source_node_name', - 'target_node_name'] - class CapabilityBase(InstanceModelMixin): """ @@ -1128,13 +1139,17 @@ class CapabilityBase(InstanceModelMixin): __tablename__ = 'capability' + __private_fields__ = ['capability_fk', + 'node_fk', + 'capability_template_fk'] + @declared_attr def capability_template(cls): - return relationships.many_to_one(cls, 'capability_template') + return relationship.many_to_one(cls, 'capability_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') min_occurrences = Column(Integer, default=None) max_occurrences = Column(Integer, default=None) @@ -1142,24 +1157,24 @@ class CapabilityBase(InstanceModelMixin): @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Capability many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def node_fk(cls): """For Node one-to-many to Capability""" - return relationships.fk('node') + return relationship.foreign_key('node') @declared_attr def capability_template_fk(cls): """For Capability many-to-one to CapabilityTemplate""" - return relationships.fk('capability_template', nullable=True) + return relationship.foreign_key('capability_template', nullable=True) # endregion @@ -1202,10 +1217,6 @@ class CapabilityBase(InstanceModelMixin): else ' or more')) utils.dump_dict_values(self.properties, 'Properties') - __private_fields__ = ['capability_fk', - 'node_fk', - 'capability_template_fk'] - class InterfaceBase(InstanceModelMixin): """ @@ -1236,50 +1247,56 @@ class InterfaceBase(InstanceModelMixin): __tablename__ = 'interface' + __private_fields__ = ['type_fk', + 'node_fk', + 'group_fk', + 'relationship_fk', + 'interface_template_fk'] + @declared_attr def interface_template(cls): - return relationships.many_to_one(cls, 'interface_template') + return relationship.many_to_one(cls, 'interface_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') description = Column(Text) @declared_attr def inputs(cls): - return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') @declared_attr def operations(cls): - return relationships.one_to_many(cls, 'operation', dict_key='name') + return relationship.one_to_many(cls, 'operation', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Interface many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def node_fk(cls): """For Node one-to-many to Interface""" - return relationships.fk('node', nullable=True) + return relationship.foreign_key('node', nullable=True) @declared_attr def group_fk(cls): """For Group one-to-many to Interface""" - return relationships.fk('group', nullable=True) + return relationship.foreign_key('group', nullable=True) @declared_attr def relationship_fk(cls): """For Relationship one-to-many to Interface""" - return relationships.fk('relationship', nullable=True) + return relationship.foreign_key('relationship', nullable=True) @declared_attr def interface_template_fk(cls): """For Interface many-to-one to InterfaceTemplate""" - return relationships.fk('interface_template', nullable=True) + return relationship.foreign_key('interface_template', nullable=True) # endregion @@ -1310,12 +1327,6 @@ class InterfaceBase(InstanceModelMixin): utils.dump_dict_values(self.inputs, 'Inputs') utils.dump_dict_values(self.operations, 'Operations') - __private_fields__ = ['type_fk', - 'node_fk', - 'group_fk', - 'relationship_fk', - 'interface_template_fk'] - class OperationBase(InstanceModelMixin): """ @@ -1352,22 +1363,27 @@ class OperationBase(InstanceModelMixin): __tablename__ = 'operation' + __private_fields__ = ['service_fk', + 'interface_fk', + 'plugin_fk', + 'operation_template_fk'] + @declared_attr def operation_template(cls): - return relationships.many_to_one(cls, 'operation_template') + return relationship.many_to_one(cls, 'operation_template') description = Column(Text) @declared_attr def plugin_specification(cls): - return relationships.one_to_one(cls, 'plugin_specification') + return relationship.one_to_one(cls, 'plugin_specification') implementation = Column(Text) dependencies = Column(modeling_types.StrictList(item_cls=basestring)) @declared_attr def inputs(cls): - return relationships.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') executor = Column(Text) max_retries = Column(Integer) @@ -1378,22 +1394,22 @@ class OperationBase(InstanceModelMixin): @declared_attr def service_fk(cls): """For Service one-to-many to Operation""" - return relationships.fk('service', nullable=True) + return relationship.foreign_key('service', nullable=True) @declared_attr def interface_fk(cls): """For Interface one-to-many to Operation""" - return relationships.fk('interface', nullable=True) + return relationship.foreign_key('interface', nullable=True) @declared_attr def plugin_specification_fk(cls): """For Operation one-to-one to PluginSpecification""" - return relationships.fk('plugin_specification', nullable=True) + return relationship.foreign_key('plugin_specification', nullable=True) @declared_attr def operation_template_fk(cls): """For Operation many-to-one to OperationTemplate""" - return relationships.fk('operation_template', nullable=True) + return relationship.foreign_key('operation_template', nullable=True) # endregion @@ -1438,11 +1454,6 @@ class OperationBase(InstanceModelMixin): context.style.literal(self.retry_interval))) utils.dump_dict_values(self.inputs, 'Inputs') - __private_fields__ = ['service_fk', - 'interface_fk', - 'plugin_fk', - 'operation_template_fk'] - class ArtifactBase(InstanceModelMixin): """ @@ -1475,13 +1486,17 @@ class ArtifactBase(InstanceModelMixin): __tablename__ = 'artifact' + __private_fields__ = ['type_fk', + 'node_fk', + 'artifact_template_fk'] + @declared_attr def artifact_template(cls): - return relationships.many_to_one(cls, 'artifact_template') + return relationship.many_to_one(cls, 'artifact_template') @declared_attr def type(cls): - return relationships.many_to_one(cls, 'type') + return relationship.many_to_one(cls, 'type') description = Column(Text) source_path = Column(Text) @@ -1491,24 +1506,24 @@ class ArtifactBase(InstanceModelMixin): @declared_attr def properties(cls): - return relationships.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # region foreign_keys @declared_attr def type_fk(cls): """For Artifact many-to-one to Type""" - return relationships.fk('type') + return relationship.foreign_key('type') @declared_attr def node_fk(cls): """For Node one-to-many to Artifact""" - return relationships.fk('node') + return relationship.foreign_key('node') @declared_attr def artifact_template_fk(cls): """For Artifact many-to-one to ArtifactTemplate""" - return relationships.fk('artifact_template', nullable=True) + return relationship.foreign_key('artifact_template', nullable=True) # endregion @@ -1547,7 +1562,3 @@ class ArtifactBase(InstanceModelMixin): console.puts('Repository credential: {0}'.format( context.style.literal(self.repository_credential))) utils.dump_dict_values(self.properties, 'Properties') - - __private_fields__ = ['type_fk', - 'node_fk', - 'artifact_template_fk']
