http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index c98a866..883ff4a 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -14,9 +14,7 @@ # limitations under the License. """ -classes: - * ModelMixin - abstract model implementation. - * ModelIDMixin - abstract model implementation with IDs. +ARIA modeling mix-ins module """ from sqlalchemy.ext import associationproxy @@ -49,11 +47,11 @@ class ModelMixin(object): def to_dict(self, fields=None, suppress_error=False): """ - Return a dict representation of the model + Create 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() @@ -79,14 +77,14 @@ class ModelMixin(object): @classmethod def fields(cls): """ - Return the list of field names for this table + List of field names for this table. - Mostly for backwards compatibility in the code (that uses ``fields``) + Mostly for backwards compatibility in the code (that uses ``fields``). """ fields = set(cls._iter_association_proxies()) fields.update(cls.__table__.columns.keys()) - return fields - set(getattr(cls, '__private_fields__', [])) + return fields - set(getattr(cls, '__private_fields__', ())) @classmethod def _iter_association_proxies(cls): @@ -101,8 +99,17 @@ class ModelMixin(object): class ModelIDMixin(object): - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(Text, index=True) + id = Column(Integer, primary_key=True, autoincrement=True, doc=""" + Unique ID. + + :type: :obj:`int` + """) + + name = Column(Text, index=True, doc=""" + Model name. + + :type: :obj:`basestring` + """) @classmethod def id_column_name(cls): @@ -115,10 +122,10 @@ class ModelIDMixin(object): class InstanceModelMixin(ModelMixin): """ - Mixin for :class:`ServiceInstance` models. + Mix-in for service instance models. - All models support validation, diagnostic dumping, and representation as - raw data (which can be translated into JSON or YAML) via ``as_raw``. + All models support validation, diagnostic dumping, and representation as raw data (which can be + translated into JSON or YAML) via :meth:`as_raw`. """ @property @@ -137,9 +144,9 @@ class InstanceModelMixin(ModelMixin): class TemplateModelMixin(InstanceModelMixin): """ - Mixin for :class:`ServiceTemplate` models. + Mix-in for service template models. - All model models can be instantiated into :class:`ServiceInstance` models. + All model models can be instantiated into service instance models. """ def instantiate(self, container): @@ -148,24 +155,26 @@ class TemplateModelMixin(InstanceModelMixin): class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): #pylint: disable=abstract-method """ - Represents a typed value. The value can contain nested intrinsic functions. + Mix-in for typed values. The value can contain nested intrinsic functions. - This model can be used as the ``container_holder`` argument for :func:`functions.evaluate`. - - :ivar name: Name - :vartype name: basestring - :ivar type_name: Type name - :vartype type_name: basestring - :ivar value: Value - :ivar description: Description - :vartype description: basestring + This model can be used as the ``container_holder`` argument for + :func:`~aria.modeling.functions.evaluate`. """ __tablename__ = 'parameter' - name = Column(Text) - type_name = Column(Text) - description = Column(Text) + type_name = Column(Text, doc=""" + Type name. + + :type: :obj:`basestring` + """) + + description = Column(Text, doc=""" + Human-readable description. + + :type: :obj:`basestring` + """) + _value = Column(PickleType) @property @@ -187,8 +196,10 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): """ The sole owner of this parameter, which is another model that relates to it. - *All* parameters should have an owner model. In case this property method fails to find - it, it will raise a ValueError, which should signify an abnormal, orphaned parameter. + *All* parameters should have an owner model. + + :raises ~exceptions.ValueError: if failed to find an owner, which signifies an abnormal, + orphaned parameter """ # Find first non-null relationship @@ -210,8 +221,10 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): The logical container is equivalent to the ``SELF`` keyword used by intrinsic functions in TOSCA. - *All* parameters should have a container model. In case this property method fails to find - it, it will raise a ValueError, which should signify an abnormal, orphaned parameter. + *All* parameters should have a container model. + + :raises ~exceptions.ValueError: if failed to find a container model, which signifies an + abnormal, orphaned parameter """ from . import models @@ -253,7 +266,11 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): @caching.cachedmethod def service(self): """ - The :class:`Service` containing this parameter, or None if not contained in a service. + The :class:`~aria.modeling.models.Service` model containing this parameter, or ``None`` if + not contained in a service. + + :raises ~exceptions.ValueError: if failed to find a container model, which signifies an + abnormal, orphaned parameter """ from . import models @@ -268,8 +285,11 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): @caching.cachedmethod def service_template(self): """ - The :class:`ServiceTemplate` containing this parameter, or None if not contained in a - service template. + The :class:`~aria.modeling.models.ServiceTemplate` model containing this parameter, or + ``None`` if not contained in a service template. + + :raises ~exceptions.ValueError: if failed to find a container model, which signifies an + abnormal, orphaned parameter """ from . import models @@ -329,10 +349,10 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): cos01 specification <http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/cos01 /TOSCA-Simple-Profile-YAML-v1.0-cos01.html#_Toc373867862>`__ - :param name: Parameter name + :param name: parameter name :type name: basestring - :param value: Parameter value - :param description: Description (optional) + :param value: parameter value + :param description: human-readable description (optional) :type description: basestring """ @@ -345,7 +365,6 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): description=description) def as_other_parameter_model(self, other_model_cls): - name, value = self.unwrapped return other_model_cls.wrap(name, value)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/models.py ---------------------------------------------------------------------- diff --git a/aria/modeling/models.py b/aria/modeling/models.py index f30b86f..cf84fdb 100644 --- a/aria/modeling/models.py +++ b/aria/modeling/models.py @@ -13,6 +13,74 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +Data models. + +Service template models +----------------------- + +.. autosummary:: + :nosignatures: + + aria.modeling.models.ServiceTemplate + aria.modeling.models.NodeTemplate + aria.modeling.models.GroupTemplate + aria.modeling.models.PolicyTemplate + aria.modeling.models.SubstitutionTemplate + aria.modeling.models.SubstitutionTemplateMapping + aria.modeling.models.RequirementTemplate + aria.modeling.models.RelationshipTemplate + aria.modeling.models.CapabilityTemplate + aria.modeling.models.InterfaceTemplate + aria.modeling.models.OperationTemplate + aria.modeling.models.ArtifactTemplate + aria.modeling.models.PluginSpecification + +Service instance models +----------------------- + +.. autosummary:: + :nosignatures: + + aria.modeling.models.Service + aria.modeling.models.Node + aria.modeling.models.Group + aria.modeling.models.Policy + aria.modeling.models.Substitution + aria.modeling.models.SubstitutionMapping + aria.modeling.models.Relationship + aria.modeling.models.Capability + aria.modeling.models.Interface + aria.modeling.models.Operation + aria.modeling.models.Artifact + +Common models +------------- + +.. autosummary:: + :nosignatures: + + aria.modeling.models.Output + aria.modeling.models.Input + aria.modeling.models.Configuration + aria.modeling.models.Property + aria.modeling.models.Attribute + aria.modeling.models.Type + aria.modeling.models.Metadata + +Orchestration models +-------------------- + +.. autosummary:: + :nosignatures: + + aria.modeling.models.Execution + aria.modeling.models.Task + aria.modeling.models.Log + aria.modeling.models.Plugin + aria.modeling.models.Argument +""" + # pylint: disable=abstract-method from sqlalchemy.ext.declarative import declarative_base @@ -28,14 +96,15 @@ from . import ( service_common, orchestration, mixins, + utils ) + aria_declarative_base = declarative_base(cls=mixins.ModelIDMixin) # See also models_to_register at the bottom of this file __all__ = ( - 'aria_declarative_base', 'models_to_register', # Service template models @@ -91,54 +160,68 @@ __all__ = ( # region service template models [email protected]_doc class ServiceTemplate(aria_declarative_base, service_template.ServiceTemplateBase): name = Column(Text, index=True, unique=True) [email protected]_doc class NodeTemplate(aria_declarative_base, service_template.NodeTemplateBase): pass [email protected]_doc class GroupTemplate(aria_declarative_base, service_template.GroupTemplateBase): pass [email protected]_doc class PolicyTemplate(aria_declarative_base, service_template.PolicyTemplateBase): pass [email protected]_doc class SubstitutionTemplate(aria_declarative_base, service_template.SubstitutionTemplateBase): pass [email protected]_doc class SubstitutionTemplateMapping(aria_declarative_base, service_template.SubstitutionTemplateMappingBase): pass [email protected]_doc class RequirementTemplate(aria_declarative_base, service_template.RequirementTemplateBase): pass [email protected]_doc class RelationshipTemplate(aria_declarative_base, service_template.RelationshipTemplateBase): pass [email protected]_doc class CapabilityTemplate(aria_declarative_base, service_template.CapabilityTemplateBase): pass [email protected]_doc class InterfaceTemplate(aria_declarative_base, service_template.InterfaceTemplateBase): pass [email protected]_doc class OperationTemplate(aria_declarative_base, service_template.OperationTemplateBase): pass [email protected]_doc class ArtifactTemplate(aria_declarative_base, service_template.ArtifactTemplateBase): pass + [email protected]_doc class PluginSpecification(aria_declarative_base, service_template.PluginSpecificationBase): pass @@ -147,46 +230,57 @@ class PluginSpecification(aria_declarative_base, service_template.PluginSpecific # region service instance models [email protected]_doc class Service(aria_declarative_base, service_instance.ServiceBase): name = Column(Text, index=True, unique=True) [email protected]_doc class Node(aria_declarative_base, service_instance.NodeBase): pass [email protected]_doc class Group(aria_declarative_base, service_instance.GroupBase): pass [email protected]_doc class Policy(aria_declarative_base, service_instance.PolicyBase): pass [email protected]_doc class Substitution(aria_declarative_base, service_instance.SubstitutionBase): pass [email protected]_doc class SubstitutionMapping(aria_declarative_base, service_instance.SubstitutionMappingBase): pass [email protected]_doc class Relationship(aria_declarative_base, service_instance.RelationshipBase): pass [email protected]_doc class Capability(aria_declarative_base, service_instance.CapabilityBase): pass [email protected]_doc class Interface(aria_declarative_base, service_instance.InterfaceBase): pass [email protected]_doc class Operation(aria_declarative_base, service_instance.OperationBase): pass [email protected]_doc class Artifact(aria_declarative_base, service_instance.ArtifactBase): pass @@ -195,14 +289,17 @@ class Artifact(aria_declarative_base, service_instance.ArtifactBase): # region service changes models [email protected]_doc class ServiceUpdate(aria_declarative_base, service_changes.ServiceUpdateBase): pass [email protected]_doc class ServiceUpdateStep(aria_declarative_base, service_changes.ServiceUpdateStepBase): pass [email protected]_doc class ServiceModification(aria_declarative_base, service_changes.ServiceModificationBase): pass @@ -211,31 +308,37 @@ class ServiceModification(aria_declarative_base, service_changes.ServiceModifica # region common service models - [email protected]_doc class Input(aria_declarative_base, service_common.InputBase): pass [email protected]_doc class Configuration(aria_declarative_base, service_common.ConfigurationBase): pass [email protected]_doc class Output(aria_declarative_base, service_common.OutputBase): pass [email protected]_doc class Property(aria_declarative_base, service_common.PropertyBase): pass [email protected]_doc class Attribute(aria_declarative_base, service_common.AttributeBase): pass [email protected]_doc class Type(aria_declarative_base, service_common.TypeBase): pass [email protected]_doc class Metadata(aria_declarative_base, service_common.MetadataBase): pass @@ -244,26 +347,30 @@ class Metadata(aria_declarative_base, service_common.MetadataBase): # region orchestration models [email protected]_doc class Execution(aria_declarative_base, orchestration.ExecutionBase): pass [email protected]_doc class Plugin(aria_declarative_base, orchestration.PluginBase): pass [email protected]_doc class Task(aria_declarative_base, orchestration.TaskBase): pass [email protected]_doc class Log(aria_declarative_base, orchestration.LogBase): pass [email protected]_doc class Argument(aria_declarative_base, orchestration.ArgumentBase): pass - # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/orchestration.py ---------------------------------------------------------------------- diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py index 829c305..55d4c74 100644 --- a/aria/modeling/orchestration.py +++ b/aria/modeling/orchestration.py @@ -14,10 +14,7 @@ # limitations under the License. """ -classes: - * Execution - execution implementation model. - * Plugin - plugin implementation model. - * Task - a task +ARIA modeling orchestration module """ # pylint: disable=no-self-argument, no-member, abstract-method @@ -34,7 +31,6 @@ from sqlalchemy import ( Float, orm, PickleType) -from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.declarative import declared_attr from ..orchestrator.exceptions import (TaskAbortException, TaskRetryException) @@ -47,13 +43,13 @@ from . import ( class ExecutionBase(mixins.ModelMixin): """ - Execution model representation. + Workflow execution. """ __tablename__ = 'execution' - __private_fields__ = ['service_fk', - 'service_template'] + __private_fields__ = ('service_fk', + 'service_template') SUCCEEDED = 'succeeded' FAILED = 'failed' @@ -72,50 +68,65 @@ class ExecutionBase(mixins.ModelMixin): CANCELLED: PENDING } - @orm.validates('status') - def validate_status(self, key, value): - """Validation function that verifies execution status transitions are OK""" - try: - current_status = getattr(self, key) - except AttributeError: - return - valid_transitions = self.VALID_TRANSITIONS.get(current_status, []) - if all([current_status is not None, - current_status != value, - value not in valid_transitions]): - raise ValueError('Cannot change execution status from {current} to {new}'.format( - current=current_status, - new=value)) - return value + # region one_to_many relationships - created_at = Column(DateTime, index=True) - started_at = Column(DateTime, nullable=True, index=True) - ended_at = Column(DateTime, nullable=True, index=True) - error = Column(Text, nullable=True) - status = Column(Enum(*STATES, name='execution_status'), default=PENDING) - workflow_name = Column(Text) + @declared_attr + def inputs(cls): + """ + Execution parameters. - def has_ended(self): - return self.status in self.END_STATES + :type: {:obj:`basestring`: :class:`Input`} + """ + return relationship.one_to_many(cls, 'input', dict_key='name') - def is_active(self): - return not self.has_ended() and self.status != self.PENDING + @declared_attr + def tasks(cls): + """ + Tasks. + + :type: [:class:`Task`] + """ + return relationship.one_to_many(cls, 'task') @declared_attr def logs(cls): + """ + General log messages for the execution (not for its tasks). + + :type: [:class:`Log`] + """ return relationship.one_to_many(cls, 'log') + # endregion + + # region many_to_one relationships + @declared_attr def service(cls): + """ + Associated service. + + :type: :class:`Service` + """ return relationship.many_to_one(cls, 'service') + # endregion + + # region association proxies + @declared_attr - def tasks(cls): - return relationship.one_to_many(cls, 'task') + def service_name(cls): + return relationship.association_proxy('service', cls.name_column_name()) @declared_attr - def inputs(cls): - return relationship.one_to_many(cls, 'input', dict_key='name') + def service_template(cls): + return relationship.association_proxy('service', 'service_template') + + @declared_attr + def service_template_name(cls): + return relationship.association_proxy('service', 'service_template_name') + + # endregion # region foreign keys @@ -125,92 +136,70 @@ class ExecutionBase(mixins.ModelMixin): # endregion - # region association proxies + created_at = Column(DateTime, index=True, doc=""" + Creation timestamp. - @declared_attr - def service_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service', cls.name_column_name()) + :type: :class:`~datetime.datetime` + """) - @declared_attr - def service_template(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service', 'service_template') + started_at = Column(DateTime, nullable=True, index=True, doc=""" + Started timestamp. - @declared_attr - def service_template_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service', 'service_template_name') + :type: :class:`~datetime.datetime` + """) - # endregion + ended_at = Column(DateTime, nullable=True, index=True, doc=""" + Ended timestamp. - def __str__(self): - return '<{0} id=`{1}` (status={2})>'.format( - self.__class__.__name__, - getattr(self, self.name_column_name()), - self.status - ) + :type: :class:`~datetime.datetime` + """) + error = Column(Text, nullable=True, doc=""" + Error message. -class PluginBase(mixins.ModelMixin): - """ - An installed plugin. + :type: :obj:`basestring` + """) - Plugins are usually packaged as `wagons <https://github.com/cloudify-cosmo/wagon>`__, which - are archives of one or more `wheels <https://packaging.python.org/distributing/#wheels>`__. - Most of these fields are indeed extracted from the installed wagon's metadata. + status = Column(Enum(*STATES, name='execution_status'), default=PENDING, doc=""" + Status. - :ivar archive_name: Filename (not the full path) of the wagon's archive, often with a ".wgn" - extension - :vartype archive_name: basestring - :ivar distribution: The name of the operating system on which the wagon was installed (e.g. - "ubuntu") - :vartype distribution: basestring - :ivar distribution_release: The release of the operating system on which the wagon was installed - (e.g. "trusty") - :vartype distribution_release: basestring - :ivar distribution_version: The version of the operating system on which the wagon was installed - (e.g. "14.04") - :vartype distribution_version: basestring - :ivar package_name: The primary Python package name used when the wagon was installed, which is - one of the wheels in the wagon (e.g. "cloudify-script-plugin") - :vartype package_name: basestring - :ivar package_source: The full install string for the primary Python package name used when the - wagon was installed (e.g. "cloudify-script-plugin==1.2") - :vartype package_source: basestring - :ivar package_version: The version for the primary Python package name used when the wagon was - installed (e.g. "1.2") - :vartype package_version: basestring - :ivar supported_platform: If the wheels are *all* pure Python then this would be "any", - otherwise it would be the installed platform name (e.g. - "linux_x86_64") - :vartype supported_platform: basestring - :ivar supported_py_versions: The Python versions supported by all the wheels (e.g. ["py26", - "py27"]) - :vartype supported_py_versions: [basestring] - :ivar wheels: The filenames of the wheels archived in the wagon, often with a ".whl" extension - :vartype wheels: [basestring] - :ivar uploaded_at: Timestamp for when the wagon was installed - :vartype uploaded_at: basestring - """ + :type: :obj:`basestring` + """) - __tablename__ = 'plugin' + workflow_name = Column(Text, doc=""" + Workflow name. - @declared_attr - def tasks(cls): - return relationship.one_to_many(cls, 'task') + :type: :obj:`basestring` + """) + + @orm.validates('status') + def validate_status(self, key, value): + """Validation function that verifies execution status transitions are OK""" + try: + current_status = getattr(self, key) + except AttributeError: + return + valid_transitions = self.VALID_TRANSITIONS.get(current_status, []) + if all([current_status is not None, + current_status != value, + value not in valid_transitions]): + raise ValueError('Cannot change execution status from {current} to {new}'.format( + current=current_status, + new=value)) + return value - archive_name = Column(Text, nullable=False, index=True) - distribution = Column(Text) - distribution_release = Column(Text) - distribution_version = Column(Text) - package_name = Column(Text, nullable=False, index=True) - package_source = Column(Text) - package_version = Column(Text) - supported_platform = Column(Text) - supported_py_versions = Column(modeling_types.StrictList(basestring)) - wheels = Column(modeling_types.StrictList(basestring), nullable=False) - uploaded_at = Column(DateTime, nullable=False, index=True) + def has_ended(self): + return self.status in self.END_STATES + + def is_active(self): + return not self.has_ended() and self.status != self.PENDING + + def __str__(self): + return '<{0} id=`{1}` (status={2})>'.format( + self.__class__.__name__, + getattr(self, self.name_column_name()), + self.status + ) class TaskBase(mixins.ModelMixin): @@ -219,46 +208,20 @@ class TaskBase(mixins.ModelMixin): outputs, as well as an atomic status, ensuring that the task can only be running once at any given time. + The Python :attr:`function` is usually provided by an associated :class:`Plugin`. The + :attr:`arguments` of the function should be set according to the specific signature of the + function. + Tasks may be "one shot" or may be configured to run repeatedly in the case of failure. Tasks are often based on :class:`Operation`, and thus act on either a :class:`Node` or a :class:`Relationship`, however this is not required. - - :ivar node: The node actor (optional) - :vartype node: :class:`Node` - :ivar relationship: The relationship actor (optional) - :vartype relationship: :class:`Relationship` - :ivar plugin: The implementing plugin (set to None for default execution plugin) - :vartype plugin: :class:`Plugin` - :ivar function: Python path to an ``@operation`` function - :vartype function: basestring - :ivar arguments: Arguments that can be used by this task - :vartype arguments: {basestring: :class:`Argument`} - :ivar max_attempts: Maximum number of retries allowed in case of failure - :vartype max_attempts: int - :ivar retry_interval: Interval between retries (in seconds) - :vartype retry_interval: int - :ivar ignore_failure: Set to True to ignore failures - :vartype ignore_failure: bool - :ivar due_at: Timestamp to start the task - :vartype due_at: datetime - :ivar execution: Assigned execution - :vartype execution: :class:`Execution` - :ivar status: Current atomic status ('pending', 'retrying', 'sent', 'started', 'success', - 'failed') - :vartype status: basestring - :ivar started_at: Timestamp for when task started - :vartype started_at: datetime - :ivar ended_at: Timestamp for when task ended - :vartype ended_at: datetime - :ivar attempts_count: How many attempts occurred - :vartype attempts_count: int """ __tablename__ = 'task' - __private_fields__ = ['dependency_operation_task_fk', 'dependency_stub_task_fk', 'node_fk', - 'relationship_fk', 'plugin_fk', 'execution_fk'] + __private_fields__ = ('dependency_operation_task_fk', 'dependency_stub_task_fk', 'node_fk', + 'relationship_fk', 'plugin_fk', 'execution_fk') START_WORKFLOW = 'start_workflow' END_WORKFLOW = 'end_workflow' @@ -292,70 +255,91 @@ class TaskBase(mixins.ModelMixin): ) INFINITE_RETRIES = -1 + # region one_to_many relationships + @declared_attr - def execution(cls): - return relationship.many_to_one(cls, 'execution') + def logs(cls): + """ + Log messages. + + :type: [:class:`Log`] + """ + return relationship.one_to_many(cls, 'log') @declared_attr - def execution_fk(cls): - return relationship.foreign_key('execution', nullable=True) + def arguments(cls): + """ + Arguments sent to the Python :attr:`function``. - status = Column(Enum(*STATES, name='status'), default=PENDING) - due_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow()) - started_at = Column(DateTime, default=None) - ended_at = Column(DateTime, default=None) - attempts_count = Column(Integer, default=1) + :type: {:obj:`basestring`: :class:`Argument`} + """ + return relationship.one_to_many(cls, 'argument', dict_key='name') - _executor = Column(PickleType) - _context_cls = Column(PickleType) - _stub_type = Column(Enum(*STUB_TYPES)) + # endregion + + # region many_one relationships @declared_attr - def logs(cls): - return relationship.one_to_many(cls, 'log') + def execution(cls): + """ + Containing execution. + + :type: :class:`Execution` + """ + return relationship.many_to_one(cls, 'execution') @declared_attr def node(cls): + """ + Node actor (can be ``None``). + + :type: :class:`Node` + """ return relationship.many_to_one(cls, 'node') @declared_attr def relationship(cls): + """ + Relationship actor (can be ``None``). + + :type: :class:`Relationship` + """ return relationship.many_to_one(cls, 'relationship') @declared_attr def plugin(cls): + """ + Associated plugin. + + :type: :class:`Plugin` + """ return relationship.many_to_one(cls, 'plugin') + # endregion + + # region association proxies + @declared_attr - def arguments(cls): - return relationship.one_to_many(cls, 'argument', dict_key='name') + def node_name(cls): + return relationship.association_proxy('node', cls.name_column_name()) - function = Column(String) - max_attempts = Column(Integer, default=1) - retry_interval = Column(Float, default=0) - ignore_failure = Column(Boolean, default=False) - interface_name = Column(String) - operation_name = Column(String) + @declared_attr + def relationship_name(cls): + return relationship.association_proxy('relationship', cls.name_column_name()) - @property - def actor(self): - """ - Return the actor of the task - :return: - """ - return self.node or self.relationship + @declared_attr + def execution_name(cls): + return relationship.association_proxy('execution', cls.name_column_name()) - @orm.validates('max_attempts') - def validate_max_attempts(self, _, value): # pylint: disable=no-self-use - """Validates that max attempts is either -1 or a positive number""" - if value < 1 and value != TaskBase.INFINITE_RETRIES: - raise ValueError('Max attempts can be either -1 (infinite) or any positive number. ' - 'Got {value}'.format(value=value)) - return value + # endregion # region foreign keys @declared_attr + def execution_fk(cls): + return relationship.foreign_key('execution', nullable=True) + + @declared_attr def node_fk(cls): return relationship.foreign_key('node', nullable=True) @@ -369,24 +353,93 @@ class TaskBase(mixins.ModelMixin): # endregion - # region association proxies + status = Column(Enum(*STATES, name='status'), default=PENDING, doc=""" + Current atomic status ('pending', 'retrying', 'sent', 'started', 'success', 'failed'). - @declared_attr - def node_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('node', cls.name_column_name()) + :type: :obj:`basestring` + """) - @declared_attr - def relationship_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('relationship', cls.name_column_name()) + due_at = Column(DateTime, nullable=False, index=True, default=datetime.utcnow(), doc=""" + Timestamp to start the task. - @declared_attr - def execution_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('execution', cls.name_column_name()) + :type: :class:`~datetime.datetime` + """) - # endregion + started_at = Column(DateTime, default=None, doc=""" + Started timestamp. + + :type: :class:`~datetime.datetime` + """) + + ended_at = Column(DateTime, default=None, doc=""" + Ended timestamp. + + :type: :class:`~datetime.datetime` + """) + + attempts_count = Column(Integer, default=1, doc=""" + How many attempts occurred. + + :type: :class:`~datetime.datetime` + """) + + function = Column(String, doc=""" + Full path to Python function. + + :type: :obj:`basestring` + """) + + max_attempts = Column(Integer, default=1, doc=""" + Maximum number of attempts allowed in case of task failure. + + :type: :obj:`int` + """) + + retry_interval = Column(Float, default=0, doc=""" + Interval between task retry attemps (in seconds). + + :type: :obj:`float` + """) + + ignore_failure = Column(Boolean, default=False, doc=""" + Set to ``True`` to ignore failures. + + :type: :obj:`bool` + """) + + interface_name = Column(String, doc=""" + Name of interface on node or relationship. + + :type: :obj:`basestring` + """) + + operation_name = Column(String, doc=""" + Name of operation in interface on node or relationship. + + :type: :obj:`basestring` + """) + + _api_id = Column(String) + _executor = Column(PickleType) + _context_cls = Column(PickleType) + _stub_type = Column(Enum(*STUB_TYPES)) + + @property + def actor(self): + """ + Actor of the task (node or relationship). + """ + return self.node or self.relationship + + @orm.validates('max_attempts') + def validate_max_attempts(self, _, value): # pylint: disable=no-self-use + """ + Validates that max attempts is either -1 or a positive number. + """ + if value < 1 and value != TaskBase.INFINITE_RETRIES: + raise ValueError('Max attempts can be either -1 (infinite) or any positive number. ' + 'Got {value}'.format(value=value)) + return value @staticmethod def abort(message=None): @@ -447,26 +500,36 @@ class TaskBase(mixins.ModelMixin): class LogBase(mixins.ModelMixin): + """ + Single log message. + """ __tablename__ = 'log' - __private_fields__ = ['execution_fk', - 'task_fk'] + __private_fields__ = ('execution_fk', + 'task_fk') + + # region many_to_one relationships @declared_attr def execution(cls): + """ + Containing execution. + + :type: :class:`Execution` + """ return relationship.many_to_one(cls, 'execution') @declared_attr def task(cls): - return relationship.many_to_one(cls, 'task') + """ + Containing task (can be ``None``). - level = Column(String) - msg = Column(String) - created_at = Column(DateTime, index=True) + :type: :class:`Task` + """ + return relationship.many_to_one(cls, 'task') - # In case of failed execution - traceback = Column(Text) + # endregion # region foreign keys @@ -480,6 +543,30 @@ class LogBase(mixins.ModelMixin): # endregion + level = Column(String, doc=""" + Log level. + + :type: :obj:`basestring` + """) + + msg = Column(String, doc=""" + Log message. + + :type: :obj:`basestring` + """) + + created_at = Column(DateTime, index=True, doc=""" + Creation timestamp. + + :type: :class:`~datetime.datetime` + """) + + traceback = Column(Text, doc=""" + Error traceback in case of failure. + + :type: :class:`~datetime.datetime` + """) + def __str__(self): return self.msg @@ -488,30 +575,137 @@ class LogBase(mixins.ModelMixin): return '{name}: {self.msg}'.format(name=name, self=self) -class ArgumentBase(mixins.ParameterMixin): +class PluginBase(mixins.ModelMixin): + """ + Installed plugin. - __tablename__ = 'argument' + Plugins are usually packaged as `wagons <https://github.com/cloudify-cosmo/wagon>`__, which + are archives of one or more `wheels <https://packaging.python.org/distributing/#wheels>`__. + Most of these fields are indeed extracted from the installed wagon's metadata. + """ - # region foreign keys + __tablename__ = 'plugin' - @declared_attr - def task_fk(cls): - return relationship.foreign_key('task', nullable=True) + # region one_to_many relationships @declared_attr - def operation_fk(cls): - return relationship.foreign_key('operation', nullable=True) + def tasks(cls): + """ + Associated Tasks. + + :type: [:class:`Task`] + """ + return relationship.one_to_many(cls, 'task') # endregion + archive_name = Column(Text, nullable=False, index=True, doc=""" + Filename (not the full path) of the wagon's archive, often with a ``.wgn`` extension. + + :type: :obj:`basestring` + """) + + distribution = Column(Text, doc=""" + Name of the operating system on which the wagon was installed (e.g. ``ubuntu``). + + :type: :obj:`basestring` + """) + + distribution_release = Column(Text, doc=""" + Release of the operating system on which the wagon was installed (e.g. ``trusty``). + + :type: :obj:`basestring` + """) + + distribution_version = Column(Text, doc=""" + Version of the operating system on which the wagon was installed (e.g. ``14.04``). + + :type: :obj:`basestring` + """) + + package_name = Column(Text, nullable=False, index=True, doc=""" + Primary Python package name used when the wagon was installed, which is one of the wheels in the + wagon (e.g. ``cloudify-script-plugin``). + + :type: :obj:`basestring` + """) + + package_source = Column(Text, doc=""" + Full install string for the primary Python package name used when the wagon was installed (e.g. + ``cloudify-script-plugin==1.2``). + + :type: :obj:`basestring` + """) + + package_version = Column(Text, doc=""" + Version for the primary Python package name used when the wagon was installed (e.g. ``1.2``). + + :type: :obj:`basestring` + """) + + supported_platform = Column(Text, doc=""" + If the wheels are *all* pure Python then this would be "any", otherwise it would be the + installed platform name (e.g. ``linux_x86_64``). + + :type: :obj:`basestring` + """) + + supported_py_versions = Column(modeling_types.StrictList(basestring), doc=""" + Python versions supported by all the wheels (e.g. ``["py26", "py27"]``) + + :type: [:obj:`basestring`] + """) + + wheels = Column(modeling_types.StrictList(basestring), nullable=False, doc=""" + Filenames of the wheels archived in the wagon, often with a ``.whl`` extension. + + :type: [:obj:`basestring`] + """) + + uploaded_at = Column(DateTime, nullable=False, index=True, doc=""" + Timestamp for when the wagon was installed. + + :type: :class:`~datetime.datetime` + """) + + +class ArgumentBase(mixins.ParameterMixin): + """ + Python function argument parameter. + """ + + __tablename__ = 'argument' + # region many_to_one relationships @declared_attr def task(cls): + """ + Containing task (can be ``None``); + + :type: :class:`Task` + """ return relationship.many_to_one(cls, 'task') @declared_attr def operation(cls): + """ + Containing operation (can be ``None``); + + :type: :class:`Operation` + """ return relationship.many_to_one(cls, 'operation') # endregion + + # region foreign keys + + @declared_attr + def task_fk(cls): + return relationship.foreign_key('task', nullable=True) + + @declared_attr + def operation_fk(cls): + return relationship.foreign_key('operation', nullable=True) + + # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/relationship.py ---------------------------------------------------------------------- diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py index 76ac316..51fbfd6 100644 --- a/aria/modeling/relationship.py +++ b/aria/modeling/relationship.py @@ -13,9 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +ARIA modeling relationship module +""" + # pylint: disable=invalid-name, redefined-outer-name + from sqlalchemy.orm import relationship, backref from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.ext.associationproxy import association_proxy as original_association_proxy from sqlalchemy import ( Column, ForeignKey, @@ -43,9 +49,9 @@ def foreign_key(other_table, nullable=False): *This utility method should only be used during class creation.* - :param other_table: Other table name + :param other_table: other table name :type other_table: basestring - :param nullable: True to allow null values (meaning that there is no relationship) + :param nullable: ``True`` to allow null values (meaning that there is no relationship) :type nullable: bool """ @@ -63,9 +69,9 @@ def one_to_one_self(model_class, fk): *This utility method should only be used during class creation.* - :param model_class: The class in which this relationship will be declared + :param model_class: class in which this relationship will be declared :type model_class: type - :param fk: Foreign key name + :param fk: foreign key name :type fk: basestring """ @@ -90,6 +96,35 @@ def one_to_one_self(model_class, fk): ) +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. + + You will need an associated foreign key to our own table. + + *This utility method should only be used during class creation.* + + :param model_class: class in which this relationship will be declared + :type model_class: type + :param fk: Foreign key name + :type fk: basestring + :param dict_key: if set the value will be a dict with this key as the dict key; otherwise will + be a list + :type dict_key: basestring + """ + 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, @@ -105,17 +140,17 @@ def one_to_one(model_class, *This utility method should only be used during class creation.* - :param model_class: The class in which this relationship will be declared + :param model_class: class in which this relationship will be declared :type model_class: type - :param other_table: Other table name + :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) + :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) + :param other_fk: foreign key name at the other table (no need specify if there's no ambiguity) :type other_fk: basestring - :param back_populates: Override name of matching many-to-many property at other table; set to - false to disable - :type back_populates: basestring|bool + :param back_populates: override name of matching many-to-many property at other table; set to + ``False`` to disable + :type back_populates: basestring or bool """ backref_kwargs = None if back_populates is not NO_BACK_POP: @@ -150,18 +185,18 @@ def one_to_many(model_class, *This utility method should only be used during class creation.* - :param model_class: The class in which this relationship will be declared + :param model_class: class in which this relationship will be declared :type model_class: type - :param child_table: Child table name + :param other_table: other table name :type other_table: basestring - :param other_fk: Foreign key name at the child table (no need specify if there's no ambiguity) + :param other_fk: foreign key name at the other table (no need specify if there's no ambiguity) :type other_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 + :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 back_populates: Override name of matching many-to-one property at child table; set to - false to disable - :type back_populates: basestring|bool + :param back_populates: override name of matching many-to-one property at other table; set to + ``false`` to disable + :type back_populates: basestring or bool """ relationship_kwargs = rel_kwargs or {} if self: @@ -201,19 +236,19 @@ def many_to_one(model_class, The declaration will automatically create a matching one-to-many property at the child model, named after the plural form of our table name. Use the ``parent_property`` argument to override this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a - Python collection then use :meth:`one_to_many` at that model. + Python collection then use :func:`one_to_many` at that model. *This utility method should only be used during class creation.* - :param model_class: The class in which this relationship will be declared + :param model_class: class in which this relationship will be declared :type model_class: type - :param parent_table: Parent table name + :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) + :param fk: foreign key name at our table (no need specify if there's no ambiguity) :type fk: basestring - :param back_populates: Override name of matching one-to-many property at parent table; set to - false to disable - :type back_populates: basestring|bool + :param back_populates: override name of matching one-to-many property at parent table; set to + ``False`` to disable + :type back_populates: basestring or bool """ if back_populates is None: back_populates = formatting.pluralize(model_class.__tablename__) @@ -241,22 +276,22 @@ def many_to_many(model_class, The declaration will automatically create a matching many-to-many property at the other model, named after the plural form of our table name. Use the ``other_property`` argument to override this name. Note: the automatic property will always be a SQLAlchemy query object; if you need a - Python collection then use :meth:`many_to_many` again at that model. + Python collection then use :func:`many_to_many` again at that model. *This utility method should only be used during class creation.* - :param model_class: The class in which this relationship will be declared + :param model_class: class in which this relationship will be declared :type model_class: type - :param other_table: Parent table name + :param other_table: parent table name :type other_table: basestring - :param prefix: Optional prefix for extra table name as well as for ``other_property`` + :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 + :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 other_property: override name of matching many-to-many property at other table; set to + ``False`` to disable + :type other_property: basestring or bool """ this_table = model_class.__tablename__ @@ -301,6 +336,21 @@ def many_to_many(model_class, return _relationship(model_class, other_table, **kwargs) +def association_proxy(*args, **kwargs): + if 'type' in kwargs: + type_ = kwargs.get('type') + del kwargs['type'] + else: + type_ = ':obj:`basestring`' + proxy = original_association_proxy(*args, **kwargs) + proxy.__doc__ = """ + Internal. For use in SQLAlchemy queries. + + :type: {0} + """.format(type_) + return proxy + + def _relationship(model_class, other_table_name, back_populates=None, http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/service_changes.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py index f632fef..061262a 100644 --- a/aria/modeling/service_changes.py +++ b/aria/modeling/service_changes.py @@ -14,10 +14,7 @@ # limitations under the License. """ -classes: - * ServiceUpdate - service update implementation model. - * ServiceUpdateStep - service update step implementation model. - * ServiceModification - service modification implementation model. +ARIA modeling service changes module """ # pylint: disable=no-self-argument, no-member, abstract-method @@ -30,7 +27,6 @@ from sqlalchemy import ( DateTime, Enum, ) -from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.ext.declarative import declared_attr from .types import (List, Dict) @@ -44,8 +40,8 @@ class ServiceUpdateBase(ModelMixin): """ __tablename__ = 'service_update' - __private_fields__ = ['service_fk', - 'execution_fk'] + __private_fields__ = ('service_fk', + 'execution_fk') created_at = Column(DateTime, nullable=False, index=True) service_plan = Column(Dict, nullable=False) @@ -55,29 +51,15 @@ class ServiceUpdateBase(ModelMixin): modified_entity_ids = Column(Dict) state = Column(Text) - # region foreign keys - - @declared_attr - def execution_fk(cls): - return relationship.foreign_key('execution', nullable=True) - - @declared_attr - def service_fk(cls): - return relationship.foreign_key('service') - - # endregion - # region association proxies @declared_attr def execution_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('execution', cls.name_column_name()) + return relationship.association_proxy('execution', cls.name_column_name()) @declared_attr def service_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service', cls.name_column_name()) + return relationship.association_proxy('service', cls.name_column_name()) # endregion @@ -105,6 +87,18 @@ class ServiceUpdateBase(ModelMixin): # endregion + # region foreign keys + + @declared_attr + def execution_fk(cls): + return relationship.foreign_key('execution', nullable=True) + + @declared_attr + def service_fk(cls): + return relationship.foreign_key('service') + + # 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 @@ -119,7 +113,7 @@ class ServiceUpdateStepBase(ModelMixin): __tablename__ = 'service_update_step' - __private_fields__ = ['service_update_fk'] + __private_fields__ = ('service_update_fk',) _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY') ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify') @@ -143,20 +137,11 @@ class ServiceUpdateStepBase(ModelMixin): entity_id = Column(Text, nullable=False) entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False) - # region foreign keys - - @declared_attr - def service_update_fk(cls): - return relationship.foreign_key('service_update') - - # endregion - # region association proxies @declared_attr def service_update_name(cls): - """Required for use by SQLAlchemy queries""" - return association_proxy('service_update', cls.name_column_name()) + return relationship.association_proxy('service_update', cls.name_column_name()) # endregion @@ -176,6 +161,14 @@ class ServiceUpdateStepBase(ModelMixin): # endregion + # region foreign keys + + @declared_attr + def service_update_fk(cls): + return relationship.foreign_key('service_update') + + # endregion + def __hash__(self): return hash((getattr(self, self.id_column_name()), self.entity_id)) @@ -211,7 +204,7 @@ class ServiceModificationBase(ModelMixin): __tablename__ = 'service_modification' - __private_fields__ = ['service_fk'] + __private_fields__ = ('service_fk',) STARTED = 'started' FINISHED = 'finished' @@ -227,20 +220,11 @@ class ServiceModificationBase(ModelMixin): nodes = Column(Dict) status = Column(Enum(*STATES, name='service_modification_status')) - # region foreign keys - - @declared_attr - def service_fk(cls): - return relationship.foreign_key('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()) + return relationship.association_proxy('service', cls.name_column_name()) # endregion @@ -253,8 +237,17 @@ class ServiceModificationBase(ModelMixin): # endregion # region many_to_one relationships + @declared_attr def service(cls): return relationship.many_to_one(cls, 'service', back_populates='modifications') # endregion + + # region foreign keys + + @declared_attr + def service_fk(cls): + return relationship.foreign_key('service') + + # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/8eef8ed9/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 0bb861f..b533a88 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +ARIA modeling service common module +""" + # pylint: disable=no-self-argument, no-member, abstract-method from sqlalchemy import ( @@ -32,9 +36,34 @@ from . import relationship class OutputBase(ParameterMixin): + """ + Output parameter or declaration for an output parameter. + """ __tablename__ = 'output' + # region many_to_one relationships + + @declared_attr + def service_template(cls): + """ + Containing service template (can be ``None``). + + :type: :class:`ServiceTemplate` + """ + return relationship.many_to_one(cls, 'service_template') + + @declared_attr + def service(cls): + """ + Containing service (can be ``None``). + + :type: :class:`ServiceTemplate` + """ + return relationship.many_to_one(cls, 'service') + + # endregion + # region foreign keys @declared_attr @@ -47,22 +76,80 @@ class OutputBase(ParameterMixin): # endregion + +class InputBase(ParameterMixin): + """ + Input parameter or declaration for an input parameter. + """ + + __tablename__ = 'input' + # region many_to_one relationships @declared_attr def service_template(cls): + """ + Containing service template (can be ``None``). + + :type: :class:`ServiceTemplate` + """ return relationship.many_to_one(cls, 'service_template') @declared_attr def service(cls): + """ + Containing service (can be ``None``). + + :type: :class:`Service` + """ return relationship.many_to_one(cls, 'service') - # endregion + @declared_attr + def interface(cls): + """ + Containing interface (can be ``None``). + :type: :class:`Interface` + """ + return relationship.many_to_one(cls, 'interface') -class InputBase(ParameterMixin): + @declared_attr + def operation(cls): + """ + Containing operation (can be ``None``). - __tablename__ = 'input' + :type: :class:`Operation` + """ + return relationship.many_to_one(cls, 'operation') + + @declared_attr + def interface_template(cls): + """ + Containing interface template (can be ``None``). + + :type: :class:`InterfaceTemplate` + """ + return relationship.many_to_one(cls, 'interface_template') + + @declared_attr + def operation_template(cls): + """ + Containing operation template (can be ``None``). + + :type: :class:`OperationTemplate` + """ + return relationship.many_to_one(cls, 'operation_template') + + @declared_attr + def execution(cls): + """ + Containing execution (can be ``None``). + + :type: :class:`Execution` + """ + return relationship.many_to_one(cls, 'execution') + + # endregion # region foreign keys @@ -100,71 +187,167 @@ class InputBase(ParameterMixin): # endregion + +class ConfigurationBase(ParameterMixin): + """ + Configuration parameter. + """ + + __tablename__ = 'configuration' + # region many_to_one relationships @declared_attr - def service_template(cls): - return relationship.many_to_one(cls, 'service_template') + def operation_template(cls): + """ + Containing operation template (can be ``None``). + + :type: :class:`OperationTemplate` + """ + return relationship.many_to_one(cls, 'operation_template') @declared_attr - def service(cls): - return relationship.many_to_one(cls, 'service') + def operation(cls): + """ + Containing operation (can be ``None``). + + :type: :class:`Operation` + """ + return relationship.many_to_one(cls, 'operation') + + # endregion + + # region foreign keys @declared_attr - def interface(cls): - return relationship.many_to_one(cls, 'interface') + def operation_template_fk(cls): + return relationship.foreign_key('operation_template', nullable=True) @declared_attr - def operation(cls): - return relationship.many_to_one(cls, 'operation') + def operation_fk(cls): + return relationship.foreign_key('operation', nullable=True) + + # endregion + + +class PropertyBase(ParameterMixin): + """ + Property parameter or declaration for a property parameter. + """ + + __tablename__ = 'property' + + # region many_to_one relationships @declared_attr - def interface_template(cls): - return relationship.many_to_one(cls, 'interface_template') + def node_template(cls): + """ + Containing node template (can be ``None``). + + :type: :class:`NodeTemplate` + """ + return relationship.many_to_one(cls, 'node_template') @declared_attr - def operation_template(cls): - return relationship.many_to_one(cls, 'operation_template') + def group_template(cls): + """ + Containing group template (can be ``None``). + + :type: :class:`GroupTemplate` + """ + return relationship.many_to_one(cls, 'group_template') @declared_attr - def execution(cls): - return relationship.many_to_one(cls, 'execution') + def policy_template(cls): + """ + Containing policy template (can be ``None``). - # endregion + :type: :class:`PolicyTemplate` + """ + return relationship.many_to_one(cls, 'policy_template') + @declared_attr + def relationship_template(cls): + """ + Containing relationship template (can be ``None``). -class ConfigurationBase(ParameterMixin): + :type: :class:`RelationshipTemplate` + """ + return relationship.many_to_one(cls, 'relationship_template') - __tablename__ = 'configuration' + @declared_attr + def capability_template(cls): + """ + Containing capability template (can be ``None``). - # region foreign keys + :type: :class:`CapabilityTemplate` + """ + return relationship.many_to_one(cls, 'capability_template') @declared_attr - def operation_template_fk(cls): - return relationship.foreign_key('operation_template', nullable=True) + def artifact_template(cls): + """ + Containing artifact template (can be ``None``). + + :type: :class:`ArtifactTemplate` + """ + return relationship.many_to_one(cls, 'artifact_template') @declared_attr - def operation_fk(cls): - return relationship.foreign_key('operation', nullable=True) + def node(cls): + """ + Containing node (can be ``None``). - # endregion + :type: :class:`Node` + """ + return relationship.many_to_one(cls, 'node') - # region many_to_one relationships + @declared_attr + def group(cls): + """ + Containing group (can be ``None``). + + :type: :class:`Group` + """ + return relationship.many_to_one(cls, 'group') @declared_attr - def operation_template(cls): - return relationship.many_to_one(cls, 'operation_template') + def policy(cls): + """ + Containing policy (can be ``None``). + + :type: :class:`Policy` + """ + return relationship.many_to_one(cls, 'policy') @declared_attr - def operation(cls): - return relationship.many_to_one(cls, 'operation') + def relationship(cls): + """ + Containing relationship (can be ``None``). - # endregion + :type: :class:`Relationship` + """ + return relationship.many_to_one(cls, 'relationship') + @declared_attr + def capability(cls): + """ + Containing capability (can be ``None``). -class PropertyBase(ParameterMixin): + :type: :class:`Capability` + """ + return relationship.many_to_one(cls, 'capability') - __tablename__ = 'property' + @declared_attr + def artifact(cls): + """ + Containing artifact (can be ``None``). + + :type: :class:`Artifact` + """ + return relationship.many_to_one(cls, 'artifact') + + # endregion # region foreign keys @@ -215,65 +398,39 @@ class PropertyBase(ParameterMixin): @declared_attr def artifact_fk(cls): return relationship.foreign_key('artifact', nullable=True) + # endregion - # region many_to_one relationships - @declared_attr - def node_template(cls): - return relationship.many_to_one(cls, 'node_template') - - @declared_attr - def group_template(cls): - return relationship.many_to_one(cls, 'group_template') +class AttributeBase(ParameterMixin): + """ + Attribute parameter or declaration for an attribute parameter. + """ - @declared_attr - def policy_template(cls): - return relationship.many_to_one(cls, 'policy_template') + __tablename__ = 'attribute' - @declared_attr - def relationship_template(cls): - return relationship.many_to_one(cls, 'relationship_template') + # region many_to_one relationships @declared_attr - def capability_template(cls): - return relationship.many_to_one(cls, 'capability_template') + def node_template(cls): + """ + Containing node template (can be ``None``). - @declared_attr - def artifact_template(cls): - return relationship.many_to_one(cls, 'artifact_template') + :type: :class:`NodeTemplate` + """ + return relationship.many_to_one(cls, 'node_template') @declared_attr def node(cls): - return relationship.many_to_one(cls, 'node') - - @declared_attr - def group(cls): - return relationship.many_to_one(cls, 'group') - - @declared_attr - def policy(cls): - return relationship.many_to_one(cls, 'policy') - - @declared_attr - def relationship(cls): - return relationship.many_to_one(cls, 'relationship') - - @declared_attr - def capability(cls): - return relationship.many_to_one(cls, 'capability') + """ + Containing node (can be ``None``). - @declared_attr - def artifact(cls): - return relationship.many_to_one(cls, 'artifact') + :type: :class:`Node` + """ + return relationship.many_to_one(cls, 'node') # endregion - -class AttributeBase(ParameterMixin): - - __tablename__ = 'attribute' - # region foreign keys @declared_attr @@ -288,40 +445,52 @@ class AttributeBase(ParameterMixin): # endregion - # region many_to_one relationships - - @declared_attr - def node_template(cls): - return relationship.many_to_one(cls, 'node_template') - - @declared_attr - def node(cls): - return relationship.many_to_one(cls, 'node') - - # endregion - class TypeBase(InstanceModelMixin): """ - Represents a type and its children. + Type and its children. Can serve as the root for a type hierarchy. """ __tablename__ = 'type' - __private_fields__ = ['parent_type_fk'] + __private_fields__ = ('parent_type_fk',) variant = Column(Text, nullable=False) - description = Column(Text) + + description = Column(Text, doc=""" + Human-readable description. + + :type: :obj:`basestring` + """) + _role = Column(Text, name='role') + # region one_to_one relationships + @declared_attr def parent(cls): + """ + Parent type (will be ``None`` for the root of a type hierarchy). + + :type: :class:`Type` + """ return relationship.one_to_one_self(cls, 'parent_type_fk') + # endregion + + # region one_to_many relationships + @declared_attr def children(cls): + """ + Children. + + :type: [:class:`Type`] + """ return relationship.one_to_many(cls, other_fk='parent_type_fk', self=True) + # endregion + # region foreign keys @declared_attr @@ -402,8 +571,9 @@ class TypeBase(InstanceModelMixin): @property def hierarchy(self): """ - Return the type hierarchy. - :return: + Type hierarchy as a list beginning with this type and ending in the root. + + :type: [:class:`Type`] """ return [self] + (self.parent.hierarchy if self.parent else []) @@ -414,9 +584,9 @@ class MetadataBase(TemplateModelMixin): This model is used by both service template and service instance elements. - :ivar name: Name + :ivar name: name :vartype name: basestring - :ivar value: Value + :ivar value: value :vartype value: basestring """
