ARIA-132-Models-cascading-deletion-raises-constraint-errors Additional fixes: - The relationships are now defined on both sides. we no longer use backref, but back_populates.
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/2de04972 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/2de04972 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/2de04972 Branch: refs/heads/ARIA-86-Create-a-basic-Hello-World-blueprint-example Commit: 2de049729f5d0fff50297d25a88f5eecdcf266f9 Parents: 07cbfcd Author: max-orlov <[email protected]> Authored: Sun Mar 26 14:13:47 2017 +0300 Committer: max-orlov <[email protected]> Committed: Tue Mar 28 15:01:15 2017 +0300 ---------------------------------------------------------------------- aria/.pylintrc | 2 +- aria/modeling/orchestration.py | 16 + aria/modeling/relationship.py | 222 +++-- aria/modeling/service_changes.py | 86 +- aria/modeling/service_common.py | 4 + aria/modeling/service_instance.py | 813 +++++++++++------- aria/modeling/service_template.py | 823 +++++++++++++------ tests/orchestrator/context/test_operation.py | 4 +- .../node-cellar/node-cellar.yaml | 1 - 9 files changed, 1299 insertions(+), 672 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/.pylintrc ---------------------------------------------------------------------- diff --git a/aria/.pylintrc b/aria/.pylintrc index 589402f..7222605 100644 --- a/aria/.pylintrc +++ b/aria/.pylintrc @@ -375,7 +375,7 @@ max-attributes=20 min-public-methods=0 # Maximum number of public methods for a class (see R0904). -max-public-methods=20 +max-public-methods=50 # Maximum number of boolean expressions in a if statement max-bool-expr=5 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/modeling/orchestration.py ---------------------------------------------------------------------- diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py index 2d58671..a13ae87 100644 --- a/aria/modeling/orchestration.py +++ b/aria/modeling/orchestration.py @@ -103,9 +103,17 @@ class ExecutionBase(ModelMixin): workflow_name = Column(Text) @declared_attr + def logs(cls): + return relationship.one_to_many(cls, 'log') + + @declared_attr def service(cls): return relationship.many_to_one(cls, 'service') + @declared_attr + def tasks(cls): + return relationship.one_to_many(cls, 'task') + # region foreign keys @declared_attr @@ -185,6 +193,10 @@ class PluginBase(ModelMixin): __tablename__ = 'plugin' + @declared_attr + def tasks(cls): + return relationship.one_to_many(cls, 'task') + archive_name = Column(Text, nullable=False, index=True) distribution = Column(Text) distribution_release = Column(Text) @@ -239,6 +251,10 @@ class TaskBase(ModelMixin): INFINITE_RETRIES = -1 @declared_attr + def logs(cls): + return relationship.one_to_many(cls, 'log') + + @declared_attr def node(cls): return relationship.many_to_one(cls, 'node') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/modeling/relationship.py ---------------------------------------------------------------------- diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py index 70691b3..ac1de28 100644 --- a/aria/modeling/relationship.py +++ b/aria/modeling/relationship.py @@ -26,9 +26,10 @@ from sqlalchemy import ( from ..utils import formatting +NO_BACK_POP = 'NO_BACK_POP' -def foreign_key(other_table, - nullable=False): + +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". @@ -54,9 +55,7 @@ def foreign_key(other_table, nullable=nullable) -def one_to_one_self(model_class, - fk, - relationship_kwargs=None): +def one_to_one_self(model_class, fk): """ Declare a one-to-one relationship property. The property value would be an instance of the same model. @@ -69,12 +68,8 @@ def one_to_one_self(model_class, :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() @@ -85,20 +80,18 @@ def one_to_one_self(model_class, 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 + return _relationship( + model_class, + model_class.__tablename__, + relationship_kwargs={ + 'primaryjoin': primaryjoin, + 'remote_side': remote_side, + 'post_update': True + } ) -def one_to_many_self(model_class, - fk, - dict_key=None, - relationship_kwargs=None): +def one_to_many_self(model_class, fk, dict_key=None): """ Declare a one-to-many relationship property. The property value would be a list or dict of instances of the same model. @@ -114,28 +107,24 @@ def one_to_many_self(model_class, :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) + return _relationship( + model_class, + model_class.__tablename__, + relationship_kwargs={ + 'remote_side': '{model_class}.{remote_column}'.format( + model_class=model_class.__name__, remote_column=fk) + }, + back_populates=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): + back_populates=None): """ Declare a one-to-one relationship property. The property value would be an instance of the other table's model. @@ -154,26 +143,25 @@ def one_to_one(model_class, :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: {} + :param back_populates: Override name of matching many-to-many property at other table; set to + false to disable + :type back_populates: basestring|bool """ + if back_populates is None: + back_populates = model_class.__tablename__ - 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) + return _relationship(model_class, + other_table, + fk=fk, + back_populates=back_populates, + 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): + back_populates=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. @@ -194,29 +182,25 @@ def one_to_many(model_class, :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 + :param back_populates: 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: {} + :type back_populates: basestring|bool """ - - 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) + if back_populates is None: + back_populates = model_class.__tablename__ + return _relationship( + model_class, + child_table, + back_populates=back_populates, + 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): + back_populates=None): """ Declare a many-to-one relationship property. The property value would be an instance of the parent table's model. @@ -236,34 +220,25 @@ def many_to_one(model_class, :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 + :param back_populates: 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: {} + :type back_populates: basestring|bool """ + if back_populates is None: + back_populates = formatting.pluralize(model_class.__tablename__) - 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) + return _relationship(model_class, + parent_table, + back_populates=back_populates, + 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): + other_property=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. @@ -280,8 +255,8 @@ def many_to_many(model_class, :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 other_table: Parent table name + :type other_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 @@ -290,10 +265,6 @@ def many_to_many(model_class, :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__ @@ -303,69 +274,68 @@ def many_to_many(model_class, 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) + secondary_table_name = '{0}_{1}'.format(this_table, other_table) if prefix is not None: - secondary_table = '{0}_{1}'.format(prefix, secondary_table) + secondary_table_name = '{0}_{1}'.format(prefix, secondary_table_name) if other_property is None: other_property = '{0}_{1}'.format(prefix, formatting.pluralize(this_table)) - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('uselist', True) - - relationship_kwargs = relationship_kwargs or {} - relationship_kwargs.setdefault('secondary', _get_secondary_table( + secondary_table = _get_secondary_table( model_class.metadata, - secondary_table, + secondary_table_name, 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) + return _relationship( + model_class, + other_table, + relationship_kwargs={'secondary': secondary_table}, + backref_kwargs={'name': other_property, 'uselist': True} if other_property else None, + dict_key=dict_key + ) -def _relationship(model_class, other_table, backref_kwargs, relationship_kwargs, other_property, - fk=None, other_fk=None, dict_key=None): +def _relationship(model_class, + other_table_name, + back_populates=None, + backref_kwargs=None, + relationship_kwargs=None, + 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)) + 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)) + relationship_kwargs.setdefault( + 'foreign_keys', + lambda: getattr(_get_class_for_table(model_class, other_table_name), 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 - ) + if backref_kwargs: + assert back_populates is None + return relationship(lambda: _get_class_for_table(model_class, other_table_name), + backref=backref(**backref_kwargs), + **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 - ) + if back_populates is not NO_BACK_POP: + relationship_kwargs['back_populates'] = back_populates + return relationship(lambda: _get_class_for_table(model_class, other_table_name), + **relationship_kwargs) def _get_class_for_table(model_class, tablename): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/modeling/service_changes.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py index a33e6ae..b1a75a2 100644 --- a/aria/modeling/service_changes.py +++ b/aria/modeling/service_changes.py @@ -42,9 +42,6 @@ class ServiceUpdateBase(ModelMixin): """ Deployment update model representation. """ - - steps = None - __tablename__ = 'service_update' __private_fields__ = ['service_fk', @@ -60,14 +57,6 @@ class ServiceUpdateBase(ModelMixin): modified_entity_ids = Column(Dict) state = Column(Text) - @declared_attr - def execution(cls): - return relationship.many_to_one(cls, 'execution') - - @declared_attr - def service(cls): - return relationship.many_to_one(cls, 'service', parent_property='updates') - # region foreign keys @declared_attr @@ -94,6 +83,34 @@ class ServiceUpdateBase(ModelMixin): # endregion + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + @declared_attr + def steps(cls): + return relationship.one_to_many(cls, 'service_update_step') + + # endregion + + # region many_to_one relationships + + @declared_attr + def execution(cls): + return relationship.one_to_one(cls, 'execution', back_populates=relationship.NO_BACK_POP) + + @declared_attr + def service(cls): + return relationship.many_to_one(cls, 'service', back_populates='updates') + + # endregion + + # region many_to_many relationships + + # endregion + def to_dict(self, suppress_error=False, **kwargs): dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error) #pylint: disable=no-member # Taking care of the fact the DeploymentSteps are _BaseModels @@ -133,10 +150,6 @@ class ServiceUpdateStepBase(ModelMixin): entity_id = Column(Text, nullable=False) entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False) - @declared_attr - def service_update(cls): - return relationship.many_to_one(cls, 'service_update', parent_property='steps') - # region foreign keys @declared_attr @@ -154,6 +167,26 @@ class ServiceUpdateStepBase(ModelMixin): # endregion + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + + @declared_attr + def service_update(cls): + return relationship.many_to_one(cls, 'service_update', back_populates='steps') + + # endregion + + # region many_to_many relationships + + # endregion + def __hash__(self): return hash((getattr(self, self.id_column_name()), self.entity_id)) @@ -206,10 +239,6 @@ class ServiceModificationBase(ModelMixin): nodes = Column(Dict) status = Column(Enum(*STATES, name='service_modification_status')) - @declared_attr - def service(cls): - return relationship.many_to_one(cls, 'service', parent_property='modifications') - # region foreign keys @declared_attr @@ -226,3 +255,22 @@ class ServiceModificationBase(ModelMixin): return association_proxy('service', cls.name_column_name()) # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + @declared_attr + def service(cls): + return relationship.many_to_one(cls, 'service', back_populates='modifications') + + # endregion + + # region many_to_many relationships + + # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index d6b1f33..48c3170 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -275,6 +275,10 @@ class PluginSpecificationBase(TemplateModelMixin): # endregion + @declared_attr + def service_template(cls): + return relationship.many_to_one(cls, 'service_template') + @property def as_raw(self): return collections.OrderedDict(( http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2de04972/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index 1e18db0..e2e5ae0 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -36,7 +36,7 @@ from . import ( ) -class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods +class ServiceBase(InstanceModelMixin): """ A service is usually an instance of a :class:`ServiceTemplate`. @@ -71,12 +71,10 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods :vartype created_at: :class:`datetime.datetime` :ivar updated_at: Update timestamp :vartype updated_at: :class:`datetime.datetime` - :ivar permalink: ?? :vartype permalink: basestring :ivar scaling_groups: ?? :vartype scaling_groups: {} - :ivar modifications: Modifications of this service :vartype modifications: [:class:`ServiceModification`] :ivar updates: Updates of this service @@ -91,16 +89,53 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods 'service_template_fk', 'service_template_name'] + # region foreign keys + @declared_attr - def service_template(cls): - return relationship.many_to_one(cls, 'service_template') + def substitution_fk(cls): + """Service one-to-one to Substitution""" + return relationship.foreign_key('substitution', nullable=True) - description = Column(Text) + @declared_attr + def service_template_fk(cls): + """For Service many-to-one to ServiceTemplate""" + return relationship.foreign_key('service_template', nullable=True) + + # endregion + + # region association proxies @declared_attr - def meta_data(cls): - # Warning! We cannot use the attr name "metadata" because it's used by SQLAlchemy! - return relationship.many_to_many(cls, 'metadata', dict_key='name') + def service_template_name(cls): + """Required for use by SQLAlchemy queries""" + return association_proxy('service_template', 'name') + + # endregion + + # region one_to_one relationships + + @declared_attr + def substitution(cls): + return relationship.one_to_one(cls, 'substitution', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region one_to_many relationships + @declared_attr + def updates(cls): + return relationship.one_to_many(cls, 'service_update') + + @declared_attr + def modifications(cls): + return relationship.one_to_many(cls, 'service_modification') + + @declared_attr + def executions(cls): + return relationship.one_to_many(cls, 'execution') + + @declared_attr + def operations(cls): + return relationship.one_to_many(cls, 'operation') @declared_attr def nodes(cls): @@ -115,8 +150,24 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods return relationship.one_to_many(cls, 'policy', dict_key='name') @declared_attr - def substitution(cls): - return relationship.one_to_one(cls, 'substitution') + def workflows(cls): + return relationship.one_to_many(cls, 'operation', dict_key='name') + + # endregion + + # region many_to_one relationships + + @declared_attr + def service_template(cls): + return relationship.many_to_one(cls, 'service_template') + + # endregion + + # region many_to_many relationships + @declared_attr + def meta_data(cls): + # Warning! We cannot use the attr name "metadata" because it's used by SQLAlchemy! + return relationship.many_to_many(cls, 'metadata', dict_key='name') @declared_attr def inputs(cls): @@ -127,13 +178,13 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods return relationship.many_to_many(cls, 'parameter', prefix='outputs', dict_key='name') @declared_attr - def workflows(cls): - return relationship.one_to_many(cls, 'operation', dict_key='name') - - @declared_attr def plugin_specifications(cls): return relationship.many_to_many(cls, 'plugin_specification', dict_key='name') + # endregion + + description = Column(Text) + created_at = Column(DateTime, nullable=False, index=True) updated_at = Column(DateTime) @@ -144,29 +195,6 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods # endregion - # region foreign keys - - @declared_attr - def substitution_fk(cls): - """Service one-to-one to Substitution""" - return relationship.foreign_key('substitution', nullable=True) - - @declared_attr - def service_template_fk(cls): - """For Service many-to-one to ServiceTemplate""" - return relationship.foreign_key('service_template', nullable=True) - - # endregion - - # region association proxies - - @declared_attr - def service_template_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service_template', 'name') - - # endregion - def satisfy_requirements(self): satisfied = True for node in self.nodes.itervalues(): @@ -290,7 +318,7 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods self._dump_graph_node(target_node) -class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods +class NodeBase(InstanceModelMixin): """ Usually an instance of a :class:`NodeTemplate`. @@ -318,7 +346,6 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods :vartype inbound_relationships: [:class:`Relationship`] :ivar host: Host node (can be self) :vartype host: :class:`Node` - :ivar runtime_properties: TODO: should be replaced with attributes :vartype runtime_properties: {} :ivar scaling_groups: ?? @@ -327,7 +354,6 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods :vartype state: string :ivar version: Used by `aria.storage.instrumentation` :vartype version: int - :ivar service: Containing service :vartype service: :class:`Service` :ivar groups: We are a member of these groups @@ -391,19 +417,52 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods except KeyError: return None + # region foreign_keys + @declared_attr - def node_template(cls): - return relationship.many_to_one(cls, 'node_template') + def type_fk(cls): + """For Node many-to-one to Type""" + return relationship.foreign_key('type') @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') + def host_fk(cls): + """For Node one-to-one to Node""" + return relationship.foreign_key('node', nullable=True) - description = Column(Text) + @declared_attr + def service_fk(cls): + """For Service one-to-many to Node""" + return relationship.foreign_key('service') @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + def node_template_fk(cls): + """For Node many-to-one to NodeTemplate""" + return relationship.foreign_key('node_template') + + # endregion + + # region association proxies + + @declared_attr + def service_name(cls): + """Required for use by SQLAlchemy queries""" + return association_proxy('service', 'name') + + # endregion + + # region one_to_one relationships + + @declared_attr + def host(cls): + return relationship.one_to_one_self(cls, 'host_fk') + + # endregion + + # region one_to_many relationships + + @declared_attr + def tasks(cls): + return relationship.one_to_many(cls, 'task') @declared_attr def interfaces(cls): @@ -419,20 +478,40 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods @declared_attr def outbound_relationships(cls): - return relationship.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', back_populates='source_node') @declared_attr def inbound_relationships(cls): - return relationship.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', back_populates='target_node') + + # endregion + + # region many_to_one relationships @declared_attr - def host(cls): - return relationship.one_to_one_self(cls, 'host_fk') + def service(cls): + return relationship.many_to_one(cls, 'service') - # region orchestration + @declared_attr + def node_template(cls): + return relationship.many_to_one(cls, 'node_template') + + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + # endregion + + # region many_to_many relationships + @declared_attr + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + + # endregion + + description = Column(Text) runtime_properties = Column(modeling_types.Dict) scaling_groups = Column(modeling_types.List) state = Column(Enum(*STATES, name='node_state'), nullable=False, default=INITIAL) @@ -454,41 +533,6 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods return host_ip_property.value return None - # endregion - - # region foreign_keys - - @declared_attr - def type_fk(cls): - """For Node many-to-one to Type""" - return relationship.foreign_key('type') - - @declared_attr - def host_fk(cls): - """For Node one-to-one to Node""" - return relationship.foreign_key('node', nullable=True) - - @declared_attr - def service_fk(cls): - """For Service one-to-many to Node""" - return relationship.foreign_key('service') - - @declared_attr - def node_template_fk(cls): - """For Node many-to-one to NodeTemplate""" - return relationship.foreign_key('node_template', nullable=True) - - # endregion - - # region association proxies - - @declared_attr - def service_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service', 'name') - - # endregion - def satisfy_requirements(self): node_template = self.node_template satisfied = True @@ -513,7 +557,7 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods from . import models context = ConsumptionContext.get_thread_local() # Find target nodes - target_nodes = target_node_template.nodes.all() + target_nodes = target_node_template.nodes if target_nodes: target_node = None target_capability = None @@ -621,6 +665,7 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods utils.dump_dict_values(self.capabilities, 'Capabilities') utils.dump_list_values(self.outbound_relationships, 'Relationships') + class GroupBase(InstanceModelMixin): """ Usually an instance of a :class:`GroupTemplate`. @@ -639,7 +684,6 @@ class GroupBase(InstanceModelMixin): :vartype properties: {basestring: :class:`Parameter`} :ivar interfaces: Bundles of operations :vartype interfaces: {basestring: :class:`Interface`} - :ivar service: Containing service :vartype service: :class:`Service` :ivar policies: Policies enacted on this group @@ -648,51 +692,73 @@ class GroupBase(InstanceModelMixin): __tablename__ = 'group' - __private_fields__ = ['type_fk', - 'service_fk', - 'group_template_fk'] + __private_fields__ = ['type_fk', 'service_fk', 'group_template_fk'] - @declared_attr - def group_template(cls): - return relationship.many_to_one(cls, 'group_template') + # region foreign_keys @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') - - description = Column(Text) + def type_fk(cls): + """For Group many-to-one to Type""" + return relationship.foreign_key('type') @declared_attr - def nodes(cls): - return relationship.many_to_many(cls, 'node') + def service_fk(cls): + """For Service one-to-many to Group""" + return relationship.foreign_key('service') @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + def group_template_fk(cls): + """For Group many-to-one to GroupTemplate""" + return relationship.foreign_key('group_template', nullable=True) + + # endregion + + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships @declared_attr def interfaces(cls): return relationship.one_to_many(cls, 'interface', dict_key='name') - # region foreign_keys + # endregion + + # region many_to_one relationships @declared_attr - def type_fk(cls): - """For Group many-to-one to Type""" - return relationship.foreign_key('type') + def service(cls): + return relationship.many_to_one(cls, 'service') @declared_attr - def service_fk(cls): - """For Service one-to-many to Group""" - return relationship.foreign_key('service') + def group_template(cls): + return relationship.many_to_one(cls, 'group_template') @declared_attr - def group_template_fk(cls): - """For Group many-to-one to GroupTemplate""" - return relationship.foreign_key('group_template', nullable=True) + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships + + @declared_attr + def nodes(cls): + return relationship.many_to_many(cls, 'node') + + @declared_attr + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') # endregion + description = Column(Text) + @property def as_raw(self): return collections.OrderedDict(( @@ -740,58 +806,79 @@ class PolicyBase(InstanceModelMixin): :vartype groups: [:class:`Group`] :ivar properties: Associated parameters :vartype properties: {basestring: :class:`Parameter`} - :ivar service: Containing service :vartype service: :class:`Service` """ __tablename__ = 'policy' - __private_fields__ = ['type_fk', - 'service_fk', - 'policy_template_fk'] + __private_fields__ = ['type_fk', 'service_fk', 'policy_template_fk'] - @declared_attr - def policy_template(cls): - return relationship.many_to_one(cls, 'policy_template') + # region foreign_keys @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') + def type_fk(cls): + """For Policy many-to-one to Type""" + return relationship.foreign_key('type') - description = Column(Text) + @declared_attr + def service_fk(cls): + """For Service one-to-many to Policy""" + return relationship.foreign_key('service') @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + def policy_template_fk(cls): + """For Policy many-to-one to PolicyTemplate""" + return relationship.foreign_key('policy_template', nullable=True) + + # endregion + + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships @declared_attr - def nodes(cls): - return relationship.many_to_many(cls, 'node') + def service(cls): + return relationship.many_to_one(cls, 'service') @declared_attr - def groups(cls): - return relationship.many_to_many(cls, 'group') + def policy_template(cls): + return relationship.many_to_one(cls, 'policy_template') - # region foreign_keys + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships @declared_attr - def type_fk(cls): - """For Policy many-to-one to Type""" - return relationship.foreign_key('type') + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') @declared_attr - def service_fk(cls): - """For Service one-to-many to Policy""" - return relationship.foreign_key('service') + def nodes(cls): + return relationship.many_to_many(cls, 'node') @declared_attr - def policy_template_fk(cls): - """For Policy many-to-one to PolicyTemplate""" - return relationship.foreign_key('policy_template', nullable=True) + def groups(cls): + return relationship.many_to_many(cls, 'group') # endregion + description = Column(Text) + @property def as_raw(self): return collections.OrderedDict(( @@ -835,7 +922,6 @@ class SubstitutionBase(InstanceModelMixin): :vartype node_type: :class:`Type` :ivar mappings: Requirement and capability mappings :vartype mappings: {basestring: :class:`SubstitutionTemplate`} - :ivar service: Containing service :vartype service: :class:`Service` """ @@ -845,29 +931,53 @@ class SubstitutionBase(InstanceModelMixin): __private_fields__ = ['node_type_fk', 'substitution_template_fk'] + # region foreign_keys + @declared_attr - def substitution_template(cls): - return relationship.many_to_one(cls, 'substitution_template') + def node_type_fk(cls): + """For Substitution many-to-one to Type""" + return relationship.foreign_key('type') @declared_attr - def node_type(cls): - return relationship.many_to_one(cls, 'type') + def substitution_template_fk(cls): + """For Substitution many-to-one to SubstitutionTemplate""" + return relationship.foreign_key('substitution_template', nullable=True) + + # endregion + + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships @declared_attr def mappings(cls): return relationship.one_to_many(cls, 'substitution_mapping', dict_key='name') - # region foreign_keys + # endregion + + # region many_to_one relationships @declared_attr - def node_type_fk(cls): - """For Substitution many-to-one to Type""" - return relationship.foreign_key('type') + def service(cls): + return relationship.one_to_one(cls, 'service', back_populates=relationship.NO_BACK_POP) @declared_attr - def substitution_template_fk(cls): - """For Substitution many-to-one to SubstitutionTemplate""" - return relationship.foreign_key('substitution_template', nullable=True) + def substitution_template(cls): + return relationship.many_to_one(cls, 'substitution_template') + + @declared_attr + def node_type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships # endregion @@ -907,7 +1017,6 @@ class SubstitutionMappingBase(InstanceModelMixin): :vartype capability: :class:`Capability` :ivar requirement_template: Requirement template in the node template :vartype requirement_template: :class:`RequirementTemplate` - :ivar substitution: Containing substitution :vartype substitution: :class:`Substitution` """ @@ -919,18 +1028,6 @@ class SubstitutionMappingBase(InstanceModelMixin): 'capability_fk', 'requirement_template_fk'] - @declared_attr - def node(cls): - return relationship.one_to_one(cls, 'node') - - @declared_attr - def capability(cls): - return relationship.one_to_one(cls, 'capability') - - @declared_attr - def requirement_template(cls): - return relationship.one_to_one(cls, 'requirement_template') - # region foreign keys @declared_attr @@ -955,6 +1052,43 @@ class SubstitutionMappingBase(InstanceModelMixin): # endregion + # region association proxies + + # endregion + + # region one_to_one relationships + + @declared_attr + def substitution(cls): + return relationship.many_to_one(cls, 'substitution', back_populates='mappings') + + @declared_attr + def node(cls): + return relationship.one_to_one(cls, 'node', back_populates=relationship.NO_BACK_POP) + + @declared_attr + def capability(cls): + return relationship.one_to_one(cls, 'capability', back_populates=relationship.NO_BACK_POP) + + @declared_attr + def requirement_template(cls): + return relationship.one_to_one( + cls, 'requirement_template', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + + # endregion + + # region many_to_many relationships + + # endregion + @property def as_raw(self): return collections.OrderedDict(( @@ -1002,12 +1136,10 @@ class RelationshipBase(InstanceModelMixin): :vartype properties: {basestring: :class:`Parameter`} :ivar interfaces: Bundles of operations :vartype interfaces: {basestring: :class:`Interfaces`} - :ivar source_position: ?? :vartype source_position: int :ivar target_position: ?? :vartype target_position: int - :ivar source_node: Source node :vartype source_node: :class:`Node` :ivar target_node: Target node @@ -1027,37 +1159,6 @@ class RelationshipBase(InstanceModelMixin): 'source_node_name', 'target_node_name'] - @declared_attr - def relationship_template(cls): - return relationship.many_to_one(cls, 'relationship_template') - - @declared_attr - def requirement_template(cls): - return relationship.many_to_one(cls, 'requirement_template') - - @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') - - @declared_attr - def target_capability(cls): - return relationship.one_to_one(cls, 'capability') - - @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') - - @declared_attr - def interfaces(cls): - return relationship.one_to_many(cls, 'interface', dict_key='name') - - # region orchestration - - source_position = Column(Integer) # ??? - target_position = Column(Integer) # ??? - - # endregion - # region foreign keys @declared_attr @@ -1106,6 +1207,63 @@ class RelationshipBase(InstanceModelMixin): # endregion + # region one_to_one relationships + + @declared_attr + def target_capability(cls): + return relationship.one_to_one(cls, 'capability', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region one_to_many relationships + + @declared_attr + def tasks(cls): + return relationship.one_to_many(cls, 'task') + + @declared_attr + def interfaces(cls): + return relationship.one_to_many(cls, 'interface', dict_key='name') + + # endregion + + # region many_to_one relationships + + @declared_attr + def source_node(cls): + return relationship.many_to_one( + cls, 'node', fk='source_node_fk', back_populates='outbound_relationships') + + @declared_attr + def target_node(cls): + return relationship.many_to_one( + cls, 'node', fk='target_node_fk', back_populates='inbound_relationships') + + @declared_attr + def relationship_template(cls): + return relationship.many_to_one(cls, 'relationship_template') + + @declared_attr + def requirement_template(cls): + return relationship.many_to_one(cls, 'requirement_template') + + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships + + @declared_attr + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + + # endregion + + source_position = Column(Integer) # ??? + target_position = Column(Integer) # ??? + @property def as_raw(self): return collections.OrderedDict(( @@ -1166,7 +1324,6 @@ class CapabilityBase(InstanceModelMixin): :vartype occurrences: int :ivar properties: Associated parameters :vartype properties: {basestring: :class:`Parameter`} - :ivar node: Containing node :vartype node: :class:`Node` :ivar relationship: Available when we are the target of a relationship @@ -1181,22 +1338,6 @@ class CapabilityBase(InstanceModelMixin): 'node_fk', 'capability_template_fk'] - @declared_attr - def capability_template(cls): - return relationship.many_to_one(cls, 'capability_template') - - @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') - - min_occurrences = Column(Integer, default=None) - max_occurrences = Column(Integer, default=None) - occurrences = Column(Integer, default=0) - - @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') - # region foreign_keys @declared_attr @@ -1216,6 +1357,46 @@ class CapabilityBase(InstanceModelMixin): # endregion + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + + @declared_attr + def node(cls): + return relationship.many_to_one(cls, 'node') + + @declared_attr + def capability_template(cls): + return relationship.many_to_one(cls, 'capability_template') + + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships + + @declared_attr + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + + # endregion + + min_occurrences = Column(Integer, default=None) + max_occurrences = Column(Integer, default=None) + occurrences = Column(Integer, default=0) + @property def has_enough_relationships(self): if self.min_occurrences is not None: @@ -1274,7 +1455,6 @@ class InterfaceBase(InstanceModelMixin): :vartype inputs: {basestring: :class:`Parameter`} :ivar operations: Operations :vartype operations: {basestring: :class:`Operation`} - :ivar node: Containing node :vartype node: :class:`Node` :ivar group: Containing group @@ -1291,24 +1471,6 @@ class InterfaceBase(InstanceModelMixin): 'relationship_fk', 'interface_template_fk'] - @declared_attr - def interface_template(cls): - return relationship.many_to_one(cls, 'interface_template') - - @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') - - description = Column(Text) - - @declared_attr - def inputs(cls): - return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') - - @declared_attr - def operations(cls): - return relationship.one_to_many(cls, 'operation', dict_key='name') - # region foreign_keys @declared_attr @@ -1338,6 +1500,56 @@ class InterfaceBase(InstanceModelMixin): # endregion + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + @declared_attr + def operations(cls): + return relationship.one_to_many(cls, 'operation', dict_key='name') + + # endregion + + # region many_to_one relationships + + @declared_attr + def node(cls): + return relationship.many_to_one(cls, 'node') + + @declared_attr + def relationship(cls): + return relationship.many_to_one(cls, 'relationship') + + @declared_attr + def group(cls): + return relationship.many_to_one(cls, 'group') + + @declared_attr + def interface_template(cls): + return relationship.many_to_one(cls, 'interface_template') + + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region many_to_many relationships + + @declared_attr + def inputs(cls): + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + + # endregion + + description = Column(Text) + @property def as_raw(self): return collections.OrderedDict(( @@ -1392,7 +1604,6 @@ class OperationBase(InstanceModelMixin): :vartype max_retries: int :ivar retry_interval: Interval between retries (in seconds) :vartype retry_interval: int - :ivar interface: Containing interface :vartype interface: :class:`Interface` :ivar service: Containing service @@ -1406,27 +1617,6 @@ class OperationBase(InstanceModelMixin): 'plugin_fk', 'operation_template_fk'] - @declared_attr - def operation_template(cls): - return relationship.many_to_one(cls, 'operation_template') - - description = Column(Text) - - @declared_attr - def plugin_specification(cls): - 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 relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') - - executor = Column(Text) - max_retries = Column(Integer) - retry_interval = Column(Integer) - # region foreign_keys @declared_attr @@ -1451,6 +1641,56 @@ class OperationBase(InstanceModelMixin): # endregion + # region association proxies + + # endregion + + # region one_to_one relationships + + @declared_attr + def plugin_specification(cls): + return relationship.one_to_one( + cls, 'plugin_specification', back_populates=relationship.NO_BACK_POP) + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + + @declared_attr + def service(cls): + return relationship.many_to_one(cls, 'service') + + @declared_attr + def interface(cls): + return relationship.many_to_one(cls, 'interface') + + @declared_attr + def operation_template(cls): + return relationship.many_to_one(cls, 'operation_template') + + # endregion + + # region many_to_many relationships + + @declared_attr + def inputs(cls): + return relationship.many_to_many(cls, 'parameter', prefix='inputs', dict_key='name') + + # endregion + + description = Column(Text) + implementation = Column(Text) + dependencies = Column(modeling_types.StrictList(item_cls=basestring)) + executor = Column(Text) + max_retries = Column(Integer) + retry_interval = Column(Integer) + + + @property def as_raw(self): return collections.OrderedDict(( @@ -1517,7 +1757,6 @@ class ArtifactBase(InstanceModelMixin): :vartype repository_credential: {basestring: basestring} :ivar properties: Associated parameters :vartype properties: {basestring: :class:`Parameter`} - :ivar node: Containing node :vartype node: :class:`Node` """ @@ -1528,24 +1767,6 @@ class ArtifactBase(InstanceModelMixin): 'node_fk', 'artifact_template_fk'] - @declared_attr - def artifact_template(cls): - return relationship.many_to_one(cls, 'artifact_template') - - @declared_attr - def type(cls): - return relationship.many_to_one(cls, 'type') - - description = Column(Text) - source_path = Column(Text) - target_path = Column(Text) - repository_url = Column(Text) - repository_credential = Column(modeling_types.StrictDict(basestring, basestring)) - - @declared_attr - def properties(cls): - return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') - # region foreign_keys @declared_attr @@ -1565,6 +1786,44 @@ class ArtifactBase(InstanceModelMixin): # endregion + # region association proxies + + # endregion + + # region one_to_one relationships + + # endregion + + # region one_to_many relationships + + # endregion + + # region many_to_one relationships + @declared_attr + def node(cls): + return relationship.many_to_one(cls, 'node') + + @declared_attr + def artifact_template(cls): + return relationship.many_to_one(cls, 'artifact_template') + + @declared_attr + def type(cls): + return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP) + # endregion + + # region many_to_many relationships + @declared_attr + def properties(cls): + return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name') + # endregion + + description = Column(Text) + source_path = Column(Text) + target_path = Column(Text) + repository_url = Column(Text) + repository_credential = Column(modeling_types.StrictDict(basestring, basestring)) + @property def as_raw(self): return collections.OrderedDict((
