././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/0000775000175000017500000000000000000000000013760 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/.coveragerc0000664000175000017500000000011100000000000016072 0ustar00zuulzuul00000000000000[run] branch = True source = mistral_lib [report] ignore_errors = True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/.stestr.conf0000664000175000017500000000006500000000000016232 0ustar00zuulzuul00000000000000[DEFAULT] test_path=./mistral_lib/tests top_path=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/.zuul.yaml0000664000175000017500000000024200000000000015717 0ustar00zuulzuul00000000000000- project: templates: - check-requirements - openstack-python3-wallaby-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/AUTHORS0000664000175000017500000000260400000000000015032 0ustar00zuulzuul0000000000000098k <18552437190@163.com> Adriano Petrich Andras Kovi Andreas Jaeger Andreas Jaeger Arundhati Surpur Brad P. Crochet Corey Bryant Cédric Jeanneret Doug Hellmann Dougal Matthews Eyal Flavio Percoco Ghanshyam Mann Hervé Beraud Ifat Afek Monty Taylor Nguyen Hai OpenStack Release Bot Renat Akhmerov Ryan Brady Sean McGinnis Sharat Sharma Vieri <15050873171@163.com> ZhijunWei ali caoyuan cuiweibo guotao huang.zhiping jacky06 nizam qingszhao ricolin songwenping sunjia sunqingliang6 zhangyangyang zhulingjie ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/CONTRIBUTING.rst0000664000175000017500000000114300000000000016420 0ustar00zuulzuul00000000000000The source repository for this project can be found at: https://opendev.org/openstack/mistral-lib Pull requests submitted through GitHub are not monitored. To start contributing to OpenStack, follow the steps in the contribution guide to set up and use Gerrit: https://docs.openstack.org/contributors/code-and-documentation/quick-start.html Bugs should be filed on Launchpad: https://bugs.launchpad.net/mistral For more specific information about contributing to this repository, see the Mistral Lib contributor guide: https://docs.openstack.org/mistral/latest/contributor/contributing.html ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/ChangeLog0000664000175000017500000001247000000000000015536 0ustar00zuulzuul00000000000000CHANGES ======= 2.5.0 ----- * [community goal] Update contributor documentation * setup.cfg: Replace dashes with underscores * Use py3 as the default runtime for tox * Dropping lower constraints testing * Update master for stable/wallaby 2.4.0 ----- * Add Python3 wallaby unit tests * Update master for stable/victoria 2.3.0 ----- * Add more specific description to param\_spec * Release notes for action providers * Add main entities related to action providers 2.2.0 ----- * drop mock from lower-constraints * Remove misplaced constants describing worklow task types * Switch to newer openstackdocstheme and reno versions * Remove six * add release-note link to README * Fix hacking min version to 3.0.1 * Bump default tox env from py37 to py38 * Add py38 package metadata * Add Python3 victoria unit tests * Update master for stable/ussuri 2.1.0 ----- * Add a utility for JSON serialization 2.0.0 ----- * Support getting a file list from a module that isn't mistral * [ussuri][goal] Cleanup python 2.7 removal * Remove python2 support * Drop py2 support and add zuul jobs 1.4.0 ----- * Move inspect utils to mistral-lib * Fix requirements, doc * Update hacking and fix warning * Use py3 only * Ensure we mask sensitive data from Mistral Action logs * Wrong call to super * tox: Keeping going with docs * Switch to Ussuri jobs 1.3.0 ----- * Update master for stable/train 1.2.0 ----- * moved generic util functions from mistral to mistral-lib * Add Python 3 Train unit tests * Sync Sphinx requirement * Replace git.openstack.org URLs with opendev.org URLs * OpenDev Migration Patch * Dropping the py35 testing * Update master for stable/stein 1.1.0 ----- * add python 3.7 unit test job * Update home-page * Update hacking version to latest * Use template for lower-constraints * Change openstack-dev to openstack-discuss * Add Python 3.6 classifier to setup.cfg * Add python 3.6 unit test job * Update the hacking to latest * Don't quote {posargs} in tox.ini * Removed older version of python added 3.5 * add python 3.6 unit test job * import zuul job settings from project-config * Make the ActionContext serializable * Update reno for stable/rocky 1.0.0 ----- * Fixed the documentation of 'run' params * Add the restructuredtext check to the flake8 job * fix tox python3 overrides * Switch to using stestr 0.5.0 ----- * add lower-constraints job * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update reno for stable/queens 0.4.0 ----- * Updated from global requirements * Updated from global requirements * fix the method description, wrong grammar * Updated from global requirements * Updated from global requirements * Fix the method description * Avoid tox\_install.sh for constraints support * Provide redirection for all security context attributes * Rename task\_id to task\_execution\_id * Add deprecated context parameters to the new security context * Add an empty \_\_init\_\_ method to the base Action class * Revert "Migrate mistral-lib to zuul v3" * Add a representation of the ActionContext in mistral-lib * Migrate mistral-lib to zuul v3 * Cleanup test-requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update reno for stable/pike * Updated from global requirements * Removes unnecessary utf-8 encoding * Drop MANIFEST.in - it's not needed by pbr * Switch from oslosphinx to openstackdocstheme * Apply Pike document structure * Enable warning-is-error * Updated from global requirements * Updated from global requirements * Updated from global requirements 0.2.0 ----- * Updated from global requirements * Updated from global requirements * Update serialization from 'mistral' repo with latest changes * Fix documentation for custom actions * Remove the unused ApplicationContextNotFoundException * Remove unused requirements * Make the Result class more convienient to use * Sync tools/tox\_install.sh with Mistral * Updated from global requirements * Updated from global requirements 0.1.0 ----- * Pass "mistral\_lib" to the coverage test * Fix the package name in the setup.cfg * Adds minimum common shared code for custom actions API * Fix the package name in the coveragerc * Fix the package name in the docs * Fix oslo\_debug\_helper not running * Updated from global requirements * Update test requirement * Updated from global requirements * Remove support for py34 * Updated from global requirements * Show team and repo badges on README * Added reno for stable/mitaka, stable/liberty and stable/newton * Added the Options for Internationalization output in conf.py * Updated from global requirements * Updated from global requirements * Update .coveragerc after the removal of openstack directory * Updated from global requirements * Enable release notes translation * Updated from global requirements * Updated from global requirements * Small changes like deletion of extra underline in the docs * Changed the invalid link in CONTRIBUTING.rst * Replaced the invalid link to documentation with a valid one * Update info in the configuration file * Cleanup tox.ini, enable constraints * Updated from global requirements * Add Babel to the requirements as it is referenced in the setup.cfg * Remove Python 3.3 and add 3.5 to match the main Mistral repo * Remove .mailmap file * Fixes namespace issue * Initial project layout * Added .gitreview ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/HACKING.rst0000664000175000017500000000022000000000000015550 0ustar00zuulzuul00000000000000mistral-lib Style Commandments ============================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/LICENSE0000664000175000017500000002363700000000000015000 0ustar00zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/PKG-INFO0000664000175000017500000000403600000000000015060 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: mistral-lib Version: 2.5.0 Summary: Mistral shared routings and utilities (Actions API, YAQL functions API, data types etc.) Home-page: https://docs.openstack.org/mistral/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/mistral-lib.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =========== mistral-lib =========== This library contains data types, exceptions, functions and utilities common to Mistral, python-mistralclient and mistral-extra repositories. This library also contains the public interfaces for 3rd party integration (e.g. Actions API, YAQL functions API, etc.) If you want to use OpenStack in your custom actions or functions, you will also need to use https://opendev.org/openstack/mistral-extra. * Free software: Apache license * Documentation: https://docs.openstack.org/mistral/latest/ * Source: https://opendev.org/openstack/mistral-lib * Bugs: https://bugs.launchpad.net/mistral * Release notes: https://docs.openstack.org/releasenotes/mistral-lib/ Features -------- * TODO Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/README.rst0000664000175000017500000000171500000000000015453 0ustar00zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/mistral-lib.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =========== mistral-lib =========== This library contains data types, exceptions, functions and utilities common to Mistral, python-mistralclient and mistral-extra repositories. This library also contains the public interfaces for 3rd party integration (e.g. Actions API, YAQL functions API, etc.) If you want to use OpenStack in your custom actions or functions, you will also need to use https://opendev.org/openstack/mistral-extra. * Free software: Apache license * Documentation: https://docs.openstack.org/mistral/latest/ * Source: https://opendev.org/openstack/mistral-lib * Bugs: https://bugs.launchpad.net/mistral * Release notes: https://docs.openstack.org/releasenotes/mistral-lib/ Features -------- * TODO ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/babel.cfg0000664000175000017500000000002100000000000015477 0ustar00zuulzuul00000000000000[python: **.py] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1461248 mistral-lib-2.5.0/doc/0000775000175000017500000000000000000000000014525 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/requirements.txt0000664000175000017500000000025600000000000020014 0ustar00zuulzuul00000000000000sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-httpdomain>=1.3.0 # BSD sphinxcontrib-pecanwsme>=0.8.0 # Apache-2.0 openstackdocstheme>=2.2.1 # Apache-2.0 reno>=3.1.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1461248 mistral-lib-2.5.0/doc/source/0000775000175000017500000000000000000000000016025 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/conf.py0000775000175000017500000000504300000000000017331 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'openstackdocstheme' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'mistral-lib' copyright = u'2017, Mistral Contributors' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # html_theme_path = ["."] html_theme = 'openstackdocs' # html_static_path = ['static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} # -- Options for openstackdocstheme ------------------------------------------- openstackdocs_repo_name = 'openstack/mistral-lib' openstackdocs_auto_name = False openstackdocs_bug_project = 'mistral' openstackdocs_bug_tag = '' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1461248 mistral-lib-2.5.0/doc/source/contributor/0000775000175000017500000000000000000000000020377 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/contributor/creating_custom_actions.rst0000664000175000017500000000277400000000000026051 0ustar00zuulzuul00000000000000============================ How to write a Custom Action ============================ 1. Write a class inherited from mistral.actions.Action .. code-block:: python from mistral_lib import actions class RunnerAction(actions.Action): def __init__(self, param): # store the incoming params self.param = param def run(self, context): # Actions can be returned in a manner of ways. The simplest is # return {'status': 0} # or using a Result object. The Result has an optional parameter data # that can be used to transfer information return actions.Result() # Failed executions can also be returned using a workflow Result object # that contains an non empty error parameter such as: # return actions.Result(error="error text") 2. Publish the class in a namespace (in your ``setup.cfg``) .. code-block:: ini [entry_points] mistral.actions = example.runner = my.mistral_plugins.somefile:RunnerAction 3. Reinstall your library package if it was installed in system (not in virtualenv). 4. Run db-sync tool to ensure your actions are in Mistral's database .. code-block:: console $ mistral-db-manage --config-file populate 5. Now you can call the action ``example.runner`` .. code-block:: yaml my_workflow: tasks: my_action_task: action: example.runner input: param: avalue_to_pass_in ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/contributor/index.rst0000664000175000017500000000017400000000000022242 0ustar00zuulzuul00000000000000===================== Developer's Reference ===================== .. toctree:: :maxdepth: 3 creating_custom_actions ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/index.rst0000664000175000017500000000130400000000000017664 0ustar00zuulzuul00000000000000Welcome to mistral-lib's documentation! ======================================= The mistral-lib is a library that contains data types, exceptions, functions and utilities common to Mistral, python-mistralclient and mistral-extra repositories. This library also contains the public interfaces for 3rd party integration (e.g. Actions API, YAQL functions API, etc.) Contents: Install guide ------------- **Installation** .. toctree:: :maxdepth: 2 install/index User guide ---------- **Usage** .. toctree:: :maxdepth: 2 user/index Developer guide --------------- .. toctree:: :maxdepth: 2 contributor/index Indices and tables ================== * :ref:`genindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1501248 mistral-lib-2.5.0/doc/source/install/0000775000175000017500000000000000000000000017473 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/install/index.rst0000664000175000017500000000031200000000000021330 0ustar00zuulzuul00000000000000============ Installation ============ At the command line:: $ pip install mistral-lib Or, if you have virtualenvwrapper installed:: $ mkvirtualenv mistral-lib $ pip install mistral-lib ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1501248 mistral-lib-2.5.0/doc/source/user/0000775000175000017500000000000000000000000017003 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/doc/source/user/index.rst0000664000175000017500000000011500000000000020641 0ustar00zuulzuul00000000000000===== Usage ===== To use mistral-lib in a project:: import mistral_lib ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1501248 mistral-lib-2.5.0/mistral_lib/0000775000175000017500000000000000000000000016261 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/__init__.py0000664000175000017500000000123300000000000020371 0ustar00zuulzuul00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version __version__ = pbr.version.VersionInfo( 'mistral-lib').version_string() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1501248 mistral-lib-2.5.0/mistral_lib/actions/0000775000175000017500000000000000000000000017721 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/__init__.py0000664000175000017500000000210700000000000022032 0ustar00zuulzuul00000000000000# Copyright 2017 - Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from mistral_lib.actions.base import Action from mistral_lib.actions.base import ActionDescriptor from mistral_lib.actions.base import ActionProvider from mistral_lib.actions.providers.composite import CompositeActionProvider from mistral_lib.actions.providers.python import PythonActionDescriptor from mistral_lib.actions.types import Result __all__ = [ 'Action', 'Result', 'ActionDescriptor', 'ActionProvider', 'PythonActionDescriptor', 'CompositeActionProvider' ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/base.py0000664000175000017500000003051600000000000021212 0ustar00zuulzuul00000000000000# Copyright 2016 - Nokia Networks. # Copyright 2017 - Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import abc from oslo_utils import importutils from mistral_lib import serialization from mistral_lib.utils import inspect_utils as i_utils class Action(serialization.MistralSerializable): """Action. Action is a means in Mistral to perform some useful work associated with a workflow during its execution. Every workflow task is configured with an action and when the task runs it eventually delegates to the action. When it happens task parameters get evaluated (calculating expressions, if any) and are treated as action parameters. So in a regular general purpose languages terminology action is a method declaration and task is a method call. Base action class initializer doesn't have arguments. However, concrete action classes may have any number of parameters defining action behavior. These parameters must correspond to parameters declared in action specification (e.g. using DSL or others). """ def __init__(self): # NOTE(d0ugal): We need to define an empty __init__ otherwise # inspect.getargspec will fail in Python 2 for actions that subclass # but don't define their own __init__. pass @abc.abstractmethod def run(self, context): """Run action logic. :param context: contains contextual information of the action. The context includes an execution context (like execution identifier and workflow name) and a security context with the authorization details. :return: Result of the action. Note that for asynchronous actions it should always be None, however, if even it's not None it will be ignored by a caller. Result can be of two types: 1) Any serializable value meaningful from a user perspective (such as string, number or dict). 2) Instance of {mistral_lib.types.Result} which has field "data" for success result and field "error" for keeping so called "error result" like HTTP error code and similar. Using the second type allows to communicate a result even in case of error and hence to have conditions in "on-error" clause of direct workflows. Depending on particular action semantics one or another option may be preferable. In case if action failed and there's no need to communicate any error result this method should throw a ActionException. """ pass def is_sync(self): """Returns True if the action is synchronous, otherwise False. :return: True if the action is synchronous and method run() returns final action result. Otherwise returns False which means that a result of method run() should be ignored and a real action result is supposed to be delivered in an asynchronous manner using public API. By default, if a concrete implementation doesn't override this method then the action is synchronous. """ return True @classmethod def get_serialization_key(cls): # By default we use the same serializer key for every action # assuming that most action class can use the same serializer. return "%s.%s" % (Action.__module__, Action.__name__) class ActionSerializer(serialization.DictBasedSerializer): def serialize_to_dict(self, entity): cls = type(entity) return { 'cls': '%s.%s' % (cls.__module__, cls.__name__), 'cls_attrs': i_utils.get_public_fields(cls), 'data': vars(entity), } def deserialize_from_dict(self, entity_dict): cls_str = entity_dict['cls'] # Rebuild action class and restore attributes. cls = importutils.import_class(cls_str) cls_attrs = entity_dict['cls_attrs'] if cls_attrs: # If we have serialized class attributes it means that we need # to create a dynamic class. cls = type(cls.__name__, (cls,), cls_attrs) # NOTE(rakhmerov): We use this hacky was of instantiating # the action here because we can't use normal __init__(), # we don't know the parameters. And even if we find out # what they are the real internal state of the object is # what was stored as vars() method that just returns all # fields. So we have to bypass __init__() and set attributes # one by one. Of course, this is a serious limitation since # action creators will need to keep in mind to avoid having # some complex initialisation logic in __init__() that # does something not reflecting in an instance state. # However, this all applies to the case when the action # has to be sent to a remote executor. action = cls.__new__(cls) for k, v in entity_dict['data'].items(): setattr(action, k, v) return action # NOTE: Every action implementation can register its own serializer # if needed, but this serializer should work for vast of majority of # actions. serialization.register_serializer(Action, ActionSerializer()) class ActionDescriptor(abc.ABC): """Provides required information about a certain type of actions. Action descriptor is not an action itself. It rather carries all important meta information about a particular action before the action is instantiated. In some sense it is similar to a class but the difference is that one type of action descriptor may support many different actions. This abstraction is responsible for validation of input parameters and instantiation of an action. """ @property @abc.abstractmethod def name(self): """The name of the action.""" pass @property @abc.abstractmethod def description(self): """The description of the action.""" pass @property @abc.abstractmethod def params_spec(self): """Comma-separated string with input parameter names. Each parameter name can be either just a name or a string "param=val" where "param" is the name of the parameter and "val" its default value. The values are only indications for the user and not used in the action instantiation process. The string is split along the commas and then the parts along the equal signs. Escaping is not possible. """ pass @property @abc.abstractmethod def namespace(self): """The namespace of the action. NOTE: Not all actions have to support namespaces. """ pass @property @abc.abstractmethod def project_id(self): """The ID of the project (tenant) this action belongs to. If it's not specified then the action can be used within all projects (tenants). NOTE: Not all actions have to support projects(tenants). """ pass @property @abc.abstractmethod def scope(self): """The scope of the action within a project (tenant). It makes sense only if the "project_id" property is not None. It should be assigned with the "public" value if the action is available in all projects and "private" if it's accessible only by users of the specified project. NOTE: Not all actions have to support projects(tenants). """ pass @property @abc.abstractmethod def action_class_name(self): """String representation of the Python class of the action. Can be None in case if the action is dynamically generated with some kind of wrapper. """ pass @property @abc.abstractmethod def action_class_attributes(self): """The attributes of the action Python class, if relevant. If the action has a static Python class associated with it and this method returns not an empty dictionary then the action can be instantiated from a new dynamic class based on the property "action_class_string" and this property that essentially carries public class field values. """ pass @abc.abstractmethod def instantiate(self, input_dict, wf_ctx): """Instantiate the required action with the given parameters. :param input_dict: Action parameters as a dictionary where keys are parameter names and values are parameter values. :param wf_ctx: Workflow context relevant for the point when action is about to start. :return: An instance of mistral_lib.actions.Action. """ pass @abc.abstractmethod def check_parameters(self, params): """Validate action parameters. The method does a preliminary check of the given actual action parameters and raises an exception if they don't match the action signature. However, a successful invocation of this method does not guarantee a further successful run of the instantiated action. :param params: Action parameters as a dictionary where keys are parameter names and values are parameter values. :return: None or raises an exception if the given parameters are not valid. """ pass @abc.abstractmethod def post_process_result(self, result): """Converts the given action result. A certain action implementation may need to do an additional conversion of the action result by its descriptor. This approach allows to implement wrapper actions running asynchronously because in such cases, the initial action result depends on a 3rd party that's responsible for delivering it to Mistral. But when it comes to Mistral we still have a chance to apply needed transformations defined by this method. :param result: Action result. An instance of mistral_lib.types.Result. :return: Converted action result. """ pass class ActionProvider(abc.ABC): """Serves as a source of actions for the system. A concrete implementation of this interface can use its own way of delivering actions to the system. It can store actions in a database, get them over HTTP, AMQP or any other transport. Or it can simply provide a static collection of actions and keep them in memory throughout the cycle of the application. """ def __init__(self, name): self._name = name @property def name(self): """The name of the action provider. Different action providers can use it differently. Some may completely ignore it, others may use it, for example, for searching actions in a certain way. """ return self._name @abc.abstractmethod def find(self, action_name, namespace=None): """Finds action descriptor by name. :param action_name: Action name. :param namespace: Action namespace. None is used for the default namespace. :return: An instance of ActionDescriptor or None, if not found. """ pass @abc.abstractmethod def find_all(self, namespace=None, limit=None, sort_fields=None, sort_dirs=None, filters=None): """Finds all action descriptors for this provider. :param namespace: Optional. Action namespace. :param limit: Positive integer or None. Maximum number of action descriptors to return in a single result. :param sort_fields: Optional. A list of action descriptor fields that define sorting of the result set. :param sort_dirs: Optional. A list of sorting orders ("asc" or "desc") in addition to the "sort_fields" argument. :param filters: Optional. A dictionary describing AND-joined filters. :return: List of ActionDescriptor instances. """ pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/context.py0000664000175000017500000000770300000000000021766 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import warnings from mistral_lib import serialization class ActionContext(serialization.MistralSerializable): def __init__(self, security_ctx=None, execution_ctx=None): self.security = security_ctx self.execution = execution_ctx def _deprecation_warning(self, name): warnings.warn( "context.{0} is deprecated from the context passed to actions. " "Please use context.security.{0}. It will be removed in a future " "release.", DeprecationWarning ) def __getattribute__(self, name): deprecated = [ "auth_cacert", "auth_token", "auth_uri", "expires_at", "insecure", "is_target", "is_trust_scoped", "project_id", "project_name", "redelivered", "region_name", "service_catalog", "trust_id", "user_name" ] if name in deprecated: self._deprecation_warning(name) return getattr(self.security, name) return super(ActionContext, self).__getattribute__(name) class SecurityContext(object): def __init__(self, auth_uri=None, auth_cacert=None, insecure=None, service_catalog=None, region_name=None, is_trust_scoped=None, redelivered=None, expires_at=None, trust_id=None, is_target=None, project_id=None, project_name=None, user_name=None, auth_token=None): self.auth_uri = auth_uri self.auth_cacert = auth_cacert self.insecure = insecure self.service_catalog = service_catalog self.region_name = region_name self.is_trust_scoped = is_trust_scoped self.redelivered = redelivered self.expires_at = expires_at self.trust_id = trust_id self.is_target = is_target self.project_id = project_id self.project_name = project_name self.user_name = user_name self.auth_token = auth_token class ExecutionContext(object): def __init__(self, workflow_execution_id=None, task_execution_id=None, action_execution_id=None, workflow_name=None, callback_url=None, task_id=None): self.workflow_execution_id = workflow_execution_id self.task_execution_id = task_execution_id self.action_execution_id = action_execution_id self.workflow_name = workflow_name self.callback_url = callback_url if task_id is not None: self.task_execution_id = task_id self._deprecate_task_id_warning() def _deprecate_task_id_warning(self): warnings.warn( "context.execution.task_id was deprecated in the Queens cycle. " "Please use context.execution.task_execution_id. It will be " "removed in a future release.", DeprecationWarning ) @property def task_id(self): self._deprecate_task_id_warning() return self.task_execution_id class ActionContextSerializer(serialization.DictBasedSerializer): def serialize_to_dict(self, entity): return { 'security': vars(entity.security), 'execution': vars(entity.execution), } def deserialize_from_dict(self, entity_dict): return ActionContext( security_ctx=SecurityContext(**entity_dict['security']), execution_ctx=ExecutionContext(**entity_dict['execution']) ) serialization.register_serializer(ActionContext, ActionContextSerializer()) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/actions/providers/0000775000175000017500000000000000000000000021736 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/providers/__init__.py0000664000175000017500000000000000000000000024035 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/providers/base.py0000664000175000017500000000634700000000000023234 0ustar00zuulzuul00000000000000# Copyright 2020 Nokia Software. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import copy from mistral_lib import actions from mistral_lib import exceptions as exc from mistral_lib import utils def _compare_parameters(expected_params, actual_params): """Compares the expected parameters with the actual parameters. :param expected_params: Expected dict of parameters. :param actual_params: Actual dict of parameters. :return: Tuple {missing parameter names, unexpected parameter names} """ missing_params = [] unexpected_params = copy.deepcopy(list((actual_params or {}).keys())) for p_name, p_value in expected_params.items(): if p_value is utils.NotDefined and p_name not in unexpected_params: missing_params.append(str(p_name)) if p_name in unexpected_params: unexpected_params.remove(p_name) return missing_params, unexpected_params class ActionDescriptorBase(actions.ActionDescriptor, abc.ABC): def __init__(self, name, desc, params_spec, namespace=None, project_id=None, scope=None): self._name = name self._desc = desc self._params_spec = params_spec self._namespace = namespace self._project_id = project_id self._scope = scope @property def name(self): return self._name @property def description(self): return self._desc @property def params_spec(self): return self._params_spec @property def namespace(self): return self._namespace @property def project_id(self): return self._project_id @property def scope(self): return self._scope @property def action_class_name(self): return None @property def action_class_attributes(self): return None def check_parameters(self, params): # Don't validate action input if action initialization # method contains ** argument. if '**' in self.params_spec: return expected_params = utils.get_dict_from_string(self.params_spec) actual_params = params or {} missing, unexpected = _compare_parameters( expected_params, actual_params ) if missing or unexpected: msg = 'Invalid input [name=%s, class=%s' msg_props = [self.name, self.action_class_name] if missing: msg += ', missing=%s' msg_props.append(missing) if unexpected: msg += ', unexpected=%s' msg_props.append(unexpected) msg += ']' raise exc.ActionException(msg % tuple(msg_props)) def post_process_result(self, result): return result ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/providers/composite.py0000664000175000017500000000335500000000000024320 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from mistral_lib.actions import base class CompositeActionProvider(base.ActionProvider): def __init__(self, name, delegates): super().__init__(name) self._delegates = delegates def find(self, action_name, namespace=None): for d in self._delegates: action_desc = d.find(action_name, namespace) if action_desc is not None: return action_desc return None def find_all(self, namespace=None, limit=None, sort_fields=None, sort_dirs=None, **filters): # TODO(rakhmerov): Implement the algorithm that takes ordering/sorting # parameters into account correctly. For now, they are just passed to # delegates. res = [] for d in self._delegates: action_descriptors = d.find_all( namespace=namespace, limit=limit, sort_fields=sort_fields, sort_dirs=sort_dirs, **filters ) if action_descriptors is not None: res.extend(action_descriptors) return res def add_action_provider(self, action_provider): self._delegates.append(action_provider) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/providers/python.py0000664000175000017500000000403600000000000023634 0ustar00zuulzuul00000000000000# Copyright 2020 Nokia Software. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from mistral_lib.actions.providers import base from mistral_lib.utils import inspect_utils as i_utils class PythonActionDescriptor(base.ActionDescriptorBase): def __init__(self, name, action_cls, action_cls_attrs=None, namespace=None, project_id=None, scope=None): super(PythonActionDescriptor, self).__init__( name, i_utils.get_docstring(action_cls), i_utils.get_arg_list_as_str(action_cls.__init__), namespace, project_id, scope ) self._action_cls = action_cls self._action_cls_attrs = action_cls_attrs def __repr__(self): return 'Python action [name=%s, cls=%s]' % ( self.name, self._action_cls ) def instantiate(self, params, wf_ctx): if not self._action_cls_attrs: # No need to create new dynamic type. return self._action_cls(**params) dynamic_cls = type( self._action_cls.__name__, (self._action_cls,), self._action_cls_attrs ) return dynamic_cls(**params) @property def action_class(self): return self._action_cls @property def action_class_name(self): return "{}.{}".format( self._action_cls.__module__, self._action_cls.__name__ ) @property def action_class_attributes(self): return self._action_cls_attrs ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/actions/types.py0000664000175000017500000000470700000000000021447 0ustar00zuulzuul00000000000000# Copyright 2014 - Mirantis, Inc. # Copyright 2015 - StackStorm, Inc. # Copyright 2016 - Brocade Communications Systems, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from mistral_lib import serialization from mistral_lib import utils class Result(serialization.MistralSerializable): """Action result.""" def __init__(self, data=None, error=None, cancel=False): self.data = data self.error = error self.cancel = cancel def __repr__(self): return 'Result [data=%s, error=%s, cancel=%s]' % ( repr(self.data), repr(self.error), str(self.cancel) ) def cut_repr(self): _data = utils.mask_data(self.data) _error = utils.mask_data(self.error) _cancel = utils.mask_data(self.cancel) return 'Result [data=%s, error=%s, cancel=%s]' % ( utils.cut(_data), utils.cut(_error), str(_cancel) ) def is_cancel(self): return self.cancel def is_error(self): return self.error is not None and not self.is_cancel() def is_success(self): return not self.is_error() and not self.is_cancel() def __eq__(self, other): return ( self.data == other.data and self.error == other.error and self.cancel == other.cancel ) def __ne__(self, other): return not self.__eq__(other) def to_dict(self): return {'result': self.data if self.is_success() else self.error} class ResultSerializer(serialization.DictBasedSerializer): def serialize_to_dict(self, entity): return { 'data': entity.data, 'error': entity.error, 'cancel': entity.cancel } def deserialize_from_dict(self, entity_dict): return Result( entity_dict['data'], entity_dict['error'], entity_dict.get('cancel', False) ) serialization.register_serializer(Result, ResultSerializer()) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/exceptions.py0000664000175000017500000000544500000000000021024 0ustar00zuulzuul00000000000000# Copyright 2013 - Mirantis, Inc. # Copyright 2015 - StackStorm, Inc. # Copyright 2016 - Brocade Communications Systems, Inc. # Copyright 2016 - Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. class MistralExceptionBase(Exception): """Base class for Mistral specific errors and exceptions. A common parent class to derive MistralError and MistralException classes from. """ message = "An unknown error occurred" http_code = 500 def __init__(self, message=None): if message is not None: self.message = message super(MistralExceptionBase, self).__init__( '%d: %s' % (self.http_code, self.message)) @property def code(self): """This is here for webob to read. https://github.com/Pylons/webob/blob/master/webob/exc.py """ return self.http_code def __str__(self): return self.message class MistralError(MistralExceptionBase): """Mistral specific error. Reserved for situations that can't be automatically handled. When it occurs it signals that there is a major environmental problem like invalid startup configuration or implementation problem (e.g. some code doesn't take care of certain corner cases). From architectural perspective it's pointless to try to handle this type of problems except doing some finalization work like transaction rollback, deleting temporary files etc. """ message = "An unknown error occurred" class MistralException(Exception): """Mistral specific exception. Reserved for situations that are not critical for program continuation. It is possible to recover from this type of problems automatically and continue program execution. Such problems may be related with invalid user input (such as invalid syntax) or temporary environmental problems. In case if an instance of a certain exception type bubbles up to API layer then this type of exception it must be associated with an http code so it's clear how to represent it for a client. To correctly use this class, inherit from it and define a 'message' and 'http_code' properties. """ message = "An unknown exception occurred" class ActionException(MistralException): message = "Failed to process an action" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/serialization.py0000664000175000017500000001424300000000000021514 0ustar00zuulzuul00000000000000# Copyright 2017 Nokia Networks. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from oslo_serialization import jsonutils _SERIALIZER = None class Serializer(object): """Base interface for entity serializers. A particular serializer knows how to convert a certain object into a string and back from that string into an object whose state is equivalent to the initial object. """ @abc.abstractmethod def serialize(self, entity): """Converts the given object into a string. :param entity: An object to be serialized. :return String containing the state of the object in serialized form. """ raise NotImplementedError @abc.abstractmethod def deserialize(self, data_str): """Converts the given string into an object. :param data_str: String containing the state of the object in serialized form. :return: An object. """ raise NotImplementedError class DictBasedSerializer(Serializer): """Dictionary-based serializer. It slightly simplifies implementing custom serializers by introducing a contract based on dictionary. A serializer class extending this class just needs to implement conversion from object into dict and from dict to object. It doesn't need to convert into string and back as required bye the base serializer contract. Conversion into string is implemented once with regard to possible problems that may occur for collection and primitive types as circular dependencies, correct date format etc. """ def serialize(self, entity): if entity is None: return None entity_dict = self.serialize_to_dict(entity) return jsonutils.dumps( jsonutils.to_primitive(entity_dict, convert_instances=True) ) def deserialize(self, data_str): if data_str is None: return None entity_dict = jsonutils.loads(data_str) return self.deserialize_from_dict(entity_dict) @abc.abstractmethod def serialize_to_dict(self, entity): raise NotImplementedError @abc.abstractmethod def deserialize_from_dict(self, entity_dict): raise NotImplementedError class MistralSerializable(object): """A mixin to generate a serialization key for a custom object.""" @classmethod def get_serialization_key(cls): return "%s.%s" % (cls.__module__, cls.__name__) class PolymorphicSerializer(Serializer): """Polymorphic serializer. The purpose of this class is to serve as a serialization router between serializers that can work with entities of particular type. All concrete serializers associated with concrete entity classes should be registered via method 'register', after that an instance of polymorphic serializer can be used as a universal serializer for an RPC system or something else. When converting an object into a string this serializer also writes a special key into the result string sequence so that it's possible to find a proper serializer when deserializing this object. If a primitive value is given as an entity this serializer doesn't do anything special and simply converts a value into a string using jsonutils. Similar when it converts a string into a primitive value. """ def __init__(self): # {serialization key: serializer} self.serializers = {} @staticmethod def _get_serialization_key(entity_cls): if issubclass(entity_cls, MistralSerializable): return entity_cls.get_serialization_key() return None def register(self, entity_cls, serializer): key = self._get_serialization_key(entity_cls) if not key: return if key in self.serializers: raise RuntimeError( "A serializer for the entity class has already been" " registered: %s" % entity_cls ) self.serializers[key] = serializer def unregister(self, entity_cls): key = self._get_serialization_key(entity_cls) if not key: return if key in self.serializers: del self.serializers[key] def cleanup(self): self.serializers.clear() def serialize(self, entity): if entity is None: return None key = self._get_serialization_key(type(entity)) # Primitive or not registered type. if not key: return jsonutils.dumps( jsonutils.to_primitive(entity, convert_instances=True) ) serializer = self.serializers.get(key) if not serializer: raise RuntimeError( "Failed to find a serializer for the key: %s" % key ) result = { '__serial_key': key, '__serial_data': serializer.serialize(entity) } return jsonutils.dumps(result) def deserialize(self, data_str): if data_str is None: return None data = jsonutils.loads(data_str) if isinstance(data, dict) and '__serial_key' in data: serializer = self.serializers.get(data['__serial_key']) return serializer.deserialize(data['__serial_data']) return data def get_polymorphic_serializer(): global _SERIALIZER if _SERIALIZER is None: _SERIALIZER = PolymorphicSerializer() return _SERIALIZER def register_serializer(entity_cls, serializer): get_polymorphic_serializer().register(entity_cls, serializer) def unregister_serializer(entity_cls): get_polymorphic_serializer().unregister(entity_cls) def cleanup(): get_polymorphic_serializer().cleanup() ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/tests/0000775000175000017500000000000000000000000017423 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/__init__.py0000664000175000017500000000000000000000000021522 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/tests/actions/0000775000175000017500000000000000000000000021063 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/actions/__init__.py0000664000175000017500000000000000000000000023162 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/actions/test_action_providers.py0000664000175000017500000000737100000000000026056 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from mistral_lib import actions from mistral_lib.actions.providers import python from mistral_lib.tests import base as tests_base class HelloAction(actions.Action): """I help with testing.""" def __init__(self, f_name, l_name): super(HelloAction, self).__init__() self._f_name = f_name self._l_name = l_name def run(self, context): return 'Hello %s %s!' % (self._f_name, self._l_name) class TestActionProvider(actions.ActionProvider): def __init__(self, name): super(TestActionProvider, self).__init__(name) self.action_descs = {} def add_action_descriptor(self, action_desc): self.action_descs[action_desc.name] = action_desc def find(self, action_name, namespace=None): return self.action_descs.get(action_name) def find_all(self, namespace=None, limit=None, sort_fields=None, sort_dirs=None, **filters): return self.action_descs.values() class TestActionProviders(tests_base.TestCase): def test_python_action_descriptor(self): action_desc = python.PythonActionDescriptor('test_action', HelloAction) # Check descriptor attributes. self.assertEqual('test_action', action_desc.name) self.assertEqual(HelloAction, action_desc.action_class) self.assertEqual( HelloAction.__module__ + '.' + HelloAction.__name__, action_desc.action_class_name ) self.assertIsNone(action_desc.action_class_attributes) self.assertEqual('I help with testing.', action_desc.description) self.assertEqual('f_name, l_name', action_desc.params_spec) # Instantiate the action and check how it works. action = action_desc.instantiate( {'f_name': 'Jhon', 'l_name': 'Doe'}, {} ) res = action.run(None) self.assertEqual('Hello Jhon Doe!', res) def test_composite_action_provider(self): # Check empty provider. composite_provider = actions.CompositeActionProvider('test', []) self.assertEqual(0, len(composite_provider.find_all())) # Add two test providers. provider1 = TestActionProvider('provider1') action_desc1 = python.PythonActionDescriptor('action1', HelloAction) action_desc2 = python.PythonActionDescriptor('action2', HelloAction) action_desc3 = python.PythonActionDescriptor('action3', HelloAction) action_desc4 = python.PythonActionDescriptor('action4', HelloAction) provider1.add_action_descriptor(action_desc1) provider1.add_action_descriptor(action_desc2) provider2 = TestActionProvider('provider2') provider2.add_action_descriptor(action_desc3) provider2.add_action_descriptor(action_desc4) composite_provider = actions.CompositeActionProvider( 'test', [provider1, provider2] ) self.assertEqual(4, len(composite_provider.find_all())) self.assertEqual(action_desc1, composite_provider.find('action1')) self.assertEqual(action_desc2, composite_provider.find('action2')) self.assertEqual(action_desc3, composite_provider.find('action3')) self.assertEqual(action_desc4, composite_provider.find('action4')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/actions/test_base.py0000664000175000017500000000200000000000000023376 0ustar00zuulzuul00000000000000# Copyright 2017 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from mistral_lib import actions from mistral_lib.actions import context from mistral_lib.tests import base as tests_base class TestAction(actions.Action): def run(self, context): return context class TestActionsBase(tests_base.TestCase): def test_run_empty_context(self): ctx = context.ActionContext() action = TestAction() result = action.run(ctx) assert result == ctx ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/actions/test_context.py0000664000175000017500000000623500000000000024166 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from mistral_lib.actions import context from mistral_lib.tests import base as tests_base def _fake_context(): security_ctx = context.SecurityContext( auth_uri='auth_uri', auth_cacert='auth_cacert', insecure='insecure', service_catalog='service_catalog', region_name='region_name', is_trust_scoped='is_trust_scoped', redelivered='redelivered', expires_at='expires_at', trust_id='trust_id', is_target='is_target', project_id='project_id') execution_ctx = context.ExecutionContext( workflow_execution_id='workflow_execution_id', task_execution_id='task_execution_id', action_execution_id='action_execution_id', workflow_name='workflow_name', callback_url='callback_url') return context.ActionContext(security_ctx, execution_ctx) class TestActionsBase(tests_base.TestCase): def test_empty_context(self): ctx = context.ActionContext( context.SecurityContext(), context.ExecutionContext() ) self.assertIsInstance(ctx.security, context.SecurityContext) self.assertIsInstance(ctx.execution, context.ExecutionContext) self.assertEqual(ctx.security.auth_uri, None) self.assertEqual(ctx.execution.workflow_name, None) def test_deprecated_properties(self): ctx = _fake_context() deprecated_properties = [ 'auth_cacert', 'auth_token', 'auth_uri', 'expires_at', 'insecure', 'is_target', 'is_trust_scoped', 'project_id', 'project_name', 'redelivered', 'region_name', 'service_catalog', 'trust_id', 'user_name' ] for deprecated in deprecated_properties: old = getattr(ctx, deprecated) new = getattr(ctx.security, deprecated) self.assertEqual(old, new) class TestActionContextSerializer(tests_base.TestCase): def test_serialization(self): ctx = _fake_context() serialiser = context.ActionContextSerializer() dict_ctx = serialiser.serialize_to_dict(ctx) self.assertEqual(dict_ctx['security'], vars(ctx.security)) self.assertEqual(dict_ctx['execution'], vars(ctx.execution)) def test_deserialization(self): ctx = _fake_context() serialiser = context.ActionContextSerializer() dict_ctx = serialiser.serialize_to_dict(ctx) ctx_2 = serialiser.deserialize_from_dict(dict_ctx) self.assertEqual(ctx.security.auth_uri, ctx_2.security.auth_uri) self.assertEqual( ctx.execution.workflow_name, ctx_2.execution.workflow_name ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/base.py0000664000175000017500000000140100000000000020703 0ustar00zuulzuul00000000000000# Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import base class TestCase(base.BaseTestCase): """Test case base class for all unit tests.""" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/test_inspect_utils.py0000664000175000017500000000521700000000000023726 0ustar00zuulzuul00000000000000# Copyright 2014 - Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import time from mistral_lib import actions from mistral_lib.tests import base from mistral_lib.utils import inspect_utils as i_u class DummyHTTPAction(actions.Action): def __init__(self, url, method="GET", params=None, body=None, headers=None, cookies=None, auth=None, timeout=None, allow_redirects=None, proxies=None, verify=None): super(DummyHTTPAction, self).__init__() def run(self, context): return context class DummyRunTask(object): def __init__(self, wf_ex, wf_spec, task_spec, ctx, triggered_by=None, handles_error=False): pass class ClassWithProperties(object): a = 1 @property def prop(self): pass class InspectUtilsTest(base.TestCase): def test_get_parameters_str(self): action_class = DummyHTTPAction parameters_str = i_u.get_arg_list_as_str(action_class.__init__) http_action_params = ( 'url, method="GET", params=null, body=null, ' 'headers=null, cookies=null, auth=null, ' 'timeout=null, allow_redirects=null, ' 'proxies=null, verify=null' ) self.assertEqual(http_action_params, parameters_str) def test_get_parameters_str_all_mandatory(self): clazz = DummyRunTask parameters_str = i_u.get_arg_list_as_str(clazz.__init__) self.assertEqual( 'wf_ex, wf_spec, task_spec, ctx, triggered_by=null,' ' handles_error=false', parameters_str ) def test_get_parameters_str_with_function_parameter(self): def test_func(foo, bar=None, test_func=time.sleep): pass parameters_str = i_u.get_arg_list_as_str(test_func) self.assertEqual("foo, bar=null", parameters_str) def test_get_public_fields(self): attrs = i_u.get_public_fields(ClassWithProperties) self.assertEqual(attrs, {'a': 1}) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/test_serialization.py0000664000175000017500000000667600000000000023730 0ustar00zuulzuul00000000000000# Copyright 2017 - Nokia Networks. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from mistral_lib import serialization from mistral_lib.tests import base class MyClass(serialization.MistralSerializable): def __init__(self, a, b): self.a = a self.b = b def __eq__(self, other): if not isinstance(other, MyClass): return False return other.a == self.a and other.b == self.b class MyClassSerializer(serialization.DictBasedSerializer): def serialize_to_dict(self, entity): return {'a': entity.a, 'b': entity.b} def deserialize_from_dict(self, entity_dict): return MyClass(entity_dict['a'], entity_dict['b']) class SerializationTest(base.TestCase): def setUp(self): super(SerializationTest, self).setUp() serialization.register_serializer(MyClass, MyClassSerializer()) self.addCleanup(serialization.unregister_serializer, MyClass) def test_dict_based_serializer(self): obj = MyClass('a', 'b') serializer = MyClassSerializer() s = serializer.serialize(obj) self.assertEqual(obj, serializer.deserialize(s)) self.assertIsNone(serializer.serialize(None)) self.assertIsNone(serializer.deserialize(None)) def test_polymorphic_serializer_primitive_types(self): serializer = serialization.get_polymorphic_serializer() self.assertEqual(17, serializer.deserialize(serializer.serialize(17))) self.assertEqual( 0.34, serializer.deserialize(serializer.serialize(0.34)) ) self.assertEqual(-5, serializer.deserialize(serializer.serialize(-5))) self.assertEqual( -6.3, serializer.deserialize(serializer.serialize(-6.3)) ) self.assertFalse(serializer.deserialize(serializer.serialize(False))) self.assertTrue(serializer.deserialize(serializer.serialize(True))) self.assertEqual( 'abc', serializer.deserialize(serializer.serialize('abc')) ) self.assertEqual( {'a': 'b', 'c': 'd'}, serializer.deserialize(serializer.serialize({'a': 'b', 'c': 'd'})) ) self.assertEqual( ['a', 'b', 'c'], serializer.deserialize(serializer.serialize(['a', 'b', 'c'])) ) def test_polymorphic_serializer_custom_object(self): serializer = serialization.get_polymorphic_serializer() obj = MyClass('a', 'b') s = serializer.serialize(obj) self.assertIn('__serial_key', s) self.assertIn('__serial_data', s) self.assertEqual(obj, serializer.deserialize(s)) self.assertIsNone(serializer.serialize(None)) self.assertIsNone(serializer.deserialize(None)) def test_register_twice(self): self.assertRaises( RuntimeError, serialization.register_serializer, MyClass, MyClassSerializer() ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/tests/test_utils.py0000664000175000017500000002346400000000000022205 0ustar00zuulzuul00000000000000# Copyright 2013 - Mirantis, Inc. # Copyright 2017 - Nokia Networks. # Copyright 2017 - Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from yaql.language import utils as yaql_utils from mistral_lib.tests import base as tests_base from mistral_lib import utils import testtools.matchers as ttm LEFT = { 'key1': { 'key11': "val11" }, 'key2': 'val2' } RIGHT = { 'key1': { 'key11': "val111111", 'key12': "val12", 'key13': { 'key131': 'val131' } }, 'key2': 'val2222', 'key3': 'val3' } class TestUtils(tests_base.TestCase): def test_merge_dicts(self): left = copy.deepcopy(LEFT) right = copy.deepcopy(RIGHT) expected = { 'key1': { 'key11': "val111111", 'key12': "val12", 'key13': { 'key131': 'val131' } }, 'key2': 'val2222', 'key3': 'val3' } utils.merge_dicts(left, right) self.assertDictEqual(left, expected) def test_merge_dicts_overwrite_false(self): left = copy.deepcopy(LEFT) right = copy.deepcopy(RIGHT) expected = { 'key1': { 'key11': "val11", 'key12': "val12", 'key13': { 'key131': 'val131' } }, 'key2': 'val2', 'key3': 'val3' } utils.merge_dicts(left, right, overwrite=False) self.assertDictEqual(left, expected) def test_itersubclasses(self): class A(object): pass class B(A): pass class C(A): pass class D(C): pass self.assertEqual([B, C, D], list(utils.iter_subclasses(A))) def test_get_dict_from_entries(self): input = ['param1', {'param2': 2}] input_dict = utils.get_dict_from_entries(input) self.assertIn('param1', input_dict) self.assertIn('param2', input_dict) self.assertEqual(2, input_dict.get('param2')) self.assertIs(input_dict.get('param1'), utils.NotDefined) def test_get_input_dict_from_string(self): self.assertDictEqual( { 'param1': utils.NotDefined, 'param2': 2, 'param3': 'var3' }, utils.get_dict_from_string('param1, param2=2, param3="var3"') ) self.assertDictEqual({}, utils.get_dict_from_string('')) def test_cut_string(self): s = 'Hello, Mistral!' self.assertEqual('Hello...', utils.cut_string(s, length=5)) self.assertEqual(s, utils.cut_string(s, length=100)) self.assertEqual(s, utils.cut_string(s, length=-1)) def test_cut_list(self): list_ = ['Hello, Mistral!', 'Hello, OpenStack!'] self.assertEqual("['Hello, M...", utils.cut_list(list_, 13)) self.assertEqual("['Hello, Mistr...", utils.cut_list(list_, 17)) self.assertEqual( "['Hello, Mistral!', 'He...", utils.cut_list(list_, 26)) self.assertEqual( "['Hello, Mistral!', 'Hello, OpenStack!']", utils.cut_list(list_, 100) ) self.assertEqual( "['Hello, Mistral!', 'Hello, OpenStack!']", utils.cut_list(list_, -1) ) self.assertEqual("[1, 2...", utils.cut_list([1, 2, 3, 4, 5], 8)) self.assertEqual("[1, 2,...", utils.cut_list([1, 2, 3, 4, 5], 9)) self.assertEqual("[1, 2, 3...", utils.cut_list([1, 2, 3, 4, 5], 11)) self.assertRaises(ValueError, utils.cut_list, (1, 2)) def test_cut_list_with_large_dict_of_str(self): d = [str(i) for i in range(65535)] s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_list_with_large_dict_of_int(self): d = [i for i in range(65535)] s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_list_with_large_dict_of_dict(self): d = [{'value': str(i)} for i in range(65535)] s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_list_for_state_info(self): d = [{'value': 'This is a string that exceeds 35 characters'} for i in range(2000)] s = utils.cut(d, 65500) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65500))) def test_cut_dict_with_strings(self): d = {'key1': 'value1', 'key2': 'value2'} s = utils.cut_dict(d, 13) self.assertIn(s, ["{'key1': '...", "{'key2': '..."]) s = utils.cut_dict(d, 15) self.assertIn(s, ["{'key1': 'va...", "{'key2': 'va..."]) s = utils.cut_dict(d, 22) self.assertIn( s, ["{'key1': 'value1', ...", "{'key2': 'value2', ..."] ) self.assertIn( utils.cut_dict(d, 100), [ "{'key1': 'value1', 'key2': 'value2'}", "{'key2': 'value2', 'key1': 'value1'}" ] ) self.assertIn( utils.cut_dict(d, -1), [ "{'key1': 'value1', 'key2': 'value2'}", "{'key2': 'value2', 'key1': 'value1'}" ] ) self.assertRaises(ValueError, utils.cut_dict, (1, 2)) def test_cut_dict_with_digits(self): d = {1: 2, 3: 4} s = utils.cut_dict(d, 10) self.assertIn(s, ["{1: 2, ...", "{3: 4, ..."]) s = utils.cut_dict(d, 11) self.assertIn(s, ["{1: 2, 3...", "{3: 4, 1..."]) s = utils.cut_dict(d, 100) self.assertIn(s, ["{1: 2, 3: 4}", "{3: 4, 1: 2}"]) def test_cut_dict_with_large_dict_of_str(self): d = {} for i in range(65535): d[str(i)] = str(i) s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_dict_with_large_dict_of_int(self): d = {} for i in range(65535): d[i] = i s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_dict_with_large_dict_of_dict(self): d = {} for i in range(65535): d[i] = {'value': str(i)} s = utils.cut(d, 65535) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65535))) def test_cut_dict_for_state_info(self): d = {} for i in range(2000): d[i] = {'value': 'This is a string that exceeds 35 characters'} s = utils.cut(d, 65500) self.assertThat(len(s), ttm.Not(ttm.GreaterThan(65500))) def test_mask_data(self): payload = {'adminPass': 'fooBarBaz'} expected = {'adminPass': '***'} self.assertEqual(expected, utils.mask_data(payload)) payload = """adminPass='fooBarBaz'""" expected = """adminPass='***'""" self.assertEqual(expected, utils.mask_data(payload)) payload = [{'adminPass': 'fooBarBaz'}, {"new_pass": "blah"}] expected = [{'adminPass': '***'}, {"new_pass": "***"}] self.assertEqual(expected, utils.mask_data(payload)) payload = ["adminPass", 'fooBarBaz'] expected = ["adminPass", 'fooBarBaz'] self.assertEqual(expected, utils.mask_data(payload)) def test_json_serialize_frozen_dict(self): data = yaql_utils.FrozenDict(a=1, b=2, c=iter([1, 2, 3])) json_str = utils.to_json_str(data) self.assertIsNotNone(json_str) self.assertIn('"a": 1', json_str) self.assertIn('"b": 2', json_str) self.assertIn('"c": [1, 2, 3]', json_str) def test_json_serialize_generator(self): def _list_stream(_list): for i in _list: yield i gen = _list_stream( [1, yaql_utils.FrozenDict(a=1), _list_stream([12, 15])] ) self.assertEqual('[1, {"a": 1}, [12, 15]]', utils.to_json_str(gen)) def test_json_serialize_dict_of_generators(self): def _f(cnt): for i in range(1, cnt + 1): yield i data = {'numbers': _f(3)} self.assertEqual('{"numbers": [1, 2, 3]}', utils.to_json_str(data)) def test_json_serialize_range(self): self.assertEqual("[1, 2, 3, 4]", utils.to_json_str(range(1, 5))) def test_json_serialize_iterator_of_frozen_dicts(self): data = iter( [ yaql_utils.FrozenDict(a=1, b=2, c=iter([1, 2, 3])), yaql_utils.FrozenDict( a=11, b=yaql_utils.FrozenDict(b='222'), c=iter( [ 1, yaql_utils.FrozenDict( a=iter([4, yaql_utils.FrozenDict(a=99)]) ) ] ) ) ] ) json_str = utils.to_json_str(data) self.assertIsNotNone(json_str) # Checking the first item. self.assertIn('"a": 1', json_str) self.assertIn('"b": 2', json_str) self.assertIn('"c": [1, 2, 3]', json_str) # Checking the first item. self.assertIn('"a": 11', json_str) self.assertIn('"b": {"b": "222"}', json_str) self.assertIn('"c": [1, {"a": [4, {"a": 99}]}]', json_str) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/utils/0000775000175000017500000000000000000000000017421 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/utils/__init__.py0000664000175000017500000003437700000000000021550 0ustar00zuulzuul00000000000000# Copyright 2013 - Mirantis, Inc. # Copyright 2017 - Nokia Networks. # Copyright 2017 - Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import functools import inspect import json import os from os import path import socket import string import sys import threading import eventlet from eventlet import corolocal from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils.strutils import mask_dict_password from oslo_utils.strutils import mask_password from oslo_utils import timeutils from oslo_utils import uuidutils import pkg_resources as pkg import random # Thread local storage. _th_loc_storage = threading.local() def generate_unicode_uuid(): return uuidutils.generate_uuid() def is_valid_uuid(uuid_string): return uuidutils.is_uuid_like(uuid_string) def _get_greenlet_local_storage(): greenlet_id = corolocal.get_ident() greenlet_locals = getattr(_th_loc_storage, "greenlet_locals", None) if not greenlet_locals: greenlet_locals = {} _th_loc_storage.greenlet_locals = greenlet_locals if greenlet_id in greenlet_locals: return greenlet_locals[greenlet_id] else: return None def has_thread_local(var_name): gl_storage = _get_greenlet_local_storage() return gl_storage and var_name in gl_storage def get_thread_local(var_name): if not has_thread_local(var_name): return None return _get_greenlet_local_storage()[var_name] def set_thread_local(var_name, val): if val is None and has_thread_local(var_name): gl_storage = _get_greenlet_local_storage() # Delete variable from greenlet local storage. if gl_storage: del gl_storage[var_name] # Delete the entire greenlet local storage from thread local storage. if gl_storage and len(gl_storage) == 0: del _th_loc_storage.greenlet_locals[corolocal.get_ident()] if val is not None: gl_storage = _get_greenlet_local_storage() if not gl_storage: gl_storage = _th_loc_storage.greenlet_locals[ corolocal.get_ident()] = {} gl_storage[var_name] = val def log_exec(logger, level=logging.DEBUG): """Decorator for logging function execution. By default, target function execution is logged with DEBUG level. """ def _decorator(func): @functools.wraps(func) def _logged(*args, **kw): params_repr = ("[args=%s, kw=%s]" % (str(args), str(kw)) if args or kw else "") func_repr = ("Called method [name=%s, doc='%s', params=%s]" % (func.__name__, func.__doc__, params_repr)) logger.log(level, func_repr) return func(*args, **kw) _logged.__doc__ = func.__doc__ return _logged return _decorator def merge_dicts(left, right, overwrite=True): """Merges two dictionaries. Values of right dictionary recursively get merged into left dictionary. :param left: Left dictionary. :param right: Right dictionary. :param overwrite: If False, left value will not be overwritten if exists. """ if left is None: return right if right is None: return left for k, v in right.items(): if k not in left: left[k] = v else: left_v = left[k] if isinstance(left_v, dict) and isinstance(v, dict): merge_dicts(left_v, v, overwrite=overwrite) elif overwrite: left[k] = v return left def update_dict(left, right): """Updates left dict with content from right dict :param left: Left dict. :param right: Right dict. :return: the updated left dictionary. """ if left is None: return right if right is None: return left left.update(right) return left def get_file_list(directory, package='mistral'): base_path = pkg.resource_filename(package, directory) return [path.join(base_path, f) for f in os.listdir(base_path) if path.isfile(path.join(base_path, f))] def cut_dict(d, length=100): """Removes dictionary entries according to the given length. This method removes a number of entries, if needed, so that a string representation would fit into the given length. The intention of this method is to optimize truncation of string representation for dictionaries where the exact precision is not critically important. Otherwise, we'd always have to convert a dict into a string first and then shrink it to a needed size which will increase memory footprint and reduce performance in case of large dictionaries (i.e. tens of thousands entries). Note that the method, due to complexity of the algorithm, has some non-zero precision which depends on exact keys and values placed into the dict. So for some dicts their reduced string representations will be only approximately equal to the given value (up to around several chars difference). :param d: A dictionary. :param length: A length limiting the dictionary string representation. :return: A dictionary which is a subset of the given dictionary. """ if not isinstance(d, dict): raise ValueError("A dictionary is expected, got: %s" % type(d)) res = "{" idx = 0 for key, value in d.items(): k = str(key) v = str(value) # Processing key. new_len = len(k) is_str = isinstance(key, str) if is_str: new_len += 2 # Account for the quotation marks if 0 <= length <= new_len + len(res): res += "'%s" % k if is_str else k break else: res += "'%s'" % k if is_str else k res += ": " # Processing value. new_len = len(v) is_str = isinstance(value, str) if is_str: new_len += 2 if 0 <= length <= new_len + len(res): res += "'%s" % v if is_str else v break else: res += "'%s'" % v if is_str else v res += ', ' if idx < len(d) - 1 else '}' idx += 1 if 0 <= length <= len(res) and res[length - 1] != '}': res = res[:length - 3] + '...' return res def cut_list(l, length=100): """Truncates string representation of a list for a given length. :param l: list to truncate :param length: amount of characters to truncate to :return: string containing given length of characters from the list """ if not isinstance(l, list): raise ValueError("A list is expected, got: %s" % type(l)) res = '[' for idx, item in enumerate(l): s = str(item) new_len = len(res) + len(s) is_str = isinstance(item, str) if is_str: new_len += 2 if 0 <= length <= new_len: res += "'%s" % s if is_str else s break else: res += "'%s'" % s if is_str else s res += ', ' if idx < len(l) - 1 else ']' if 0 <= length <= len(res) and res[length - 1] != ']': res = res[:length - 3] + '...' return res def cut_string(s, length=100): """Truncates a string for a given length. :param s: string to truncate :param length: amount of characters to truncate to :return: string containing given length of characters """ if 0 <= length < len(s): return "%s..." % s[:length] return s def cut(data, length=100): """Truncates string representation of data for a given length. :param data: a dictionary, list or string to truncate :param length: amount of characters to truncate to :return: string containing given length of characters """ if not data: return data if isinstance(data, list): return cut_list(data, length=length) if isinstance(data, dict): return cut_dict(data, length=length) return cut_string(str(data), length=length) def cut_by_kb(data, kilobytes): length = get_number_of_chars_from_kilobytes(kilobytes) return cut(data, length) def cut_by_char(data, length): return cut(data, length) def iter_subclasses(cls, _seen=None): """Generator over all subclasses of a given class in depth first order.""" if not isinstance(cls, type): raise TypeError('iter_subclasses must be called with new-style class' ', not %.100r' % cls) _seen = _seen or set() try: subs = cls.__subclasses__() except TypeError: # fails only when cls is type subs = cls.__subclasses__(cls) for sub in subs: if sub not in _seen: _seen.add(sub) yield sub for _sub in iter_subclasses(sub, _seen): yield _sub def random_sleep(limit=1): """Sleeps for a random period of time not exceeding the given limit. Mostly intended to be used by tests to emulate race conditions. :param limit: Float number of seconds that a sleep period must not exceed. """ seconds = random.Random().randint(0, limit * 1000) * 0.001 print("Sleep: %s sec..." % seconds) eventlet.sleep(seconds) class NotDefined(object): """Marker of an empty value. In a number of cases None can't be used to express the semantics of a not defined value because None is just a normal value rather than a value set to denote that it's not defined. This class can be used in such cases instead of None. """ pass def get_number_of_chars_from_kilobytes(kilobytes): bytes_per_char = sys.getsizeof('s') - sys.getsizeof('') total_number_of_chars = int(kilobytes * 1024 / bytes_per_char) return total_number_of_chars def get_dict_from_string(string, delimiter=','): if not string: return {} kv_dicts = [] for kv_pair_str in string.split(delimiter): kv_str = kv_pair_str.strip() kv_list = kv_str.split('=') if len(kv_list) > 1: try: value = json.loads(kv_list[1]) except ValueError: value = kv_list[1] kv_dicts += [{kv_list[0]: value}] else: kv_dicts += [kv_list[0]] return get_dict_from_entries(kv_dicts) def get_dict_from_entries(entries): """Transforms a list of entries into dictionary. :param entries: A list of entries. If an entry is a dictionary the method simply updates the result dictionary with its content. If an entry is not a dict adds {entry, NotDefined} into the result. """ result = {} for e in entries: if isinstance(e, dict): result.update(e) else: # NOTE(kong): we put NotDefined here as the value of # param without value specified, to distinguish from # the valid values such as None, ''(empty string), etc. result[e] = NotDefined return result def get_process_identifier(): """Gets current running process identifier.""" return "%s_%s" % (socket.gethostname(), os.getpid()) def utc_now_sec(): """Returns current time and drops microseconds.""" return drop_microseconds(timeutils.utcnow()) def drop_microseconds(date): """Drops microseconds and returns date.""" return date.replace(microsecond=0) def datetime_to_str(val, sep=' '): """Converts datetime value to string. If the given value is not an instance of datetime then the method returns the same value. :param val: datetime value. :param sep: Separator between date and time. :return: Datetime as a string. """ if isinstance(val, datetime.datetime): return val.isoformat(sep) return val def datetime_to_str_in_dict(d, key, sep=' '): """Converts datetime value in te given dict to string. :param d: A dictionary. :param key: The key for which we need to convert the value. :param sep: Separator between date and time. """ val = d.get(key) if val is not None: d[key] = datetime_to_str(d[key], sep=sep) def generate_string(length): """Returns random string. :param length: the length of returned string """ return ''.join(random.choice( string.ascii_uppercase + string.digits) for _ in range(length) ) def mask_data(obj): if isinstance(obj, dict): return mask_dict_password(obj) elif isinstance(obj, list): return [mask_data(i) for i in obj] else: return mask_password(obj) def to_json_str(obj): """Serializes an object into a JSON string. :param obj: Object to serialize. :return: JSON string. """ if obj is None: return None def _fallback(value): if inspect.isgenerator(value): result = list(value) # The result of the generator call may be again not primitive # so we need to call "to_primitive" again with the same fallback # function. Note that the endless recursion here is not a problem # because "to_primitive" limits the depth for custom classes, # if they are present in the object graph being traversed. return jsonutils.to_primitive( result, convert_instances=True, fallback=_fallback ) return value # We need to convert the root of the given object graph into # a primitive by hand so that we also enable conversion of # object of custom classes into primitives. Otherwise, they are # ignored by the "json" lib. return jsonutils.dumps( jsonutils.to_primitive(obj, convert_instances=True, fallback=_fallback) ) def from_json_str(json_str): """Reconstructs an object from a JSON string. :param json_str: A JSON string. :return: Deserialized object. """ if json_str is None: return None return jsonutils.loads(json_str) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/utils/inspect_utils.py0000664000175000017500000000445000000000000022663 0ustar00zuulzuul00000000000000# Copyright 2014 - Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import inspect import json def get_public_fields(obj): """Returns only public fields from object or class.""" public_attributes = [attr for attr in dir(obj) if not attr.startswith("_")] public_fields = {} for attribute_str in public_attributes: attr = getattr(obj, attribute_str) is_field = not (inspect.isbuiltin(attr) or inspect.isfunction(attr) or inspect.ismethod(attr) or isinstance(attr, property)) if is_field: public_fields[attribute_str] = attr return public_fields def get_docstring(obj): return inspect.getdoc(obj) def get_arg_list(func): argspec = get_args_spec(func) args = argspec.args if 'self' in args: args.remove('self') return args def get_arg_list_as_str(func): args = getattr(func, "__arguments__", None) if args: return args argspec = get_args_spec(func) defs = list(argspec.defaults or []) args = get_arg_list(func) diff_args_defs = len(args) - len(defs) arg_str_list = [] for index, default in enumerate(args): if index >= diff_args_defs: try: arg_str_list.append( "%s=%s" % ( args[index], json.dumps(defs[index - diff_args_defs]) ) ) except TypeError: pass else: arg_str_list.append("%s" % args[index]) if argspec.varkw: arg_str_list.append("**%s" % argspec.varkw) return ", ".join(arg_str_list) def get_args_spec(func): return inspect.getfullargspec(func) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/yaql/0000775000175000017500000000000000000000000017227 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/yaql/__init__.py0000664000175000017500000000000000000000000021326 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1629878210.154125 mistral-lib-2.5.0/mistral_lib/yaql/api/0000775000175000017500000000000000000000000020000 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/mistral_lib/yaql/api/__init__.py0000664000175000017500000000000000000000000022077 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1501248 mistral-lib-2.5.0/mistral_lib.egg-info/0000775000175000017500000000000000000000000017753 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/PKG-INFO0000664000175000017500000000403600000000000021053 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: mistral-lib Version: 2.5.0 Summary: Mistral shared routings and utilities (Actions API, YAQL functions API, data types etc.) Home-page: https://docs.openstack.org/mistral/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/mistral-lib.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =========== mistral-lib =========== This library contains data types, exceptions, functions and utilities common to Mistral, python-mistralclient and mistral-extra repositories. This library also contains the public interfaces for 3rd party integration (e.g. Actions API, YAQL functions API, etc.) If you want to use OpenStack in your custom actions or functions, you will also need to use https://opendev.org/openstack/mistral-extra. * Free software: Apache license * Documentation: https://docs.openstack.org/mistral/latest/ * Source: https://opendev.org/openstack/mistral-lib * Bugs: https://bugs.launchpad.net/mistral * Release notes: https://docs.openstack.org/releasenotes/mistral-lib/ Features -------- * TODO Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/SOURCES.txt0000664000175000017500000000405000000000000021636 0ustar00zuulzuul00000000000000.coveragerc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/contributor/creating_custom_actions.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/user/index.rst mistral_lib/__init__.py mistral_lib/exceptions.py mistral_lib/serialization.py mistral_lib.egg-info/PKG-INFO mistral_lib.egg-info/SOURCES.txt mistral_lib.egg-info/dependency_links.txt mistral_lib.egg-info/not-zip-safe mistral_lib.egg-info/pbr.json mistral_lib.egg-info/requires.txt mistral_lib.egg-info/top_level.txt mistral_lib/actions/__init__.py mistral_lib/actions/base.py mistral_lib/actions/context.py mistral_lib/actions/types.py mistral_lib/actions/providers/__init__.py mistral_lib/actions/providers/base.py mistral_lib/actions/providers/composite.py mistral_lib/actions/providers/python.py mistral_lib/tests/__init__.py mistral_lib/tests/base.py mistral_lib/tests/test_inspect_utils.py mistral_lib/tests/test_serialization.py mistral_lib/tests/test_utils.py mistral_lib/tests/actions/__init__.py mistral_lib/tests/actions/test_action_providers.py mistral_lib/tests/actions/test_base.py mistral_lib/tests/actions/test_context.py mistral_lib/utils/__init__.py mistral_lib/utils/inspect_utils.py mistral_lib/yaql/__init__.py mistral_lib/yaql/api/__init__.py releasenotes/notes/.placeholder releasenotes/notes/action_providers-54a50a4d629016ce.yaml releasenotes/notes/mask-password-6899d868d213f722.yaml releasenotes/notes/rename-task-id-to-task-execution-id-f17d671fcef0127a.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/ussuri.rst releasenotes/source/victoria.rst releasenotes/source/wallaby.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/dependency_links.txt0000664000175000017500000000000100000000000024021 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/not-zip-safe0000664000175000017500000000000100000000000022201 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/pbr.json0000664000175000017500000000005600000000000021432 0ustar00zuulzuul00000000000000{"git_version": "44c33f2", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/requires.txt0000664000175000017500000000014500000000000022353 0ustar00zuulzuul00000000000000eventlet!=0.20.1,>=0.20.0 oslo.log>=3.36.0 oslo.serialization>=2.21.1 pbr!=2.1.0,>=2.0.0 yaql>=1.1.3 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878210.0 mistral-lib-2.5.0/mistral_lib.egg-info/top_level.txt0000664000175000017500000000001400000000000022500 0ustar00zuulzuul00000000000000mistral_lib ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1421247 mistral-lib-2.5.0/releasenotes/0000775000175000017500000000000000000000000016451 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/releasenotes/notes/0000775000175000017500000000000000000000000017601 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000022052 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/notes/action_providers-54a50a4d629016ce.yaml0000664000175000017500000000167400000000000026174 0ustar00zuulzuul00000000000000--- features: - | Added the new infrastracture that will allow to manage Mistral actons in a much more flexible way. It is based on the action provider concept. An action provider is responsible for delivering actions to Mistral. Mistral cluster administrators can plugin multiple action providers that will deliver actions in their own way, possibly using different backend storages, caching techinques and so on. Along with the ActionProvider interface there has been added the ActionDescriptor interface that carries meta information about an action and responsible for instantiating the action. Also added base convenient classes. fixes: - | Fixed a number of typos and logical mistakes in docstrings and also added the exception class ActionException that actions can raise in case of their internal errors. Mistral will be handling those exceptions starting from the Victoria official version. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/notes/mask-password-6899d868d213f722.yaml0000664000175000017500000000021700000000000025302 0ustar00zuulzuul00000000000000--- security: - Ensure we mask sensitive data before logging Action return values fixes: - https://bugs.launchpad.net/tripleo/+bug/1850843 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/notes/rename-task-id-to-task-execution-id-f17d671fcef0127a.yaml0000664000175000017500000000020500000000000031532 0ustar00zuulzuul00000000000000--- deprecations: - | The attribute `context.execution.task_id` has been renamed to `context.execution.task_execution_id`. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/releasenotes/source/0000775000175000017500000000000000000000000017751 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/releasenotes/source/_static/0000775000175000017500000000000000000000000021377 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000000000000000023650 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1581252 mistral-lib-2.5.0/releasenotes/source/_templates/0000775000175000017500000000000000000000000022106 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000000000000000024357 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/conf.py0000664000175000017500000002142100000000000021250 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # Mistral-lib Release Notes documentation build configuration file, created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'mistral-lib Release Notes' copyright = u'2016, OpenStack Foundation' # Release notes are version independent release = '' version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. html_use_index = False # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'MistrallibReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'MistralLibReleaseNotes.tex', u'Mistral Library Release Notes Documentation', u'Mistral ' u'Library Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'mistrallibreleasenotes', u'Mistral Library Release Notes ' u'Documentation', [u'Mistral Library Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'MistralLibReleaseNotes', u'Mistral Library Release Notes ' u'Documentation', u'Mistral Library Developers', 'MistralLibReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] # -- Options for openstackdocstheme ------------------------------------------- openstackdocs_repo_name = 'openstack/mistral-lib' openstackdocs_auto_name = False openstackdocs_bug_project = 'mistral' openstackdocs_bug_tag = '' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/index.rst0000664000175000017500000000036000000000000021611 0ustar00zuulzuul00000000000000============================================ mistral-lib Release Notes ============================================ .. toctree:: :maxdepth: 1 unreleased wallaby victoria ussuri train stein rocky queens pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/pike.rst0000664000175000017500000000021700000000000021433 0ustar00zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/queens.rst0000664000175000017500000000022300000000000022000 0ustar00zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000021625 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/stein.rst0000664000175000017500000000022100000000000021620 0ustar00zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/train.rst0000664000175000017500000000017600000000000021624 0ustar00zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/unreleased.rst0000664000175000017500000000016000000000000022627 0ustar00zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/ussuri.rst0000664000175000017500000000020200000000000022027 0ustar00zuulzuul00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/victoria.rst0000664000175000017500000000021200000000000022316 0ustar00zuulzuul00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: stable/victoria ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/releasenotes/source/wallaby.rst0000664000175000017500000000020600000000000022134 0ustar00zuulzuul00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: stable/wallaby ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/requirements.txt0000664000175000017500000000057200000000000017250 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. eventlet!=0.20.1,>=0.20.0 # MIT oslo.log>=3.36.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0 oslo.serialization>=2.21.1 # Apache-2.0 yaql>=1.1.3 # Apache 2.0 License ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1629878210.1621253 mistral-lib-2.5.0/setup.cfg0000664000175000017500000000213600000000000015603 0ustar00zuulzuul00000000000000[metadata] name = mistral-lib summary = Mistral shared routings and utilities (Actions API, YAQL functions API, data types etc.) description_file = README.rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/mistral/latest/ python_requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 [files] packages = mistral_lib [compile_catalog] directory = mistral-lib/locale domain = mistral-lib [update_catalog] domain = mistral-lib output_dir = mistral-lib/locale input_file = mistral-lib/locale/mistral-lib.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = mistral-lib/locale/mistral-lib.pot [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/setup.py0000664000175000017500000000137600000000000015501 0ustar00zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/test-requirements.txt0000664000175000017500000000063600000000000020226 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking>=3.0.1,<3.1.0 # Apache-2.0 doc8>=0.6.0 # Apache-2.0 Pygments>=2.2.0 # BSD license coverage!=4.4,>=4.0 # Apache-2.0 oslotest>=3.2.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0 testtools>=2.2.0 # MIT ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1629878174.0 mistral-lib-2.5.0/tox.ini0000664000175000017500000000322700000000000015277 0ustar00zuulzuul00000000000000[tox] minversion = 3.1.1 envlist = py3,pep8 skipsdist = True ignore_basepython_conflict = True [testenv] basepython = python3 usedevelop = True setenv = VIRTUAL_ENV={envdir} PYTHONDONTWRITEBYTECODE = 1 PYTHONWARNINGS=default::DeprecationWarning passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = find . -type f -name "*.pyc" -delete stestr run --slowest {posargs} whitelist_externals = rm find [testenv:pep8] commands = doc8 doc/source flake8 {posargs} [testenv:venv] commands = {posargs} [testenv:cover] setenv = PYTHON=coverage run --source $project --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:docs] deps = -r{toxinidir}/doc/requirements.txt setenv = PYTHONHASHSEED=0 commands = rm -rf doc/build sphinx-build -E -W --keep-going -b html doc/source doc/build/html [testenv:releasenotes] commands = rm -rf releasenotes/build sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html [testenv:debug] whitelist_externals = oslo_debug_helper commands = oslo_debug_helper -t mistral_lib/tests {posargs} [doc8] extensions = .rst, .yaml, .mistral # Maximal line length should be 80. max-line-length = 80 [flake8] # E123, E125 skipped as they are invalid PEP-8. show-source = True ignore = E123,E125,W504 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build