Full documentation of models; fix to subtitution
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/4ebbbb15 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/4ebbbb15 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/4ebbbb15 Branch: refs/heads/ARIA-286-sphinx-documentation Commit: 4ebbbb15664a09d80088cf82e8d3c3f1ee6fde62 Parents: 7ed92d0 Author: Tal Liron <[email protected]> Authored: Tue Jun 27 21:49:59 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Tue Jun 27 21:49:59 2017 -0500 ---------------------------------------------------------------------- aria/cli/inputs.py | 16 +- aria/cli/service_template_utils.py | 2 +- aria/cli/table.py | 16 +- aria/cli/utils.py | 16 +- aria/logger.py | 2 +- aria/modeling/functions.py | 4 +- aria/modeling/mixins.py | 45 +- aria/modeling/models.py | 19 +- aria/modeling/orchestration.py | 591 ++++--- aria/modeling/service_changes.py | 57 +- aria/modeling/service_common.py | 395 +++-- aria/modeling/service_instance.py | 1465 ++++++++++------- aria/modeling/service_template.py | 1508 +++++++++++------- aria/modeling/types.py | 2 +- aria/modeling/utils.py | 6 +- aria/parser/loading/context.py | 2 +- aria/parser/presentation/presentation.py | 4 +- aria/utils/caching.py | 2 +- aria/utils/collections.py | 17 +- docs/aria.modeling.models.rst | 1 + docs/conf.py | 26 +- .../simple_v1_0/modeling/__init__.py | 2 - .../orchestrator/workflows/core/test_events.py | 4 +- 23 files changed, 2560 insertions(+), 1642 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/cli/inputs.py ---------------------------------------------------------------------- diff --git a/aria/cli/inputs.py b/aria/cli/inputs.py index 11f70a0..bea3e1a 100644 --- a/aria/cli/inputs.py +++ b/aria/cli/inputs.py @@ -29,14 +29,14 @@ def inputs_to_dict(resources): """ Returns a dictionary of inputs - ``resources`` can be: - - * A list of files. - * A single file - * A directory containing multiple input files - * A key1=value1;key2=value2 pairs string. - * A string formatted as JSON/YAML. - * Wildcard based string (e.g. ``*-inputs.yaml``) + :param resources: can be: + + * list of files + * single file + * directory containing multiple input files + * ``key1=value1;key2=value2`` pairs string. + * string formatted as JSON/YAML + * wildcard based string (e.g. ``*-inputs.yaml``) """ if not resources: return dict() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/cli/service_template_utils.py ---------------------------------------------------------------------- diff --git a/aria/cli/service_template_utils.py b/aria/cli/service_template_utils.py index abfbf9a..5312522 100644 --- a/aria/cli/service_template_utils.py +++ b/aria/cli/service_template_utils.py @@ -41,7 +41,7 @@ def get(source, service_template_filename): :param source: path/URL/GitHub repo to archive/service-template file :type source: basestring :param service_template_filename: path to service template if source is a non-CSAR archive - (with CSAR archives, this is read from the metadata file) + with CSAR archives, this is read from the metadata file) :type service_template_filename: basestring :return: path to main service template file :rtype: basestring http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/cli/table.py ---------------------------------------------------------------------- diff --git a/aria/cli/table.py b/aria/cli/table.py index a4e959c..74487ae 100644 --- a/aria/cli/table.py +++ b/aria/cli/table.py @@ -33,16 +33,15 @@ def print_data(columns, items, header_text, :param columns: columns of the table, e.g. ``['id','name']`` :type columns: iterable of basestring :param items: each element must have keys or attributes corresponding to the ``columns`` items, - e.g. ``[{'id':'123', 'name':'Pete'}]`` + e.g. ``[{'id':'123', 'name':'Pete'}]`` :type data: [{:obj:`basestring`: :obj:`basestring`}] :param column_formatters: maps column name to formatter, a function that may manipulate the - string values printed for this column, e.g. - ``{'created_at': timestamp_formatter}`` + string values printed for this column, e.g. ``{'created_at': timestamp_formatter}`` + :type column_formatters: {:obj:`basestring`: :obj:`function`} :param col_max_width: maximum width of table :type col_max_width: int - :type column_formatters: {obj:`basestring`: :obj:`function`} :param defaults: default values for keys that don't exist in the data itself, e.g. - ``{'serviceId':'123'}`` + ``{'serviceId':'123'}`` :type defaults: {:obj:`basestring`: :obj:`basestring`} """ if items is None: @@ -68,14 +67,13 @@ def _generate(cols, data, column_formatters=None, defaults=None): :param cols: columns of the table, e.g. ``['id','name']`` :type cols: iterable of :obj:`basestring` :param data: each element must have keys or attributes corresponding to the ``cols`` items, - e.g. ``[{'id':'123', 'name':'Pete'}]`` + e.g. ``[{'id':'123', 'name':'Pete'}]`` :type data: [{:obj:`basestring`: :obj:`basestring`}] :param column_formatters: maps column name to formatter, a function that may manipulate the - string values printed for this column, e.g. - ``{'created_at': timestamp_formatter}`` + string values printed for this column, e.g. ``{'created_at': timestamp_formatter}`` :type column_formatters: {:obj:`basestring`: :obj:`function`} :param defaults: default values for keys that don't exist in the data itself, e.g. - ``{'serviceId':'123'}`` + ``{'serviceId':'123'}`` :type defaults: {:obj:`basestring`: :obj:`basestring`} """ def get_values_per_column(column, row_data): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/cli/utils.py ---------------------------------------------------------------------- diff --git a/aria/cli/utils.py b/aria/cli/utils.py index 0d0666b..697ff37 100644 --- a/aria/cli/utils.py +++ b/aria/cli/utils.py @@ -80,8 +80,8 @@ def generate_progress_handler(file_path, action='', max_bar_length=80): :param max_bar_length: maximum allowed length of the bar :return: configured ``print_progress`` function """ - # We want to limit the maximum line length to 80, but allow for a smaller - # terminal size. We also include the action string, and some extra chars + # We want to limit the maximum line length to 80, but allow for a smaller terminal size. We also + # include the action string, and some extra chars terminal_width = get_terminal_size().columns # This takes care of the case where there is no terminal (e.g. unittest) @@ -105,16 +105,12 @@ def generate_progress_handler(file_path, action='', max_bar_length=80): :param total_bytes: total number of bytes in the file """ - filled_length = min(bar_length, int(round(bar_length * read_bytes / - float(total_bytes)))) - percents = min(100.00, round( - 100.00 * (read_bytes / float(total_bytes)), 2)) + filled_length = min(bar_length, int(round(bar_length * read_bytes / float(total_bytes)))) + percents = min(100.00, round(100.00 * (read_bytes / float(total_bytes)), 2)) bar = '#' * filled_length + '-' * (bar_length - filled_length) # pylint: disable=blacklisted-name - # The \r caret makes sure the cursor moves back to the beginning of - # the line - sys.stdout.write('\r{0} {1} |{2}| {3}%'.format( - action, file_name, bar, percents)) + # The \r caret makes sure the cursor moves back to the beginning of the line + sys.stdout.write('\r{0} {1} |{2}| {3}%'.format(action, file_name, bar, percents)) if read_bytes >= total_bytes: sys.stdout.write(os.linesep) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/logger.py ---------------------------------------------------------------------- diff --git a/aria/logger.py b/aria/logger.py index 2ccff25..3b01e48 100644 --- a/aria/logger.py +++ b/aria/logger.py @@ -125,7 +125,7 @@ def create_sqla_log_handler(model, log_cls, execution_id, level=logging.DEBUG): class _DefaultConsoleFormat(logging.Formatter): """ Info level log format: ``%(message)s`` - + Every other log level is formatted: ``%(levelname)s: %(message)s`` """ def format(self, record): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/modeling/functions.py ---------------------------------------------------------------------- diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py index b764aa8..6544adf 100644 --- a/aria/modeling/functions.py +++ b/aria/modeling/functions.py @@ -42,7 +42,7 @@ class Function(object): :rtype: :class:`Evaluation` (or any object with ``value`` and ``final`` properties) :raises CannotEvaluateFunctionException: if cannot be evaluated at this time (do *not* just - return ``None``) + return ``None``) """ raise NotImplementedError @@ -55,7 +55,7 @@ class Function(object): class Evaluation(object): """ An evaluated :class:`Function` return value. - + :ivar value: evaluated value :ivar final: whether the value is final :vartype final: boolean http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index e707f02..aafc1e9 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -99,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): @@ -150,21 +159,22 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): This model can be used as the ``container_holder`` argument for :func:`~aria.modeling.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 """ __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 @@ -189,7 +199,7 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): *All* parameters should have an owner model. :raises exceptions.ValueError: if failed to find an owner, which signifies an abnormal, - orphaned parameter + orphaned parameter """ # Find first non-null relationship @@ -214,7 +224,7 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): *All* parameters should have a container model. :raises exceptions.ValueError: if failed to find a container model, which signifies an - abnormal, orphaned parameter + abnormal, orphaned parameter """ from . import models @@ -260,7 +270,7 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): not contained in a service. :raises exceptions.ValueError: if failed to find a container model, which signifies an - abnormal, orphaned parameter + abnormal, orphaned parameter """ from . import models @@ -279,7 +289,7 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods): ``None`` if not contained in a service template. :raises exceptions.ValueError: if failed to find a container model, which signifies an - abnormal, orphaned parameter + abnormal, orphaned parameter """ from . import models @@ -355,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/4ebbbb15/aria/modeling/models.py ---------------------------------------------------------------------- diff --git a/aria/modeling/models.py b/aria/modeling/models.py index 129b5bb..f2aa1ea 100644 --- a/aria/modeling/models.py +++ b/aria/modeling/models.py @@ -31,15 +31,15 @@ from . import ( service_changes, service_common, orchestration, - mixins, + mixins ) + 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 @@ -93,16 +93,25 @@ __all__ = ( ) +def _clean_init_doc(cls): + # Make sure Sphinx doesn't grab the base constructor's docstring + original_init = cls.__init__ + def init(*args, **kwargs): + original_init(*args, **kwargs) + cls.__init__ = init + return cls + + def _fix_doc(cls): + # Use the last base class's docstring cls.__doc__ = cls.__bases__[-1].__doc__ - return cls + return _clean_init_doc(cls) # region service template models @_fix_doc class ServiceTemplate(aria_declarative_base, service_template.ServiceTemplateBase): - #__doc__ = service_template.ServiceTemplateBase.__doc__ name = Column(Text, index=True, unique=True) @@ -249,7 +258,6 @@ class ServiceModification(aria_declarative_base, service_changes.ServiceModifica # region common service models - @_fix_doc class Input(aria_declarative_base, service_common.InputBase): pass @@ -313,7 +321,6 @@ class Log(aria_declarative_base, orchestration.LogBase): class Argument(aria_declarative_base, orchestration.ArgumentBase): pass - # endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ebbbb15/aria/modeling/orchestration.py ---------------------------------------------------------------------- diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py index 6096fcc..1c94966 100644 --- a/aria/modeling/orchestration.py +++ b/aria/modeling/orchestration.py @@ -44,7 +44,7 @@ from . import ( class ExecutionBase(mixins.ModelMixin): """ - Execution model representation. + Workflow execution. """ __tablename__ = 'execution' @@ -69,56 +69,47 @@ 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 - - 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) - - 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 + # region one_to_many relationships @declared_attr - def logs(cls): - return relationship.one_to_many(cls, 'log') + def inputs(cls): + """ + Execution parameters. - @declared_attr - def service(cls): - return relationship.many_to_one(cls, 'service') + :type: {:obj:`basestring`: :class:`Input`} + """ + return relationship.one_to_many(cls, 'input', dict_key='name') @declared_attr def tasks(cls): + """ + Tasks. + + :type: [:class:`Task`] + """ return relationship.one_to_many(cls, 'task') @declared_attr - def inputs(cls): - return relationship.one_to_many(cls, 'input', dict_key='name') + def logs(cls): + """ + General log messages for the execution (not for its tasks). - # region foreign keys + :type: [:class:`Log`] + """ + return relationship.one_to_many(cls, 'log') + + # endregion + + # region many_to_one relationships @declared_attr - def service_fk(cls): - return relationship.foreign_key('service') + def service(cls): + """ + Associated service. + + :type: :class:`Service` + """ + return relationship.many_to_one(cls, 'service') # endregion @@ -141,73 +132,78 @@ class ExecutionBase(mixins.ModelMixin): # endregion - def __str__(self): - return '<{0} id=`{1}` (status={2})>'.format( - self.__class__.__name__, - getattr(self, self.name_column_name()), - self.status - ) + # region foreign keys + + @declared_attr + def service_fk(cls): + return relationship.foreign_key('service') + # endregion -class PluginBase(mixins.ModelMixin): - """ - An installed plugin. + created_at = Column(DateTime, index=True, doc=""" + Creation timestamp. - 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. + :type: :class:`~datetime.datetime` + """) - :ivar archive_name: filename (not the full path) of the wagon's archive, often with a ".wgn" - extension - :vartype archive_name: basestring - :ivar distribution: name of the operating system on which the wagon was installed (e.g. - "ubuntu") - :vartype distribution: basestring - :ivar distribution_release: release of the operating system on which the wagon was installed - (e.g. "trusty") - :vartype distribution_release: basestring - :ivar distribution_version: version of the operating system on which the wagon was installed - (e.g. "14.04") - :vartype distribution_version: basestring - :ivar package_name: 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: 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: 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: Python versions supported by all the wheels (e.g. ["py26", - "py27"]) - :vartype supported_py_versions: [basestring] - :ivar wheels: 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 - """ + started_at = Column(DateTime, nullable=True, index=True, doc=""" + Started timestamp. - __tablename__ = 'plugin' + :type: :class:`~datetime.datetime` + """) - @declared_attr - def tasks(cls): - return relationship.one_to_many(cls, 'task') + ended_at = Column(DateTime, nullable=True, index=True, doc=""" + Ended timestamp. + + :type: :class:`~datetime.datetime` + """) - 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) + error = Column(Text, nullable=True, doc=""" + Error message. + + :type: :obj:`basestring` + """) + + status = Column(Enum(*STATES, name='execution_status'), default=PENDING, doc=""" + Status. + + :type: :obj:`basestring` + """) + + workflow_name = Column(Text, doc=""" + Workflow name. + + :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 + + 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): @@ -216,40 +212,14 @@ 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: node actor (optional) - :vartype node: Node - :ivar relationship: relationship actor (optional) - :vartype relationship: Relationship - :ivar plugin: implementing plugin (set to None for default execution plugin) - :vartype plugin: Plugin - :ivar function: Python path to an ``@operation`` function - :vartype function: basestring - :ivar arguments: arguments that can be used by this task - :vartype arguments: {:obj:`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: 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' @@ -289,71 +259,94 @@ 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') - _api_id = Column(String) - _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): + """Required for use by SQLAlchemy queries""" + return 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): + """Required for use by SQLAlchemy queries""" + return 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): + """Required for use by SQLAlchemy queries""" + return 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) @@ -367,24 +360,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): @@ -451,26 +513,36 @@ class TaskBase(mixins.ModelMixin): class LogBase(mixins.ModelMixin): + """ + Single log message. + """ __tablename__ = 'log' __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 @@ -484,6 +556,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 @@ -492,30 +588,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/4ebbbb15/aria/modeling/service_changes.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py index 97f8643..b536443 100644 --- a/aria/modeling/service_changes.py +++ b/aria/modeling/service_changes.py @@ -52,18 +52,6 @@ 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 @@ -102,6 +90,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 @@ -140,14 +140,6 @@ 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 @@ -173,6 +165,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)) @@ -224,14 +224,6 @@ 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 @@ -250,8 +242,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/4ebbbb15/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 2ba3aa3..850a1ef 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -37,17 +37,33 @@ from . import relationship class OutputBase(ParameterMixin): """ - :ivar name: name - :vartype name: basestring - :ivar type_name: type name - :vartype type_name: basestring - :ivar value: value - :ivar description: human-readable description - :vartype description: basestring + 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 @@ -60,31 +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): - """ - :ivar name: name - :vartype name: basestring - :ivar type_name: type name - :vartype type_name: basestring - :ivar value: value - :ivar description: human-readable description - :vartype description: basestring - """ + @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 @@ -122,89 +187,167 @@ class InputBase(ParameterMixin): # endregion - # region many_to_one relationships - @declared_attr - def service_template(cls): - return relationship.many_to_one(cls, 'service_template') +class ConfigurationBase(ParameterMixin): + """ + Configuration parameter. + """ - @declared_attr - def service(cls): - return relationship.many_to_one(cls, 'service') + __tablename__ = 'configuration' + + # region many_to_one relationships @declared_attr - def interface(cls): - return relationship.many_to_one(cls, 'interface') + def operation_template(cls): + """ + Containing operation template (can be ``None``). + + :type: :class:`OperationTemplate` + """ + return relationship.many_to_one(cls, 'operation_template') @declared_attr def operation(cls): + """ + Containing operation (can be ``None``). + + :type: :class:`Operation` + """ return relationship.many_to_one(cls, 'operation') - @declared_attr - def interface_template(cls): - return relationship.many_to_one(cls, 'interface_template') + # endregion + + # region foreign keys @declared_attr - def operation_template(cls): - return relationship.many_to_one(cls, 'operation_template') + def operation_template_fk(cls): + return relationship.foreign_key('operation_template', nullable=True) @declared_attr - def execution(cls): - return relationship.many_to_one(cls, 'execution') + def operation_fk(cls): + return relationship.foreign_key('operation', nullable=True) # endregion -class ConfigurationBase(ParameterMixin): +class PropertyBase(ParameterMixin): """ - :ivar name: name - :vartype name: basestring - :ivar type_name: type name - :vartype type_name: basestring - :ivar value: value - :ivar description: human-readable description - :vartype description: basestring + Property parameter or declaration for a property parameter. """ - __tablename__ = 'configuration' + __tablename__ = 'property' - # region foreign keys + # region many_to_one relationships @declared_attr - def operation_template_fk(cls): - return relationship.foreign_key('operation_template', nullable=True) + 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_fk(cls): - return relationship.foreign_key('operation', nullable=True) + def group_template(cls): + """ + Containing group template (can be ``None``). - # endregion + :type: :class:`GroupTemplate` + """ + return relationship.many_to_one(cls, 'group_template') - # region many_to_one relationships + @declared_attr + def policy_template(cls): + """ + Containing policy template (can be ``None``). + + :type: :class:`PolicyTemplate` + """ + return relationship.many_to_one(cls, 'policy_template') @declared_attr - def operation_template(cls): - return relationship.many_to_one(cls, 'operation_template') + def relationship_template(cls): + """ + Containing relationship template (can be ``None``). + + :type: :class:`RelationshipTemplate` + """ + return relationship.many_to_one(cls, 'relationship_template') @declared_attr - def operation(cls): - return relationship.many_to_one(cls, 'operation') + def capability_template(cls): + """ + Containing capability template (can be ``None``). - # endregion + :type: :class:`CapabilityTemplate` + """ + return relationship.many_to_one(cls, 'capability_template') + @declared_attr + def artifact_template(cls): + """ + Containing artifact template (can be ``None``). -class PropertyBase(ParameterMixin): - """ - :ivar name: name - :vartype name: basestring - :ivar type_name: type name - :vartype type_name: basestring - :ivar value: value - :ivar description: human-readable description - :vartype description: basestring - """ + :type: :class:`ArtifactTemplate` + """ + return relationship.many_to_one(cls, 'artifact_template') - __tablename__ = 'property' + @declared_attr + def node(cls): + """ + Containing node (can be ``None``). + + :type: :class:`Node` + """ + return relationship.many_to_one(cls, 'node') + + @declared_attr + def group(cls): + """ + Containing group (can be ``None``). + + :type: :class:`Group` + """ + return relationship.many_to_one(cls, 'group') + + @declared_attr + def policy(cls): + """ + Containing policy (can be ``None``). + + :type: :class:`Policy` + """ + return relationship.many_to_one(cls, 'policy') + + @declared_attr + def relationship(cls): + """ + Containing relationship (can be ``None``). + + :type: :class:`Relationship` + """ + return relationship.many_to_one(cls, 'relationship') + + @declared_attr + def capability(cls): + """ + Containing capability (can be ``None``). + + :type: :class:`Capability` + """ + return relationship.many_to_one(cls, 'capability') + + @declared_attr + def artifact(cls): + """ + Containing artifact (can be ``None``). + + :type: :class:`Artifact` + """ + return relationship.many_to_one(cls, 'artifact') + + # endregion # region foreign keys @@ -258,72 +401,36 @@ class PropertyBase(ParameterMixin): # 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): - """ - :ivar name: name - :vartype name: basestring - :ivar type_name: type name - :vartype type_name: basestring - :ivar value: value - :ivar description: human-readable description - :vartype description: basestring - """ - - __tablename__ = 'attribute' - # region foreign keys @declared_attr @@ -338,25 +445,10 @@ 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. - - :ivar name: name - :vartype name: basestring + Type and its children. Can serve as the root for a type hierarchy. """ __tablename__ = 'type' @@ -364,17 +456,41 @@ class TypeBase(InstanceModelMixin): __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 on_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 on_to_many relationships + @declared_attr def children(cls): + """ + Children. + + :type: [:class:`Type`] + """ return relationship.one_to_many_self(cls, 'parent_type_fk') + # endregion + # region foreign keys @declared_attr @@ -455,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 [])
