mistral-lib-2.1.0/0000775000175000017500000000000013642134047013757 5ustar zuulzuul00000000000000mistral-lib-2.1.0/.zuul.yaml0000664000175000017500000000031213642133776015724 0ustar zuulzuul00000000000000- project: templates: - check-requirements - openstack-lower-constraints-jobs - openstack-python3-ussuri-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 mistral-lib-2.1.0/LICENSE0000664000175000017500000002363713642133776015007 0ustar zuulzuul00000000000000 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. mistral-lib-2.1.0/setup.py0000664000175000017500000000137613642133776015510 0ustar zuulzuul00000000000000# 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) mistral-lib-2.1.0/releasenotes/0000775000175000017500000000000013642134047016450 5ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/0000775000175000017500000000000013642134047017750 5ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/_static/0000775000175000017500000000000013642134047021376 5ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000013642133776023657 0ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/unreleased.rst0000664000175000017500000000016013642133776022636 0ustar zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: mistral-lib-2.1.0/releasenotes/source/pike.rst0000664000175000017500000000021713642133776021442 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike mistral-lib-2.1.0/releasenotes/source/queens.rst0000664000175000017500000000022313642133776022007 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens mistral-lib-2.1.0/releasenotes/source/stein.rst0000664000175000017500000000022113642133776021627 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein mistral-lib-2.1.0/releasenotes/source/index.rst0000664000175000017500000000031713642133776021622 0ustar zuulzuul00000000000000============================================ mistral-lib Release Notes ============================================ .. toctree:: :maxdepth: 1 unreleased train stein rocky queens pike mistral-lib-2.1.0/releasenotes/source/rocky.rst0000664000175000017500000000022113642133776021634 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky mistral-lib-2.1.0/releasenotes/source/_templates/0000775000175000017500000000000013642134047022105 5ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013642133776024366 0ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/source/train.rst0000664000175000017500000000017613642133776021633 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train mistral-lib-2.1.0/releasenotes/source/conf.py0000664000175000017500000002166613642133776021272 0ustar zuulzuul00000000000000# 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 = 'sphinx' # 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 not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # Must set this variable to include year, month, day, hours, and minutes. html_last_updated_fmt = '%Y-%m-%d %H:%M' # 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 ------------------------------------------- repository_name = 'openstack/mistral-lib' bug_project = 'mistral-lib' bug_tag = '' mistral-lib-2.1.0/releasenotes/notes/0000775000175000017500000000000013642134047017600 5ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/notes/.placeholder0000664000175000017500000000000013642133776022061 0ustar zuulzuul00000000000000mistral-lib-2.1.0/releasenotes/notes/rename-task-id-to-task-execution-id-f17d671fcef0127a.yaml0000664000175000017500000000020513642133776031541 0ustar zuulzuul00000000000000--- deprecations: - | The attribute `context.execution.task_id` has been renamed to `context.execution.task_execution_id`. mistral-lib-2.1.0/releasenotes/notes/mask-password-6899d868d213f722.yaml0000664000175000017500000000021713642133776025311 0ustar zuulzuul00000000000000--- security: - Ensure we mask sensitive data before logging Action return values fixes: - https://bugs.launchpad.net/tripleo/+bug/1850843 mistral-lib-2.1.0/mistral_lib/0000775000175000017500000000000013642134047016260 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/tests/0000775000175000017500000000000013642134047017422 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/tests/actions/0000775000175000017500000000000013642134047021062 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/tests/actions/test_base.py0000664000175000017500000000200013642133776023405 0ustar zuulzuul00000000000000# 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 mistral-lib-2.1.0/mistral_lib/tests/actions/test_context.py0000664000175000017500000000624113642133776024172 0ustar zuulzuul00000000000000# 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') ctx = context.ActionContext(security_ctx, execution_ctx) return 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) mistral-lib-2.1.0/mistral_lib/tests/actions/__init__.py0000664000175000017500000000000013642133776023171 0ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/tests/test_inspect_utils.py0000664000175000017500000000521713642133776023735 0ustar zuulzuul00000000000000# 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}) mistral-lib-2.1.0/mistral_lib/tests/base.py0000664000175000017500000000140113642133776020712 0ustar zuulzuul00000000000000# 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.""" mistral-lib-2.1.0/mistral_lib/tests/test_utils.py0000664000175000017500000002346413642133776022214 0ustar zuulzuul00000000000000# 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) mistral-lib-2.1.0/mistral_lib/tests/test_serialization.py0000664000175000017500000000667613642133776023737 0ustar zuulzuul00000000000000# 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() ) mistral-lib-2.1.0/mistral_lib/tests/__init__.py0000664000175000017500000000000013642133776021531 0ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/actions/0000775000175000017500000000000013642134047017720 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/actions/base.py0000664000175000017500000000667013642133776021225 0ustar zuulzuul00000000000000# 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 class Action(object): """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.workflow.utils.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 mistral-lib-2.1.0/mistral_lib/actions/types.py0000664000175000017500000000502413642133776021447 0ustar zuulzuul00000000000000# 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): """Explicit data structure containing a result of task execution.""" 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 {'result': 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()) mistral-lib-2.1.0/mistral_lib/actions/context.py0000664000175000017500000000767613642133776022006 0ustar zuulzuul00000000000000# 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()) mistral-lib-2.1.0/mistral_lib/actions/__init__.py0000664000175000017500000000132313642133776022040 0ustar zuulzuul00000000000000# 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.types import Result __all__ = ['Action', 'Result'] mistral-lib-2.1.0/mistral_lib/yaql/0000775000175000017500000000000013642134047017226 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/yaql/api/0000775000175000017500000000000013642134047017777 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/yaql/api/__init__.py0000664000175000017500000000000013642133776022106 0ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/yaql/__init__.py0000664000175000017500000000000013642133776021335 0ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/utils/0000775000175000017500000000000013642134047017420 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib/utils/inspect_utils.py0000664000175000017500000000444713642133776022700 0ustar zuulzuul00000000000000# 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) mistral-lib-2.1.0/mistral_lib/utils/__init__.py0000664000175000017500000003476313642133776021556 0ustar zuulzuul00000000000000# 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() # TODO(rakhmerov): these two constants are misplaced. Utility methods # should not be Mistral specific. They should be generic enough so to # be moved to any other project w/o changes. ACTION_TASK_TYPE = 'ACTION' WORKFLOW_TASK_TYPE = 'WORKFLOW' 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) mistral-lib-2.1.0/mistral_lib/serialization.py0000664000175000017500000001424313642133776021523 0ustar zuulzuul00000000000000# 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() mistral-lib-2.1.0/mistral_lib/exceptions.py0000664000175000017500000000531613642133776021030 0ustar zuulzuul00000000000000# 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" mistral-lib-2.1.0/mistral_lib/__init__.py0000664000175000017500000000123313642133776020400 0ustar zuulzuul00000000000000# -*- 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() mistral-lib-2.1.0/setup.cfg0000664000175000017500000000206713642134047015605 0ustar zuulzuul00000000000000[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 [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 mistral-lib-2.1.0/AUTHORS0000664000175000017500000000231713642134047015032 0ustar zuulzuul0000000000000098k <18552437190@163.com> Adriano Petrich Andreas Jaeger Andreas Jaeger Arundhati Surpur Brad P. Crochet Corey Bryant Cédric Jeanneret Doug Hellmann Dougal Matthews Eyal Flavio Percoco Ghanshyam Mann Ifat Afek Monty Taylor Nguyen Hai OpenStack Release Bot Renat Akhmerov Ryan Brady Sharat Sharma Vieri <15050873171@163.com> ZhijunWei ali caoyuan guotao huang.zhiping jacky06 nizam qingszhao ricolin sunjia sunqingliang6 zhangyangyang zhulingjie mistral-lib-2.1.0/lower-constraints.txt0000664000175000017500000000174213642133776020231 0ustar zuulzuul00000000000000alabaster==0.7.10 appdirs==1.3.0 Babel==2.3.4 coverage==4.0 debtcollector==1.2.0 docutils==0.11 doc8==0.6.0 dulwich==0.15.0 eventlet==0.20.0 extras==1.0.0 fixtures==3.0.0 flake8==2.5.5 hacking==0.12.0 imagesize==0.7.1 iso8601==0.1.11 Jinja2==2.10 keystoneauth1==3.4.0 linecache2==1.0.0 MarkupSafe==1.0 mccabe==0.2.1 mock==2.0.0 monotonic==0.6 mox3==0.20.0 msgpack-python==0.4.0 netaddr==0.7.18 netifaces==0.10.4 openstackdocstheme==1.18.1 os-client-config==1.28.0 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.serialization==2.21.1 oslo.utils==3.33.0 oslotest==3.2.0 pbr==2.0.0 pep8==1.5.7 pyflakes==0.8.1 Pygments==2.2.0 pyparsing==2.1.0 python-mimeparse==1.6.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.12 reno==2.5.0 requests==2.18.0 requestsexceptions==1.2.0 six==1.10.0 snowballstemmer==1.2.1 Sphinx==1.8.0 sphinxcontrib-websupport==1.0.1 stestr==2.0.0 stevedore==1.20.0 testrepository==0.0.18 testtools==2.2.0 traceback2==1.4.0 unittest2==1.1.0 wrapt==1.7.0 yaql==1.1.3 # Apache 2.0 License mistral-lib-2.1.0/requirements.txt0000664000175000017500000000057213642133776017257 0ustar zuulzuul00000000000000# 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 mistral-lib-2.1.0/CONTRIBUTING.rst0000664000175000017500000000121513642133776016427 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://docs.openstack.org/infra/manual/developers.html If you already have a good understanding of how the system works and your OpenStack accounts are set up, you can skip to the development workflow section of this documentation to learn how changes to OpenStack should be submitted for review via the Gerrit tool: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/mistral mistral-lib-2.1.0/mistral_lib.egg-info/0000775000175000017500000000000013642134047017752 5ustar zuulzuul00000000000000mistral-lib-2.1.0/mistral_lib.egg-info/SOURCES.txt0000664000175000017500000000331613642134047021641 0ustar zuulzuul00000000000000.coveragerc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg lower-constraints.txt 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/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_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/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/_static/.placeholder releasenotes/source/_templates/.placeholdermistral-lib-2.1.0/mistral_lib.egg-info/requires.txt0000664000175000017500000000014513642134047022352 0ustar zuulzuul00000000000000eventlet!=0.20.1,>=0.20.0 oslo.log>=3.36.0 pbr!=2.1.0,>=2.0.0 oslo.serialization>=2.21.1 yaql>=1.1.3 mistral-lib-2.1.0/mistral_lib.egg-info/top_level.txt0000664000175000017500000000001413642134047022477 0ustar zuulzuul00000000000000mistral_lib mistral-lib-2.1.0/mistral_lib.egg-info/dependency_links.txt0000664000175000017500000000000113642134047024020 0ustar zuulzuul00000000000000 mistral-lib-2.1.0/mistral_lib.egg-info/pbr.json0000664000175000017500000000005613642134047021431 0ustar zuulzuul00000000000000{"git_version": "44a2738", "is_release": true}mistral-lib-2.1.0/mistral_lib.egg-info/PKG-INFO0000664000175000017500000000363613642134047021057 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: mistral-lib Version: 2.1.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 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 Requires-Python: >=3.6 mistral-lib-2.1.0/mistral_lib.egg-info/not-zip-safe0000664000175000017500000000000113642134047022200 0ustar zuulzuul00000000000000 mistral-lib-2.1.0/tox.ini0000664000175000017500000000345113642133776015305 0ustar zuulzuul00000000000000[tox] minversion = 3.1.1 envlist = py37,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 [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt mistral-lib-2.1.0/test-requirements.txt0000664000175000017500000000062713642133776020235 0ustar zuulzuul00000000000000# 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>=1.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 mistral-lib-2.1.0/doc/0000775000175000017500000000000013642134047014524 5ustar zuulzuul00000000000000mistral-lib-2.1.0/doc/source/0000775000175000017500000000000013642134047016024 5ustar zuulzuul00000000000000mistral-lib-2.1.0/doc/source/install/0000775000175000017500000000000013642134047017472 5ustar zuulzuul00000000000000mistral-lib-2.1.0/doc/source/install/index.rst0000664000175000017500000000031213642133776021337 0ustar zuulzuul00000000000000============ Installation ============ At the command line:: $ pip install mistral-lib Or, if you have virtualenvwrapper installed:: $ mkvirtualenv mistral-lib $ pip install mistral-lib mistral-lib-2.1.0/doc/source/contributor/0000775000175000017500000000000013642134047020376 5ustar zuulzuul00000000000000mistral-lib-2.1.0/doc/source/contributor/creating_custom_actions.rst0000664000175000017500000000277413642133776026060 0ustar zuulzuul00000000000000============================ 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 mistral-lib-2.1.0/doc/source/contributor/index.rst0000664000175000017500000000017413642133776022251 0ustar zuulzuul00000000000000===================== Developer's Reference ===================== .. toctree:: :maxdepth: 3 creating_custom_actions mistral-lib-2.1.0/doc/source/user/0000775000175000017500000000000013642134047017002 5ustar zuulzuul00000000000000mistral-lib-2.1.0/doc/source/user/index.rst0000664000175000017500000000011513642133776020650 0ustar zuulzuul00000000000000===== Usage ===== To use mistral-lib in a project:: import mistral_lib mistral-lib-2.1.0/doc/source/index.rst0000664000175000017500000000130413642133776017673 0ustar zuulzuul00000000000000Welcome 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` mistral-lib-2.1.0/doc/source/conf.py0000775000175000017500000000535213642133776017343 0ustar zuulzuul00000000000000# 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 = 'sphinx' # -- 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} # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # Must set this variable to include year, month, day, hours, and minutes. html_last_updated_fmt = '%Y-%m-%d %H:%M' # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/mistral-lib' bug_project = 'mistral' bug_tag = '' mistral-lib-2.1.0/doc/requirements.txt0000664000175000017500000000025713642133776020024 0ustar zuulzuul00000000000000sphinx>=1.8.0,!=2.1.0 # BSD sphinxcontrib-httpdomain>=1.3.0 # BSD sphinxcontrib-pecanwsme>=0.8.0 # Apache-2.0 openstackdocstheme>=1.30.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 mistral-lib-2.1.0/ChangeLog0000664000175000017500000001076513642134047015542 0ustar zuulzuul00000000000000CHANGES ======= 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 mistral-lib-2.1.0/PKG-INFO0000664000175000017500000000363613642134047015064 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: mistral-lib Version: 2.1.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 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 Requires-Python: >=3.6 mistral-lib-2.1.0/.coveragerc0000664000175000017500000000011113642133776016101 0ustar zuulzuul00000000000000[run] branch = True source = mistral_lib [report] ignore_errors = True mistral-lib-2.1.0/README.rst0000664000175000017500000000160713642133776015462 0ustar zuulzuul00000000000000======================== 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 Features -------- * TODO mistral-lib-2.1.0/babel.cfg0000664000175000017500000000002113642133776015506 0ustar zuulzuul00000000000000[python: **.py] mistral-lib-2.1.0/HACKING.rst0000664000175000017500000000022013642133776015557 0ustar zuulzuul00000000000000mistral-lib Style Commandments ============================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ mistral-lib-2.1.0/.stestr.conf0000664000175000017500000000006513642133776016241 0ustar zuulzuul00000000000000[DEFAULT] test_path=./mistral_lib/tests top_path=./