You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by em...@apache.org on 2017/03/14 23:41:42 UTC
[3/3] incubator-ariatosca git commit: Many cleanups;
use dict for many models
Many cleanups; use dict for many 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/0f040a2b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/0f040a2b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/0f040a2b
Branch: refs/heads/ARIA-105-integrate-modeling
Commit: 0f040a2b9a72cd7ed66b067a69df63d0bb367f0d
Parents: f6da64a
Author: Tal Liron <ta...@gmail.com>
Authored: Tue Mar 14 18:40:55 2017 -0500
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue Mar 14 18:40:55 2017 -0500
----------------------------------------------------------------------
aria/cli/commands.py | 7 +-
aria/cli/dry.py | 2 +-
aria/modeling/mixins.py | 398 +++++++++------
aria/modeling/orchestration.py | 97 ++--
aria/modeling/relationship.py | 421 ----------------
aria/modeling/service_changes.py | 79 +--
aria/modeling/service_common.py | 30 +-
aria/modeling/service_instance.py | 493 ++++++++++---------
aria/modeling/service_template.py | 460 ++++++++---------
aria/modeling/types.py | 30 +-
aria/orchestrator/workflows/api/task.py | 162 +++---
.../simple_v1_0/modeling/__init__.py | 35 +-
tests/end2end/test_orchestrator.py | 7 +-
tests/mock/models.py | 8 +-
tests/modeling/test_models.py | 10 +-
tests/orchestrator/context/test_operation.py | 10 +-
tests/orchestrator/context/test_serialize.py | 2 +-
tests/orchestrator/context/test_toolbelt.py | 2 +-
tests/orchestrator/workflows/api/test_task.py | 14 +-
.../workflows/builtin/test_execute_operation.py | 2 +-
tests/orchestrator/workflows/core/test_task.py | 3 +-
.../executor/test_process_executor_extension.py | 4 +-
.../test_process_executor_tracked_changes.py | 4 +-
tests/storage/test_resource_storage.py | 58 +--
24 files changed, 1029 insertions(+), 1309 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index f9d3053..1eef61d 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -235,12 +235,7 @@ class WorkflowCommand(BaseCommand):
workflow_name))
inputs = {}
else:
- workflow = None
- for policy in context.modeling.instance.policies:
- if policy.name == workflow_name:
- workflow = policy
- break
-
+ workflow = context.modeling.instance.policies.get(workflow_name)
if workflow is None:
raise AttributeError('workflow policy does not exist: "{0}"'.format(workflow_name))
if workflow.type.role != 'workflow':
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/cli/dry.py
----------------------------------------------------------------------
diff --git a/aria/cli/dry.py b/aria/cli/dry.py
index 114ab7a..82faf42 100644
--- a/aria/cli/dry.py
+++ b/aria/cli/dry.py
@@ -34,7 +34,7 @@ def convert_to_dry(service):
for workflow in service.workflows:
convert_operation_to_dry(workflow)
- for node in service.nodes:
+ for node in service.nodes.itervalues():
for interface in node.interfaces.itervalues():
for oper in interface.operations.itervalues():
convert_operation_to_dry(oper)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index 9133a85..06f2497 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# pylint: disable=invalid-name
+
"""
ARIA's storage.structures module
Path: aria.storage.structures
@@ -35,13 +37,13 @@ from sqlalchemy import (
Table,
)
-from . import utils
+from .utils import classproperty
from ..utils import formatting
class ModelMixin(object):
- @utils.classproperty
+ @classproperty
def __modelname__(cls): # pylint: disable=no-self-argument
return getattr(cls, '__mapiname__', cls.__tablename__)
@@ -57,9 +59,9 @@ 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 retrieve (e.g., if a relationship wasn't established
- yet, and so it's impossible to access a property through it)
+ :param suppress_error: If set to True, sets `None` to attributes that it's unable to
+ retrieve (e.g., if a relationship wasn't established yet, and so it's
+ impossible to access a property through it)
"""
res = dict()
@@ -95,22 +97,64 @@ class ModelMixin(object):
return fields - set(getattr(cls, '__private_fields__', []))
@classmethod
- def _create_foreign_key(cls, parent_table, nullable=False):
+ def _iter_association_proxies(cls):
+ for col, value in vars(cls).items():
+ if isinstance(value, associationproxy.AssociationProxy):
+ yield col
+
+ def __repr__(self):
+ return '<{cls} id=`{id}`>'.format(
+ cls=self.__class__.__name__,
+ id=getattr(self, self.name_column_name()))
+
+ # Model property declaration helpers
+
+ @classmethod
+ def _declare_fk(cls,
+ other_table,
+ nullable=False):
"""
- Return a ForeignKey object.
+ 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".
- :param parent_table: Parent table name
- :param nullable: Should the column be allowed to remain empty
+ 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=parent_table),
- ondelete='CASCADE'),
+ ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'),
nullable=nullable)
@classmethod
- def _create_relationship_to_self(cls,
- column_name,
- relationship_kwargs=None):
+ def _declare_one_to_one_self(cls,
+ 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 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 = '{cls}.{remote_column}'.format(
@@ -121,11 +165,11 @@ class ModelMixin(object):
primaryjoin = '{remote_side} == {cls}.{column}'.format(
remote_side=remote_side,
cls=cls.__name__,
- column=column_name
+ column=fk
)
return relationship(
- cls._get_cls_by_tablename(cls.__tablename__).__name__,
+ cls._get_class_for_table(cls.__tablename__).__name__,
primaryjoin=primaryjoin,
remote_side=remote_side,
post_update=True,
@@ -133,100 +177,197 @@ class ModelMixin(object):
)
@classmethod
- def _create_one_to_many_relationship_to_self(cls,
- key,
- dict_key=None,
- relationship_kwargs=None):
+ def _declare_one_to_many_self(cls,
+ 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 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', '{cls}.{remote_column}'.format(
cls=cls.__name__,
- remote_column=key
+ remote_column=fk
))
- return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
- backreference=False, dict_key=dict_key)
+ return cls._declare_relationship(cls.__tablename__, None, relationship_kwargs,
+ other_property=False, dict_key=dict_key)
@classmethod
- def _create_one_to_one_relationship(cls,
- other_table,
- key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
+ def _declare_one_to_one(cls,
+ 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 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 cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
- backreference, key=key)
+ return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs,
+ other_property, fk=fk, other_fk=other_fk)
@classmethod
- def _create_one_to_many_relationship(cls,
- child_table,
- key=None,
- foreign_key=None,
- dict_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
+ def _declare_one_to_many(cls,
+ 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 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 cls._create_relationship(child_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, foreign_key=foreign_key,
- dict_key=dict_key)
+ return cls._declare_relationship(child_table, backref_kwargs, relationship_kwargs,
+ child_property, other_fk=child_fk, dict_key=dict_key)
@classmethod
- def _create_many_to_one_relationship(cls,
- parent_table,
- key=None,
- foreign_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
+ def _declare_many_to_one(cls,
+ parent_table,
+ fk=None,
+ parent_fk=None,
+ parent_property=None,
+ relationship_kwargs=None,
+ backref_kwargs=None):
"""
- Return a one-to-many SQL relationship object
- Meant to be used from inside the *child* object
+ 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:`_declare_one_to_many` at that model.
+
+ *This utility method should only be used during class creation.*
- :param parent_table: Name of the parent table
- :param foreign_key: The column of the foreign key (from the child table)
- :param backreference: The name to give to the reference to the child (on the parent table)
+ :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 backreference is None:
- backreference = formatting.pluralize(cls.__tablename__)
+ if parent_property is None:
+ parent_property = formatting.pluralize(cls.__tablename__)
backref_kwargs = backref_kwargs or {}
backref_kwargs.setdefault('uselist', True)
backref_kwargs.setdefault('lazy', 'dynamic')
- # The following line make sure that when the *parent* is deleted, all its connected children
- # are deleted as well
- backref_kwargs.setdefault('cascade', 'all')
+ backref_kwargs.setdefault('cascade', 'all') # delete children when parent is deleted
- return cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, foreign_key=foreign_key)
+ return cls._declare_relationship(parent_table, backref_kwargs, relationship_kwargs,
+ parent_property, fk=fk, other_fk=parent_fk)
@classmethod
- def _create_many_to_many_relationship(cls,
- other_table,
- table_prefix=None,
- key=None,
- dict_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
+ def _declare_many_to_many(cls,
+ other_table,
+ prefix=None,
+ dict_key=None,
+ other_property=None,
+ relationship_kwargs=None,
+ backref_kwargs=None):
"""
- Return a many-to-many SQL relationship object
+ 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.
- Notes:
+ 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:`_declare_many_to_many` again at that model.
- 1. The backreference name is the current table's table name
- 2. This method creates a new helper table in the DB
+ *This utility method should only be used during class creation.*
- :param cls: The class of the table we're connecting from
- :param other_table: The class of the table we're connecting to
- :param table_prefix: Custom prefix for the helper table name and the backreference name
- :param dict_key: If provided, will use a dict class with this column as the key
+ :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 = cls.__tablename__
@@ -236,87 +377,87 @@ class ModelMixin(object):
other_column_name = '{0}_id'.format(other_table)
other_foreign_key = '{0}.id'.format(other_table)
- helper_table = '{0}_{1}'.format(this_table, other_table)
+ secondary_table = '{0}_{1}'.format(this_table, other_table)
- if backreference is None:
- backreference = formatting.pluralize(this_table)
- if table_prefix:
- helper_table = '{0}_{1}'.format(table_prefix, helper_table)
- backreference = '{0}_{1}'.format(table_prefix, backreference)
+ 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', cls._get_secondary_table(
+ relationship_kwargs.setdefault('secondary', ModelMixin._get_secondary_table(
cls.metadata,
- helper_table,
+ secondary_table,
this_column_name,
other_column_name,
this_foreign_key,
other_foreign_key
))
- return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, dict_key=dict_key)
+ return cls._declare_relationship(other_table, backref_kwargs, relationship_kwargs,
+ other_property, dict_key=dict_key)
@classmethod
- def _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
- key=None, foreign_key=None, dict_key=None):
+ def _declare_relationship(cls, other_table, backref_kwargs, relationship_kwargs,
+ other_property, fk=None, other_fk=None, dict_key=None):
relationship_kwargs = relationship_kwargs or {}
- if key:
+ if fk:
relationship_kwargs.setdefault('foreign_keys',
lambda: getattr(
- cls._get_cls_by_tablename(cls.__tablename__),
- key))
+ cls._get_class_for_table(cls.__tablename__),
+ fk))
- elif foreign_key:
+ elif other_fk:
relationship_kwargs.setdefault('foreign_keys',
lambda: getattr(
- cls._get_cls_by_tablename(table),
- foreign_key))
+ cls._get_class_for_table(other_table),
+ other_fk))
if dict_key:
relationship_kwargs.setdefault('collection_class',
attribute_mapped_collection(dict_key))
- if backreference is False:
+ if other_property is False:
+ # No backref
return relationship(
- lambda: cls._get_cls_by_tablename(table),
+ lambda: cls._get_class_for_table(other_table),
**relationship_kwargs
)
else:
- if backreference is None:
- backreference = cls.__tablename__
+ if other_property is None:
+ other_property = cls.__tablename__
backref_kwargs = backref_kwargs or {}
return relationship(
- lambda: cls._get_cls_by_tablename(table),
- backref=backref(backreference, **backref_kwargs),
+ lambda: cls._get_class_for_table(other_table),
+ backref=backref(other_property, **backref_kwargs),
**relationship_kwargs
)
+ @classmethod
+ def _get_class_for_table(cls, tablename):
+ if tablename in (cls.__name__, cls.__tablename__):
+ return cls
+
+ for table_cls in cls._decl_class_registry.values():
+ if tablename == getattr(table_cls, '__tablename__', None):
+ return table_cls
+
+ raise ValueError('unknown table: {0}'.format(tablename))
+
@staticmethod
def _get_secondary_table(metadata,
- helper_table,
+ name,
first_column,
second_column,
first_foreign_key,
second_foreign_key):
- """
- Create a helper table for a many-to-many relationship
-
- :param helper_table: The name of the table
- :param first_column_name: The name of the first column in the table
- :param second_column_name: The name of the second column in the table
- :param first_foreign_key: The string representing the first foreign key,
- for example `blueprint.storage_id`, or `tenants.id`
- :param second_foreign_key: The string representing the second foreign key
- :return: A Table object
- """
-
return Table(
- helper_table,
+ name,
metadata,
Column(
first_column,
@@ -330,33 +471,6 @@ class ModelMixin(object):
)
)
- @classmethod
- def _iter_association_proxies(cls):
- for col, value in vars(cls).items():
- if isinstance(value, associationproxy.AssociationProxy):
- yield col
-
- @classmethod
- def _get_cls_by_tablename(cls, tablename):
- """
- Return class reference mapped to table.
-
- :param tablename: String with name of table.
- :return: Class reference or None.
- """
-
- if tablename in (cls.__name__, cls.__tablename__):
- return cls
-
- for table_cls in cls._decl_class_registry.values():
- if tablename == getattr(table_cls, '__tablename__', None):
- return table_cls
-
- def __repr__(self):
- return '<{cls} id=`{id}`>'.format(
- cls=self.__class__.__name__,
- id=getattr(self, self.name_column_name()))
-
class ModelIDMixin(object):
id = Column(Integer, primary_key=True, autoincrement=True)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/orchestration.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index d8bdd3c..683c526 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -100,28 +100,33 @@ class ExecutionBase(ModelMixin):
workflow_name = Column(Text)
@declared_attr
- def service_template(cls):
- return association_proxy('service', 'service_template')
+ def service(cls):
+ return cls._declare_many_to_one('service')
+
+ # region foreign keys
@declared_attr
- def service(cls):
- return cls._create_many_to_one_relationship('service')
+ def service_fk(cls):
+ return cls._declare_fk('service')
+
+ # endregion
+
+ # region association proxies
@declared_attr
def service_name(cls):
+ """Required for use by SqlAlchemy queries"""
return association_proxy('service', cls.name_column_name())
@declared_attr
- def service_template_name(cls):
- return association_proxy('service', 'service_template_name')
-
- # region foreign keys
-
- __private_fields__ = ['service_fk']
+ def service_template(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('service', 'service_template')
@declared_attr
- def service_fk(cls):
- return cls._create_foreign_key('service')
+ def service_template_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('service', 'service_template_name')
# endregion
@@ -132,6 +137,11 @@ class ExecutionBase(ModelMixin):
self.status
)
+ __private_fields__ = ['service_fk',
+ 'service_name',
+ 'service_template',
+ 'service_template_name']
+
class PluginBase(ModelMixin):
"""
@@ -186,37 +196,24 @@ class TaskBase(ModelMixin):
INFINITE_RETRIES = -1
@declared_attr
- def node_name(cls):
- return association_proxy('node', cls.name_column_name())
-
- @declared_attr
def node(cls):
- return cls._create_many_to_one_relationship('node')
-
- @declared_attr
- def relationship_name(cls):
- return association_proxy('relationship', cls.name_column_name())
+ return cls._declare_many_to_one('node')
@declared_attr
def relationship(cls):
- return cls._create_many_to_one_relationship('relationship')
+ return cls._declare_many_to_one('relationship')
@declared_attr
def plugin(cls):
- return cls._create_many_to_one_relationship('plugin')
+ return cls._declare_many_to_one('plugin')
@declared_attr
def execution(cls):
- return cls._create_many_to_one_relationship('execution')
-
- @declared_attr
- def execution_name(cls):
- return association_proxy('execution', cls.name_column_name())
+ return cls._declare_many_to_one('execution')
@declared_attr
def inputs(cls):
- return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
+ return cls._declare_many_to_many('parameter', prefix='inputs', dict_key='name')
status = Column(Enum(*STATES, name='status'), default=PENDING)
@@ -260,26 +257,40 @@ class TaskBase(ModelMixin):
# region foreign keys
- __private_fields__ = ['node_fk',
- 'relationship_fk',
- 'plugin_fk',
- 'execution_fk']
-
@declared_attr
def node_fk(cls):
- return cls._create_foreign_key('node', nullable=True)
+ return cls._declare_fk('node', nullable=True)
@declared_attr
def relationship_fk(cls):
- return cls._create_foreign_key('relationship', nullable=True)
+ return cls._declare_fk('relationship', nullable=True)
@declared_attr
def plugin_fk(cls):
- return cls._create_foreign_key('plugin', nullable=True)
+ return cls._declare_fk('plugin', nullable=True)
@declared_attr
def execution_fk(cls):
- return cls._create_foreign_key('execution', nullable=True)
+ return cls._declare_fk('execution', nullable=True)
+
+ # endregion
+
+ # region association proxies
+
+ @declared_attr
+ def node_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('node', cls.name_column_name())
+
+ @declared_attr
+ def relationship_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('relationship', cls.name_column_name())
+
+ @declared_attr
+ def execution_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('execution', cls.name_column_name())
# endregion
@@ -298,3 +309,11 @@ class TaskBase(ModelMixin):
@staticmethod
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']
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/relationship.py
----------------------------------------------------------------------
diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py
deleted file mode 100644
index 2fdbe0c..0000000
--- a/aria/modeling/relationship.py
+++ /dev/null
@@ -1,421 +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, unused-argument
-
-from functools import wraps
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy import (
- Column,
- ForeignKey,
- Integer,
- Table
-)
-
-from ..utils import formatting
-
-
-def fk(other_table,
- nullable=False):
- """
- Decorator for 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. If you do not do so, SqlAlchemy will fail to create
- the relationship 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.
-
- :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
- """
-
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- if not hasattr(cls, '__private_fields__'):
- cls.__private_fields__ = []
- cls.__private_fields__.append(fn.__name__)
- return Column(Integer,
- ForeignKey('{table}.id'.format(table=other_table), ondelete='CASCADE'),
- nullable=nullable)
- return attr
- return decorator
-
-
-def one_to_one_self(fk,
- relationship_kwargs=None):
- """
- Decorator for 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.
-
- :param fk: Foreign key name
- :type fk: basestring
- :param relationship_kwargs: Extra kwargs for SqlAlchemy `relationship`
- :type relationship_kwargs: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- relationship_kwargs = scope['relationship_kwargs'] or {}
-
- remote_side = '{cls}.{remote_column}'.format(
- cls=cls.__name__,
- remote_column=cls.id_column_name()
- )
-
- primaryjoin = '{remote_side} == {cls}.{column}'.format(
- remote_side=remote_side,
- cls=cls.__name__,
- column=fk
- )
-
- return relationship(
- _get_class_for_table(cls, cls.__tablename__).__name__,
- primaryjoin=primaryjoin,
- remote_side=remote_side,
- post_update=True,
- **relationship_kwargs
- )
- return attr
- return decorator
-
-
-def one_to_many_self(fk,
- dict_key=None,
- relationship_kwargs=None):
- """
- Decorator for 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.
-
- :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: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- relationship_kwargs = scope['relationship_kwargs'] or {}
-
- relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
- cls=cls.__name__,
- remote_column=fk
- ))
-
- return _create_relationship(cls, cls.__tablename__, None, relationship_kwargs,
- other_property=False, dict_key=dict_key)
- return attr
- return decorator
-
-
-def one_to_one(other_table,
- fk=None,
- other_fk=None,
- other_property=None,
- relationship_kwargs=None,
- backref_kwargs=None):
- """
- Decorator for 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).
-
- :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: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- backref_kwargs = scope['backref_kwargs'] or {}
- backref_kwargs.setdefault('uselist', False)
-
- return _create_relationship(cls, other_table, backref_kwargs, relationship_kwargs,
- other_property, fk=fk, other_fk=other_fk)
- return attr
- return decorator
-
-
-def one_to_many(child_table,
- child_fk=None,
- dict_key=None,
- child_property=None,
- relationship_kwargs=None,
- backref_kwargs=None):
- """
- Decorator for 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 decorator 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.
-
- :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: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- backref_kwargs = scope['backref_kwargs'] or {}
- backref_kwargs.setdefault('uselist', False)
-
- return _create_relationship(cls, child_table, backref_kwargs, relationship_kwargs,
- child_property, other_fk=child_fk, dict_key=dict_key)
- return attr
- return decorator
-
-
-def many_to_one(parent_table,
- fk=None,
- parent_fk=None,
- parent_property=None,
- relationship_kwargs=None,
- backref_kwargs=None):
- """
- Decorator for 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 decorator 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 list; if you need it to be a dict, use
- the :func:`one_to_many` decorator on that model instead.
-
- :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: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- parent_property = scope['parent_property']
- if parent_property is None:
- parent_property = formatting.pluralize(cls.__tablename__)
-
- backref_kwargs = scope['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 _create_relationship(cls, parent_table, backref_kwargs, relationship_kwargs,
- parent_property, fk=fk, other_fk=parent_fk)
- return attr
- return decorator
-
-
-def many_to_many(other_table,
- prefix=None,
- dict_key=None,
- other_property=None,
- relationship_kwargs=None,
- backref_kwargs=None):
- """
- Decorator for 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 decorator 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 list; if you need it to be a dict, use
- the `many_to_many` decorator again on that model.
-
- :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: {}
- """
-
- scope = locals()
- def decorator(fn):
- @declared_attr
- @wraps(fn)
- def attr(cls):
- this_table = cls.__tablename__
- this_column_name = '{0}_id'.format(this_table)
- this_foreign_key = '{0}.id'.format(this_table)
-
- other_column_name = '{0}_id'.format(other_table)
- other_foreign_key = '{0}.id'.format(other_table)
-
- secondary_table = '{0}_{1}'.format(this_table, other_table)
-
- other_property = scope['other_property']
- 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 = scope['backref_kwargs'] or {}
- backref_kwargs.setdefault('uselist', True)
-
- relationship_kwargs = scope['relationship_kwargs'] or {}
- relationship_kwargs.setdefault('secondary', _get_secondary_table(
- cls.metadata,
- secondary_table,
- this_column_name,
- other_column_name,
- this_foreign_key,
- other_foreign_key
- ))
-
- return _create_relationship(cls, other_table, backref_kwargs, relationship_kwargs,
- other_property, dict_key=dict_key)
- return attr
- return decorator
-
-
-def _create_relationship(cls, 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(cls, cls.__tablename__),
- fk))
-
- elif other_fk:
- relationship_kwargs.setdefault('foreign_keys',
- lambda: getattr(
- _get_class_for_table(cls, 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(cls, other_table),
- **relationship_kwargs
- )
- else:
- if other_property is None:
- other_property = cls.__tablename__
- backref_kwargs = backref_kwargs or {}
- return relationship(
- lambda: _get_class_for_table(cls, other_table),
- backref=backref(other_property, **backref_kwargs),
- **relationship_kwargs
- )
-
-
-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)
- )
- )
-
-
-def _get_class_for_table(cls, tablename):
- if tablename in (cls.__name__, cls.__tablename__):
- return cls
-
- for table_cls in cls._decl_class_registry.values():
- if tablename == getattr(table_cls, '__tablename__', None):
- return table_cls
-
- raise ValueError('unknown table: {0}'.format(tablename))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/0f040a2b/aria/modeling/service_changes.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py
index b83a376..eb7bb27 100644
--- a/aria/modeling/service_changes.py
+++ b/aria/modeling/service_changes.py
@@ -65,33 +65,35 @@ class ServiceUpdateBase(ModelMixin):
@declared_attr
def execution(cls):
- return cls._create_many_to_one_relationship('execution')
+ return cls._declare_many_to_one('execution')
@declared_attr
- def execution_name(cls):
- return association_proxy('execution', cls.name_column_name())
+ def service(cls):
+ return cls._declare_many_to_one('service', parent_property='updates')
+
+ # region foreign keys
@declared_attr
- def service(cls):
- return cls._create_many_to_one_relationship('service',
- backreference='updates')
+ def execution_fk(cls):
+ return cls._declare_fk('execution', nullable=True)
@declared_attr
- def service_name(cls):
- return association_proxy('service', cls.name_column_name())
+ def service_fk(cls):
+ return cls._declare_fk('service')
- # region foreign keys
+ # endregion
- __private_fields__ = ['service_fk',
- 'execution_fk']
+ # region association proxies
@declared_attr
- def execution_fk(cls):
- return cls._create_foreign_key('execution', nullable=True)
+ def execution_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('execution', cls.name_column_name())
@declared_attr
- def service_fk(cls):
- return cls._create_foreign_key('service')
+ def service_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('service', cls.name_column_name())
# endregion
@@ -101,6 +103,11 @@ 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):
"""
@@ -133,20 +140,22 @@ class ServiceUpdateStepBase(ModelMixin):
@declared_attr
def service_update(cls):
- return cls._create_many_to_one_relationship('service_update',
- backreference='steps')
+ return cls._declare_many_to_one('service_update', parent_property='steps')
+
+ # region foreign keys
@declared_attr
- def service_update_name(cls):
- return association_proxy('service_update', cls.name_column_name())
+ def service_update_fk(cls):
+ return cls._declare_fk('service_update')
- # region foreign keys
+ # endregion
- __private_fields__ = ['service_update_fk']
+ # region association proxies
@declared_attr
- def service_update_fk(cls):
- return cls._create_foreign_key('service_update')
+ def service_update_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ return association_proxy('service_update', cls.name_column_name())
# endregion
@@ -177,6 +186,9 @@ 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):
"""
@@ -201,19 +213,24 @@ class ServiceModificationBase(ModelMixin):
@declared_attr
def service(cls):
- return cls._create_many_to_one_relationship('service',
- backreference='modifications')
+ return cls._declare_many_to_one('service', parent_property='modifications')
+
+ # region foreign keys
@declared_attr
- def service_name(cls):
- return association_proxy('service', cls.name_column_name())
+ def service_fk(cls):
+ return cls._declare_fk('service')
- # region foreign keys
+ # endregion
- __private_fields__ = ['service_fk']
+ # region association proxies
@declared_attr
- def service_fk(cls):
- return cls._create_foreign_key('service')
+ def service_name(cls):
+ """Required for use by SqlAlchemy queries"""
+ 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/0f040a2b/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index 7422211..6214bb2 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -16,7 +16,6 @@
# pylint: disable=no-self-argument, no-member, abstract-method
import cPickle as pickle
-import logging
from sqlalchemy import (
Column,
@@ -68,9 +67,9 @@ class ParameterBase(TemplateModelMixin):
return None
try:
return pickle.loads(self._value)
- except BaseException:
+ except BaseException as e:
raise exceptions.ValueFormatException('bad format for parameter of type "{0}": {1}'
- .format(self.type_name, self._value))
+ .format(self.type_name, self._value), cause=e)
@value.setter
def value(self, value):
@@ -79,9 +78,9 @@ class ParameterBase(TemplateModelMixin):
else:
try:
self._value = pickle.dumps(value)
- except (pickle.PicklingError, TypeError):
- logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
- .format(self.type_name, value))
+ except (pickle.PicklingError, TypeError) as e:
+ #raise exceptions.ValueFormatException('bad format for parameter of type "{0}": {1}'
+ # .format(self.type_name, self.value), cause=e)
self._value = pickle.dumps(str(value))
def instantiate(self, container):
@@ -124,20 +123,18 @@ class TypeBase(InstanceModelMixin):
@declared_attr
def parent(cls):
- return cls._create_relationship_to_self('parent_type_fk')
+ return cls._declare_one_to_one_self('parent_type_fk')
@declared_attr
def children(cls):
- return cls._create_one_to_many_relationship_to_self('parent_type_fk')
+ return cls._declare_one_to_many_self('parent_type_fk')
# region foreign keys
- __private_fields__ = ['parent_type_fk']
-
- # Type one-to-many to Type
@declared_attr
def parent_type_fk(cls):
- return cls._create_foreign_key('type', nullable=True)
+ """For Type one-to-many to Type"""
+ return cls._declare_fk('type', nullable=True)
# endregion
@@ -206,6 +203,8 @@ class TypeBase(InstanceModelMixin):
types.append(raw_child)
child._append_raw_children(types)
+ __private_fields__ = ['parent_type_fk']
+
class MetadataBase(TemplateModelMixin):
"""
@@ -258,11 +257,10 @@ class PluginSpecificationBase(InstanceModelMixin):
# region foreign keys
- __private_fields__ = ['service_template_fk']
-
@declared_attr
def service_template_fk(cls):
- return cls._create_foreign_key('service_template', nullable=True)
+ """For ServiceTemplate one-to-many to PluginSpecification"""
+ return cls._declare_fk('service_template', nullable=True)
# endregion
@@ -272,3 +270,5 @@ class PluginSpecificationBase(InstanceModelMixin):
if plugin.name == self.name:
return plugin
return None
+
+ __private_fields__ = ['service_template_fk']