Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I59dd6c58e15d7d0b5b12bbc892aadfdc662305c3
This commit is contained in:
parent
6696c55574
commit
2c4701236a
|
@ -1,53 +0,0 @@
|
|||
*.py[cod]
|
||||
*.sqlite
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg*
|
||||
dist
|
||||
build
|
||||
.venv
|
||||
.eggs
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
cover/*
|
||||
.tox
|
||||
nosetests.xml
|
||||
functional_creds.conf
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
.idea
|
||||
.DS_Store
|
||||
etc/mistral.conf
|
||||
|
||||
#swap file
|
||||
*.swp
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
# Files created by doc build
|
||||
doc/source/api
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/python-mistralclient.git
|
|
@ -1,16 +0,0 @@
|
|||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps documented at:
|
||||
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
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/python-mistralclient
|
175
LICENSE
175
LICENSE
|
@ -1,175 +0,0 @@
|
|||
|
||||
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.
|
|
@ -0,0 +1,14 @@
|
|||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
100
README.rst
100
README.rst
|
@ -1,100 +0,0 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: https://governance.openstack.org/badges/python-mistralclient.svg
|
||||
:target: https://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
Mistral
|
||||
=======
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/python-mistralclient.svg
|
||||
:target: https://pypi.python.org/pypi/python-mistralclient/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/dm/python-mistralclient.svg
|
||||
:target: https://pypi.python.org/pypi/python-mistralclient/
|
||||
:alt: Downloads
|
||||
|
||||
Mistral is a workflow service. Most business processes consist of multiple
|
||||
distinct interconnected steps that need to be executed in a particular
|
||||
order in a distributed environment. A user can describe such a process as a set
|
||||
of tasks and their transitions. After that, it is possible to upload such a
|
||||
description to Mistral, which will take care of state management, correct
|
||||
execution order, parallelism, synchronization and high availability.
|
||||
|
||||
Mistral also provides flexible task scheduling so that it can run a process
|
||||
according to a specified schedule (for example, every Sunday at 4.00pm) instead
|
||||
of running it immediately. In Mistral terminology such a set of tasks and
|
||||
relations between them is called a workflow.
|
||||
|
||||
Mistral client
|
||||
==============
|
||||
|
||||
Python client for Mistral REST API. Includes python library for Mistral API and
|
||||
Command Line Interface (CLI) library.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
First of all, clone the repo and go to the repo directory:
|
||||
|
||||
$ git clone git://git.openstack.org/openstack/python-mistralclient.git
|
||||
$ cd python-mistralclient
|
||||
|
||||
Then just run:
|
||||
|
||||
$ pip install -e .
|
||||
|
||||
or
|
||||
|
||||
$ pip install -r requirements.txt
|
||||
$ python setup.py install
|
||||
|
||||
|
||||
Running Mistral client
|
||||
----------------------
|
||||
|
||||
If Mistral authentication is enabled, provide the information about OpenStack
|
||||
auth to environment variables. Type:
|
||||
|
||||
$ export OS_AUTH_URL=http://<Keystone_host>:5000/v2.0
|
||||
$ export OS_USERNAME=admin
|
||||
$ export OS_TENANT_NAME=tenant
|
||||
$ export OS_PASSWORD=secret
|
||||
$ export OS_MISTRAL_URL=http://<Mistral host>:8989/v2 (optional, by
|
||||
default URL=http://localhost:8989/v2)
|
||||
|
||||
and in the case that you are authenticating against keystone over https:
|
||||
|
||||
$ export OS_CACERT=<path_to_ca_cert>
|
||||
|
||||
.. note:: In client, we can use both Keystone auth versions - v2.0 and v3. But
|
||||
server supports only v3.*
|
||||
|
||||
To make sure Mistral client works, type:
|
||||
|
||||
$ mistral workbook-list
|
||||
|
||||
You can see the list of available commands typing:
|
||||
|
||||
$ mistral --help
|
||||
|
||||
Useful Links
|
||||
============
|
||||
|
||||
* `PyPi`_ - package installation
|
||||
* `Launchpad project`_ - release management
|
||||
* `Blueprints`_ - feature specifications
|
||||
* `Bugs`_ - issue tracking
|
||||
* `Source`_
|
||||
* `Specs`_
|
||||
* `How to Contribute`_
|
||||
|
||||
.. _PyPi: https://pypi.python.org/pypi/python-mistralclient
|
||||
.. _Launchpad project: https://launchpad.net/python-mistralclient
|
||||
.. _Blueprints: https://blueprints.launchpad.net/python-mistralclient
|
||||
.. _Bugs: https://bugs.launchpad.net/python-mistralclient
|
||||
.. _Source: https://git.openstack.org/cgit/openstack/python-mistralclient
|
||||
.. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html
|
||||
.. _Specs: https://specs.openstack.org/openstack/mistral-specs/
|
|
@ -1,7 +0,0 @@
|
|||
Python Mistral bindings class refrence
|
||||
======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
api/autoindex
|
|
@ -1,2 +0,0 @@
|
|||
Using Mistral with KeyCloak Server
|
||||
==================================
|
|
@ -1,36 +0,0 @@
|
|||
Using Mistral with OpenStack
|
||||
============================
|
||||
|
||||
The **mistral** shell utility interacts with OpenStack Mistral API from the
|
||||
command-line. It supports the features in the OpenStack Mistral API.
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
In order to use the CLI, you must provide your OpenStack credentials
|
||||
(for both user and project), and auth endpoint. Use the corresponding
|
||||
configuration options (``--os-username``, ``--os-password``,
|
||||
``--os-project-name``, ``--os-user-domain-id``, ``os-project-domain-id``, and
|
||||
``--os-auth-url``), but it is easier to set them in environment variables.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ export OS_AUTH_URL=http://<Keystone_host>:5000/v2.0
|
||||
$ export OS_USERNAME=admin
|
||||
$ export OS_TENANT_NAME=tenant
|
||||
$ export OS_PASSWORD=secret
|
||||
$ export OS_MISTRAL_URL=http://<Mistral host>:8989/v2
|
||||
|
||||
When authenticating against keystone over https:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ export OS_CACERT=<path_to_ca_cert>
|
||||
|
||||
Once you've configured your authentication parameters, you can run **mistral**
|
||||
commands. All commands take the form of::
|
||||
|
||||
mistral <command> [arguments...]
|
||||
|
||||
Run **mistral --help** to get a full list of all possible commands, and run
|
||||
**mistral help <command>** to get detailed help for that command.
|
|
@ -1,42 +0,0 @@
|
|||
Using Mistral without Authentication
|
||||
====================================
|
||||
|
||||
It is possible to execute a workflow on any arbitrary cloud without additional
|
||||
configuration on the Mistral server side. If authentication is turned off in
|
||||
the Mistral server (Pecan's `auth_enable = False` option in `mistral.conf`),
|
||||
there is no need to set the `keystone_authtoken` section. It is possible to
|
||||
have Mistral use an external OpenStack cloud even when it isn't deployed in
|
||||
an OpenStack environment (i.e. no Keystone integration).
|
||||
|
||||
This setup is particularly useful when Mistral is used in standalone mode,
|
||||
where the Mistral service is not part of the OpenStack cloud and runs
|
||||
separately.
|
||||
|
||||
To enable this operation, the user can use ``--os-target-username``,
|
||||
``--os-target-password``, ``--os-target-tenant-id``,
|
||||
``--os-target-tenant-name``, ``--os-target-auth-token``,
|
||||
``--os-target-auth-url``, ``--os-target_cacert``, and
|
||||
``--os-target-region-name`` parameters.
|
||||
|
||||
For example, the user can return the heat stack list with this setup as shown
|
||||
below:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ mistral \
|
||||
--os-target-auth-url=http://keystone2.example.com:5000/v3 \
|
||||
--os-target-username=testuser \
|
||||
--os-target-tenant=testtenant \
|
||||
--os-target-password="MistralRuleZ" \
|
||||
--os-mistral-url=http://mistral.example.com:8989/v2 \
|
||||
run-action heat.stacks_list
|
||||
|
||||
The OS-TARGET-* parameters can be set in environment variables as:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ export OS_TARGET_AUTH_URL=http://keystone2.example.com:5000/v3
|
||||
$ export OS_TARGET_USERNAME=admin
|
||||
$ export OS_TARGET_TENANT_NAME=tenant
|
||||
$ export OS_TARGET_PASSWORD=secret
|
||||
$ export OS_TARGET_REGION_NAME=region
|
|
@ -1,111 +0,0 @@
|
|||
# Mistral documentation build configuration file
|
||||
|
||||
import os
|
||||
import pbr.version
|
||||
import sys
|
||||
|
||||
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
|
||||
|
||||
# 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('../../'))
|
||||
sys.path.insert(0, os.path.abspath('../'))
|
||||
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',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
# templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Mistral Client'
|
||||
copyright = u'2016, Mistral Contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
version_info = pbr.version.VersionInfo('python-mistralclient')
|
||||
release = version_info.release_string()
|
||||
version = version_info.version_string()
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# 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 = ['mistralclient.']
|
||||
|
||||
|
||||
# -- 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'
|
||||
|
||||
# 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'
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = 'MistralClient'
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
'index': [
|
||||
'sidebarlinks.html', 'localtoc.html', 'searchbox.html',
|
||||
'sourcelink.html'
|
||||
],
|
||||
'**': [
|
||||
'localtoc.html', 'relations.html',
|
||||
'searchbox.html', 'sourcelink.html'
|
||||
]
|
||||
}
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Mistraldoc'
|
||||
|
||||
|
||||
# -- Options for manual page output ------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'mistral_client', u'Mistral Client Documentation',
|
||||
[u'Mistral Contributors'], 1)
|
||||
]
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
repository_name = 'openstack/python-mistralclient'
|
||||
bug_project = 'python-mistralclient'
|
||||
bug_tag = ''
|
|
@ -1,40 +0,0 @@
|
|||
Python bindings to the OpenStack Workflow API
|
||||
=============================================
|
||||
|
||||
This is a client for OpenStack Mistral API. There's a Python API
|
||||
(the :mod:`mistralclient` module), and a command-line script
|
||||
(installed as :program:`mistral`).
|
||||
|
||||
Using mistralclient
|
||||
-------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
cli/cli_usage_with_openstack
|
||||
cli/cli_usage_with_keycloak
|
||||
cli/cli_usage_without_auth
|
||||
class_reference
|
||||
|
||||
For information about using the mistral command-line client, see
|
||||
`Workflow service command-line client`_.
|
||||
|
||||
.. _Workflow service command-line client: https://docs.openstack.org/mistral/latest/cli/index.html
|
||||
|
||||
Python API Reference
|
||||
--------------------
|
||||
|
||||
* `REST API Specification`_
|
||||
|
||||
.. _REST API Specification: https://docs.openstack.org/mistral/latest/api/v2.html
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Code is hosted `on GitHub`_. Submit bugs to the python-mistralclient project on
|
||||
`Launchpad`_. Submit code to the openstack/python-mistralclient project
|
||||
using `Gerrit`_.
|
||||
|
||||
.. _on GitHub: https://github.com/openstack/python-mistralclient
|
||||
.. _Launchpad: https://launchpad.net/python-mistralclient
|
||||
.. _Gerrit: https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
|
@ -1,12 +0,0 @@
|
|||
# Credentials for functional testing
|
||||
[auth]
|
||||
uri = http://10.42.0.50:5000/v2.0
|
||||
|
||||
[admin]
|
||||
user = admin
|
||||
tenant = admin
|
||||
pass = secrete
|
||||
[demo]
|
||||
user = demo
|
||||
tenant = demo
|
||||
pass = demo_secret
|
|
@ -1,26 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# 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 script is executed inside post_test_hook function in devstack gate.
|
||||
|
||||
RETVAL=0
|
||||
|
||||
sudo chmod -R a+rw /opt/stack/new/
|
||||
cd /opt/stack/new/python-mistralclient
|
||||
|
||||
echo "Running CLI python-mistralclient tests"
|
||||
./functionaltests/run_tests.sh
|
||||
RETVAL=$?
|
||||
|
||||
exit $RETVAL
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
version: "2.0"
|
||||
|
||||
greeting:
|
||||
description: "This action says 'Hello'"
|
||||
base: std.echo
|
||||
base-input:
|
||||
output: 'Hello, <% $.name %>'
|
||||
input:
|
||||
- name
|
||||
output:
|
||||
string: <% $.output %>
|
||||
|
||||
farewell:
|
||||
base: std.echo
|
||||
base-input:
|
||||
output: 'Bye!'
|
||||
output:
|
||||
info: <% $.output %>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
version: "2.0"
|
||||
|
||||
greeting:
|
||||
description: "This action says 'Hello'"
|
||||
tags: [tag, tag1]
|
||||
base: std.echo
|
||||
base-input:
|
||||
output: 'Hello, <% $.name %>'
|
||||
input:
|
||||
- name
|
||||
output:
|
||||
string: <% $.output %>
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
name: wb
|
||||
|
||||
workflows:
|
||||
wf1:
|
||||
type: direct
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
action: std.echo output="Hello"
|
||||
publish:
|
||||
result: <% task(hello).result %>
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
name: wb
|
||||
tags: [tag]
|
||||
|
||||
workflows:
|
||||
wf1:
|
||||
type: direct
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
action: std.echo output="Hello"
|
||||
publish:
|
||||
result: <% task(hello).result %>
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
wf:
|
||||
type: direct
|
||||
tags: [tag]
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
action: std.echo output="Hello"
|
||||
publish:
|
||||
result: <% task(hello).result %>
|
||||
wait-after: 1
|
||||
on-success:
|
||||
- task2
|
||||
|
||||
task2:
|
||||
action: std.echo output="Task 2"
|
||||
publish:
|
||||
task2: <% task(task2).result %>
|
|
@ -1,11 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
wf_single:
|
||||
type: direct
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
action: std.echo output="Hello"
|
||||
publish:
|
||||
result: <% task(hello).result %>
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
wf:
|
||||
type: direct
|
||||
|
||||
tasks:
|
||||
hello:
|
||||
action: std.echo output="Hello"
|
||||
wait-before: 5
|
||||
publish:
|
||||
result: <% task(hello).result %>
|
||||
|
||||
wf1:
|
||||
type: reverse
|
||||
tags: [tag]
|
||||
input:
|
||||
- farewell
|
||||
|
||||
tasks:
|
||||
addressee:
|
||||
action: std.echo output="John"
|
||||
publish:
|
||||
name: <% task(addressee).result %>
|
||||
|
||||
goodbye:
|
||||
action: std.echo output="<% $.farewell %>, <% $.name %>"
|
||||
requires: [addressee]
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
version: '2.0'
|
||||
|
||||
wrapping_wf:
|
||||
type: direct
|
||||
tasks:
|
||||
hello:
|
||||
workflow: wf
|
|
@ -1,58 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# 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.
|
||||
|
||||
# How many seconds to wait for the API to be responding before giving up
|
||||
API_RESPONDING_TIMEOUT=20
|
||||
|
||||
if ! timeout ${API_RESPONDING_TIMEOUT} sh -c "until curl --output /dev/null --silent --head --fail http://localhost:8989; do sleep 1; done"; then
|
||||
echo "Mistral API failed to respond within ${API_RESPONDING_TIMEOUT} seconds"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Successfully contacted Mistral API"
|
||||
|
||||
export BASE=/opt/stack
|
||||
export MISTRALCLIENT_DIR="$BASE/new/python-mistralclient"
|
||||
|
||||
# Get demo credentials.
|
||||
cd ${BASE}/new/devstack
|
||||
source openrc alt_demo alt_demo
|
||||
|
||||
export OS_ALT_USERNAME=${OS_USERNAME}
|
||||
export OS_ALT_TENANT_NAME=${OS_TENANT_NAME}
|
||||
export OS_ALT_PASSWORD=${OS_PASSWORD}
|
||||
|
||||
# Get admin credentials.
|
||||
source openrc admin admin
|
||||
|
||||
# Store these credentials into the config file.
|
||||
CREDS_FILE=${MISTRALCLIENT_DIR}/functional_creds.conf
|
||||
cat <<EOF > ${CREDS_FILE}
|
||||
# Credentials for functional testing
|
||||
[auth]
|
||||
uri = $OS_AUTH_URL
|
||||
[admin]
|
||||
user = $OS_USERNAME
|
||||
tenant = $OS_TENANT_NAME
|
||||
pass = $OS_PASSWORD
|
||||
[demo]
|
||||
user = $OS_ALT_USERNAME
|
||||
tenant = $OS_ALT_TENANT_NAME
|
||||
pass = $OS_ALT_PASSWORD
|
||||
EOF
|
||||
|
||||
cd $MISTRALCLIENT_DIR
|
||||
|
||||
# Run tests
|
||||
tox -efunctional -- nosetests -sv mistralclient/tests/functional
|
|
@ -1,19 +0,0 @@
|
|||
# Copyright 2014 Rackspace Hosting
|
||||
#
|
||||
# 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(
|
||||
'python-mistralclient').version_string()
|
|
@ -1,164 +0,0 @@
|
|||
# Copyright 2013 - 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 copy
|
||||
import json
|
||||
|
||||
|
||||
class Resource(object):
|
||||
resource_name = 'Something'
|
||||
defaults = {}
|
||||
|
||||
def __init__(self, manager, data):
|
||||
self.manager = manager
|
||||
self._data = data
|
||||
self._set_defaults()
|
||||
self._set_attributes()
|
||||
|
||||
def _set_defaults(self):
|
||||
for k, v in self.defaults.items():
|
||||
if k not in self._data:
|
||||
self._data[k] = v
|
||||
|
||||
def _set_attributes(self):
|
||||
for k, v in self._data.items():
|
||||
try:
|
||||
setattr(self, k, v)
|
||||
except AttributeError:
|
||||
# In this case we already defined the attribute on the class
|
||||
pass
|
||||
|
||||
def to_dict(self):
|
||||
return copy.deepcopy(self._data)
|
||||
|
||||
def __str__(self):
|
||||
vals = ", ".join(["%s='%s'" % (n, v)
|
||||
for n, v in self._data.items()])
|
||||
return "%s [%s]" % (self.resource_name, vals)
|
||||
|
||||
|
||||
def _check_items(obj, searches):
|
||||
try:
|
||||
return all(getattr(obj, attr) == value for (attr, value) in searches)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
def extract_json(response, response_key):
|
||||
if response_key is not None:
|
||||
return get_json(response)[response_key]
|
||||
else:
|
||||
return get_json(response)
|
||||
|
||||
|
||||
class ResourceManager(object):
|
||||
resource_class = None
|
||||
|
||||
def __init__(self, http_client):
|
||||
self.http_client = http_client
|
||||
|
||||
def find(self, **kwargs):
|
||||
return [i for i in self.list() if _check_items(i, kwargs.items())]
|
||||
|
||||
def _ensure_not_empty(self, **kwargs):
|
||||
for name, value in kwargs.items():
|
||||
if value is None or (isinstance(value, str) and len(value) == 0):
|
||||
raise APIException(
|
||||
400,
|
||||
'%s is missing field "%s"' %
|
||||
(self.resource_class.__name__, name)
|
||||
)
|
||||
|
||||
def _copy_if_defined(self, data, **kwargs):
|
||||
for name, value in kwargs.items():
|
||||
if value is not None:
|
||||
data[name] = value
|
||||
|
||||
def _create(self, url, data, response_key=None, dump_json=True):
|
||||
if dump_json:
|
||||
data = json.dumps(data)
|
||||
|
||||
resp = self.http_client.post(url, data)
|
||||
|
||||
if resp.status_code != 201:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return self.resource_class(self, extract_json(resp, response_key))
|
||||
|
||||
def _update(self, url, data, response_key=None, dump_json=True):
|
||||
if dump_json:
|
||||
data = json.dumps(data)
|
||||
|
||||
resp = self.http_client.put(url, data)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return self.resource_class(self, extract_json(resp, response_key))
|
||||
|
||||
def _list(self, url, response_key=None):
|
||||
resp = self.http_client.get(url)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return [self.resource_class(self, resource_data)
|
||||
for resource_data in extract_json(resp, response_key)]
|
||||
|
||||
def _get(self, url, response_key=None):
|
||||
resp = self.http_client.get(url)
|
||||
|
||||
if resp.status_code == 200:
|
||||
return self.resource_class(self, extract_json(resp, response_key))
|
||||
else:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
def _delete(self, url):
|
||||
resp = self.http_client.delete(url)
|
||||
|
||||
if resp.status_code != 204:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
def _plurify_resource_name(self):
|
||||
return self.resource_class.resource_name + 's'
|
||||
|
||||
def _raise_api_exception(self, resp):
|
||||
try:
|
||||
error_data = (resp.headers.get("Server-Error-Message", None) or
|
||||
get_json(resp).get("faultstring"))
|
||||
except ValueError:
|
||||
error_data = resp.content
|
||||
raise APIException(error_code=resp.status_code,
|
||||
error_message=error_data)
|
||||
|
||||
|
||||
def get_json(response):
|
||||
"""Gets JSON representation of response.
|
||||
|
||||
This method provided backward compatibility with old versions
|
||||
of requests library.
|
||||
"""
|
||||
json_field_or_function = getattr(response, 'json', None)
|
||||
|
||||
if callable(json_field_or_function):
|
||||
return response.json()
|
||||
else:
|
||||
return json.loads(response.content)
|
||||
|
||||
|
||||
class APIException(Exception):
|
||||
def __init__(self, error_code=None, error_message=None):
|
||||
super(APIException, self).__init__(error_message)
|
||||
self.error_code = error_code
|
||||
self.error_message = error_message
|
|
@ -1,26 +0,0 @@
|
|||
# Copyright 2013 - 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.
|
||||
|
||||
from mistralclient.api.v2 import client as client_v2
|
||||
|
||||
|
||||
def client(auth_type='keystone', **kwargs):
|
||||
return client_v2.Client(auth_type=auth_type, **kwargs)
|
||||
|
||||
|
||||
def determine_client_version(mistral_version):
|
||||
if mistral_version.find("v2") != -1:
|
||||
return 2
|
||||
|
||||
raise RuntimeError("Cannot determine mistral API version")
|
|
@ -1,203 +0,0 @@
|
|||
# Copyright 2013 - Mirantis, Inc.
|
||||
# Copyright 2016 - StackStorm, 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 base64
|
||||
import copy
|
||||
import os
|
||||
|
||||
from oslo_utils import importutils
|
||||
import requests
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
AUTH_TOKEN = 'auth_token'
|
||||
CACERT = 'cacert'
|
||||
CERT_FILE = 'cert'
|
||||
CERT_KEY = 'key'
|
||||
INSECURE = 'insecure'
|
||||
PROJECT_ID = 'project_id'
|
||||
USER_ID = 'user_id'
|
||||
REGION_NAME = 'region_name'
|
||||
|
||||
TARGET_AUTH_TOKEN = 'target_auth_token'
|
||||
TARGET_AUTH_URI = 'target_auth_url'
|
||||
TARGET_PROJECT_ID = 'target_project_id'
|
||||
TARGET_USER_ID = 'target_user_id'
|
||||
TARGET_INSECURE = 'target_insecure'
|
||||
TARGET_SERVICE_CATALOG = 'target_service_catalog'
|
||||
TARGET_REGION_NAME = 'target_region_name'
|
||||
TARGET_USER_DOMAIN_NAME = 'target_user_domain_name'
|
||||
TARGET_PROJECT_DOMAIN_NAME = 'target_project_domain_name'
|
||||
|
||||
osprofiler_web = importutils.try_import("osprofiler.web")
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def log_request(func):
|
||||
def decorator(self, *args, **kwargs):
|
||||
resp = func(self, *args, **kwargs)
|
||||
LOG.debug("HTTP %s %s %d", resp.request.method, resp.url,
|
||||
resp.status_code)
|
||||
return resp
|
||||
return decorator
|
||||
|
||||
|
||||
class HTTPClient(object):
|
||||
def __init__(self, base_url, **kwargs):
|
||||
self.base_url = base_url
|
||||
self.session = kwargs.pop('session', None)
|
||||
self.auth_token = kwargs.get(AUTH_TOKEN)
|
||||
self.project_id = kwargs.get(PROJECT_ID)
|
||||
self.user_id = kwargs.get(USER_ID)
|
||||
self.cacert = kwargs.get(CACERT)
|
||||
self.insecure = kwargs.get(INSECURE, False)
|
||||
self.region_name = kwargs.get(REGION_NAME)
|
||||
self.ssl_options = {}
|
||||
|
||||
self.target_auth_token = kwargs.get(TARGET_AUTH_TOKEN)
|
||||
self.target_auth_uri = kwargs.get(TARGET_AUTH_URI)
|
||||
self.target_user_id = kwargs.get(TARGET_USER_ID)
|
||||
self.target_project_id = kwargs.get(TARGET_PROJECT_ID)
|
||||
self.target_service_catalog = kwargs.get(TARGET_SERVICE_CATALOG)
|
||||
self.target_region_name = kwargs.get(TARGET_REGION_NAME)
|
||||
self.target_insecure = kwargs.get(TARGET_INSECURE)
|
||||
self.target_user_domain_name = kwargs.get(TARGET_USER_DOMAIN_NAME)
|
||||
self.target_project_domain_name = kwargs.get(
|
||||
TARGET_PROJECT_DOMAIN_NAME
|
||||
)
|
||||
|
||||
if self.session:
|
||||
self.crud_provider = self.session
|
||||
else:
|
||||
self.crud_provider = requests
|
||||
|
||||
if self.base_url.startswith('https'):
|
||||
if self.cacert and not os.path.exists(self.cacert):
|
||||
raise ValueError('Unable to locate cacert file '
|
||||
'at %s.' % self.cacert)
|
||||
|
||||
if self.cacert and self.insecure:
|
||||
LOG.warning('Client is set to not verify even though '
|
||||
'cacert is provided.')
|
||||
|
||||
# These are already set by the session, so it's not needed
|
||||
if not self.session:
|
||||
if self.insecure:
|
||||
self.ssl_options['verify'] = False
|
||||
else:
|
||||
if self.cacert:
|
||||
self.ssl_options['verify'] = self.cacert
|
||||
else:
|
||||
self.ssl_options['verify'] = True
|
||||
|
||||
self.ssl_options['cert'] = (
|
||||
kwargs.get(CERT_FILE),
|
||||
kwargs.get(CERT_KEY)
|
||||
)
|
||||
|
||||
@log_request
|
||||
def get(self, url, headers=None):
|
||||
options = self._get_request_options('get', headers)
|
||||
|
||||
return self.crud_provider.get(self.base_url + url, **options)
|
||||
|
||||
@log_request
|
||||
def post(self, url, body, headers=None):
|
||||
options = self._get_request_options('post', headers)
|
||||
|
||||
return self.crud_provider.post(self.base_url + url,
|
||||
data=body, **options)
|
||||
|
||||
@log_request
|
||||
def put(self, url, body, headers=None):
|
||||
options = self._get_request_options('put', headers)
|
||||
|
||||
return self.crud_provider.put(self.base_url + url,
|
||||
data=body, **options)
|
||||
|
||||
@log_request
|
||||
def delete(self, url, headers=None):
|
||||
options = self._get_request_options('delete', headers)
|
||||
|
||||
return self.crud_provider.delete(self.base_url + url,
|
||||
**options)
|
||||
|
||||
def _get_request_options(self, method, headers):
|
||||
headers = self._update_headers(headers)
|
||||
|
||||
if method in ['post', 'put']:
|
||||
content_type = headers.get('content-type', 'application/json')
|
||||
headers['content-type'] = content_type
|
||||
|
||||
options = copy.deepcopy(self.ssl_options)
|
||||
options['headers'] = headers
|
||||
|
||||
return options
|
||||
|
||||
def _update_headers(self, headers):
|
||||
if not headers:
|
||||
headers = {}
|
||||
|
||||
if not self.session:
|
||||
if self.auth_token:
|
||||
headers['x-auth-token'] = self.auth_token
|
||||
|
||||
if self.project_id:
|
||||
headers['X-Project-Id'] = self.project_id
|
||||
|
||||
if self.user_id:
|
||||
headers['X-User-Id'] = self.user_id
|
||||
|
||||
if self.region_name:
|
||||
headers['X-Region-Name'] = self.region_name
|
||||
|
||||
if self.target_auth_token:
|
||||
headers['X-Target-Auth-Token'] = self.target_auth_token
|
||||
|
||||
if self.target_auth_uri:
|
||||
headers['X-Target-Auth-Uri'] = self.target_auth_uri
|
||||
|
||||
if self.target_project_id:
|
||||
headers['X-Target-Project-Id'] = self.target_project_id
|
||||
|
||||
if self.target_user_id:
|
||||
headers['X-Target-User-Id'] = self.target_user_id
|
||||
|
||||
if self.target_insecure:
|
||||
headers['X-Target-Insecure'] = self.target_insecure
|
||||
|
||||
if self.target_region_name:
|
||||
headers['X-Target-Region-Name'] = self.target_region_name
|
||||
|
||||
if self.target_user_domain_name:
|
||||
headers['X-Target-User-Domain-Name'] = self.target_user_domain_name
|
||||
|
||||
if self.target_project_domain_name:
|
||||
h_name = 'X-Target-Project-Domain-Name'
|
||||
|
||||
headers[h_name] = self.target_project_domain_name
|
||||
|
||||
if self.target_service_catalog:
|
||||
headers['X-Target-Service-Catalog'] = base64.b64encode(
|
||||
self.target_service_catalog.encode('utf-8')
|
||||
)
|
||||
|
||||
if osprofiler_web:
|
||||
# Add headers for osprofiler.
|
||||
headers.update(osprofiler_web.get_trace_id_headers())
|
||||
|
||||
return headers
|
|
@ -1,87 +0,0 @@
|
|||
# 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 json
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
|
||||
class ActionExecution(base.Resource):
|
||||
resource_name = 'ActionExecution'
|
||||
|
||||
|
||||
class ActionExecutionManager(base.ResourceManager):
|
||||
resource_class = ActionExecution
|
||||
|
||||
def create(self, name, input=None, **params):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
data = {'name': name}
|
||||
|
||||
if input:
|
||||
data['input'] = json.dumps(input)
|
||||
|
||||
if params:
|
||||
data['params'] = json.dumps(params)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/action_executions',
|
||||
json.dumps(data)
|
||||
)
|
||||
|
||||
if resp.status_code != 201:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return self.resource_class(self, base.get_json(resp))
|
||||
|
||||
def update(self, id, state=None, output=None):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
if not (state or output):
|
||||
raise base.APIException(
|
||||
400,
|
||||
"Please provide either state or output for action execution."
|
||||
)
|
||||
|
||||
data = {}
|
||||
if state:
|
||||
data['state'] = state
|
||||
|
||||
if output:
|
||||
data['output'] = output
|
||||
|
||||
return self._update('/action_executions/%s' % id, data)
|
||||
|
||||
def list(self, task_execution_id=None, limit=None):
|
||||
url = '/action_executions'
|
||||
|
||||
qparams = {}
|
||||
|
||||
if task_execution_id:
|
||||
url = '/tasks/%s/action_executions' % task_execution_id
|
||||
|
||||
if limit:
|
||||
qparams['limit'] = limit
|
||||
|
||||
return self._list(url, response_key='action_executions')
|
||||
|
||||
def get(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
return self._get('/action_executions/%s' % id)
|
||||
|
||||
def delete(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
self._delete('/action_executions/%s' % id)
|
|
@ -1,123 +0,0 @@
|
|||
# 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 six
|
||||
|
||||
from mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Action(base.Resource):
|
||||
resource_name = 'Action'
|
||||
|
||||
|
||||
class ActionManager(base.ResourceManager):
|
||||
resource_class = Action
|
||||
|
||||
def create(self, definition, scope='private'):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/actions?scope=%s' % scope,
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 201:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return [self.resource_class(self, resource_data)
|
||||
for resource_data in base.extract_json(resp, 'actions')]
|
||||
|
||||
def update(self, definition, scope='private', id=None):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
url_pre = ('/actions/%s' % id) if id else '/actions'
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.put(
|
||||
'%s?scope=%s' % (url_pre, scope),
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return [self.resource_class(self, resource_data)
|
||||
for resource_data in base.extract_json(resp, 'actions')]
|
||||
|
||||
def list(self, marker='', limit=None, sort_keys='', sort_dirs='',
|
||||
**filters):
|
||||
qparams = {}
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
|
||||
return self._list(
|
||||
'/actions%s' % query_string,
|
||||
response_key='actions',
|
||||
)
|
||||
|
||||
def get(self, identifier):
|
||||
self._ensure_not_empty(identifier=identifier)
|
||||
|
||||
return self._get('/actions/%s' % identifier)
|
||||
|
||||
def delete(self, identifier):
|
||||
self._ensure_not_empty(identifier=identifier)
|
||||
|
||||
self._delete('/actions/%s' % identifier)
|
||||
|
||||
def validate(self, definition):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/actions/validate',
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return base.extract_json(resp, None)
|
|
@ -1,82 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 copy
|
||||
import six
|
||||
|
||||
from oslo_utils import importutils
|
||||
|
||||
from mistralclient.api import httpclient
|
||||
from mistralclient.api.v2 import action_executions
|
||||
from mistralclient.api.v2 import actions
|
||||
from mistralclient.api.v2 import cron_triggers
|
||||
from mistralclient.api.v2 import environments
|
||||
from mistralclient.api.v2 import event_triggers
|
||||
from mistralclient.api.v2 import executions
|
||||
from mistralclient.api.v2 import members
|
||||
from mistralclient.api.v2 import services
|
||||
from mistralclient.api.v2 import tasks
|
||||
from mistralclient.api.v2 import workbooks
|
||||
from mistralclient.api.v2 import workflows
|
||||
from mistralclient import auth
|
||||
|
||||
|
||||
osprofiler_profiler = importutils.try_import("osprofiler.profiler")
|
||||
|
||||
_DEFAULT_MISTRAL_URL = "http://localhost:8989/v2"
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, auth_type='keystone', **kwargs):
|
||||
# We get the session at this point, as some instances of session
|
||||
# objects might have mutexes that can't be deep-copied.
|
||||
session = kwargs.pop('session', None)
|
||||
req = copy.deepcopy(kwargs)
|
||||
mistral_url = req.get('mistral_url')
|
||||
profile = req.get('profile')
|
||||
|
||||
if mistral_url and not isinstance(mistral_url, six.string_types):
|
||||
raise RuntimeError('Mistral url should be a string.')
|
||||
|
||||
auth_handler = auth.get_auth_handler(auth_type)
|
||||
auth_response = auth_handler.authenticate(req, session=session) or {}
|
||||
|
||||
req.update(auth_response)
|
||||
|
||||
mistral_url = auth_response.get('mistral_url') or mistral_url
|
||||
|
||||
if not mistral_url:
|
||||
mistral_url = _DEFAULT_MISTRAL_URL
|
||||
|
||||
if profile:
|
||||
osprofiler_profiler.init(profile)
|
||||
|
||||
http_client = httpclient.HTTPClient(mistral_url, session=session,
|
||||
**req)
|
||||
|
||||
# Create all resource managers.
|
||||
self.workbooks = workbooks.WorkbookManager(http_client)
|
||||
self.executions = executions.ExecutionManager(http_client)
|
||||
self.tasks = tasks.TaskManager(http_client)
|
||||
self.actions = actions.ActionManager(http_client)
|
||||
self.workflows = workflows.WorkflowManager(http_client)
|
||||
self.cron_triggers = cron_triggers.CronTriggerManager(http_client)
|
||||
self.event_triggers = event_triggers.EventTriggerManager(http_client)
|
||||
self.environments = environments.EnvironmentManager(http_client)
|
||||
self.action_executions = action_executions.ActionExecutionManager(
|
||||
http_client)
|
||||
self.services = services.ServiceManager(http_client)
|
||||
self.members = members.MemberManager(http_client)
|
|
@ -1,68 +0,0 @@
|
|||
# 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 json
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
|
||||
class CronTrigger(base.Resource):
|
||||
resource_name = 'CronTrigger'
|
||||
|
||||
|
||||
class CronTriggerManager(base.ResourceManager):
|
||||
resource_class = CronTrigger
|
||||
|
||||
def create(self, name, workflow_identifier, workflow_input=None,
|
||||
workflow_params=None, pattern=None,
|
||||
first_time=None, count=None):
|
||||
self._ensure_not_empty(
|
||||
name=name,
|
||||
workflow_identifier=workflow_identifier
|
||||
)
|
||||
|
||||
data = {
|
||||
'name': name,
|
||||
'pattern': pattern,
|
||||
'first_execution_time': first_time,
|
||||
'remaining_executions': count
|
||||
}
|
||||
|
||||
if uuidutils.is_uuid_like(workflow_identifier):
|
||||
data.update({'workflow_id': workflow_identifier})
|
||||
else:
|
||||
data.update({'workflow_name': workflow_identifier})
|
||||
|
||||
if workflow_input:
|
||||
data.update({'workflow_input': json.dumps(workflow_input)})
|
||||
|
||||
if workflow_params:
|
||||
data.update({'workflow_params': json.dumps(workflow_params)})
|
||||
|
||||
return self._create('/cron_triggers', data)
|
||||
|
||||
def list(self):
|
||||
return self._list('/cron_triggers', response_key='cron_triggers')
|
||||
|
||||
def get(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
return self._get('/cron_triggers/%s' % name)
|
||||
|
||||
def delete(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
self._delete('/cron_triggers/%s' % name)
|
|
@ -1,85 +0,0 @@
|
|||
# Copyright 2015 - StackStorm, 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 json
|
||||
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
class Environment(base.Resource):
|
||||
resource_name = 'Environment'
|
||||
|
||||
def _set_attributes(self):
|
||||
"""Override loading of the "variables" attribute from text to dict."""
|
||||
for k, v in self._data.items():
|
||||
if k == 'variables' and isinstance(v, six.string_types):
|
||||
v = json.loads(v)
|
||||
|
||||
try:
|
||||
setattr(self, k, v)
|
||||
except AttributeError:
|
||||
# In this case we already defined the attribute on the class
|
||||
pass
|
||||
|
||||
|
||||
class EnvironmentManager(base.ResourceManager):
|
||||
resource_class = Environment
|
||||
|
||||
def create(self, **kwargs):
|
||||
# Check to see if the file name or URI is being passed in. If so,
|
||||
# read it's contents first.
|
||||
if 'file' in kwargs:
|
||||
file = kwargs['file']
|
||||
kwargs = utils.load_content(utils.get_contents_if_file(file))
|
||||
|
||||
self._ensure_not_empty(name=kwargs.get('name', None),
|
||||
variables=kwargs.get('variables', None))
|
||||
|
||||
# Convert dict to text for the variables attribute.
|
||||
if isinstance(kwargs['variables'], dict):
|
||||
kwargs['variables'] = json.dumps(kwargs['variables'])
|
||||
|
||||
return self._create('/environments', kwargs)
|
||||
|
||||
def update(self, **kwargs):
|
||||
# Check to see if the file name or URI is being passed in. If so,
|
||||
# read it's contents first.
|
||||
if 'file' in kwargs:
|
||||
file = kwargs['file']
|
||||
kwargs = utils.load_content(utils.get_contents_if_file(file))
|
||||
|
||||
name = kwargs.get('name', None)
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
# Convert dict to text for the variables attribute.
|
||||
if kwargs.get('variables') and isinstance(kwargs['variables'], dict):
|
||||
kwargs['variables'] = json.dumps(kwargs['variables'])
|
||||
|
||||
return self._update('/environments', kwargs)
|
||||
|
||||
def list(self):
|
||||
return self._list('/environments', response_key='environments')
|
||||
|
||||
def get(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
return self._get('/environments/%s' % name)
|
||||
|
||||
def delete(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
self._delete('/environments/%s' % name)
|
|
@ -1,61 +0,0 @@
|
|||
# Copyright 2017, OpenStack Foundation
|
||||
#
|
||||
# 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 json
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
|
||||
class EventTrigger(base.Resource):
|
||||
resource_name = 'EventTrigger'
|
||||
|
||||
|
||||
class EventTriggerManager(base.ResourceManager):
|
||||
resource_class = EventTrigger
|
||||
|
||||
def create(self, name, workflow_id, exchange, topic, event,
|
||||
workflow_input=None, workflow_params=None):
|
||||
self._ensure_not_empty(
|
||||
name=name,
|
||||
workflow_id=workflow_id
|
||||
)
|
||||
|
||||
data = {
|
||||
'workflow_id': workflow_id,
|
||||
'name': name,
|
||||
'exchange': exchange,
|
||||
'topic': topic,
|
||||
'event': event
|
||||
}
|
||||
|
||||
if workflow_input:
|
||||
data.update({'workflow_input': json.dumps(workflow_input)})
|
||||
|
||||
if workflow_params:
|
||||
data.update({'workflow_params': json.dumps(workflow_params)})
|
||||
|
||||
return self._create('/event_triggers', data)
|
||||
|
||||
def list(self):
|
||||
return self._list('/event_triggers', response_key='event_triggers')
|
||||
|
||||
def get(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
return self._get('/event_triggers/%s' % id)
|
||||
|
||||
def delete(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
self._delete('/event_triggers/%s' % id)
|
|
@ -1,110 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 json
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Execution(base.Resource):
|
||||
resource_name = 'Execution'
|
||||
|
||||
|
||||
class ExecutionManager(base.ResourceManager):
|
||||
resource_class = Execution
|
||||
|
||||
def create(self, workflow_identifier, workflow_input=None, description='',
|
||||
**params):
|
||||
self._ensure_not_empty(workflow_identifier=workflow_identifier)
|
||||
|
||||
data = {
|
||||
'description': description
|
||||
}
|
||||
|
||||
if uuidutils.is_uuid_like(workflow_identifier):
|
||||
data.update({'workflow_id': workflow_identifier})
|
||||
else:
|
||||
data.update({'workflow_name': workflow_identifier})
|
||||
|
||||
if workflow_input:
|
||||
if isinstance(workflow_input, six.string_types):
|
||||
data.update({'input': workflow_input})
|
||||
else:
|
||||
data.update({'input': json.dumps(workflow_input)})
|
||||
|
||||
if params:
|
||||
data.update({'params': json.dumps(params)})
|
||||
|
||||
return self._create('/executions', data)
|
||||
|
||||
def update(self, id, state, description=None, env=None):
|
||||
data = {}
|
||||
|
||||
if state:
|
||||
data['state'] = state
|
||||
|
||||
if description:
|
||||
data['description'] = description
|
||||
|
||||
if env:
|
||||
data['params'] = {'env': env}
|
||||
|
||||
return self._update('/executions/%s' % id, data)
|
||||
|
||||
def list(self, task=None, marker='', limit=None, sort_keys='',
|
||||
sort_dirs='', **filters):
|
||||
qparams = {}
|
||||
|
||||
if task:
|
||||
qparams['task_execution_id'] = task
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
|
||||
return self._list(
|
||||
'/executions%s' % query_string,
|
||||
response_key='executions',
|
||||
)
|
||||
|
||||
def get(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
return self._get('/executions/%s' % id)
|
||||
|
||||
def delete(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
self._delete('/executions/%s' % id)
|
|
@ -1,76 +0,0 @@
|
|||
# Copyright 2016 - Catalyst IT Limited
|
||||
#
|
||||
# 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 mistralclient.api import base
|
||||
|
||||
|
||||
class Member(base.Resource):
|
||||
resource_name = 'Member'
|
||||
|
||||
|
||||
class MemberManager(base.ResourceManager):
|
||||
resource_class = Member
|
||||
|
||||
def create(self, resource_id, resource_type, member_id):
|
||||
self._ensure_not_empty(
|
||||
resource_id=resource_id,
|
||||
resource_type=resource_type,
|
||||
member_id=member_id
|
||||
)
|
||||
|
||||
data = {
|
||||
'member_id': member_id,
|
||||
}
|
||||
|
||||
url = '/%ss/%s/members' % (resource_type, resource_id)
|
||||
|
||||
return self._create(url, data)
|
||||
|
||||
def update(self, resource_id, resource_type, member_id='',
|
||||
status='accepted'):
|
||||
if not member_id:
|
||||
member_id = self.http_client.project_id
|
||||
|
||||
url = '/%ss/%s/members/%s' % (resource_type, resource_id, member_id)
|
||||
|
||||
return self._update(url, {'status': status})
|
||||
|
||||
def list(self, resource_id, resource_type):
|
||||
url = '/%ss/%s/members' % (resource_type, resource_id)
|
||||
|
||||
return self._list(url, response_key='members')
|
||||
|
||||
def get(self, resource_id, resource_type, member_id=None):
|
||||
self._ensure_not_empty(
|
||||
resource_id=resource_id,
|
||||
resource_type=resource_type,
|
||||
)
|
||||
|
||||
if not member_id:
|
||||
member_id = self.http_client.project_id
|
||||
|
||||
url = '/%ss/%s/members/%s' % (resource_type, resource_id, member_id)
|
||||
|
||||
return self._get(url)
|
||||
|
||||
def delete(self, resource_id, resource_type, member_id):
|
||||
self._ensure_not_empty(
|
||||
resource_id=resource_id,
|
||||
resource_type=resource_type,
|
||||
member_id=member_id
|
||||
)
|
||||
|
||||
url = '/%ss/%s/members/%s' % (resource_type, resource_id, member_id)
|
||||
|
||||
self._delete(url)
|
|
@ -1,26 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 mistralclient.api import base
|
||||
|
||||
|
||||
class Service(base.Resource):
|
||||
resource_name = 'Service'
|
||||
|
||||
|
||||
class ServiceManager(base.ResourceManager):
|
||||
resource_class = Service
|
||||
|
||||
def list(self):
|
||||
return self._list('/services', response_key='services')
|
|
@ -1,79 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 json
|
||||
import six
|
||||
|
||||
from mistralclient.api import base
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Task(base.Resource):
|
||||
resource_name = 'Task'
|
||||
|
||||
|
||||
class TaskManager(base.ResourceManager):
|
||||
resource_class = Task
|
||||
|
||||
def list(self, workflow_execution_id=None, marker='', limit=None,
|
||||
sort_keys='', sort_dirs='', **filters):
|
||||
url = '/tasks'
|
||||
|
||||
if workflow_execution_id:
|
||||
url = '/executions/%s/tasks' % workflow_execution_id
|
||||
|
||||
url += '%s'
|
||||
|
||||
qparams = {}
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
|
||||
return self._list(url % query_string, response_key='tasks')
|
||||
|
||||
def get(self, id):
|
||||
self._ensure_not_empty(id=id)
|
||||
|
||||
return self._get('/tasks/%s' % id)
|
||||
|
||||
def rerun(self, task_ex_id, reset=True, env=None):
|
||||
url = '/tasks/%s' % task_ex_id
|
||||
|
||||
body = {
|
||||
'id': task_ex_id,
|
||||
'state': 'RUNNING',
|
||||
'reset': reset
|
||||
}
|
||||
|
||||
if env:
|
||||
body['env'] = json.dumps(env)
|
||||
|
||||
return self._update(url, body)
|
|
@ -1,92 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
class Workbook(base.Resource):
|
||||
resource_name = 'Workbook'
|
||||
|
||||
|
||||
class WorkbookManager(base.ResourceManager):
|
||||
resource_class = Workbook
|
||||
|
||||
def create(self, definition):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/workbooks',
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 201:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return self.resource_class(self, base.extract_json(resp, None))
|
||||
|
||||
def update(self, definition):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.put(
|
||||
'/workbooks',
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return self.resource_class(self, base.extract_json(resp, None))
|
||||
|
||||
def list(self):
|
||||
return self._list('/workbooks', response_key='workbooks')
|
||||
|
||||
def get(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
return self._get('/workbooks/%s' % name)
|
||||
|
||||
def delete(self, name):
|
||||
self._ensure_not_empty(name=name)
|
||||
|
||||
self._delete('/workbooks/%s' % name)
|
||||
|
||||
def validate(self, definition):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/workbooks/validate',
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return base.extract_json(resp, None)
|
|
@ -1,128 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 six
|
||||
|
||||
from mistralclient.api import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
urlparse = six.moves.urllib.parse
|
||||
|
||||
|
||||
class Workflow(base.Resource):
|
||||
resource_name = 'Workflow'
|
||||
|
||||
|
||||
class WorkflowManager(base.ResourceManager):
|
||||
resource_class = Workflow
|
||||
|
||||
def create(self, definition, scope='private'):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/workflows?scope=%s' % scope,
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 201:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return [self.resource_class(self, resource_data)
|
||||
for resource_data in base.extract_json(resp, 'workflows')]
|
||||
|
||||
def update(self, definition, scope='private', id=None):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
url_pre = ('/workflows/%s' % id) if id else '/workflows'
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.put(
|
||||
'%s?scope=%s' % (url_pre, scope),
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
if id:
|
||||
return self.resource_class(self, base.extract_json(resp, None))
|
||||
|
||||
return [self.resource_class(self, resource_data)
|
||||
for resource_data in base.extract_json(resp, 'workflows')]
|
||||
|
||||
def list(self, marker='', limit=None, sort_keys='', sort_dirs='',
|
||||
**filters):
|
||||
qparams = {}
|
||||
|
||||
if marker:
|
||||
qparams['marker'] = marker
|
||||
|
||||
if limit:
|
||||
qparams['limit'] = limit
|
||||
|
||||
if sort_keys:
|
||||
qparams['sort_keys'] = sort_keys
|
||||
|
||||
if sort_dirs:
|
||||
qparams['sort_dirs'] = sort_dirs
|
||||
|
||||
for name, val in filters.items():
|
||||
qparams[name] = val
|
||||
|
||||
query_string = ("?%s" % urlparse.urlencode(list(qparams.items()))
|
||||
if qparams else "")
|
||||
|
||||
return self._list(
|
||||
'/workflows%s' % query_string,
|
||||
response_key='workflows',
|
||||
)
|
||||
|
||||
def get(self, identifier):
|
||||
self._ensure_not_empty(identifier=identifier)
|
||||
|
||||
return self._get('/workflows/%s' % identifier)
|
||||
|
||||
def delete(self, identifier):
|
||||
self._ensure_not_empty(identifier=identifier)
|
||||
|
||||
self._delete('/workflows/%s' % identifier)
|
||||
|
||||
def validate(self, definition):
|
||||
self._ensure_not_empty(definition=definition)
|
||||
|
||||
# If the specified definition is actually a file, read in the
|
||||
# definition file
|
||||
definition = utils.get_contents_if_file(definition)
|
||||
|
||||
resp = self.http_client.post(
|
||||
'/workflows/validate',
|
||||
definition,
|
||||
headers={'content-type': 'text/plain'}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
self._raise_api_exception(resp)
|
||||
|
||||
return base.extract_json(resp, None)
|
|
@ -1,37 +0,0 @@
|
|||
# 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.
|
||||
|
||||
import abc
|
||||
|
||||
import six
|
||||
from stevedore import driver
|
||||
|
||||
|
||||
def get_auth_handler(auth_type):
|
||||
mgr = driver.DriverManager(
|
||||
'mistralclient.auth',
|
||||
auth_type,
|
||||
invoke_on_load=True
|
||||
)
|
||||
|
||||
return mgr.driver
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AuthHandler(object):
|
||||
"""Abstract base class for an authentication plugin."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def authenticate(self, req):
|
||||
raise NotImplementedError()
|
|
@ -1,21 +0,0 @@
|
|||
# Copyright 2016 - 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 stevedore import extension
|
||||
|
||||
# Valid authentication types.
|
||||
ALL = extension.ExtensionManager(
|
||||
namespace='mistralclient.auth',
|
||||
invoke_on_load=False
|
||||
).names()
|
|
@ -1,170 +0,0 @@
|
|||
# Copyright 2016 - 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 logging
|
||||
import pprint
|
||||
import requests
|
||||
|
||||
from mistralclient import auth
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KeycloakAuthHandler(auth.AuthHandler):
|
||||
|
||||
def authenticate(self, req, session=None):
|
||||
"""Performs authentication using Keycloak OpenID Protocol.
|
||||
|
||||
:param req: Request dict containing list of parameters required
|
||||
for Keycloak authentication.
|
||||
|
||||
* auth_url: Base authentication url of KeyCloak server (e.g.
|
||||
"https://my.keycloak:8443/auth"
|
||||
* client_id: Client ID (according to OpenID Connect protocol).
|
||||
* client_secret: Client secret (according to OpenID Connect
|
||||
protocol).
|
||||
* realm_name: KeyCloak realm name.
|
||||
* username: User name (Optional, if None then access_token must be
|
||||
provided).
|
||||
* password: Password (Optional).
|
||||
* access_token: Access token. If passed, username and password are
|
||||
not used and this method just validates the token and refreshes
|
||||
it if needed (Optional, if None then username must be
|
||||
provided).
|
||||
* cacert: SSL certificate file (Optional).
|
||||
* insecure: If True, SSL certificate is not verified (Optional).
|
||||
|
||||
:param session: Keystone session object. Not used by this plugin.
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(req, dict):
|
||||
raise TypeError('The input "req" is not typeof dict.')
|
||||
|
||||
auth_url = req.get('auth_url')
|
||||
client_id = req.get('client_id')
|
||||
client_secret = req.get('client_secret')
|
||||
realm_name = req.get('realm_name')
|
||||
username = req.get('username')
|
||||
password = req.get('password')
|
||||
access_token = req.get('access_token')
|
||||
cacert = req.get('cacert')
|
||||
insecure = req.get('insecure', False)
|
||||
|
||||
if not auth_url:
|
||||
raise ValueError('Base authentication url is not provided.')
|
||||
|
||||
if not client_id:
|
||||
raise ValueError('Client ID is not provided.')
|
||||
|
||||
if not client_secret:
|
||||
raise ValueError('Client secret is not provided.')
|
||||
|
||||
if not realm_name:
|
||||
raise ValueError('Project(realm) name is not provided.')
|
||||
|
||||
if username and access_token:
|
||||
raise ValueError(
|
||||
"User name and access token can't be "
|
||||
"provided at the same time."
|
||||
)
|
||||
|
||||
if not username and not access_token:
|
||||
raise ValueError(
|
||||
'Either user name or access token must be provided.'
|
||||
)
|
||||
|
||||
if access_token:
|
||||
response = self._authenticate_with_token(
|
||||
auth_url,
|
||||
client_id,
|
||||
client_secret,
|
||||
access_token,
|
||||
cacert,
|
||||
insecure
|
||||
)
|
||||
else:
|
||||
response = self._authenticate_with_password(
|
||||
auth_url,
|
||||
client_id,
|
||||
client_secret,
|
||||
realm_name,
|
||||
username,
|
||||
password,
|
||||
cacert,
|
||||
insecure
|
||||
)
|
||||
|
||||
response['project_id'] = realm_name
|
||||
|
||||
return response
|
||||
|
||||
def _authenticate_with_token(auth_url, client_id, client_secret,
|
||||
auth_token, cacert=None, insecure=None):
|
||||
# TODO(rakhmerov): Implement.
|
||||
raise NotImplementedError
|
||||
|
||||
def _authenticate_with_password(auth_url, client_id, client_secret,
|
||||
realm_name, username, password,
|
||||
cacert=None, insecure=None):
|
||||
access_token_endpoint = (
|
||||
"%s/realms/%s/protocol/openid-connect/token" %
|
||||
(auth_url, realm_name)
|
||||
)
|
||||
|
||||
client_auth = (client_id, client_secret)
|
||||
|
||||
body = {
|
||||
'grant_type': 'password',
|
||||
'username': username,
|
||||
'password': password,
|
||||
'scope': 'profile'
|
||||
}
|
||||
|
||||
resp = requests.post(
|
||||
access_token_endpoint,
|
||||
auth=client_auth,
|
||||
data=body,
|
||||
verify=not insecure
|
||||
)
|
||||
|
||||
try:
|
||||
resp.raise_for_status()
|
||||
except Exception as e:
|
||||
raise Exception("Failed to get access token:\n %s" % str(e))
|
||||
|
||||
LOG.debug("HTTP response from OIDC provider: %s",
|
||||
pprint.pformat(resp.json()))
|
||||
|
||||
return resp.json()['access_token']
|
||||
|
||||
|
||||
# An example of using KeyCloak OpenID authentication.
|
||||
if __name__ == '__main__':
|
||||
print("Using username/password to get access token from KeyCloak...")
|
||||
|
||||
auth_handler = KeycloakAuthHandler()
|
||||
|
||||
a_token = auth_handler.authenticate(
|
||||
"https://my.keycloak:8443/auth",
|
||||
client_id="mistral_client",
|
||||
client_secret="4a080907-921b-409a-b793-c431609c3a47",
|
||||
realm_name="mistral",
|
||||
username="user",
|
||||
password="secret",
|
||||
insecure=True
|
||||
)
|
||||
|
||||
print("Access token: %s" % a_token)
|
|
@ -1,146 +0,0 @@
|
|||
# Copyright 2016 - 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 keystoneclient import client
|
||||
from mistralclient import auth
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
import mistralclient.api.httpclient as api
|
||||
|
||||
|
||||
class KeystoneAuthHandler(auth.AuthHandler):
|
||||
|
||||
def authenticate(self, req, session=None):
|
||||
"""Performs authentication via Keystone.
|
||||
|
||||
:param req: Request dict containing list of parameters required
|
||||
for Keystone authentication.
|
||||
|
||||
"""
|
||||
if not isinstance(req, dict):
|
||||
raise TypeError('The input "req" is not typeof dict.')
|
||||
|
||||
session = session
|
||||
mistral_url = req.get('mistral_url')
|
||||
endpoint_type = req.get('endpoint_type', 'publicURL')
|
||||
service_type = req.get('service_type', 'workflowv2')
|
||||
|
||||
auth_url = req.get('auth_url')
|
||||
username = req.get('username')
|
||||
user_id = req.get('user_id')
|
||||
api_key = req.get('api_key')
|
||||
auth_token = req.get('auth_token')
|
||||
project_name = req.get('project_name')
|
||||
project_id = req.get('project_id')
|
||||
region_name = req.get('region_name')
|
||||
user_domain_name = req.get('user_domain_name', 'Default')
|
||||
project_domain_name = req.get('project_domain_name', 'Default')
|
||||
cacert = req.get('cacert')
|
||||
insecure = req.get('insecure', False)
|
||||
|
||||
target_auth_url = req.get('target_auth_url')
|
||||
target_username = req.get('target_username')
|
||||
target_user_id = req.get('target_user_id')
|
||||
target_api_key = req.get('target_api_key')
|
||||
target_auth_token = req.get('target_auth_token')
|
||||
target_project_name = req.get('target_project_name')
|
||||
target_project_id = req.get('target_project_id')
|
||||
target_region_name = req.get('target_region_name')
|
||||
target_user_domain_name = req.get('target_user_domain_name', 'Default')
|
||||
target_project_domain_name = req.get(
|
||||
'target_project_domain_name',
|
||||
'Default'
|
||||
)
|
||||
target_cacert = req.get('target_cacert')
|
||||
target_insecure = req.get('target_insecure')
|
||||
|
||||
if project_name and project_id:
|
||||
raise RuntimeError(
|
||||
'Only project name or project id should be set'
|
||||
)
|
||||
|
||||
if username and user_id:
|
||||
raise RuntimeError(
|
||||
'Only user name or user id should be set'
|
||||
)
|
||||
|
||||
auth_response = {}
|
||||
|
||||
if session:
|
||||
keystone = client.Client(session=session)
|
||||
elif auth_url:
|
||||
keystone = client.Client(
|
||||
username=username,
|
||||
user_id=user_id,
|
||||
password=api_key,
|
||||
token=auth_token,
|
||||
tenant_id=project_id,
|
||||
tenant_name=project_name,
|
||||
auth_url=auth_url,
|
||||
cacert=cacert,
|
||||
insecure=insecure,
|
||||
user_domain_name=user_domain_name,
|
||||
project_domain_name=project_domain_name
|
||||
)
|
||||
keystone.authenticate()
|
||||
auth_response.update({
|
||||
api.AUTH_TOKEN: keystone.auth_token,
|
||||
api.PROJECT_ID: keystone.project_id,
|
||||
api.USER_ID: keystone.user_id,
|
||||
})
|
||||
|
||||
if session or auth_url:
|
||||
if not mistral_url:
|
||||
try:
|
||||
mistral_url = keystone.service_catalog.url_for(
|
||||
service_type=service_type,
|
||||
endpoint_type=endpoint_type,
|
||||
region_name=region_name
|
||||
)
|
||||
except Exception:
|
||||
mistral_url = None
|
||||
|
||||
auth_response['mistral_url'] = mistral_url
|
||||
|
||||
if target_auth_url:
|
||||
target_keystone = client.Client(
|
||||
username=target_username,
|
||||
user_id=target_user_id,
|
||||
password=target_api_key,
|
||||
token=target_auth_token,
|
||||
tenant_id=target_project_id,
|
||||
tenant_name=target_project_name,
|
||||
project_id=target_project_id,
|
||||
project_name=target_project_name,
|
||||
auth_url=target_auth_url,
|
||||
cacert=target_cacert,
|
||||
insecure=target_insecure,
|
||||
region_name=target_region_name,
|
||||
user_domain_name=target_user_domain_name,
|
||||
project_domain_name=target_project_domain_name
|
||||
)
|
||||
|
||||
target_keystone.authenticate()
|
||||
|
||||
auth_response.update({
|
||||
api.TARGET_AUTH_TOKEN: target_keystone.auth_token,
|
||||
api.TARGET_PROJECT_ID: target_keystone.project_id,
|
||||
api.TARGET_USER_ID: target_keystone.user_id,
|
||||
api.TARGET_AUTH_URI: target_auth_url,
|
||||
api.TARGET_SERVICE_CATALOG: jsonutils.dumps(
|
||||
target_keystone.auth_ref
|
||||
)
|
||||
})
|
||||
|
||||
return auth_response
|
|
@ -1,338 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, 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.
|
||||
#
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_list(action_ex=None):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow name',
|
||||
'Task name',
|
||||
'Task ID',
|
||||
'State',
|
||||
'Accepted',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if action_ex:
|
||||
data = (
|
||||
action_ex.id,
|
||||
action_ex.name,
|
||||
action_ex.workflow_name,
|
||||
action_ex.task_name if hasattr(action_ex, 'task_name') else None,
|
||||
action_ex.task_execution_id,
|
||||
action_ex.state,
|
||||
action_ex.accepted,
|
||||
action_ex.created_at,
|
||||
action_ex.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def format(action_ex=None):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow name',
|
||||
'Task name',
|
||||
'Task ID',
|
||||
'State',
|
||||
'State info',
|
||||
'Accepted',
|
||||
'Created at',
|
||||
'Updated at',
|
||||
)
|
||||
|
||||
if action_ex:
|
||||
data = (
|
||||
action_ex.id,
|
||||
action_ex.name,
|
||||
action_ex.workflow_name,
|
||||
action_ex.task_name if hasattr(action_ex, 'task_name') else None,
|
||||
action_ex.task_execution_id,
|
||||
action_ex.state,
|
||||
action_ex.state_info,
|
||||
action_ex.accepted,
|
||||
action_ex.created_at,
|
||||
action_ex.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new Action execution or just run specific action."""
|
||||
|
||||
def produce_output(self, parsed_args, column_names, data):
|
||||
if not column_names:
|
||||
return 0
|
||||
|
||||
return super(Create, self).produce_output(
|
||||
parsed_args,
|
||||
column_names,
|
||||
data
|
||||
)
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'name',
|
||||
help='Action name to execute.'
|
||||
)
|
||||
parser.add_argument(
|
||||
dest='input',
|
||||
nargs='?',
|
||||
help='Action input.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s',
|
||||
'--save-result',
|
||||
dest='save_result',
|
||||
action='store_true',
|
||||
help='Save the result into DB.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--run-sync',
|
||||
dest='run_sync',
|
||||
action='store_true',
|
||||
help='Run the action synchronously.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-t',
|
||||
'--target',
|
||||
dest='target',
|
||||
help='Action will be executed on <target> executor.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
params = {}
|
||||
|
||||
if parsed_args.save_result:
|
||||
params['save_result'] = parsed_args.save_result
|
||||
|
||||
if parsed_args.run_sync:
|
||||
params['run_sync'] = parsed_args.run_sync
|
||||
|
||||
if parsed_args.target:
|
||||
params['target'] = parsed_args.target
|
||||
|
||||
action_input = None
|
||||
|
||||
if parsed_args.input:
|
||||
action_input = utils.load_json(parsed_args.input)
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
action_ex = mistral_client.action_executions.create(
|
||||
parsed_args.name,
|
||||
action_input,
|
||||
**params
|
||||
)
|
||||
|
||||
if not parsed_args.run_sync and parsed_args.save_result:
|
||||
return format(action_ex)
|
||||
else:
|
||||
self.app.stdout.write("%s\n" % action_ex.output)
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all Action executions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'task_execution_id',
|
||||
nargs='?',
|
||||
help='Task execution ID.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of action-executions to return in a single '
|
||||
'result. limit is set to %s by default. Use --limit -1 to '
|
||||
'fetch the full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
LOG.info("limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT)
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.action_executions.list(
|
||||
parsed_args.task_execution_id,
|
||||
limit=parsed_args.limit,
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific Action execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('action_execution', help='Action execution ID.')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
execution = mistral_client.action_executions.get(
|
||||
parsed_args.action_execution
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
"""Update specific Action execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Action execution ID.')
|
||||
parser.add_argument(
|
||||
'--state',
|
||||
dest='state',
|
||||
choices=['IDLE', 'RUNNING', 'SUCCESS', 'ERROR', 'CANCELLED'],
|
||||
help='Action execution state')
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
dest='output',
|
||||
help='Action execution output')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
output = None
|
||||
if parsed_args.output:
|
||||
output = utils.load_json(parsed_args.output)
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
execution = mistral_client.action_executions.update(
|
||||
parsed_args.id,
|
||||
parsed_args.state,
|
||||
output
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class GetOutput(command.Command):
|
||||
"""Show Action execution output data."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetOutput, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Action execution ID.')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
output = mistral_client.action_executions.get(parsed_args.id).output
|
||||
|
||||
try:
|
||||
output = json.loads(output)
|
||||
output = json.dumps(output, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Task result is not JSON.")
|
||||
|
||||
self.app.stdout.write(output or "\n")
|
||||
|
||||
|
||||
class GetInput(command.Command):
|
||||
"""Show Action execution input data."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetInput, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Action execution ID.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
result = mistral_client.action_executions.get(parsed_args.id).input
|
||||
|
||||
try:
|
||||
result = json.loads(result)
|
||||
result = json.dumps(result, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Task result is not JSON.")
|
||||
|
||||
self.app.stdout.write(result or "\n")
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete action execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'action_execution',
|
||||
nargs='+',
|
||||
help='Id of action execution identifier(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.action_executions.delete(s),
|
||||
parsed_args.action_execution,
|
||||
"Request to delete action execution %s has been accepted.",
|
||||
"Unable to delete the specified action execution(s)."
|
||||
)
|
|
@ -1,255 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, 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 argparse
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(action=None):
|
||||
return format(action, lister=True)
|
||||
|
||||
|
||||
def format(action=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Is system',
|
||||
'Input',
|
||||
'Description',
|
||||
'Tags',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if action:
|
||||
tags = getattr(action, 'tags', None) or []
|
||||
input = action.input if not lister else base.cut(action.input)
|
||||
desc = (action.description if not lister
|
||||
else base.cut(action.description))
|
||||
|
||||
data = (
|
||||
action.id,
|
||||
action.name,
|
||||
action.is_system,
|
||||
input,
|
||||
desc,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
action.created_at,
|
||||
)
|
||||
|
||||
if hasattr(action, 'updated_at'):
|
||||
data += (action.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all actions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.actions.list(
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific action."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('action', help='Action (name or ID)')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
action = mistral_client.actions.get(parsed_args.action)
|
||||
|
||||
return format(action)
|
||||
|
||||
|
||||
class Create(base.MistralLister):
|
||||
"""Create new action."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Action definition file'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
action='store_true',
|
||||
help='With this flag action will be marked as "public".'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _validate_parsed_args(self, parsed_args):
|
||||
if not parsed_args.definition:
|
||||
raise RuntimeError("Provide action definition file.")
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.actions.create(
|
||||
parsed_args.definition.read(),
|
||||
scope=scope
|
||||
)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete action."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'action',
|
||||
nargs='+',
|
||||
help='Name or ID of action(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.actions.delete(s),
|
||||
parsed_args.action,
|
||||
"Request to delete action %s has been accepted.",
|
||||
"Unable to delete the specified action(s)."
|
||||
)
|
||||
|
||||
|
||||
class Update(base.MistralLister):
|
||||
"""Update action."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Action definition file'
|
||||
)
|
||||
parser.add_argument('--id', help='Action ID.')
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
action='store_true',
|
||||
help='With this flag action will be marked as "public".'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.actions.update(
|
||||
parsed_args.definition.read(),
|
||||
scope=scope,
|
||||
id=parsed_args.id
|
||||
)
|
||||
|
||||
|
||||
class GetDefinition(command.Command):
|
||||
"""Show action definition."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetDefinition, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('name', help='Action name')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
definition = mistral_client.actions.get(parsed_args.name).definition
|
||||
|
||||
self.app.stdout.write(definition or "\n")
|
||||
|
||||
|
||||
class Validate(command.ShowOne):
|
||||
"""Validate action."""
|
||||
|
||||
def _format(self, result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
data = (result.get('valid'), result.get('error'))
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Validate, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='action definition file'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
result = mistral_client.actions.validate(
|
||||
parsed_args.definition.read()
|
||||
)
|
||||
|
||||
return self._format(result)
|
|
@ -1,85 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, 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 abc
|
||||
import textwrap
|
||||
|
||||
from osc_lib.command import command
|
||||
import six
|
||||
|
||||
|
||||
DEFAULT_LIMIT = 100
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MistralLister(command.Lister):
|
||||
@abc.abstractmethod
|
||||
def _get_format_function(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_resources(self, parsed_args):
|
||||
"""Gets a list of API resources (e.g. using client)."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _validate_parsed_args(self, parsed_args):
|
||||
# No-op by default.
|
||||
pass
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
self._validate_parsed_args(parsed_args)
|
||||
|
||||
f = self._get_format_function()
|
||||
|
||||
ret = self._get_resources(parsed_args)
|
||||
if not isinstance(ret, list):
|
||||
ret = [ret]
|
||||
|
||||
data = [f(r)[1] for r in ret]
|
||||
|
||||
if data:
|
||||
return f()[0], data
|
||||
else:
|
||||
return f()
|
||||
|
||||
|
||||
def cut(string, length=25):
|
||||
if string and len(string) > length:
|
||||
return "%s..." % string[:length]
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
def wrap(string, width=25):
|
||||
if string and len(string) > width:
|
||||
return textwrap.fill(string, width)
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
def get_filters(parsed_args):
|
||||
filters = {}
|
||||
|
||||
if parsed_args.filters:
|
||||
for f in parsed_args.filters:
|
||||
arr = f.split('=')
|
||||
|
||||
if len(arr) != 2:
|
||||
raise ValueError('Invalid filter: %s' % f)
|
||||
|
||||
filters[arr[0]] = arr[1]
|
||||
|
||||
return filters
|
|
@ -1,216 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, 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 time
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(trigger=None):
|
||||
return format(trigger, lister=True)
|
||||
|
||||
|
||||
def format(trigger=None, lister=False):
|
||||
columns = (
|
||||
'Name',
|
||||
'Workflow',
|
||||
'Params',
|
||||
'Pattern',
|
||||
# TODO(rakhmerov): Uncomment when passwords are handled properly.
|
||||
# TODO(rakhmerov): Add 'Workflow input' column.
|
||||
'Next execution time',
|
||||
'Remaining executions',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if trigger:
|
||||
# TODO(rakhmerov): Add following here:
|
||||
# TODO(rakhmerov): wf_input = trigger.workflow_input if not lister
|
||||
# TODO(rakhmerov:): else base.cut(trigger.workflow_input)
|
||||
|
||||
data = (
|
||||
trigger.name,
|
||||
trigger.workflow_name,
|
||||
trigger.workflow_params,
|
||||
trigger.pattern,
|
||||
# TODO(rakhmerov): Uncomment when passwords are handled properly.
|
||||
# TODo(rakhmerov): Add 'wf_input' here.
|
||||
trigger.next_execution_time,
|
||||
trigger.remaining_executions,
|
||||
trigger.created_at,
|
||||
)
|
||||
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all cron triggers."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.cron_triggers.list()
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific cron trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('cron_trigger', help='Cron trigger name')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return format(mistral_client.cron_triggers.get(
|
||||
parsed_args.cron_trigger
|
||||
))
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('name', help='Cron trigger name')
|
||||
parser.add_argument('workflow_identifier', help='Workflow name or ID')
|
||||
|
||||
parser.add_argument(
|
||||
'workflow_input',
|
||||
nargs='?',
|
||||
help='Workflow input'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--params',
|
||||
help='Workflow params',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--pattern',
|
||||
type=str,
|
||||
help='Cron trigger pattern',
|
||||
metavar='<* * * * *>'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--first-time',
|
||||
type=str,
|
||||
default=None,
|
||||
help=("Date and time of the first execution. Time is treated as "
|
||||
"local time unless --utc is also specified"),
|
||||
metavar='<YYYY-MM-DD HH:MM>'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--count',
|
||||
type=int,
|
||||
help="Number of wanted executions",
|
||||
metavar='<integer>'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--utc',
|
||||
action='store_true',
|
||||
help="All times specified should be treated as UTC"
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def _get_file_content_or_dict(string):
|
||||
if string:
|
||||
return utils.load_json(string)
|
||||
else:
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def _convert_time_string_to_utc(time_string):
|
||||
datetime_format = '%Y-%m-%d %H:%M'
|
||||
|
||||
the_time = time_string
|
||||
if the_time:
|
||||
the_time = datetime.datetime.strptime(
|
||||
the_time, datetime_format)
|
||||
|
||||
is_dst = time.daylight and time.localtime().tm_isdst > 0
|
||||
utc_offset = - (time.altzone if is_dst else time.timezone)
|
||||
|
||||
the_time = (the_time - datetime.timedelta(
|
||||
0, utc_offset)).strftime(datetime_format)
|
||||
|
||||
return the_time
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
wf_input = self._get_file_content_or_dict(parsed_args.workflow_input)
|
||||
wf_params = self._get_file_content_or_dict(parsed_args.params)
|
||||
|
||||
first_time = parsed_args.first_time
|
||||
if not parsed_args.utc:
|
||||
first_time = self._convert_time_string_to_utc(
|
||||
parsed_args.first_time)
|
||||
|
||||
trigger = mistral_client.cron_triggers.create(
|
||||
parsed_args.name,
|
||||
parsed_args.workflow_identifier,
|
||||
wf_input,
|
||||
wf_params,
|
||||
parsed_args.pattern,
|
||||
first_time,
|
||||
parsed_args.count
|
||||
)
|
||||
|
||||
return format(trigger)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'cron_trigger',
|
||||
nargs='+', help='Name of cron trigger(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.cron_triggers.delete(s),
|
||||
parsed_args.cron_trigger,
|
||||
"Request to delete cron trigger %s has been accepted.",
|
||||
"Unable to delete the specified cron trigger(s)."
|
||||
)
|
|
@ -1,186 +0,0 @@
|
|||
# Copyright 2015 - StackStorm, 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 argparse
|
||||
import json
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(environment=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Description',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if environment:
|
||||
data = (
|
||||
environment.name,
|
||||
environment.description,
|
||||
environment.scope,
|
||||
environment.created_at,
|
||||
)
|
||||
|
||||
if hasattr(environment, 'updated_at'):
|
||||
data += (environment.updated_at or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def format(environment=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Description',
|
||||
'Variables',
|
||||
'Scope',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if environment:
|
||||
data = (environment.name,)
|
||||
|
||||
if hasattr(environment, 'description'):
|
||||
data += (environment.description or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
data += (
|
||||
json.dumps(environment.variables, indent=4),
|
||||
environment.scope,
|
||||
environment.created_at,
|
||||
)
|
||||
|
||||
if hasattr(environment, 'updated_at'):
|
||||
data += (environment.updated_at or '<none>',)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all environments."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.environments.list()
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific environment."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'environment',
|
||||
help='Environment name'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
environment = mistral_client.environments.get(parsed_args.environment)
|
||||
|
||||
return format(environment)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new environment."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'file',
|
||||
type=argparse.FileType('r'),
|
||||
help='Environment configuration file in JSON or YAML'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = utils.load_content(parsed_args.file.read())
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
environment = mistral_client.environments.create(**data)
|
||||
|
||||
return format(environment)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete environment."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'environment',
|
||||
nargs='+',
|
||||
help='Name of environment(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.environments.delete(s),
|
||||
parsed_args.environment,
|
||||
"Request to delete environment %s has been accepted.",
|
||||
"Unable to delete the specified environment(s)."
|
||||
)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
"""Update environment."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'file',
|
||||
type=argparse.FileType('r'),
|
||||
help='Environment configuration file in JSON or YAML'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
data = utils.load_content(parsed_args.file.read())
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
environment = mistral_client.environments.update(**data)
|
||||
|
||||
return format(environment)
|
|
@ -1,167 +0,0 @@
|
|||
# Copyright 2017, OpenStack Foundation
|
||||
#
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(trigger=None):
|
||||
return format(trigger, lister=True)
|
||||
|
||||
|
||||
def format(trigger=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow ID',
|
||||
'Params',
|
||||
'Exchange',
|
||||
'Topic',
|
||||
'Event',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if trigger:
|
||||
data = (
|
||||
trigger.id,
|
||||
trigger.name,
|
||||
trigger.workflow_id,
|
||||
trigger.workflow_params,
|
||||
trigger.exchange,
|
||||
trigger.topic,
|
||||
trigger.event,
|
||||
trigger.created_at,
|
||||
)
|
||||
|
||||
if hasattr(trigger, 'updated_at'):
|
||||
data += (trigger.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all event triggers."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.event_triggers.list()
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific event trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('event_trigger', help='Event trigger ID')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return format(mistral_client.event_triggers.get(
|
||||
parsed_args.event_trigger
|
||||
))
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('name', help='Event trigger name')
|
||||
parser.add_argument('workflow_id', help='Workflow ID')
|
||||
|
||||
parser.add_argument('exchange',
|
||||
type=str,
|
||||
help='Event trigger exchange')
|
||||
|
||||
parser.add_argument('topic',
|
||||
type=str,
|
||||
help='Event trigger topic')
|
||||
|
||||
parser.add_argument('event',
|
||||
type=str,
|
||||
help='Event trigger event name')
|
||||
|
||||
parser.add_argument('workflow_input',
|
||||
nargs='?',
|
||||
help='Workflow input')
|
||||
|
||||
parser.add_argument('--params',
|
||||
help='Workflow params')
|
||||
|
||||
return parser
|
||||
|
||||
@staticmethod
|
||||
def _get_json_string_or_dict(string):
|
||||
if string:
|
||||
return utils.load_json(string)
|
||||
else:
|
||||
return {}
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
wf_input = self._get_json_string_or_dict(parsed_args.workflow_input)
|
||||
wf_params = self._get_json_string_or_dict(parsed_args.params)
|
||||
|
||||
trigger = mistral_client.event_triggers.create(
|
||||
parsed_args.name,
|
||||
parsed_args.workflow_id,
|
||||
parsed_args.exchange,
|
||||
parsed_args.topic,
|
||||
parsed_args.event,
|
||||
wf_input,
|
||||
wf_params,
|
||||
)
|
||||
|
||||
return format(trigger)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete trigger."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'event_trigger_id',
|
||||
nargs='+', help='ID of event trigger(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.event_triggers.delete(s),
|
||||
parsed_args.event_trigger_id,
|
||||
"Request to delete event trigger %s has been accepted.",
|
||||
"Unable to delete the specified event trigger(s)."
|
||||
)
|
|
@ -1,338 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, Inc.
|
||||
# Copyright 2016 - Brocade Communications Systems, 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 json
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_list(execution=None):
|
||||
return format(execution, lister=True)
|
||||
|
||||
|
||||
def format(execution=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Workflow ID',
|
||||
'Workflow name',
|
||||
'Description',
|
||||
'Task Execution ID',
|
||||
'State',
|
||||
'State info',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
# TODO(nmakhotkin) Add parent task id when it's implemented in API.
|
||||
|
||||
if execution:
|
||||
state_info = (execution.state_info if not lister
|
||||
else base.cut(execution.state_info))
|
||||
|
||||
data = (
|
||||
execution.id,
|
||||
execution.workflow_id,
|
||||
execution.workflow_name,
|
||||
execution.description,
|
||||
execution.task_execution_id or '<none>',
|
||||
execution.state,
|
||||
state_info,
|
||||
execution.created_at,
|
||||
execution.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all executions."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(List, self).get_parser(parsed_args)
|
||||
parser.add_argument(
|
||||
'--task',
|
||||
nargs='?',
|
||||
help="Parent task execution ID associated with workflow "
|
||||
"execution list.",
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
type=str,
|
||||
help='The last execution uuid of the previous page, displays list '
|
||||
'of executions after "marker".',
|
||||
default='',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of executions to return in a single result. '
|
||||
'limit is set to %s by default. Use --limit -1 to fetch the '
|
||||
'full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_keys',
|
||||
help='Comma-separated list of sort keys to sort results by. '
|
||||
'Default: created_at. '
|
||||
'Example: mistral execution-list --sort_keys=id,description',
|
||||
default='created_at',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--sort_dirs',
|
||||
help='Comma-separated list of sort directions. Default: asc. '
|
||||
'Example: mistral execution-list --sort_keys=id,description '
|
||||
'--sort_dirs=asc,desc',
|
||||
default='asc',
|
||||
nargs='?'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
LOG.info("limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT)
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.executions.list(
|
||||
task=parsed_args.task,
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
sort_keys=parsed_args.sort_keys,
|
||||
sort_dirs=parsed_args.sort_dirs,
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('execution', help='Execution identifier')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
execution = mistral_client.executions.get(parsed_args.execution)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'workflow_identifier',
|
||||
help='Workflow ID or name. Workflow name will be deprecated since '
|
||||
'Mitaka.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'workflow_input',
|
||||
nargs='?',
|
||||
help='Workflow input'
|
||||
)
|
||||
parser.add_argument(
|
||||
'params',
|
||||
nargs='?',
|
||||
help='Workflow additional parameters'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--description',
|
||||
dest='description',
|
||||
default='',
|
||||
help='Execution description'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
if parsed_args.workflow_input:
|
||||
wf_input = utils.load_json(parsed_args.workflow_input)
|
||||
else:
|
||||
wf_input = {}
|
||||
|
||||
if parsed_args.params:
|
||||
params = utils.load_json(parsed_args.params)
|
||||
else:
|
||||
params = {}
|
||||
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
execution = mistral_client.executions.create(
|
||||
parsed_args.workflow_identifier,
|
||||
wf_input,
|
||||
parsed_args.description,
|
||||
**params
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'execution',
|
||||
nargs='+',
|
||||
help='Id of execution identifier(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.executions.delete(s),
|
||||
parsed_args.execution,
|
||||
"Request to delete execution %s has been accepted.",
|
||||
"Unable to delete the specified execution(s)."
|
||||
)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
"""Update execution."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Execution identifier'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-s',
|
||||
'--state',
|
||||
dest='state',
|
||||
choices=['RUNNING', 'PAUSED', 'SUCCESS', 'ERROR', 'CANCELLED'],
|
||||
help='Execution state'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-e',
|
||||
'--env',
|
||||
dest='env',
|
||||
help='Environment variables'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d',
|
||||
'--description',
|
||||
dest='description',
|
||||
help='Execution description'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
env = (
|
||||
utils.load_file(parsed_args.env)
|
||||
if parsed_args.env and os.path.isfile(parsed_args.env)
|
||||
else utils.load_content(parsed_args.env)
|
||||
)
|
||||
|
||||
execution = mistral_client.executions.update(
|
||||
parsed_args.id,
|
||||
parsed_args.state,
|
||||
description=parsed_args.description,
|
||||
env=env
|
||||
)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class GetInput(command.Command):
|
||||
"""Show execution input data."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetInput, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help='Execution ID')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
ex_input = mistral_client.executions.get(parsed_args.id).input
|
||||
|
||||
try:
|
||||
ex_input = json.loads(ex_input)
|
||||
ex_input = json.dumps(ex_input, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Execution input is not JSON.")
|
||||
|
||||
self.app.stdout.write(ex_input or "\n")
|
||||
|
||||
|
||||
class GetOutput(command.Command):
|
||||
"""Show execution output data."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetOutput, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('id', help='Execution ID')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
output = mistral_client.executions.get(parsed_args.id).output
|
||||
|
||||
try:
|
||||
output = json.loads(output)
|
||||
output = json.dumps(output, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Execution output is not JSON.")
|
||||
|
||||
self.app.stdout.write(output or "\n")
|
|
@ -1,235 +0,0 @@
|
|||
# Copyright 2016 - Catalyst IT Limited
|
||||
#
|
||||
# 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 osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import exceptions
|
||||
|
||||
|
||||
def format_list(member=None):
|
||||
return format(member, lister=True)
|
||||
|
||||
|
||||
def format(member=None, lister=False):
|
||||
columns = (
|
||||
'Resource ID',
|
||||
'Resource Type',
|
||||
'Resource Owner',
|
||||
'Member ID',
|
||||
'Status',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if member:
|
||||
data = (
|
||||
member.resource_id,
|
||||
member.resource_type,
|
||||
member.project_id,
|
||||
member.member_id,
|
||||
member.status,
|
||||
member.created_at,
|
||||
)
|
||||
|
||||
if hasattr(member, 'updated_at'):
|
||||
data += (member.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all members."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def get_parser(self, parsed_args):
|
||||
parser = super(List, self).get_parser(parsed_args)
|
||||
|
||||
parser.add_argument(
|
||||
'resource_id',
|
||||
help='Resource id to be shared.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource_type',
|
||||
help='Resource type.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.members.list(
|
||||
parsed_args.resource_id,
|
||||
parsed_args.resource_type
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific member information."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'resource',
|
||||
help='Resource ID to be shared.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource_type',
|
||||
help='Resource type.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-m',
|
||||
'--member-id',
|
||||
default='',
|
||||
help='Project ID to whom the resource is shared to. No need to '
|
||||
'provide this param if you are the resource member.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
member = mistral_client.members.get(
|
||||
parsed_args.resource,
|
||||
parsed_args.resource_type,
|
||||
parsed_args.member_id,
|
||||
)
|
||||
|
||||
return format(member)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Shares a resource to another tenant."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'resource_id',
|
||||
help='Resource ID to be shared.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource_type',
|
||||
help='Resource type.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'member_id',
|
||||
help='Project ID to whom the resource is shared to.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
member = mistral_client.members.create(
|
||||
parsed_args.resource_id,
|
||||
parsed_args.resource_type,
|
||||
parsed_args.member_id,
|
||||
)
|
||||
|
||||
return format(member)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete a resource sharing relationship."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'resource',
|
||||
help='Resource ID to be shared.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource_type',
|
||||
help='Resource type.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'member_id',
|
||||
help='Project ID to whom the resource is shared to.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
try:
|
||||
mistral_client.members.delete(
|
||||
parsed_args.resource,
|
||||
parsed_args.resource_type,
|
||||
parsed_args.member_id,
|
||||
)
|
||||
|
||||
print(
|
||||
"Request to delete %s member %s has been accepted." %
|
||||
(parsed_args.resource_type, parsed_args.member_id)
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
error_msg = "Unable to delete the specified member."
|
||||
raise exceptions.MistralClientException(error_msg)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
"""Update resource sharing status."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'resource_id',
|
||||
help='Resource ID to be shared.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'resource_type',
|
||||
help='Resource type.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-m',
|
||||
'--member-id',
|
||||
default='',
|
||||
help='Project ID to whom the resource is shared to. No need to '
|
||||
'provide this param if you are the resource member.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s',
|
||||
'--status',
|
||||
default='accepted',
|
||||
choices=['pending', 'accepted', 'rejected'],
|
||||
help='status of the sharing.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
member = mistral_client.members.update(
|
||||
parsed_args.resource_id,
|
||||
parsed_args.resource_type,
|
||||
parsed_args.member_id,
|
||||
status=parsed_args.status
|
||||
)
|
||||
|
||||
return format(member)
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 mistralclient.commands.v2 import base
|
||||
|
||||
|
||||
def format_list(service=None):
|
||||
columns = ('Name', 'Type')
|
||||
|
||||
if service:
|
||||
data = (service.name, service.type)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all services."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.services.list()
|
|
@ -1,222 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 json
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_list(task=None):
|
||||
return format(task, lister=True)
|
||||
|
||||
|
||||
def format(task=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Workflow name',
|
||||
'Execution ID',
|
||||
'State',
|
||||
'State info',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if task:
|
||||
state_info = (task.state_info if not lister
|
||||
else base.cut(task.state_info))
|
||||
|
||||
data = (
|
||||
task.id,
|
||||
task.name,
|
||||
task.workflow_name,
|
||||
task.workflow_execution_id,
|
||||
task.state,
|
||||
state_info,
|
||||
task.created_at,
|
||||
task.updated_at or '<none>'
|
||||
)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all tasks."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'workflow_execution',
|
||||
nargs='?',
|
||||
help='Workflow execution ID associated with list of Tasks.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
help='Maximum number of tasks to return in a single result. '
|
||||
'limit is set to %s by default. Use --limit -1 to fetch the '
|
||||
'full result set.' % base.DEFAULT_LIMIT,
|
||||
nargs='?'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
if parsed_args.limit is None:
|
||||
parsed_args.limit = base.DEFAULT_LIMIT
|
||||
LOG.info("limit is set to %s by default. Set "
|
||||
"the limit explicitly using \'--limit\', if required. "
|
||||
"Use \'--limit\' -1 to fetch the full result set.",
|
||||
base.DEFAULT_LIMIT)
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.tasks.list(
|
||||
parsed_args.workflow_execution,
|
||||
limit=parsed_args.limit,
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific task."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('task', help='Task identifier')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
execution = mistral_client.tasks.get(parsed_args.task)
|
||||
|
||||
return format(execution)
|
||||
|
||||
|
||||
class GetResult(command.Command):
|
||||
"""Show task output data."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetResult, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Task ID')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
result = mistral_client.tasks.get(parsed_args.id).result
|
||||
|
||||
try:
|
||||
result = json.loads(result)
|
||||
result = json.dumps(result, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Task result is not JSON.")
|
||||
|
||||
self.app.stdout.write(result or "\n")
|
||||
|
||||
|
||||
class GetPublished(command.Command):
|
||||
"""Show task published variables."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetPublished, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Task ID')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
result = mistral_client.tasks.get(parsed_args.id).published
|
||||
|
||||
try:
|
||||
result = json.loads(result)
|
||||
result = json.dumps(result, indent=4) + "\n"
|
||||
except Exception:
|
||||
LOG.debug("Task result is not JSON.")
|
||||
|
||||
self.app.stdout.write(result or "\n")
|
||||
|
||||
|
||||
class Rerun(command.ShowOne):
|
||||
"""Rerun an existing task."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Rerun, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'id',
|
||||
help='Task identifier'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--resume',
|
||||
action='store_true',
|
||||
dest='resume',
|
||||
default=False,
|
||||
help=('rerun only failed or unstarted action '
|
||||
'executions for with-items task')
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-e',
|
||||
'--env',
|
||||
dest='env',
|
||||
help='Environment variables'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
env = (
|
||||
utils.load_file(parsed_args.env)
|
||||
if parsed_args.env and os.path.isfile(parsed_args.env)
|
||||
else utils.load_content(parsed_args.env)
|
||||
)
|
||||
|
||||
execution = mistral_client.tasks.rerun(
|
||||
parsed_args.id,
|
||||
reset=(not parsed_args.resume),
|
||||
env=env
|
||||
)
|
||||
|
||||
return format(execution)
|
|
@ -1,194 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 argparse
|
||||
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format(workbook=None):
|
||||
columns = (
|
||||
'Name',
|
||||
'Tags',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if workbook:
|
||||
data = (
|
||||
workbook.name,
|
||||
base.wrap(', '.join(workbook.tags or '')) or '<none>',
|
||||
workbook.created_at,
|
||||
)
|
||||
|
||||
if hasattr(workbook, 'updated_at'):
|
||||
data += (workbook.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all workbooks."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
return mistral_client.workbooks.list()
|
||||
|
||||
|
||||
class Get(command.ShowOne):
|
||||
"""Show specific workbook."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'workbook',
|
||||
help='Workbook name'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
workbook = mistral_client.workbooks.get(parsed_args.workbook)
|
||||
|
||||
return format(workbook)
|
||||
|
||||
|
||||
class Create(command.ShowOne):
|
||||
"""Create new workbook."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workbook definition file'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
workbook = mistral_client.workbooks.create(
|
||||
parsed_args.definition.read()
|
||||
)
|
||||
|
||||
return format(workbook)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete workbook."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('workbook', nargs='+', help='Name of workbook(s).')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.workbooks.delete(s),
|
||||
parsed_args.workbook,
|
||||
"Request to delete workbook %s has been accepted.",
|
||||
"Unable to delete the specified workbook(s)."
|
||||
)
|
||||
|
||||
|
||||
class Update(command.ShowOne):
|
||||
"""Update workbook."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workbook definition file'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
workbook = mistral_client.workbooks.update(
|
||||
parsed_args.definition.read()
|
||||
)
|
||||
|
||||
return format(workbook)
|
||||
|
||||
|
||||
class GetDefinition(command.Command):
|
||||
"""Show workbook definition."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetDefinition, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('name', help='Workbook name')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
definition = mistral_client.workbooks.get(parsed_args.name).definition
|
||||
|
||||
self.app.stdout.write(definition or "\n")
|
||||
|
||||
|
||||
class Validate(command.ShowOne):
|
||||
"""Validate workbook."""
|
||||
|
||||
def _format(self, result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
data = (result.get('valid'), result.get('error'),)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Validate, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workbook definition file'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
result = mistral_client.workbooks.validate(
|
||||
parsed_args.definition.read()
|
||||
)
|
||||
|
||||
return self._format(result)
|
|
@ -1,247 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 argparse
|
||||
|
||||
from cliff import command
|
||||
from cliff import show
|
||||
|
||||
from mistralclient.commands.v2 import base
|
||||
from mistralclient import utils
|
||||
|
||||
|
||||
def format_list(workflow=None):
|
||||
return format(workflow, lister=True)
|
||||
|
||||
|
||||
def format(workflow=None, lister=False):
|
||||
columns = (
|
||||
'ID',
|
||||
'Name',
|
||||
'Project ID',
|
||||
'Tags',
|
||||
'Input',
|
||||
'Created at',
|
||||
'Updated at'
|
||||
)
|
||||
|
||||
if workflow:
|
||||
tags = getattr(workflow, 'tags', None) or []
|
||||
|
||||
data = (
|
||||
workflow.id,
|
||||
workflow.name,
|
||||
workflow.project_id,
|
||||
base.wrap(', '.join(tags)) or '<none>',
|
||||
workflow.input if not lister else base.cut(workflow.input),
|
||||
workflow.created_at
|
||||
)
|
||||
|
||||
if hasattr(workflow, 'updated_at'):
|
||||
data += (workflow.updated_at,)
|
||||
else:
|
||||
data += (None,)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
class List(base.MistralLister):
|
||||
"""List all workflows."""
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(List, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'--filter',
|
||||
dest='filters',
|
||||
action='append',
|
||||
help='Filters. Can be repeated.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.workflows.list(
|
||||
**base.get_filters(parsed_args)
|
||||
)
|
||||
|
||||
|
||||
class Get(show.ShowOne):
|
||||
"""Show specific workflow."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Get, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('workflow', help='Workflow ID or name.')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
wf = mistral_client.workflows.get(parsed_args.workflow)
|
||||
|
||||
return format(wf)
|
||||
|
||||
|
||||
class Create(base.MistralLister):
|
||||
"""Create new workflow."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Create, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workflow definition file.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
action='store_true',
|
||||
help='With this flag workflow will be marked as "public".'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _validate_parsed_args(self, parsed_args):
|
||||
if not parsed_args.definition:
|
||||
raise RuntimeError("You must provide path to workflow "
|
||||
"definition file.")
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.workflows.create(
|
||||
parsed_args.definition.read(),
|
||||
scope=scope
|
||||
)
|
||||
|
||||
|
||||
class Delete(command.Command):
|
||||
"""Delete workflow."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Delete, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'workflow',
|
||||
nargs='+',
|
||||
help='Name or ID of workflow(s).'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
utils.do_action_on_many(
|
||||
lambda s: mistral_client.workflows.delete(s),
|
||||
parsed_args.workflow,
|
||||
"Request to delete workflow %s has been accepted.",
|
||||
"Unable to delete the specified workflow(s)."
|
||||
)
|
||||
|
||||
|
||||
class Update(base.MistralLister):
|
||||
"""Update workflow."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Update, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workflow definition'
|
||||
)
|
||||
parser.add_argument('--id', help='Workflow ID.')
|
||||
parser.add_argument(
|
||||
'--public',
|
||||
action='store_true',
|
||||
help='With this flag workflow will be marked as "public".'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def _get_format_function(self):
|
||||
return format_list
|
||||
|
||||
def _get_resources(self, parsed_args):
|
||||
scope = 'public' if parsed_args.public else 'private'
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
|
||||
return mistral_client.workflows.update(
|
||||
parsed_args.definition.read(),
|
||||
scope=scope,
|
||||
id=parsed_args.id
|
||||
)
|
||||
|
||||
|
||||
class GetDefinition(command.Command):
|
||||
"""Show workflow definition."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(GetDefinition, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument('identifier', help='Workflow ID or name.')
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
wf = mistral_client.workflows.get(parsed_args.identifier)
|
||||
|
||||
self.app.stdout.write(wf.definition or "\n")
|
||||
|
||||
|
||||
class Validate(show.ShowOne):
|
||||
"""Validate workflow."""
|
||||
|
||||
def _format(self, result=None):
|
||||
columns = ('Valid', 'Error')
|
||||
|
||||
if result:
|
||||
data = (result.get('valid'), result.get('error'),)
|
||||
else:
|
||||
data = (tuple('<none>' for _ in range(len(columns))),)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(Validate, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'definition',
|
||||
type=argparse.FileType('r'),
|
||||
help='Workflow definition file'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
mistral_client = self.app.client_manager.workflow_engine
|
||||
result = mistral_client.workflows.validate(
|
||||
parsed_args.definition.read()
|
||||
)
|
||||
|
||||
return self._format(result)
|
|
@ -1,40 +0,0 @@
|
|||
# Copyright 2013 - 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.
|
||||
|
||||
|
||||
class MistralClientException(Exception):
|
||||
"""Base Exception for Mistral client
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
a 'message' and 'code' properties.
|
||||
"""
|
||||
message = "An unknown exception occurred"
|
||||
code = "UNKNOWN_EXCEPTION"
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def __init__(self, message=message):
|
||||
self.message = message
|
||||
super(MistralClientException, self).__init__(
|
||||
'%s: %s' % (self.code, self.message))
|
||||
|
||||
|
||||
class IllegalArgumentException(MistralClientException):
|
||||
message = "IllegalArgumentException occurred"
|
||||
code = "ILLEGAL_ARGUMENT_EXCEPTION"
|
||||
|
||||
def __init__(self, message=None):
|
||||
if message:
|
||||
self.message = message
|
|
@ -1,22 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
See https://docs.openstack.org/oslo.i18n/latest/user/usage.html
|
||||
"""
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='mistralclient')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
|
@ -1,62 +0,0 @@
|
|||
#
|
||||
# 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.
|
||||
|
||||
"""OpenStackClient plugin for Workflow service."""
|
||||
|
||||
import logging
|
||||
|
||||
from osc_lib import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_WORKFLOW_API_VERSION = '2'
|
||||
API_VERSION_OPTION = 'os_workflow_api_version'
|
||||
API_NAME = 'workflow_engine'
|
||||
API_VERSIONS = {
|
||||
'2': 'mistralclient.api.v2.client.Client',
|
||||
}
|
||||
|
||||
|
||||
def make_client(instance):
|
||||
"""Returns a workflow_engine service client."""
|
||||
version = instance._api_version[API_NAME]
|
||||
workflow_client = utils.get_client_class(
|
||||
API_NAME,
|
||||
version,
|
||||
API_VERSIONS)
|
||||
|
||||
LOG.debug('Instantiating workflow engine client: %s', workflow_client)
|
||||
|
||||
mistral_url = instance.get_endpoint_for_service_type(
|
||||
'workflowv2',
|
||||
interface='publicURL'
|
||||
)
|
||||
|
||||
client = workflow_client(mistral_url=mistral_url, session=instance.session)
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def build_option_parser(parser):
|
||||
"""Hook to add global options."""
|
||||
parser.add_argument(
|
||||
'--os-workflow-api-version',
|
||||
metavar='<workflow-api-version>',
|
||||
default=utils.env(
|
||||
'OS_WORKFLOW_API_VERSION',
|
||||
default=DEFAULT_WORKFLOW_API_VERSION),
|
||||
help='Workflow API version, default=' +
|
||||
DEFAULT_WORKFLOW_API_VERSION +
|
||||
' (Env: OS_WORKFLOW_API_VERSION)')
|
||||
|
||||
return parser
|
|
@ -1,709 +0,0 @@
|
|||
# Copyright 2015 - StackStorm, 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.
|
||||
|
||||
"""
|
||||
Command-line interface to the Mistral APIs
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from cliff import app
|
||||
from cliff import commandmanager
|
||||
from osc_lib.command import command
|
||||
|
||||
from mistralclient.api import client
|
||||
from mistralclient.auth import auth_types
|
||||
import mistralclient.commands.v2.action_executions
|
||||
import mistralclient.commands.v2.actions
|
||||
import mistralclient.commands.v2.cron_triggers
|
||||
import mistralclient.commands.v2.environments
|
||||
import mistralclient.commands.v2.event_triggers
|
||||
import mistralclient.commands.v2.executions
|
||||
import mistralclient.commands.v2.members
|
||||
import mistralclient.commands.v2.services
|
||||
import mistralclient.commands.v2.tasks
|
||||
import mistralclient.commands.v2.workbooks
|
||||
import mistralclient.commands.v2.workflows
|
||||
from mistralclient import exceptions as exe
|
||||
|
||||
|
||||
def env(*args, **kwargs):
|
||||
"""Returns the first environment variable set.
|
||||
|
||||
If all are empty, defaults to '' or keyword arg `default`.
|
||||
"""
|
||||
for arg in args:
|
||||
value = os.environ.get(arg)
|
||||
if value:
|
||||
return value
|
||||
return kwargs.get('default', '')
|
||||
|
||||
|
||||
class OpenStackHelpFormatter(argparse.HelpFormatter):
|
||||
|
||||
def __init__(self, prog, indent_increment=2, max_help_position=32,
|
||||
width=None):
|
||||
super(OpenStackHelpFormatter, self).__init__(
|
||||
prog,
|
||||
indent_increment,
|
||||
max_help_position,
|
||||
width
|
||||
)
|
||||
|
||||
def start_section(self, heading):
|
||||
# Title-case the headings.
|
||||
heading = '%s%s' % (heading[0].upper(), heading[1:])
|
||||
super(OpenStackHelpFormatter, self).start_section(heading)
|
||||
|
||||
|
||||
class HelpAction(argparse.Action):
|
||||
"""Custom help action.
|
||||
|
||||
Provide a custom action so the -h and --help options
|
||||
to the main app will print a list of the commands.
|
||||
|
||||
The commands are determined by checking the CommandManager
|
||||
instance, passed in as the "default" value for the action.
|
||||
|
||||
"""
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
outputs = []
|
||||
max_len = 0
|
||||
app = self.default
|
||||
parser.print_help(app.stdout)
|
||||
app.stdout.write('\nCommands for API v2 :\n')
|
||||
|
||||
for name, ep in sorted(app.command_manager):
|
||||
factory = ep.load()
|
||||
cmd = factory(self, None)
|
||||
one_liner = cmd.get_description().split('\n')[0]
|
||||
outputs.append((name, one_liner))
|
||||
max_len = max(len(name), max_len)
|
||||
|
||||
for (name, one_liner) in outputs:
|
||||
app.stdout.write(' %s %s\n' % (name.ljust(max_len), one_liner))
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
class BashCompletionCommand(command.Command):
|
||||
"""Prints all of the commands and options for bash-completion."""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
commands = set()
|
||||
options = set()
|
||||
|
||||
for option, _action in self.app.parser._option_string_actions.items():
|
||||
options.add(option)
|
||||
|
||||
for command_name, _cmd in self.app.command_manager:
|
||||
commands.add(command_name)
|
||||
|
||||
print(' '.join(commands | options))
|
||||
|
||||
|
||||
class MistralShell(app.App):
|
||||
|
||||
def __init__(self):
|
||||
super(MistralShell, self).__init__(
|
||||
description=__doc__.strip(),
|
||||
version=mistralclient.__version__,
|
||||
command_manager=commandmanager.CommandManager('mistral.cli'),
|
||||
)
|
||||
|
||||
# Set v2 commands by default
|
||||
self._set_shell_commands(self._get_commands_v2())
|
||||
|
||||
def configure_logging(self):
|
||||
log_lvl = logging.DEBUG if self.options.debug else logging.WARNING
|
||||
logging.basicConfig(
|
||||
format="%(levelname)s (%(module)s) %(message)s",
|
||||
level=log_lvl
|
||||
)
|
||||
logging.getLogger('iso8601').setLevel(logging.WARNING)
|
||||
|
||||
if self.options.verbose_level <= 1:
|
||||
logging.getLogger('requests').setLevel(logging.WARNING)
|
||||
|
||||
def build_option_parser(self, description, version,
|
||||
argparse_kwargs=None):
|
||||
"""Return an argparse option parser for this application.
|
||||
|
||||
Subclasses may override this method to extend
|
||||
the parser with more global options.
|
||||
|
||||
:param description: full description of the application
|
||||
:paramtype description: str
|
||||
:param version: version number for the application
|
||||
:paramtype version: str
|
||||
:param argparse_kwargs: extra keyword argument passed to the
|
||||
ArgumentParser constructor
|
||||
:paramtype extra_kwargs: dict
|
||||
"""
|
||||
argparse_kwargs = argparse_kwargs or {}
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=description,
|
||||
add_help=False,
|
||||
formatter_class=OpenStackHelpFormatter,
|
||||
**argparse_kwargs
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--version',
|
||||
action='version',
|
||||
version='%(prog)s {0}'.format(version),
|
||||
help='Show program\'s version number and exit.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='count',
|
||||
dest='verbose_level',
|
||||
default=self.DEFAULT_VERBOSE_LEVEL,
|
||||
help='Increase verbosity of output. Can be repeated.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--log-file',
|
||||
action='store',
|
||||
default=None,
|
||||
help='Specify a file to log output. Disabled by default.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-q', '--quiet',
|
||||
action='store_const',
|
||||
dest='verbose_level',
|
||||
const=0,
|
||||
help='Suppress output except warnings and errors.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-h', '--help',
|
||||
action=HelpAction,
|
||||
nargs=0,
|
||||
default=self, # tricky
|
||||
help="Show this help message and exit.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--debug',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Show tracebacks on errors.',
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-url',
|
||||
action='store',
|
||||
dest='mistral_url',
|
||||
default=env('OS_MISTRAL_URL'),
|
||||
help='Mistral API host (Env: OS_MISTRAL_URL)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-version',
|
||||
action='store',
|
||||
dest='mistral_version',
|
||||
default=env('OS_MISTRAL_VERSION', default='v2'),
|
||||
help='Mistral API version (default = v2) (Env: '
|
||||
'OS_MISTRAL_VERSION)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-service-type',
|
||||
action='store',
|
||||
dest='service_type',
|
||||
default=env('OS_MISTRAL_SERVICE_TYPE', default='workflowv2'),
|
||||
help='Mistral service-type (should be the same name as in '
|
||||
'keystone-endpoint) (default = workflowv2) (Env: '
|
||||
'OS_MISTRAL_SERVICE_TYPE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-mistral-endpoint-type',
|
||||
action='store',
|
||||
dest='endpoint_type',
|
||||
default=env('OS_MISTRAL_ENDPOINT_TYPE', default='publicURL'),
|
||||
help='Mistral endpoint-type (should be the same name as in '
|
||||
'keystone-endpoint) (default = publicURL) (Env: '
|
||||
'OS_MISTRAL_ENDPOINT_TYPE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-username',
|
||||
action='store',
|
||||
dest='username',
|
||||
default=env('OS_USERNAME'),
|
||||
help='Authentication username (Env: OS_USERNAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-password',
|
||||
action='store',
|
||||
dest='password',
|
||||
default=env('OS_PASSWORD'),
|
||||
help='Authentication password (Env: OS_PASSWORD)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-tenant-id',
|
||||
action='store',
|
||||
dest='tenant_id',
|
||||
default=env('OS_TENANT_ID', 'OS_PROJECT_ID'),
|
||||
help='Authentication tenant identifier (Env: OS_TENANT_ID'
|
||||
' or OS_PROJECT_ID)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-id',
|
||||
action='store',
|
||||
dest='project_id',
|
||||
default=env('OS_TENANT_ID', 'OS_PROJECT_ID'),
|
||||
help='Authentication project identifier (Env: OS_TENANT_ID'
|
||||
' or OS_PROJECT_ID), will use tenant_id if both tenant_id'
|
||||
' and project_id are set'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-tenant-name',
|
||||
action='store',
|
||||
dest='tenant_name',
|
||||
default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME',
|
||||
default='Default'),
|
||||
help='Authentication tenant name (Env: OS_TENANT_NAME'
|
||||
' or OS_PROJECT_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-name',
|
||||
action='store',
|
||||
dest='project_name',
|
||||
default=env('OS_TENANT_NAME', 'OS_PROJECT_NAME',
|
||||
default='Default'),
|
||||
help='Authentication project name (Env: OS_TENANT_NAME'
|
||||
' or OS_PROJECT_NAME), will use tenant_name if both'
|
||||
' tenant_name and project_name are set'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-token',
|
||||
action='store',
|
||||
dest='token',
|
||||
default=env('OS_AUTH_TOKEN'),
|
||||
help='Authentication token (Env: OS_AUTH_TOKEN)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-project-domain-name',
|
||||
action='store',
|
||||
dest='project_domain_name',
|
||||
default=env('OS_PROJECT_DOMAIN_NAME', default='Default'),
|
||||
help='Authentication project domain name'
|
||||
' (Env: OS_PROJECT_DOMAIN_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-user-domain-name',
|
||||
action='store',
|
||||
dest='user_domain_name',
|
||||
default=env('OS_USER_DOMAIN_NAME', default='Default'),
|
||||
help='Authentication user domain name'
|
||||
' (Env: OS_USER_DOMAIN_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-url',
|
||||
action='store',
|
||||
dest='auth_url',
|
||||
default=env('OS_AUTH_URL'),
|
||||
help='Authentication URL (Env: OS_AUTH_URL)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cert',
|
||||
action='store',
|
||||
dest='os_cert',
|
||||
default=env('OS_CERT'),
|
||||
help='Client Certificate (Env: OS_CERT)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-key',
|
||||
action='store',
|
||||
dest='os_key',
|
||||
default=env('OS_KEY'),
|
||||
help='Client Key (Env: OS_KEY)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-cacert',
|
||||
action='store',
|
||||
dest='os_cacert',
|
||||
default=env('OS_CACERT'),
|
||||
help='Authentication CA Certificate (Env: OS_CACERT)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-region-name',
|
||||
action='store',
|
||||
dest='region_name',
|
||||
default=env('OS_REGION_NAME'),
|
||||
help='Region name (Env: OS_REGION_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--insecure',
|
||||
action='store_true',
|
||||
dest='insecure',
|
||||
default=env('MISTRALCLIENT_INSECURE', default=False),
|
||||
help='Disables SSL/TLS certificate verification '
|
||||
'(Env: MISTRALCLIENT_INSECURE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--auth-type',
|
||||
action='store',
|
||||
dest='auth_type',
|
||||
default=env('MISTRAL_AUTH_TYPE', default='keystone'),
|
||||
help='Authentication type. Valid options are: %s.'
|
||||
' (Env: MISTRAL_AUTH_TYPE)' % ', '.join(auth_types.ALL)
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--openid-client-id',
|
||||
action='store',
|
||||
dest='client_id',
|
||||
default=env('OPENID_CLIENT_ID'),
|
||||
help='Client ID (according to OpenID Connect).'
|
||||
' (Env: OPENID_CLIENT_ID)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--openid-client-secret',
|
||||
action='store',
|
||||
dest='client_secret',
|
||||
default=env('OPENID_CLIENT_SECRET'),
|
||||
help='Client secret (according to OpenID Connect)'
|
||||
' (Env: OPENID_CLIENT_SECRET)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-username',
|
||||
action='store',
|
||||
dest='target_username',
|
||||
default=env('OS_TARGET_USERNAME', default='admin'),
|
||||
help='Authentication username for target cloud'
|
||||
' (Env: OS_TARGET_USERNAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-password',
|
||||
action='store',
|
||||
dest='target_password',
|
||||
default=env('OS_TARGET_PASSWORD'),
|
||||
help='Authentication password for target cloud'
|
||||
' (Env: OS_TARGET_PASSWORD)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-tenant-id',
|
||||
action='store',
|
||||
dest='target_tenant_id',
|
||||
default=env('OS_TARGET_TENANT_ID'),
|
||||
help='Authentication tenant identifier for target cloud'
|
||||
' (Env: OS_TARGET_TENANT_ID)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-tenant-name',
|
||||
action='store',
|
||||
dest='target_tenant_name',
|
||||
default=env('OS_TARGET_TENANT_NAME', 'Default'),
|
||||
help='Authentication tenant name for target cloud'
|
||||
' (Env: OS_TARGET_TENANT_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-auth-token',
|
||||
action='store',
|
||||
dest='target_token',
|
||||
default=env('OS_TARGET_AUTH_TOKEN'),
|
||||
help='Authentication token for target cloud'
|
||||
' (Env: OS_TARGET_AUTH_TOKEN)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-auth-url',
|
||||
action='store',
|
||||
dest='target_auth_url',
|
||||
default=env('OS_TARGET_AUTH_URL'),
|
||||
help='Authentication URL for target cloud'
|
||||
' (Env: OS_TARGET_AUTH_URL)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target_cacert',
|
||||
action='store',
|
||||
dest='target_cacert',
|
||||
default=env('OS_TARGET_CACERT'),
|
||||
help='Authentication CA Certificate for target cloud'
|
||||
' (Env: OS_TARGET_CACERT)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-region-name',
|
||||
action='store',
|
||||
dest='target_region_name',
|
||||
default=env('OS_TARGET_REGION_NAME'),
|
||||
help='Region name for target cloud'
|
||||
'(Env: OS_TARGET_REGION_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-user-domain-name',
|
||||
action='store',
|
||||
dest='target_user_domain_name',
|
||||
default=env('OS_TARGET_USER_DOMAIN_NAME'),
|
||||
help='User domain name for target cloud'
|
||||
'(Env: OS_TARGET_USER_DOMAIN_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--os-target-project-domain-name',
|
||||
action='store',
|
||||
dest='target_project_domain_name',
|
||||
default=env('OS_TARGET_PROJECT_DOMAIN_NAME'),
|
||||
help='Project domain name for target cloud'
|
||||
'(Env: OS_TARGET_PROJECT_DOMAIN_NAME)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--target_insecure',
|
||||
action='store_true',
|
||||
dest='target_insecure',
|
||||
default=env('TARGET_MISTRALCLIENT_INSECURE', default=False),
|
||||
help='Disables SSL/TLS certificate verification for target cloud '
|
||||
'(Env: TARGET_MISTRALCLIENT_INSECURE)'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--profile',
|
||||
dest='profile',
|
||||
metavar='HMAC_KEY',
|
||||
default=env('OS_PROFILE'),
|
||||
help='HMAC key to use for encrypting context data for performance '
|
||||
'profiling of operation. This key should be one of the '
|
||||
'values configured for the osprofiler middleware in mistral, '
|
||||
'it is specified in the profiler section of the mistral '
|
||||
'configuration (i.e. /etc/mistral/mistral.conf). Without the '
|
||||
'key, profiling will not be triggered even if osprofiler is '
|
||||
'enabled on the server side.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
def initialize_app(self, argv):
|
||||
self._clear_shell_commands()
|
||||
|
||||
ver = client.determine_client_version(self.options.mistral_version)
|
||||
|
||||
self._set_shell_commands(self._get_commands(ver))
|
||||
|
||||
do_help = ('help' in argv) or ('-h' in argv) or not argv
|
||||
|
||||
# Set default for auth_url if not supplied. The default is not
|
||||
# set at the parser to support use cases where auth is not enabled.
|
||||
# An example use case would be a developer's environment.
|
||||
if not self.options.auth_url:
|
||||
if self.options.password or self.options.token:
|
||||
self.options.auth_url = 'http://localhost:35357/v3'
|
||||
|
||||
# bash-completion should not require authentification.
|
||||
if do_help or ('bash-completion' in argv):
|
||||
self.options.auth_url = None
|
||||
|
||||
if self.options.auth_url and not self.options.token:
|
||||
if not self.options.username:
|
||||
raise exe.IllegalArgumentException(
|
||||
("You must provide a username "
|
||||
"via --os-username env[OS_USERNAME]")
|
||||
)
|
||||
|
||||
if not self.options.password:
|
||||
raise exe.IllegalArgumentException(
|
||||
("You must provide a password "
|
||||
"via --os-password env[OS_PASSWORD]")
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
'cert': self.options.os_cert,
|
||||
'key': self.options.os_key,
|
||||
'user_domain_name': self.options.user_domain_name,
|
||||
'project_domain_name': self.options.project_domain_name
|
||||
}
|
||||
|
||||
self.client = client.client(
|
||||
mistral_url=self.options.mistral_url,
|
||||
username=self.options.username,
|
||||
api_key=self.options.password,
|
||||
project_name=self.options.tenant_name or self.options.project_name,
|
||||
auth_url=self.options.auth_url,
|
||||
project_id=self.options.tenant_id or self.options.project_id,
|
||||
endpoint_type=self.options.endpoint_type,
|
||||
service_type=self.options.service_type,
|
||||
region_name=self.options.region_name,
|
||||
auth_token=self.options.token,
|
||||
cacert=self.options.os_cacert,
|
||||
insecure=self.options.insecure,
|
||||
profile=self.options.profile,
|
||||
auth_type=self.options.auth_type,
|
||||
client_id=self.options.client_id,
|
||||
client_secret=self.options.client_secret,
|
||||
target_username=self.options.target_username,
|
||||
target_api_key=self.options.target_password,
|
||||
target_project_name=self.options.target_tenant_name,
|
||||
target_auth_url=self.options.target_auth_url,
|
||||
target_project_id=self.options.target_tenant_id,
|
||||
target_auth_token=self.options.target_token,
|
||||
target_cacert=self.options.target_cacert,
|
||||
target_region_name=self.options.target_region_name,
|
||||
target_insecure=self.options.target_insecure,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
# Adding client_manager variable to make mistral client work with
|
||||
# unified OpenStack client.
|
||||
ClientManager = type(
|
||||
'ClientManager',
|
||||
(object,),
|
||||
dict(workflow_engine=self.client)
|
||||
)
|
||||
|
||||
self.client_manager = ClientManager()
|
||||
|
||||
def _set_shell_commands(self, cmds_dict):
|
||||
for k, v in cmds_dict.items():
|
||||
self.command_manager.add_command(k, v)
|
||||
|
||||
def _clear_shell_commands(self):
|
||||
exclude_cmds = ['help', 'complete']
|
||||
|
||||
cmds = self.command_manager.commands.copy()
|
||||
for k, v in cmds.items():
|
||||
if k not in exclude_cmds:
|
||||
self.command_manager.commands.pop(k)
|
||||
|
||||
def _get_commands(self, version):
|
||||
if version == 2:
|
||||
return self._get_commands_v2()
|
||||
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def _get_commands_v2():
|
||||
return {
|
||||
'bash-completion': BashCompletionCommand,
|
||||
'workbook-list': mistralclient.commands.v2.workbooks.List,
|
||||
'workbook-get': mistralclient.commands.v2.workbooks.Get,
|
||||
'workbook-create': mistralclient.commands.v2.workbooks.Create,
|
||||
'workbook-delete': mistralclient.commands.v2.workbooks.Delete,
|
||||
'workbook-update': mistralclient.commands.v2.workbooks.Update,
|
||||
'workbook-get-definition':
|
||||
mistralclient.commands.v2.workbooks.GetDefinition,
|
||||
'workbook-validate': mistralclient.commands.v2.workbooks.Validate,
|
||||
'workflow-list': mistralclient.commands.v2.workflows.List,
|
||||
'workflow-get': mistralclient.commands.v2.workflows.Get,
|
||||
'workflow-create': mistralclient.commands.v2.workflows.Create,
|
||||
'workflow-delete': mistralclient.commands.v2.workflows.Delete,
|
||||
'workflow-update': mistralclient.commands.v2.workflows.Update,
|
||||
'workflow-get-definition':
|
||||
mistralclient.commands.v2.workflows.GetDefinition,
|
||||
'workflow-validate': mistralclient.commands.v2.workflows.Validate,
|
||||
'environment-create':
|
||||
mistralclient.commands.v2.environments.Create,
|
||||
'environment-delete':
|
||||
mistralclient.commands.v2.environments.Delete,
|
||||
'environment-update':
|
||||
mistralclient.commands.v2.environments.Update,
|
||||
'environment-list': mistralclient.commands.v2.environments.List,
|
||||
'environment-get': mistralclient.commands.v2.environments.Get,
|
||||
'run-action': mistralclient.commands.v2.action_executions.Create,
|
||||
'action-execution-list':
|
||||
mistralclient.commands.v2.action_executions.List,
|
||||
'action-execution-get':
|
||||
mistralclient.commands.v2.action_executions.Get,
|
||||
'action-execution-get-input':
|
||||
mistralclient.commands.v2.action_executions.GetInput,
|
||||
'action-execution-get-output':
|
||||
mistralclient.commands.v2.action_executions.GetOutput,
|
||||
'action-execution-update':
|
||||
mistralclient.commands.v2.action_executions.Update,
|
||||
'action-execution-delete':
|
||||
mistralclient.commands.v2.action_executions.Delete,
|
||||
'execution-create': mistralclient.commands.v2.executions.Create,
|
||||
'execution-delete': mistralclient.commands.v2.executions.Delete,
|
||||
'execution-update': mistralclient.commands.v2.executions.Update,
|
||||
'execution-list': mistralclient.commands.v2.executions.List,
|
||||
'execution-get': mistralclient.commands.v2.executions.Get,
|
||||
'execution-get-input':
|
||||
mistralclient.commands.v2.executions.GetInput,
|
||||
'execution-get-output':
|
||||
mistralclient.commands.v2.executions.GetOutput,
|
||||
'task-list': mistralclient.commands.v2.tasks.List,
|
||||
'task-get': mistralclient.commands.v2.tasks.Get,
|
||||
'task-get-published': mistralclient.commands.v2.tasks.GetPublished,
|
||||
'task-get-result': mistralclient.commands.v2.tasks.GetResult,
|
||||
'task-rerun': mistralclient.commands.v2.tasks.Rerun,
|
||||
'action-list': mistralclient.commands.v2.actions.List,
|
||||
'action-get': mistralclient.commands.v2.actions.Get,
|
||||
'action-create': mistralclient.commands.v2.actions.Create,
|
||||
'action-delete': mistralclient.commands.v2.actions.Delete,
|
||||
'action-update': mistralclient.commands.v2.actions.Update,
|
||||
'action-get-definition':
|
||||
mistralclient.commands.v2.actions.GetDefinition,
|
||||
'action-validate': mistralclient.commands.v2.actions.Validate,
|
||||
'cron-trigger-list': mistralclient.commands.v2.cron_triggers.List,
|
||||
'cron-trigger-get': mistralclient.commands.v2.cron_triggers.Get,
|
||||
'cron-trigger-create':
|
||||
mistralclient.commands.v2.cron_triggers.Create,
|
||||
'cron-trigger-delete':
|
||||
mistralclient.commands.v2.cron_triggers.Delete,
|
||||
'event-trigger-list':
|
||||
mistralclient.commands.v2.event_triggers.List,
|
||||
'event-trigger-get': mistralclient.commands.v2.event_triggers.Get,
|
||||
'event-trigger-create':
|
||||
mistralclient.commands.v2.event_triggers.Create,
|
||||
'event-trigger-delete':
|
||||
mistralclient.commands.v2.event_triggers.Delete,
|
||||
'service-list': mistralclient.commands.v2.services.List,
|
||||
'member-create': mistralclient.commands.v2.members.Create,
|
||||
'member-delete': mistralclient.commands.v2.members.Delete,
|
||||
'member-update': mistralclient.commands.v2.members.Update,
|
||||
'member-list': mistralclient.commands.v2.members.List,
|
||||
'member-get': mistralclient.commands.v2.members.Get,
|
||||
}
|
||||
|
||||
|
||||
def main(argv=sys.argv[1:]):
|
||||
return MistralShell().run(argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
|
@ -1,148 +0,0 @@
|
|||
# Copyright (c) 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 os
|
||||
|
||||
from six.moves import configparser
|
||||
from tempest.lib.cli import base
|
||||
|
||||
|
||||
CLI_DIR = os.environ.get(
|
||||
'OS_MISTRALCLIENT_EXEC_DIR',
|
||||
os.path.join(os.path.abspath('.'), '.tox/functional/bin')
|
||||
)
|
||||
_CREDS_FILE = 'functional_creds.conf'
|
||||
|
||||
|
||||
def credentials(group='admin'):
|
||||
"""Retrieves credentials to run functional tests.
|
||||
|
||||
Credentials are either read from the environment or from a config file
|
||||
('functional_creds.conf'). Environment variables override those from the
|
||||
config file.
|
||||
|
||||
The 'functional_creds.conf' file is the clean and new way to use (by
|
||||
default tox 2.0 does not pass environment variables).
|
||||
"""
|
||||
if group == 'admin':
|
||||
username = os.environ.get('OS_USERNAME')
|
||||
password = os.environ.get('OS_PASSWORD')
|
||||
tenant_name = os.environ.get('OS_TENANT_NAME')
|
||||
else:
|
||||
username = os.environ.get('OS_ALT_USERNAME')
|
||||
password = os.environ.get('OS_ALT_PASSWORD')
|
||||
tenant_name = os.environ.get('OS_ALT_TENANT_NAME')
|
||||
|
||||
auth_url = os.environ.get('OS_AUTH_URL')
|
||||
|
||||
config = configparser.RawConfigParser()
|
||||
if config.read(_CREDS_FILE):
|
||||
username = username or config.get(group, 'user')
|
||||
password = password or config.get(group, 'pass')
|
||||
tenant_name = tenant_name or config.get(group, 'tenant')
|
||||
auth_url = auth_url or config.get('auth', 'uri')
|
||||
|
||||
# TODO(ddeja): Default value of OS_AUTH_URL is to provide url to v3 API.
|
||||
# Since tempest openstack client doesn't properly handle it, we switch
|
||||
# it back to v2. Once tempest openstack starts to use v3, this can be
|
||||
# deleted.
|
||||
# https://github.com/openstack/tempest/blob/master/tempest/lib/cli/base.py#L363
|
||||
return {
|
||||
'username': username,
|
||||
'password': password,
|
||||
'tenant_name': tenant_name,
|
||||
'auth_url': auth_url.replace('v3', 'v2.0')
|
||||
}
|
||||
|
||||
|
||||
class MistralCLIAuth(base.ClientTestBase):
|
||||
|
||||
_mistral_url = None
|
||||
|
||||
def _get_admin_clients(self):
|
||||
creds = credentials()
|
||||
|
||||
clients = base.CLIClient(
|
||||
username=creds['username'],
|
||||
password=creds['password'],
|
||||
tenant_name=creds['tenant_name'],
|
||||
uri=creds['auth_url'],
|
||||
cli_dir=CLI_DIR
|
||||
)
|
||||
|
||||
return clients
|
||||
|
||||
def _get_clients(self):
|
||||
return self._get_admin_clients()
|
||||
|
||||
def mistral(self, action, flags='', params='', fail_ok=False):
|
||||
"""Executes Mistral command."""
|
||||
mistral_url_op = "--os-mistral-url %s" % self._mistral_url
|
||||
|
||||
if 'WITHOUT_AUTH' in os.environ:
|
||||
return base.execute(
|
||||
'mistral %s' % mistral_url_op,
|
||||
action,
|
||||
flags,
|
||||
params,
|
||||
fail_ok,
|
||||
merge_stderr=False,
|
||||
cli_dir=''
|
||||
)
|
||||
else:
|
||||
return self.clients.cmd_with_auth(
|
||||
'mistral %s' % mistral_url_op,
|
||||
action,
|
||||
flags,
|
||||
params,
|
||||
fail_ok
|
||||
)
|
||||
|
||||
def get_project_id(self, project='admin'):
|
||||
project_name = credentials(project)['tenant_name']
|
||||
|
||||
admin_clients = self._get_clients()
|
||||
projects = self.parser.listing(
|
||||
admin_clients.openstack('project show', params=project_name)
|
||||
)
|
||||
|
||||
return [o['Value'] for o in projects if o['Field'] == 'id'][0]
|
||||
|
||||
|
||||
class MistralCLIAltAuth(base.ClientTestBase):
|
||||
|
||||
_mistral_url = None
|
||||
|
||||
def _get_alt_clients(self):
|
||||
creds = credentials('demo')
|
||||
|
||||
clients = base.CLIClient(
|
||||
username=creds['username'],
|
||||
password=creds['password'],
|
||||
tenant_name=creds['tenant_name'],
|
||||
uri=creds['auth_url'],
|
||||
cli_dir=CLI_DIR
|
||||
)
|
||||
|
||||
return clients
|
||||
|
||||
def _get_clients(self):
|
||||
return self._get_alt_clients()
|
||||
|
||||
def mistral_alt(self, action, flags='', params='', mode='alt_user'):
|
||||
"""Executes Mistral command for alt_user from alt_tenant."""
|
||||
mistral_url_op = "--os-mistral-url %s" % self._mistral_url
|
||||
|
||||
return self.clients.cmd_with_auth(
|
||||
'mistral %s' % mistral_url_op, action, flags, params)
|
|
@ -1,279 +0,0 @@
|
|||
# Copyright (c) 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 os
|
||||
import time
|
||||
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from mistralclient.tests.functional.cli import base
|
||||
|
||||
|
||||
MISTRAL_URL = "http://localhost:8989/v2"
|
||||
|
||||
|
||||
class MistralClientTestBase(base.MistralCLIAuth, base.MistralCLIAltAuth):
|
||||
|
||||
_mistral_url = MISTRAL_URL
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(MistralClientTestBase, cls).setUpClass()
|
||||
|
||||
cls.wb_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/wb_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.wb_with_tags_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/wb_with_tags_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.wf_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/wf_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.wf_single_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/wf_single_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.wf_with_delay_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/wf_delay_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.wf_wrapping_wf = os.path.relpath(
|
||||
'functionaltests/resources/v2/wf_wrapping_wf_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.act_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/action_v2.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
cls.act_tag_def = os.path.relpath(
|
||||
'functionaltests/resources/v2/action_v2_tags.yaml', os.getcwd()
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(MistralClientTestBase, self).setUp()
|
||||
|
||||
def get_field_value(self, obj, field):
|
||||
return [
|
||||
o['Value'] for o in obj
|
||||
if o['Field'] == "{0}".format(field)
|
||||
][0]
|
||||
|
||||
def get_item_info(self, get_from, get_by, value):
|
||||
return [i for i in get_from if i[get_by] == value][0]
|
||||
|
||||
def mistral_admin(self, cmd, params=""):
|
||||
self.clients = self._get_admin_clients()
|
||||
|
||||
return self.parser.listing(
|
||||
self.mistral('{0}'.format(cmd), params='{0}'.format(params))
|
||||
)
|
||||
|
||||
def mistral_alt_user(self, cmd, params=""):
|
||||
self.clients = self._get_alt_clients()
|
||||
|
||||
return self.parser.listing(
|
||||
self.mistral_alt('{0}'.format(cmd), params='{0}'.format(params))
|
||||
)
|
||||
|
||||
def mistral_cli(self, admin, cmd, params=''):
|
||||
if admin:
|
||||
return self.mistral_admin(cmd, params)
|
||||
else:
|
||||
return self.mistral_alt_user(cmd, params)
|
||||
|
||||
def workbook_create(self, wb_def, admin=True):
|
||||
wb = self.mistral_cli(
|
||||
admin,
|
||||
'workbook-create',
|
||||
params='{0}'.format(wb_def)
|
||||
)
|
||||
|
||||
wb_name = self.get_field_value(wb, "Name")
|
||||
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'workbook-delete',
|
||||
params=wb_name
|
||||
)
|
||||
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'workflow-delete',
|
||||
params='wb.wf1'
|
||||
)
|
||||
|
||||
return wb
|
||||
|
||||
def workflow_create(self, wf_def, admin=True, scope='private'):
|
||||
params = '{0}'.format(wf_def)
|
||||
|
||||
if scope == 'public':
|
||||
params += ' --public'
|
||||
|
||||
wf = self.mistral_cli(
|
||||
admin,
|
||||
'workflow-create',
|
||||
params=params
|
||||
)
|
||||
|
||||
for workflow in wf:
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'workflow-delete',
|
||||
params=workflow['ID']
|
||||
)
|
||||
|
||||
return wf
|
||||
|
||||
def workflow_member_create(self, wf_id):
|
||||
cmd_param = (
|
||||
'%s workflow %s' % (wf_id, self.get_project_id("demo"))
|
||||
)
|
||||
member = self.mistral_admin("member-create", params=cmd_param)
|
||||
|
||||
self.addCleanup(
|
||||
self.mistral_admin,
|
||||
'member-delete',
|
||||
params=cmd_param
|
||||
)
|
||||
|
||||
return member
|
||||
|
||||
def action_create(self, act_def, admin=True, scope='private'):
|
||||
params = '{0}'.format(act_def)
|
||||
|
||||
if scope == 'public':
|
||||
params += ' --public'
|
||||
|
||||
acts = self.mistral_cli(
|
||||
admin,
|
||||
'action-create',
|
||||
params=params
|
||||
)
|
||||
|
||||
for action in acts:
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'action-delete',
|
||||
params=action['Name']
|
||||
)
|
||||
|
||||
return acts
|
||||
|
||||
def cron_trigger_create(self, name, wf_name, wf_input, pattern=None,
|
||||
count=None, first_time=None, admin=True):
|
||||
optional_params = ""
|
||||
|
||||
if pattern:
|
||||
optional_params += ' --pattern "{}"'.format(pattern)
|
||||
if count:
|
||||
optional_params += ' --count {}'.format(count)
|
||||
if first_time:
|
||||
optional_params += ' --first-time "{}"'.format(first_time)
|
||||
|
||||
trigger = self.mistral_cli(
|
||||
admin,
|
||||
'cron-trigger-create',
|
||||
params='{} {} {} {}'.format(name, wf_name, wf_input,
|
||||
optional_params))
|
||||
|
||||
self.addCleanup(self.mistral_cli,
|
||||
admin,
|
||||
'cron-trigger-delete',
|
||||
params=name)
|
||||
|
||||
return trigger
|
||||
|
||||
def event_trigger_create(self, name, wf_id, exchange,
|
||||
topic, event, wf_input, admin=True):
|
||||
|
||||
trigger = self.mistral_cli(
|
||||
admin,
|
||||
'event-trigger-create',
|
||||
params=' '.join((name, wf_id, exchange, topic, event, wf_input)))
|
||||
ev_tr_id = self.get_field_value(trigger, 'ID')
|
||||
self.addCleanup(self.mistral_cli,
|
||||
admin,
|
||||
'event-trigger-delete',
|
||||
params=ev_tr_id)
|
||||
|
||||
return trigger
|
||||
|
||||
def execution_create(self, params, admin=True):
|
||||
ex = self.mistral_cli(admin, 'execution-create', params=params)
|
||||
exec_id = self.get_field_value(ex, 'ID')
|
||||
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'execution-delete',
|
||||
params=exec_id
|
||||
)
|
||||
|
||||
return ex
|
||||
|
||||
def environment_create(self, params, admin=True):
|
||||
env = self.mistral_cli(admin, 'environment-create', params=params)
|
||||
env_name = self.get_field_value(env, 'Name')
|
||||
|
||||
self.addCleanup(
|
||||
self.mistral_cli,
|
||||
admin,
|
||||
'environment-delete',
|
||||
params=env_name
|
||||
)
|
||||
|
||||
return env
|
||||
|
||||
def create_file(self, file_name, file_body=""):
|
||||
f = open(file_name, 'w')
|
||||
f.write(file_body)
|
||||
f.close()
|
||||
|
||||
self.addCleanup(os.remove, file_name)
|
||||
|
||||
def wait_execution_success(self, exec_id, timeout=180):
|
||||
start_time = time.time()
|
||||
|
||||
ex = self.mistral_admin('execution-get', params=exec_id)
|
||||
exec_state = self.get_field_value(ex, 'State')
|
||||
|
||||
expected_states = ['SUCCESS', 'RUNNING']
|
||||
|
||||
while exec_state != 'SUCCESS':
|
||||
if time.time() - start_time > timeout:
|
||||
msg = ("Execution exceeds timeout {0} to change state "
|
||||
"to SUCCESS. Execution: {1}".format(timeout, ex))
|
||||
|
||||
raise exceptions.TimeoutException(msg)
|
||||
|
||||
ex = self.mistral_admin('execution-get', params=exec_id)
|
||||
exec_state = self.get_field_value(ex, 'State')
|
||||
|
||||
if exec_state not in expected_states:
|
||||
msg = ("Execution state %s is not in expected "
|
||||
"states: %s" % (exec_state, expected_states))
|
||||
|
||||
raise exceptions.TempestException(msg)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
return True
|
|
@ -1,488 +0,0 @@
|
|||
# Copyright (c) 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.
|
||||
|
||||
from tempest.lib import exceptions
|
||||
|
||||
from mistralclient.tests.functional.cli.v2 import base_v2
|
||||
|
||||
|
||||
class StandardItemsAvailabilityCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_std_workflows_availability(self):
|
||||
wfs = self.mistral_admin("workflow-list")
|
||||
|
||||
self.assertTableStruct(
|
||||
wfs,
|
||||
["Name", "Tags", "Input", "Created at", "Updated at"]
|
||||
)
|
||||
self.assertIn("std.create_instance",
|
||||
[workflow["Name"] for workflow in wfs])
|
||||
|
||||
wfs = self.mistral_alt_user("workflow-list")
|
||||
|
||||
self.assertTableStruct(
|
||||
wfs,
|
||||
["Name", "Tags", "Input", "Created at", "Updated at"]
|
||||
)
|
||||
self.assertIn("std.create_instance",
|
||||
[workflow["Name"] for workflow in wfs])
|
||||
|
||||
def test_std_actions_availability(self):
|
||||
acts = self.mistral_admin("action-list")
|
||||
|
||||
self.assertTableStruct(
|
||||
acts,
|
||||
["Name", "Is system", "Input", "Description",
|
||||
"Tags", "Created at", "Updated at"]
|
||||
)
|
||||
self.assertIn("glance.images_list",
|
||||
[action["Name"] for action in acts])
|
||||
|
||||
acts = self.mistral_alt_user("action-list")
|
||||
|
||||
self.assertTableStruct(
|
||||
acts,
|
||||
["Name", "Is system", "Input", "Description",
|
||||
"Tags", "Created at", "Updated at"]
|
||||
)
|
||||
self.assertIn("glance.images_list",
|
||||
[action["Name"] for action in acts])
|
||||
|
||||
|
||||
class WorkbookIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_workbook_name_uniqueness(self):
|
||||
self.workbook_create(self.wb_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_admin,
|
||||
"workbook-create",
|
||||
params="{0}".format(self.wb_def)
|
||||
)
|
||||
|
||||
self.workbook_create(self.wb_def, admin=False)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workbook-create",
|
||||
params="{0}".format(self.wb_def)
|
||||
)
|
||||
|
||||
def test_wb_isolation(self):
|
||||
wb = self.workbook_create(self.wb_def)
|
||||
wb_name = self.get_field_value(wb, "Name")
|
||||
wbs = self.mistral_admin("workbook-list")
|
||||
|
||||
self.assertIn(wb_name, [w["Name"] for w in wbs])
|
||||
|
||||
alt_wbs = self.mistral_alt_user("workbook-list")
|
||||
|
||||
self.assertNotIn(wb_name, [w["Name"] for w in alt_wbs])
|
||||
|
||||
def test_get_wb_from_another_tenant(self):
|
||||
wb = self.workbook_create(self.wb_def)
|
||||
name = self.get_field_value(wb, "Name")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workbook-get",
|
||||
params=name
|
||||
)
|
||||
|
||||
def test_delete_wb_from_another_tenant(self):
|
||||
wb = self.workbook_create(self.wb_def)
|
||||
name = self.get_field_value(wb, "Name")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workbook-delete",
|
||||
params=name
|
||||
)
|
||||
|
||||
|
||||
class WorkflowIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_workflow_name_uniqueness(self):
|
||||
self.workflow_create(self.wf_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_admin,
|
||||
"workflow-create",
|
||||
params="{0}".format(self.wf_def)
|
||||
)
|
||||
|
||||
self.workflow_create(self.wf_def, admin=False)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workflow-create",
|
||||
params="{0}".format(self.wf_def)
|
||||
)
|
||||
|
||||
def test_wf_isolation(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
wfs = self.mistral_admin("workflow-list")
|
||||
|
||||
self.assertIn(wf[0]["Name"], [w["Name"] for w in wfs])
|
||||
|
||||
alt_wfs = self.mistral_alt_user("workflow-list")
|
||||
|
||||
self.assertNotIn(wf[0]["Name"], [w["Name"] for w in alt_wfs])
|
||||
|
||||
def test_get_wf_from_another_tenant(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workflow-get",
|
||||
params=wf[0]["ID"]
|
||||
)
|
||||
|
||||
def test_create_public_workflow(self):
|
||||
wf = self.workflow_create(self.wf_def, scope='public')
|
||||
|
||||
same_wf = self.mistral_alt_user(
|
||||
"workflow-get",
|
||||
params=wf[0]["Name"]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
wf[0]["Name"],
|
||||
self.get_field_value(same_wf, "Name")
|
||||
)
|
||||
|
||||
def test_delete_wf_from_another_tenant(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"workflow-delete",
|
||||
params=wf[0]["ID"]
|
||||
)
|
||||
|
||||
|
||||
class WorkflowSharingCLITests(base_v2.MistralClientTestBase):
|
||||
def setUp(self):
|
||||
super(WorkflowSharingCLITests, self).setUp()
|
||||
|
||||
self.wf = self.workflow_create(self.wf_def, admin=True)
|
||||
|
||||
def _update_shared_workflow(self, new_status='accepted'):
|
||||
member = self.workflow_member_create(self.wf[0]["ID"])
|
||||
status = self.get_field_value(member, 'Status')
|
||||
|
||||
self.assertEqual('pending', status)
|
||||
|
||||
cmd_param = '%s workflow --status %s' % (self.wf[0]["ID"], new_status)
|
||||
member = self.mistral_alt_user("member-update", params=cmd_param)
|
||||
status = self.get_field_value(member, 'Status')
|
||||
|
||||
self.assertEqual(new_status, status)
|
||||
|
||||
def test_list_accepted_shared_workflow(self):
|
||||
wfs = self.mistral_alt_user("workflow-list")
|
||||
|
||||
self.assertNotIn(self.wf[0]["ID"], [w["ID"] for w in wfs])
|
||||
|
||||
self._update_shared_workflow(new_status='accepted')
|
||||
alt_wfs = self.mistral_alt_user("workflow-list")
|
||||
|
||||
self.assertIn(self.wf[0]["ID"], [w["ID"] for w in alt_wfs])
|
||||
self.assertIn(
|
||||
self.get_project_id("admin"),
|
||||
[w["Project ID"] for w in alt_wfs]
|
||||
)
|
||||
|
||||
def test_list_rejected_shared_workflow(self):
|
||||
self._update_shared_workflow(new_status='rejected')
|
||||
alt_wfs = self.mistral_alt_user("workflow-list")
|
||||
|
||||
self.assertNotIn(self.wf[0]["ID"], [w["ID"] for w in alt_wfs])
|
||||
|
||||
def test_create_execution_using_shared_workflow(self):
|
||||
self._update_shared_workflow(new_status='accepted')
|
||||
|
||||
execution = self.execution_create(self.wf[0]["ID"], admin=False)
|
||||
wf_name = self.get_field_value(execution, 'Workflow name')
|
||||
|
||||
self.assertEqual(self.wf[0]["Name"], wf_name)
|
||||
|
||||
def test_create_contrigger_using_shared_workflow(self):
|
||||
self._update_shared_workflow(new_status='accepted')
|
||||
|
||||
trigger = self.cron_trigger_create(
|
||||
"test_trigger",
|
||||
self.wf[0]["ID"],
|
||||
"{}",
|
||||
"5 * * * *",
|
||||
admin=False
|
||||
)
|
||||
wf_name = self.get_field_value(trigger, 'Workflow')
|
||||
|
||||
self.assertEqual(self.wf[0]["Name"], wf_name)
|
||||
|
||||
# Admin project can not delete the shared workflow, because it is used
|
||||
# in a cron-trigger of another project.
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_admin,
|
||||
'workflow-delete',
|
||||
params=self.wf[0]["ID"]
|
||||
)
|
||||
|
||||
|
||||
class ActionIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_actions_name_uniqueness(self):
|
||||
self.action_create(self.act_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_admin,
|
||||
"action-create",
|
||||
params="{0}".format(self.act_def)
|
||||
)
|
||||
|
||||
self.action_create(self.act_def, admin=False)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"action-create",
|
||||
params="{0}".format(self.act_def)
|
||||
)
|
||||
|
||||
def test_action_isolation(self):
|
||||
act = self.action_create(self.act_def)
|
||||
acts = self.mistral_admin("action-list")
|
||||
|
||||
self.assertIn(act[0]["Name"], [a["Name"] for a in acts])
|
||||
|
||||
alt_acts = self.mistral_alt_user("action-list")
|
||||
|
||||
self.assertNotIn(act[0]["Name"], [a["Name"] for a in alt_acts])
|
||||
|
||||
def test_get_action_from_another_tenant(self):
|
||||
act = self.action_create(self.act_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"action-get",
|
||||
params=act[0]["Name"]
|
||||
)
|
||||
|
||||
def test_delete_action_from_another_tenant(self):
|
||||
act = self.action_create(self.act_def)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"action-delete",
|
||||
params=act[0]["Name"]
|
||||
)
|
||||
|
||||
def test_create_public_action(self):
|
||||
act = self.action_create(self.act_def, scope='public')
|
||||
|
||||
same_act = self.mistral_alt_user(
|
||||
"action-get",
|
||||
params=act[0]["Name"]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
act[0]["Name"],
|
||||
self.get_field_value(same_act, "Name")
|
||||
)
|
||||
|
||||
|
||||
class CronTriggerIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
def test_cron_trigger_name_uniqueness(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
self.cron_trigger_create(
|
||||
"admin_trigger",
|
||||
wf[0]["ID"],
|
||||
"{}",
|
||||
"5 * * * *"
|
||||
)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.cron_trigger_create,
|
||||
"admin_trigger",
|
||||
wf[0]["ID"],
|
||||
"{}"
|
||||
"5 * * * *",
|
||||
)
|
||||
|
||||
wf = self.workflow_create(self.wf_def, admin=False)
|
||||
self.cron_trigger_create(
|
||||
"user_trigger",
|
||||
wf[0]["ID"],
|
||||
"{}",
|
||||
"5 * * * *",
|
||||
None,
|
||||
None,
|
||||
admin=False
|
||||
)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.cron_trigger_create,
|
||||
"user_trigger",
|
||||
wf[0]["ID"],
|
||||
"{}",
|
||||
"5 * * * *",
|
||||
None,
|
||||
None,
|
||||
admin=False
|
||||
)
|
||||
|
||||
def test_cron_trigger_isolation(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
self.cron_trigger_create(
|
||||
"trigger", wf[0]["Name"], "{}", "5 * * * *")
|
||||
|
||||
alt_trs = self.mistral_alt_user("cron-trigger-list")
|
||||
|
||||
self.assertNotIn("trigger", [t["Name"] for t in alt_trs])
|
||||
|
||||
|
||||
class ExecutionIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_execution_isolation(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
ex = self.execution_create(wf[0]["Name"])
|
||||
exec_id = self.get_field_value(ex, "ID")
|
||||
|
||||
execs = self.mistral_admin("execution-list")
|
||||
self.assertIn(exec_id, [e["ID"] for e in execs])
|
||||
|
||||
alt_execs = self.mistral_alt_user("execution-list")
|
||||
self.assertNotIn(exec_id, [e["ID"] for e in alt_execs])
|
||||
|
||||
def test_get_execution_from_another_tenant(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
ex = self.execution_create(wf[0]["Name"])
|
||||
exec_id = self.get_field_value(ex, "ID")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"execution-get",
|
||||
params=exec_id
|
||||
)
|
||||
|
||||
|
||||
class EnvironmentIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(EnvironmentIsolationCLITests, self).setUp()
|
||||
|
||||
self.env_file = "env.yaml"
|
||||
self.create_file("{0}".format(self.env_file),
|
||||
"name: env\n"
|
||||
"description: Test env\n"
|
||||
"variables:\n"
|
||||
" var: value")
|
||||
|
||||
def test_environment_name_uniqueness(self):
|
||||
self.environment_create(self.env_file)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_admin,
|
||||
"environment-create",
|
||||
params=self.env_file
|
||||
)
|
||||
|
||||
self.environment_create(self.env_file, admin=False)
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"environment-create",
|
||||
params=self.env_file
|
||||
)
|
||||
|
||||
def test_environment_isolation(self):
|
||||
env = self.environment_create(self.env_file)
|
||||
env_name = self.get_field_value(env, "Name")
|
||||
envs = self.mistral_admin("environment-list")
|
||||
|
||||
self.assertIn(env_name, [en["Name"] for en in envs])
|
||||
|
||||
alt_envs = self.mistral_alt_user("environment-list")
|
||||
|
||||
self.assertNotIn(env_name, [en["Name"] for en in alt_envs])
|
||||
|
||||
def test_get_env_from_another_tenant(self):
|
||||
env = self.environment_create(self.env_file)
|
||||
env_name = self.get_field_value(env, "Name")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"environment-get",
|
||||
params=env_name
|
||||
)
|
||||
|
||||
def test_delete_env_from_another_tenant(self):
|
||||
env = self.environment_create(self.env_file)
|
||||
env_name = self.get_field_value(env, "Name")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"environment-delete",
|
||||
params=env_name
|
||||
)
|
||||
|
||||
|
||||
class ActionExecutionIsolationCLITests(base_v2.MistralClientTestBase):
|
||||
|
||||
def test_action_execution_isolation(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
wf_exec = self.execution_create(wf[0]["Name"])
|
||||
direct_ex_id = self.get_field_value(wf_exec, 'ID')
|
||||
|
||||
self.wait_execution_success(direct_ex_id)
|
||||
|
||||
act_execs = self.mistral_admin("action-execution-list")
|
||||
self.assertIn(wf[0]["Name"],
|
||||
[act["Workflow name"] for act in act_execs])
|
||||
|
||||
alt_act_execs = self.mistral_alt_user("action-execution-list")
|
||||
self.assertNotIn(wf[0]["Name"],
|
||||
[act["Workflow name"] for act in alt_act_execs])
|
||||
|
||||
def test_get_action_execution_from_another_tenant(self):
|
||||
wf = self.workflow_create(self.wf_def)
|
||||
ex = self.execution_create(wf[0]["Name"])
|
||||
exec_id = self.get_field_value(ex, "ID")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.CommandFailed,
|
||||
self.mistral_alt_user,
|
||||
"action-execution-get",
|
||||
params=exec_id
|
||||
)
|
File diff suppressed because it is too large
Load Diff
|
@ -1,39 +0,0 @@
|
|||
# Copyright 2013 - 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 mock
|
||||
from oslotest import base
|
||||
from requests_mock.contrib import fixture
|
||||
|
||||
|
||||
class BaseClientTest(base.BaseTestCase):
|
||||
_client = None
|
||||
|
||||
def setUp(self):
|
||||
super(BaseClientTest, self).setUp()
|
||||
self.requests_mock = self.useFixture(fixture.Fixture())
|
||||
|
||||
|
||||
class BaseCommandTest(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(BaseCommandTest, self).setUp()
|
||||
self.app = mock.Mock()
|
||||
self.client = self.app.client_manager.workflow_engine
|
||||
|
||||
def call(self, command, app_args=[], prog_name=''):
|
||||
cmd = command(self.app, app_args)
|
||||
|
||||
parsed_args = cmd.get_parser(prog_name).parse_args(app_args)
|
||||
|
||||
return cmd.take_action(parsed_args)
|
|
@ -1,47 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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
|
||||
|
||||
from oslotest import base
|
||||
import six
|
||||
|
||||
from mistralclient import shell
|
||||
|
||||
|
||||
class BaseShellTests(base.BaseTestCase):
|
||||
|
||||
def shell(self, argstr):
|
||||
orig = (sys.stdout, sys.stderr)
|
||||
clean_env = {}
|
||||
_old_env, os.environ = os.environ, clean_env.copy()
|
||||
|
||||
try:
|
||||
sys.stdout = six.moves.cStringIO()
|
||||
sys.stderr = six.moves.cStringIO()
|
||||
_shell = shell.MistralShell()
|
||||
_shell.run(argstr.split())
|
||||
except SystemExit:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
self.assertEqual(0, exc_value.code)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
stderr = sys.stderr.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
sys.stdout, sys.stderr = orig
|
||||
os.environ = _old_env
|
||||
|
||||
return stdout, stderr
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
---
|
||||
version: 2.0
|
||||
|
||||
my_action:
|
||||
base: std.echo
|
||||
base-input:
|
||||
output: 'Bye!'
|
||||
output:
|
||||
info: <% $.output %>
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"context": {
|
||||
"server": {
|
||||
"name": "name"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"name": "env1",
|
||||
"description": "Test Environment #1",
|
||||
"scope": "private",
|
||||
"variables": {
|
||||
"server": "localhost"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
|
||||
"name": "env1"
|
||||
"description": "Test Environment #1"
|
||||
"scope": "private"
|
||||
"variables":
|
||||
"server": "localhost"
|
|
@ -1,21 +0,0 @@
|
|||
|
||||
---
|
||||
version: 2.0
|
||||
|
||||
name: wb
|
||||
|
||||
workflows:
|
||||
wf1:
|
||||
type: direct
|
||||
input:
|
||||
- param1
|
||||
- param2
|
||||
|
||||
tasks:
|
||||
task1:
|
||||
action: std.http url="localhost:8989"
|
||||
on-success:
|
||||
- test_subsequent
|
||||
|
||||
test_subsequent:
|
||||
action: std.http url="http://some_url" server_id=1
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
---
|
||||
version: 2.0
|
||||
|
||||
my_wf:
|
||||
type: direct
|
||||
|
||||
tasks:
|
||||
task1:
|
||||
action: std.echo output="hello, world"
|
|
@ -1,276 +0,0 @@
|
|||
# Copyright 2015 - Huawei Technologies Co., Ltd.
|
||||
# Copyright 2016 - StackStorm, 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 json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
from oslotest import base
|
||||
import osprofiler.profiler
|
||||
|
||||
from mistralclient.api import client
|
||||
|
||||
AUTH_HTTP_URL_v3 = 'http://localhost:35357/v3'
|
||||
AUTH_HTTP_URL_v2_0 = 'http://localhost:35357/v2.0'
|
||||
AUTH_HTTPS_URL = AUTH_HTTP_URL_v3.replace('http', 'https')
|
||||
MISTRAL_HTTP_URL = 'http://localhost:8989/v2'
|
||||
MISTRAL_HTTPS_URL = MISTRAL_HTTP_URL.replace('http', 'https')
|
||||
PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
|
||||
|
||||
|
||||
class BaseClientTests(base.BaseTestCase):
|
||||
|
||||
@staticmethod
|
||||
def setup_keystone_mock(keystone_client_mock):
|
||||
keystone_client_instance = keystone_client_mock.return_value
|
||||
keystone_client_instance.auth_token = uuidutils.generate_uuid()
|
||||
keystone_client_instance.project_id = uuidutils.generate_uuid()
|
||||
keystone_client_instance.user_id = uuidutils.generate_uuid()
|
||||
keystone_client_instance.auth_ref = str(json.dumps({}))
|
||||
return keystone_client_instance
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_mistral_url_from_catalog_v2(self, keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock(
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
url_for = mock.Mock(return_value='http://mistral_host:8989/v2')
|
||||
keystone_client_instance.service_catalog.url_for = url_for
|
||||
|
||||
mistralclient = client.client(
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v2_0,
|
||||
service_type='workflowv2'
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
'http://mistral_host:8989/v2',
|
||||
mistralclient.actions.http_client.base_url
|
||||
)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_mistral_url_from_catalog(self, keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock(
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
url_for = mock.Mock(return_value='http://mistral_host:8989/v2')
|
||||
|
||||
keystone_client_instance.service_catalog.url_for = url_for
|
||||
|
||||
mistralclient = client.client(
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
service_type='workflowv2'
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
'http://mistral_host:8989/v2',
|
||||
mistralclient.actions.http_client.base_url
|
||||
)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_mistral_url_default(self, http_client_mock, keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock(
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
url_for = mock.Mock(side_effect=Exception)
|
||||
keystone_client_instance.service_catalog.url_for = url_for
|
||||
|
||||
client.client(
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3
|
||||
)
|
||||
|
||||
self.assertTrue(http_client_mock.called)
|
||||
mistral_url_for_http = http_client_mock.call_args[0][0]
|
||||
kwargs = http_client_mock.call_args[1]
|
||||
self.assertEqual(MISTRAL_HTTP_URL, mistral_url_for_http)
|
||||
self.assertEqual(
|
||||
keystone_client_instance.auth_token, kwargs['auth_token']
|
||||
)
|
||||
self.assertEqual(
|
||||
keystone_client_instance.project_id, kwargs['project_id']
|
||||
)
|
||||
self.assertEqual(
|
||||
keystone_client_instance.user_id, kwargs['user_id']
|
||||
)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_target_parameters_processed(
|
||||
self,
|
||||
http_client_mock,
|
||||
keystone_client_mock
|
||||
):
|
||||
keystone_client_instance = self.setup_keystone_mock(
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
url_for = mock.Mock(return_value='http://mistral_host:8989/v2')
|
||||
keystone_client_instance.service_catalog.url_for = url_for
|
||||
|
||||
client.client(
|
||||
target_username='tmistral',
|
||||
target_project_name='tmistralp',
|
||||
target_auth_url=AUTH_HTTP_URL_v3,
|
||||
target_region_name='tregion'
|
||||
)
|
||||
|
||||
self.assertTrue(http_client_mock.called)
|
||||
mistral_url_for_http = http_client_mock.call_args[0][0]
|
||||
kwargs = http_client_mock.call_args[1]
|
||||
self.assertEqual(MISTRAL_HTTP_URL, mistral_url_for_http)
|
||||
|
||||
expected_values = {
|
||||
'target_project_id': keystone_client_instance.project_id,
|
||||
'target_auth_token': keystone_client_instance.auth_token,
|
||||
'target_user_id': keystone_client_instance.user_id,
|
||||
'target_auth_url': AUTH_HTTP_URL_v3,
|
||||
'target_project_name': 'tmistralp',
|
||||
'target_username': 'tmistral',
|
||||
'target_region_name': 'tregion',
|
||||
'target_service_catalog': '"{}"'
|
||||
}
|
||||
|
||||
for key in expected_values:
|
||||
self.assertEqual(expected_values[key], kwargs[key])
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_mistral_url_https_insecure(self, http_client_mock,
|
||||
keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock( # noqa
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
expected_args = (
|
||||
MISTRAL_HTTPS_URL,
|
||||
)
|
||||
|
||||
client.client(
|
||||
mistral_url=MISTRAL_HTTPS_URL,
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
cacert=None,
|
||||
insecure=True
|
||||
)
|
||||
|
||||
self.assertTrue(http_client_mock.called)
|
||||
self.assertEqual(http_client_mock.call_args[0], expected_args)
|
||||
self.assertEqual(http_client_mock.call_args[1]['insecure'], True)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_mistral_url_https_secure(self, http_client_mock,
|
||||
keystone_client_mock):
|
||||
fd, cert_path = tempfile.mkstemp(suffix='.pem')
|
||||
|
||||
keystone_client_instance = self.setup_keystone_mock( # noqa
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
expected_args = (
|
||||
MISTRAL_HTTPS_URL,
|
||||
)
|
||||
|
||||
try:
|
||||
client.client(
|
||||
mistral_url=MISTRAL_HTTPS_URL,
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
cacert=cert_path,
|
||||
insecure=False
|
||||
)
|
||||
finally:
|
||||
os.close(fd)
|
||||
os.unlink(cert_path)
|
||||
|
||||
self.assertTrue(http_client_mock.called)
|
||||
self.assertEqual(http_client_mock.call_args[0], expected_args)
|
||||
self.assertEqual(http_client_mock.call_args[1]['cacert'], cert_path)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_mistral_url_https_bad_cacert(self, keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock( # noqa
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
client.client,
|
||||
mistral_url=MISTRAL_HTTPS_URL,
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
cacert='/path/to/foobar',
|
||||
insecure=False
|
||||
)
|
||||
|
||||
@mock.patch('logging.Logger.warning')
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_mistral_url_https_bad_insecure(self, keystone_client_mock,
|
||||
log_warning_mock):
|
||||
fd, path = tempfile.mkstemp(suffix='.pem')
|
||||
|
||||
keystone_client_instance = self.setup_keystone_mock(
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
try:
|
||||
client.client(
|
||||
mistral_url=MISTRAL_HTTPS_URL,
|
||||
user_id=keystone_client_instance.user_id,
|
||||
project_id=keystone_client_instance.project_id,
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
cacert=path,
|
||||
insecure=True
|
||||
)
|
||||
finally:
|
||||
os.close(fd)
|
||||
os.unlink(path)
|
||||
|
||||
self.assertTrue(log_warning_mock.called)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
@mock.patch('mistralclient.api.httpclient.HTTPClient')
|
||||
def test_mistral_profile_enabled(self, http_client_mock,
|
||||
keystone_client_mock):
|
||||
keystone_client_instance = self.setup_keystone_mock( # noqa
|
||||
keystone_client_mock
|
||||
)
|
||||
|
||||
client.client(
|
||||
username='mistral',
|
||||
project_name='mistral',
|
||||
auth_url=AUTH_HTTP_URL_v3,
|
||||
profile=PROFILER_HMAC_KEY
|
||||
)
|
||||
|
||||
self.assertTrue(http_client_mock.called)
|
||||
|
||||
profiler = osprofiler.profiler.get()
|
||||
|
||||
self.assertEqual(profiler.hmac_key, PROFILER_HMAC_KEY)
|
|
@ -1,276 +0,0 @@
|
|||
# Copyright 2016 - StackStorm, 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 base64
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
from osprofiler import _utils as osprofiler_utils
|
||||
import osprofiler.profiler
|
||||
|
||||
from mistralclient.api import httpclient
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
API_BASE_URL = 'http://localhost:8989/v2'
|
||||
API_URL = '/executions'
|
||||
|
||||
EXPECTED_URL = API_BASE_URL + API_URL
|
||||
|
||||
AUTH_TOKEN = uuidutils.generate_uuid()
|
||||
PROJECT_ID = uuidutils.generate_uuid()
|
||||
USER_ID = uuidutils.generate_uuid()
|
||||
REGION_NAME = 'fake_region'
|
||||
PROFILER_HMAC_KEY = 'SECRET_HMAC_KEY'
|
||||
PROFILER_TRACE_ID = uuidutils.generate_uuid()
|
||||
|
||||
EXPECTED_AUTH_HEADERS = {
|
||||
'x-auth-token': AUTH_TOKEN,
|
||||
'X-Project-Id': PROJECT_ID,
|
||||
'X-User-Id': USER_ID,
|
||||
'X-Region-Name': REGION_NAME
|
||||
}
|
||||
|
||||
EXPECTED_REQ_OPTIONS = {
|
||||
'headers': EXPECTED_AUTH_HEADERS
|
||||
}
|
||||
|
||||
EXPECTED_BODY = {
|
||||
'k1': 'abc',
|
||||
'k2': 123,
|
||||
'k3': True
|
||||
}
|
||||
|
||||
|
||||
class HTTPClientTest(base.BaseClientTest):
|
||||
|
||||
def setUp(self):
|
||||
super(HTTPClientTest, self).setUp()
|
||||
osprofiler.profiler.init(None)
|
||||
self.client = httpclient.HTTPClient(
|
||||
API_BASE_URL,
|
||||
auth_token=AUTH_TOKEN,
|
||||
project_id=PROJECT_ID,
|
||||
user_id=USER_ID,
|
||||
region_name=REGION_NAME
|
||||
)
|
||||
|
||||
def assertExpectedAuthHeaders(self):
|
||||
headers = self.requests_mock.last_request.headers
|
||||
|
||||
self.assertEqual(AUTH_TOKEN, headers['X-Auth-Token'])
|
||||
self.assertEqual(PROJECT_ID, headers['X-Project-Id'])
|
||||
self.assertEqual(USER_ID, headers['X-User-Id'])
|
||||
|
||||
return headers
|
||||
|
||||
def assertExpectedBody(self):
|
||||
text = self.requests_mock.last_request.text
|
||||
form = urlparse.parse_qs(text, strict_parsing=True)
|
||||
|
||||
self.assertEqual(len(EXPECTED_BODY), len(form))
|
||||
|
||||
for k, v in EXPECTED_BODY.items():
|
||||
self.assertEqual([str(v)], form[k])
|
||||
|
||||
return form
|
||||
|
||||
def test_get_request_options(self):
|
||||
m = self.requests_mock.get(EXPECTED_URL, text='text')
|
||||
|
||||
self.client.get(API_URL)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
self.assertExpectedAuthHeaders()
|
||||
|
||||
@mock.patch.object(
|
||||
osprofiler.profiler._Profiler,
|
||||
'get_base_id',
|
||||
mock.MagicMock(return_value=PROFILER_TRACE_ID)
|
||||
)
|
||||
@mock.patch.object(
|
||||
osprofiler.profiler._Profiler,
|
||||
'get_id',
|
||||
mock.MagicMock(return_value=PROFILER_TRACE_ID)
|
||||
)
|
||||
def test_get_request_options_with_profile_enabled(self):
|
||||
m = self.requests_mock.get(EXPECTED_URL, text='text')
|
||||
osprofiler.profiler.init(PROFILER_HMAC_KEY)
|
||||
|
||||
data = {'base_id': PROFILER_TRACE_ID, 'parent_id': PROFILER_TRACE_ID}
|
||||
signed_data = osprofiler_utils.signed_pack(data, PROFILER_HMAC_KEY)
|
||||
|
||||
headers = {
|
||||
'X-Trace-Info': signed_data[0],
|
||||
'X-Trace-HMAC': signed_data[1]
|
||||
}
|
||||
|
||||
self.client.get(API_URL)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
headers = self.assertExpectedAuthHeaders()
|
||||
self.assertEqual(signed_data[0], headers['X-Trace-Info'])
|
||||
self.assertEqual(signed_data[1], headers['X-Trace-HMAC'])
|
||||
|
||||
def test_get_request_options_with_headers_for_get(self):
|
||||
m = self.requests_mock.get(EXPECTED_URL, text='text')
|
||||
target_auth_url = uuidutils.generate_uuid()
|
||||
target_auth_token = uuidutils.generate_uuid()
|
||||
target_user_id = 'target_user'
|
||||
target_project_id = 'target_project'
|
||||
target_service_catalog = 'this should be there'
|
||||
target_insecure = 'target insecure'
|
||||
target_region = 'target region name'
|
||||
target_user_domain_name = 'target user domain name'
|
||||
target_project_domain_name = 'target project domain name'
|
||||
|
||||
target_client = httpclient.HTTPClient(
|
||||
API_BASE_URL,
|
||||
auth_token=AUTH_TOKEN,
|
||||
project_id=PROJECT_ID,
|
||||
user_id=USER_ID,
|
||||
region_name=REGION_NAME,
|
||||
target_auth_url=target_auth_url,
|
||||
target_auth_token=target_auth_token,
|
||||
target_project_id=target_project_id,
|
||||
target_user_id=target_user_id,
|
||||
target_service_catalog=target_service_catalog,
|
||||
target_region_name=target_region,
|
||||
target_user_domain_name=target_user_domain_name,
|
||||
target_project_domain_name=target_project_domain_name,
|
||||
target_insecure=target_insecure
|
||||
)
|
||||
|
||||
target_client.get(API_URL)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
headers = self.assertExpectedAuthHeaders()
|
||||
self.assertEqual(target_auth_url, headers['X-Target-Auth-Uri'])
|
||||
self.assertEqual(target_auth_token, headers['X-Target-Auth-Token'])
|
||||
self.assertEqual(target_user_id, headers['X-Target-User-Id'])
|
||||
self.assertEqual(target_project_id, headers['X-Target-Project-Id'])
|
||||
self.assertEqual(target_insecure, headers['X-Target-Insecure'])
|
||||
self.assertEqual(target_region, headers['X-Target-Region-Name'])
|
||||
self.assertEqual(target_user_domain_name,
|
||||
headers['X-Target-User-Domain-Name'])
|
||||
self.assertEqual(target_project_domain_name,
|
||||
headers['X-Target-Project-Domain-Name'])
|
||||
|
||||
catalog = base64.b64encode(target_service_catalog.encode('utf-8'))
|
||||
self.assertEqual(catalog, headers['X-Target-Service-Catalog'])
|
||||
|
||||
def test_get_request_options_with_headers_for_post(self):
|
||||
m = self.requests_mock.post(EXPECTED_URL, text='text')
|
||||
headers = {'foo': 'bar'}
|
||||
|
||||
self.client.post(API_URL, EXPECTED_BODY, headers=headers)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
headers = self.assertExpectedAuthHeaders()
|
||||
self.assertEqual('application/json', headers['Content-Type'])
|
||||
self.assertEqual('bar', headers['foo'])
|
||||
self.assertExpectedBody()
|
||||
|
||||
def test_get_request_options_with_headers_for_put(self):
|
||||
m = self.requests_mock.put(EXPECTED_URL, text='text')
|
||||
headers = {'foo': 'bar'}
|
||||
|
||||
self.client.put(API_URL, EXPECTED_BODY, headers=headers)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
headers = self.assertExpectedAuthHeaders()
|
||||
self.assertEqual('application/json', headers['Content-Type'])
|
||||
self.assertEqual('bar', headers['foo'])
|
||||
self.assertExpectedBody()
|
||||
|
||||
def test_get_request_options_with_headers_for_delete(self):
|
||||
m = self.requests_mock.delete(EXPECTED_URL, text='text')
|
||||
headers = {'foo': 'bar'}
|
||||
|
||||
self.client.delete(API_URL, headers=headers)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
headers = self.assertExpectedAuthHeaders()
|
||||
self.assertEqual('bar', headers['foo'])
|
||||
|
||||
@mock.patch.object(
|
||||
httpclient.HTTPClient,
|
||||
'_get_request_options',
|
||||
mock.MagicMock(return_value=copy.deepcopy(EXPECTED_REQ_OPTIONS))
|
||||
)
|
||||
def test_http_get(self):
|
||||
m = self.requests_mock.get(EXPECTED_URL, text='text')
|
||||
self.client.get(API_URL)
|
||||
|
||||
httpclient.HTTPClient._get_request_options.assert_called_with(
|
||||
'get',
|
||||
None
|
||||
)
|
||||
self.assertTrue(m.called_once)
|
||||
self.assertExpectedAuthHeaders()
|
||||
|
||||
@mock.patch.object(
|
||||
httpclient.HTTPClient,
|
||||
'_get_request_options',
|
||||
mock.MagicMock(return_value=copy.deepcopy(EXPECTED_REQ_OPTIONS))
|
||||
)
|
||||
def test_http_post(self):
|
||||
m = self.requests_mock.post(EXPECTED_URL, status_code=201, text='text')
|
||||
self.client.post(API_URL, EXPECTED_BODY)
|
||||
|
||||
httpclient.HTTPClient._get_request_options.assert_called_with(
|
||||
'post',
|
||||
None
|
||||
)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
self.assertExpectedAuthHeaders()
|
||||
self.assertExpectedBody()
|
||||
|
||||
@mock.patch.object(
|
||||
httpclient.HTTPClient,
|
||||
'_get_request_options',
|
||||
mock.MagicMock(return_value=copy.deepcopy(EXPECTED_REQ_OPTIONS))
|
||||
)
|
||||
def test_http_put(self):
|
||||
m = self.requests_mock.put(EXPECTED_URL, json={})
|
||||
self.client.put(API_URL, EXPECTED_BODY)
|
||||
|
||||
httpclient.HTTPClient._get_request_options.assert_called_with(
|
||||
'put',
|
||||
None
|
||||
)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
self.assertExpectedAuthHeaders()
|
||||
self.assertExpectedBody()
|
||||
|
||||
@mock.patch.object(
|
||||
httpclient.HTTPClient,
|
||||
'_get_request_options',
|
||||
mock.MagicMock(return_value=copy.deepcopy(EXPECTED_REQ_OPTIONS))
|
||||
)
|
||||
def test_http_delete(self):
|
||||
m = self.requests_mock.delete(EXPECTED_URL, text='text')
|
||||
self.client.delete(API_URL)
|
||||
|
||||
httpclient.HTTPClient._get_request_options.assert_called_with(
|
||||
'delete',
|
||||
None
|
||||
)
|
||||
|
||||
self.assertTrue(m.called_once)
|
||||
self.assertExpectedAuthHeaders()
|
|
@ -1,136 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
import mistralclient.tests.unit.base_shell_test as base
|
||||
|
||||
|
||||
class TestShell(base.BaseShellTests):
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_command_no_mistral_url(self, client_mock):
|
||||
self.shell(
|
||||
'workbook-list'
|
||||
)
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('', params[1]['mistral_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_command_with_mistral_url(self, client_mock):
|
||||
self.shell(
|
||||
'--os-mistral-url=http://localhost:8989/v2 workbook-list'
|
||||
)
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('http://localhost:8989/v2',
|
||||
params[1]['mistral_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.determine_client_version')
|
||||
def test_mistral_version(self, client_mock):
|
||||
self.shell(
|
||||
'--os-mistral-version=v1 workbook-list'
|
||||
)
|
||||
self.assertTrue(client_mock.called)
|
||||
mistral_version = client_mock.call_args
|
||||
self.assertEqual('v1', mistral_version[0][0])
|
||||
|
||||
@mock.patch('mistralclient.api.client.determine_client_version')
|
||||
def test_no_mistral_version(self, client_mock):
|
||||
self.shell('workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
mistral_version = client_mock.call_args
|
||||
self.assertEqual('v2', mistral_version[0][0])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_service_type(self, client_mock):
|
||||
self.shell('--os-mistral-service-type=test workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
parmters = client_mock.call_args
|
||||
self.assertEqual('test', parmters[1]['service_type'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_no_service_type(self, client_mock):
|
||||
self.shell('workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('workflowv2', params[1]['service_type'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_endpoint_type(self, client_mock):
|
||||
self.shell('--os-mistral-endpoint-type=adminURL workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('adminURL', params[1]['endpoint_type'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_no_endpoint_type(self, client_mock):
|
||||
self.shell('workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('publicURL', params[1]['endpoint_type'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_auth_url(self, client_mock):
|
||||
self.shell(
|
||||
'--os-auth-url=https://127.0.0.1:35357/v3 '
|
||||
'--os-username=admin '
|
||||
'--os-password=1234 '
|
||||
'workbook-list'
|
||||
)
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('https://127.0.0.1:35357/v3', params[1]['auth_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_no_auth_url(self, client_mock):
|
||||
self.shell('workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('', params[1]['auth_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_default_auth_url_with_os_password(self, client_mock):
|
||||
self.shell('--os-username=admin --os-password=1234 workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('http://localhost:35357/v3', params[1]['auth_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_default_auth_url_with_os_auth_token(self, client_mock):
|
||||
self.shell(
|
||||
'--os-auth-token=abcd1234 '
|
||||
'workbook-list'
|
||||
)
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('http://localhost:35357/v3', params[1]['auth_url'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_profile(self, client_mock):
|
||||
self.shell('--profile=SECRET_HMAC_KEY workbook-list')
|
||||
self.assertTrue(client_mock.called)
|
||||
params = client_mock.call_args
|
||||
self.assertEqual('SECRET_HMAC_KEY', params[1]['profile'])
|
||||
|
||||
@mock.patch('mistralclient.api.client.client')
|
||||
def test_region_name(self, client_mock):
|
||||
self.shell('--os-region-name=RegionOne workbook-list')
|
||||
|
||||
self.assertTrue(client_mock.called)
|
||||
|
||||
params = client_mock.call_args
|
||||
|
||||
self.assertEqual('RegionOne', params[1]['region_name'])
|
|
@ -1,65 +0,0 @@
|
|||
# Copyright 2015 - StackStorm, 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 json
|
||||
import os.path
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
from mistralclient import utils
|
||||
from oslotest import base
|
||||
|
||||
|
||||
ENV_DICT = {'k1': 'abc', 'k2': 123, 'k3': True}
|
||||
ENV_STR = json.dumps(ENV_DICT)
|
||||
ENV_YAML = yaml.safe_dump(ENV_DICT, default_flow_style=False)
|
||||
|
||||
|
||||
class UtilityTest(base.BaseTestCase):
|
||||
|
||||
def test_load_empty(self):
|
||||
self.assertDictEqual(dict(), utils.load_content(None))
|
||||
self.assertDictEqual(dict(), utils.load_content(''))
|
||||
self.assertDictEqual(dict(), utils.load_content('{}'))
|
||||
self.assertListEqual(list(), utils.load_content('[]'))
|
||||
|
||||
def test_load_json_content(self):
|
||||
self.assertDictEqual(ENV_DICT, utils.load_content(ENV_STR))
|
||||
|
||||
def test_load_json_file(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(ENV_STR.encode('utf-8'))
|
||||
f.flush()
|
||||
file_path = os.path.abspath(f.name)
|
||||
|
||||
self.assertDictEqual(ENV_DICT, utils.load_file(file_path))
|
||||
|
||||
def test_load_yaml_content(self):
|
||||
self.assertDictEqual(ENV_DICT, utils.load_content(ENV_YAML))
|
||||
|
||||
def test_load_yaml_file(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(ENV_YAML.encode('utf-8'))
|
||||
f.flush()
|
||||
file_path = os.path.abspath(f.name)
|
||||
|
||||
self.assertDictEqual(ENV_DICT, utils.load_file(file_path))
|
||||
|
||||
def test_load_json(self):
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(ENV_STR.encode('utf-8'))
|
||||
f.flush()
|
||||
self.assertDictEqual(ENV_DICT, utils.load_json(f.name))
|
||||
|
||||
self.assertDictEqual(ENV_DICT, utils.load_json(ENV_STR))
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 mistralclient.api.v2 import client
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
class BaseClientV2Test(base.BaseClientTest):
|
||||
|
||||
TEST_URL = 'http://mistral.example.com'
|
||||
|
||||
def setUp(self):
|
||||
super(BaseClientV2Test, self).setUp()
|
||||
|
||||
self._client = client.Client(project_name="test",
|
||||
mistral_url=self.TEST_URL)
|
||||
self.workbooks = self._client.workbooks
|
||||
self.executions = self._client.executions
|
||||
self.tasks = self._client.tasks
|
||||
self.workflows = self._client.workflows
|
||||
self.environments = self._client.environments
|
||||
self.action_executions = self._client.action_executions
|
||||
self.actions = self._client.actions
|
||||
self.services = self._client.services
|
||||
self.members = self._client.members
|
|
@ -1,114 +0,0 @@
|
|||
# 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.
|
||||
|
||||
from mistralclient.api.v2 import action_executions
|
||||
from mistralclient.tests.unit.v2 import base
|
||||
|
||||
# TODO(everyone): later we need additional tests verifying all the errors etc.
|
||||
|
||||
ACTION_EXEC = {
|
||||
'id': "1",
|
||||
'name': 'my_action_execution',
|
||||
'workflow_name': 'my_wf',
|
||||
'state': 'RUNNING',
|
||||
}
|
||||
|
||||
|
||||
URL_TEMPLATE = '/action_executions'
|
||||
URL_TEMPLATE_ID = '/action_executions/%s'
|
||||
|
||||
|
||||
class TestActionExecutions(base.BaseClientV2Test):
|
||||
def test_create(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE,
|
||||
json=ACTION_EXEC,
|
||||
status_code=201)
|
||||
|
||||
body = {
|
||||
'name': ACTION_EXEC['name']
|
||||
}
|
||||
|
||||
action_execution = self.action_executions.create(
|
||||
'my_action_execution',
|
||||
{}
|
||||
)
|
||||
|
||||
self.assertIsNotNone(action_execution)
|
||||
self.assertEqual(action_executions.ActionExecution(
|
||||
self.action_executions, ACTION_EXEC
|
||||
).to_dict(), action_execution.to_dict())
|
||||
|
||||
self.assertEqual(body, self.requests_mock.last_request.json())
|
||||
|
||||
def test_update(self):
|
||||
url = self.TEST_URL + URL_TEMPLATE_ID % ACTION_EXEC['id']
|
||||
self.requests_mock.put(url, json=ACTION_EXEC)
|
||||
|
||||
body = {
|
||||
'state': ACTION_EXEC['state']
|
||||
}
|
||||
|
||||
action_execution = self.action_executions.update(
|
||||
ACTION_EXEC['id'],
|
||||
ACTION_EXEC['state']
|
||||
)
|
||||
|
||||
self.assertIsNotNone(action_execution)
|
||||
|
||||
expected = action_executions.ActionExecution(
|
||||
self.action_executions,
|
||||
ACTION_EXEC
|
||||
).to_dict()
|
||||
|
||||
self.assertEqual(
|
||||
expected,
|
||||
action_execution.to_dict()
|
||||
)
|
||||
|
||||
self.assertEqual(body, self.requests_mock.last_request.json())
|
||||
|
||||
def test_list(self):
|
||||
self.requests_mock.get(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'action_executions': [ACTION_EXEC]})
|
||||
|
||||
action_execution_list = self.action_executions.list()
|
||||
|
||||
self.assertEqual(1, len(action_execution_list))
|
||||
action_execution = action_execution_list[0]
|
||||
|
||||
expected = action_executions.ActionExecution(
|
||||
self.action_executions,
|
||||
ACTION_EXEC
|
||||
).to_dict()
|
||||
|
||||
self.assertEqual(expected, action_execution.to_dict())
|
||||
|
||||
def test_get(self):
|
||||
url = self.TEST_URL + URL_TEMPLATE_ID % ACTION_EXEC['id']
|
||||
self.requests_mock.get(url, json=ACTION_EXEC)
|
||||
|
||||
action_execution = self.action_executions.get(ACTION_EXEC['id'])
|
||||
|
||||
expected = action_executions.ActionExecution(
|
||||
self.action_executions,
|
||||
ACTION_EXEC
|
||||
).to_dict()
|
||||
|
||||
self.assertEqual(expected, action_execution.to_dict())
|
||||
|
||||
def test_delete(self):
|
||||
url = self.TEST_URL + URL_TEMPLATE_ID % ACTION_EXEC['id']
|
||||
self.requests_mock.delete(url, status_code=204)
|
||||
|
||||
self.action_executions.delete(ACTION_EXEC['id'])
|
|
@ -1,269 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 pkg_resources as pkg
|
||||
from six.moves.urllib import parse
|
||||
from six.moves.urllib import request
|
||||
|
||||
from mistralclient.api import base as api_base
|
||||
from mistralclient.api.v2 import actions
|
||||
from mistralclient.tests.unit.v2 import base
|
||||
|
||||
|
||||
ACTION_DEF = """
|
||||
---
|
||||
version: 2.0
|
||||
|
||||
my_action:
|
||||
base: std.echo
|
||||
base-input:
|
||||
output: 'Bye!'
|
||||
output:
|
||||
info: <% $.output %>
|
||||
"""
|
||||
|
||||
INVALID_ACTION_DEF = """
|
||||
---
|
||||
version: 2.0
|
||||
|
||||
my_action:
|
||||
base: std.echo
|
||||
unexpected-property: 'this should fail'
|
||||
base-input:
|
||||
output: 'Bye!'
|
||||
output:
|
||||
info: <% $.output %>
|
||||
"""
|
||||
|
||||
ACTION = {
|
||||
'id': '123',
|
||||
'name': 'my_action',
|
||||
'input': '',
|
||||
'definition': ACTION_DEF
|
||||
}
|
||||
|
||||
URL_TEMPLATE = '/actions'
|
||||
URL_TEMPLATE_SCOPE = '/actions?scope=private'
|
||||
URL_TEMPLATE_NAME = '/actions/%s'
|
||||
URL_TEMPLATE_VALIDATE = '/actions/validate'
|
||||
|
||||
|
||||
class TestActionsV2(base.BaseClientV2Test):
|
||||
def test_create(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION]},
|
||||
status_code=201)
|
||||
|
||||
actions = self.actions.create(ACTION_DEF)
|
||||
|
||||
self.assertIsNotNone(actions)
|
||||
self.assertEqual(ACTION_DEF, actions[0].definition)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
|
||||
def test_create_with_file(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION]},
|
||||
status_code=201)
|
||||
|
||||
# The contents of action_v2.yaml must be identical to ACTION_DEF
|
||||
path = pkg.resource_filename(
|
||||
'mistralclient',
|
||||
'tests/unit/resources/action_v2.yaml'
|
||||
)
|
||||
|
||||
actions = self.actions.create(path)
|
||||
|
||||
self.assertIsNotNone(actions)
|
||||
self.assertEqual(ACTION_DEF, actions[0].definition)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
|
||||
def test_update_with_id(self):
|
||||
self.requests_mock.put(self.TEST_URL + URL_TEMPLATE_NAME % 123,
|
||||
json={'actions': [ACTION]})
|
||||
|
||||
actions = self.actions.update(ACTION_DEF, id=123)
|
||||
|
||||
self.assertIsNotNone(actions)
|
||||
self.assertEqual(ACTION_DEF, actions[0].definition)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
|
||||
self.assertEqual('scope=private', last_request.query)
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
|
||||
def test_update(self):
|
||||
self.requests_mock.put(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION]})
|
||||
|
||||
actions = self.actions.update(ACTION_DEF)
|
||||
|
||||
self.assertIsNotNone(actions)
|
||||
self.assertEqual(ACTION_DEF, actions[0].definition)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
|
||||
self.assertEqual('scope=private', last_request.query)
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
|
||||
def test_update_with_file_uri(self):
|
||||
self.requests_mock.put(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION]})
|
||||
|
||||
# The contents of action_v2.yaml must be identical to ACTION_DEF
|
||||
path = pkg.resource_filename(
|
||||
'mistralclient',
|
||||
'tests/unit/resources/action_v2.yaml'
|
||||
)
|
||||
|
||||
# Convert the file path to file URI
|
||||
uri = parse.urljoin('file:', request.pathname2url(path))
|
||||
|
||||
actions = self.actions.update(uri)
|
||||
|
||||
self.assertIsNotNone(actions)
|
||||
self.assertEqual(ACTION_DEF, actions[0].definition)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual('scope=private', last_request.query)
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
|
||||
def test_list(self):
|
||||
self.requests_mock.get(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION]})
|
||||
|
||||
action_list = self.actions.list()
|
||||
|
||||
self.assertEqual(1, len(action_list))
|
||||
|
||||
action = action_list[0]
|
||||
|
||||
self.assertEqual(
|
||||
actions.Action(self.actions, ACTION).to_dict(),
|
||||
action.to_dict()
|
||||
)
|
||||
|
||||
def test_list_with_pagination(self):
|
||||
self.requests_mock.get(self.TEST_URL + URL_TEMPLATE,
|
||||
json={'actions': [ACTION],
|
||||
'next': '/actions?fake'})
|
||||
|
||||
action_list = self.actions.list(
|
||||
limit=1,
|
||||
sort_keys='created_at',
|
||||
sort_dirs='asc'
|
||||
)
|
||||
|
||||
self.assertEqual(1, len(action_list))
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
|
||||
# The url param order is unpredictable.
|
||||
self.assertEqual(['1'], last_request.qs['limit'])
|
||||
self.assertEqual(['created_at'], last_request.qs['sort_keys'])
|
||||
self.assertEqual(['asc'], last_request.qs['sort_dirs'])
|
||||
|
||||
def test_get(self):
|
||||
self.requests_mock.get(self.TEST_URL + URL_TEMPLATE_NAME % 'action',
|
||||
json=ACTION)
|
||||
|
||||
action = self.actions.get('action')
|
||||
|
||||
self.assertIsNotNone(action)
|
||||
self.assertEqual(
|
||||
actions.Action(self.actions, ACTION).to_dict(),
|
||||
action.to_dict()
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
url = self.TEST_URL + URL_TEMPLATE_NAME % 'action'
|
||||
m = self.requests_mock.delete(url, status_code=204)
|
||||
|
||||
self.actions.delete('action')
|
||||
|
||||
self.assertEqual(1, m.call_count)
|
||||
|
||||
def test_validate(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE_VALIDATE,
|
||||
json={'valid': True})
|
||||
|
||||
result = self.actions.validate(ACTION_DEF)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn('valid', result)
|
||||
self.assertTrue(result['valid'])
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
|
||||
def test_validate_with_file(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE_VALIDATE,
|
||||
json={'valid': True})
|
||||
|
||||
# The contents of action_v2.yaml must be identical to ACTION_DEF
|
||||
path = pkg.resource_filename(
|
||||
'mistralclient',
|
||||
'tests/unit/resources/action_v2.yaml'
|
||||
)
|
||||
|
||||
result = self.actions.validate(path)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn('valid', result)
|
||||
self.assertTrue(result['valid'])
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
|
||||
def test_validate_failed(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE_VALIDATE,
|
||||
json={"valid": False,
|
||||
"error": "mocked error message"})
|
||||
|
||||
result = self.actions.validate(INVALID_ACTION_DEF)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn('valid', result)
|
||||
self.assertFalse(result['valid'])
|
||||
self.assertIn('error', result)
|
||||
self.assertIn("mocked error message", result['error'])
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
|
||||
def test_validate_api_failed(self):
|
||||
self.requests_mock.post(self.TEST_URL + URL_TEMPLATE_VALIDATE,
|
||||
status_code=500,
|
||||
json={})
|
||||
|
||||
self.assertRaises(
|
||||
api_base.APIException,
|
||||
self.actions.validate,
|
||||
ACTION_DEF
|
||||
)
|
||||
|
||||
last_request = self.requests_mock.last_request
|
||||
|
||||
self.assertEqual('text/plain', last_request.headers['content-type'])
|
||||
self.assertEqual(ACTION_DEF, last_request.text)
|
|
@ -1,228 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, 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.
|
||||
#
|
||||
|
||||
import copy
|
||||
import json
|
||||
import sys
|
||||
|
||||
from six import StringIO
|
||||
|
||||
import mock
|
||||
|
||||
from mistralclient.api.v2 import action_executions as action_ex
|
||||
from mistralclient.commands.v2 import action_executions as action_ex_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
ACTION_EX_DICT = {
|
||||
'id': '123',
|
||||
'name': 'some',
|
||||
'workflow_name': 'thing',
|
||||
'task_name': 'task1',
|
||||
'task_execution_id': "1-2-3-4",
|
||||
'state': 'RUNNING',
|
||||
'state_info': 'RUNNING somehow.',
|
||||
'accepted': True,
|
||||
'created_at': '1',
|
||||
'updated_at': '1',
|
||||
}
|
||||
|
||||
ACTION_EX_RESULT = {"test": "is", "passed": "successfully"}
|
||||
ACTION_EX_INPUT = {"param1": "val1", "param2": 2}
|
||||
|
||||
ACTION_EX_WITH_OUTPUT_DICT = ACTION_EX_DICT.copy()
|
||||
ACTION_EX_WITH_OUTPUT_DICT.update({'output': json.dumps(ACTION_EX_RESULT)})
|
||||
|
||||
ACTION_EX_WITH_INPUT_DICT = ACTION_EX_DICT.copy()
|
||||
ACTION_EX_WITH_INPUT_DICT.update({'input': json.dumps(ACTION_EX_INPUT)})
|
||||
|
||||
ACTION_EX = action_ex.ActionExecution(mock, ACTION_EX_DICT)
|
||||
ACTION_EX_WITH_OUTPUT = action_ex.ActionExecution(
|
||||
mock,
|
||||
ACTION_EX_WITH_OUTPUT_DICT
|
||||
)
|
||||
ACTION_EX_WITH_INPUT = action_ex.ActionExecution(
|
||||
mock,
|
||||
ACTION_EX_WITH_INPUT_DICT
|
||||
)
|
||||
|
||||
|
||||
class TestCLIActionExecutions(base.BaseCommandTest):
|
||||
def test_create(self):
|
||||
(self.client.action_executions.create.
|
||||
return_value) = ACTION_EX_WITH_OUTPUT
|
||||
|
||||
self.call(
|
||||
action_ex_cmd.Create,
|
||||
app_args=['some', '{"output": "Hello!"}']
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
ACTION_EX_RESULT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_create_save_result(self):
|
||||
(self.client.action_executions.create.
|
||||
return_value) = ACTION_EX_WITH_OUTPUT
|
||||
|
||||
result = self.call(
|
||||
action_ex_cmd.Create,
|
||||
app_args=[
|
||||
'some', '{"output": "Hello!"}', '--save-result'
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
('123', 'some', 'thing', 'task1', '1-2-3-4', 'RUNNING',
|
||||
'RUNNING somehow.', True, '1', '1'),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_create_run_sync(self):
|
||||
(self.client.action_executions.create.
|
||||
return_value) = ACTION_EX_WITH_OUTPUT
|
||||
|
||||
self.call(
|
||||
action_ex_cmd.Create,
|
||||
app_args=[
|
||||
'some', '{"output": "Hello!"}', '--run-sync'
|
||||
]
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
ACTION_EX_RESULT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_create_run_sync_and_save_result(self):
|
||||
(self.client.action_executions.create.
|
||||
return_value) = ACTION_EX_WITH_OUTPUT
|
||||
|
||||
self.call(
|
||||
action_ex_cmd.Create,
|
||||
app_args=[
|
||||
'some', '{"output": "Hello!"}', '--save-result', '--run-sync'
|
||||
]
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
ACTION_EX_RESULT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_update(self):
|
||||
states = ['IDLE', 'RUNNING', 'SUCCESS', 'ERROR', 'CANCELLED']
|
||||
|
||||
for state in states:
|
||||
action_ex_dict = copy.deepcopy(ACTION_EX_DICT)
|
||||
action_ex_dict['state'] = state
|
||||
action_ex_dict['state_info'] = 'testing update'
|
||||
action_ex_obj = action_ex.ActionExecution(mock, action_ex_dict)
|
||||
self.client.action_executions.update.return_value = action_ex_obj
|
||||
|
||||
result = self.call(
|
||||
action_ex_cmd.Update,
|
||||
app_args=['id', '--state', state]
|
||||
)
|
||||
|
||||
expected_result = (
|
||||
action_ex_dict['id'],
|
||||
action_ex_dict['name'],
|
||||
action_ex_dict['workflow_name'],
|
||||
action_ex_dict['task_name'],
|
||||
action_ex_dict['task_execution_id'],
|
||||
action_ex_dict['state'],
|
||||
action_ex_dict['state_info'],
|
||||
action_ex_dict['accepted'],
|
||||
action_ex_dict['created_at'],
|
||||
action_ex_dict['updated_at']
|
||||
)
|
||||
|
||||
self.assertEqual(expected_result, result[1])
|
||||
|
||||
def test_update_invalid_state(self):
|
||||
states = ['PAUSED', 'WAITING', 'DELAYED']
|
||||
|
||||
# Redirect the stderr so it doesn't show during tox
|
||||
_stderr = sys.stderr
|
||||
sys.stderr = StringIO()
|
||||
|
||||
for state in states:
|
||||
self.assertRaises(
|
||||
SystemExit,
|
||||
self.call,
|
||||
action_ex_cmd.Update,
|
||||
app_args=['id', '--state', state]
|
||||
)
|
||||
|
||||
# Stop the redirection
|
||||
print(sys.stderr.getvalue())
|
||||
sys.stderr = _stderr
|
||||
|
||||
def test_list(self):
|
||||
self.client.action_executions.list.return_value = [ACTION_EX]
|
||||
|
||||
result = self.call(action_ex_cmd.List)
|
||||
|
||||
self.assertEqual(
|
||||
[('123', 'some', 'thing', 'task1', '1-2-3-4', 'RUNNING', True,
|
||||
'1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.action_executions.get.return_value = ACTION_EX
|
||||
|
||||
result = self.call(action_ex_cmd.Get, app_args=['id'])
|
||||
|
||||
self.assertEqual(
|
||||
('123', 'some', 'thing', 'task1', '1-2-3-4', 'RUNNING',
|
||||
'RUNNING somehow.', True, '1', '1'), result[1]
|
||||
)
|
||||
|
||||
def test_get_output(self):
|
||||
self.client.action_executions.get.return_value = ACTION_EX_WITH_OUTPUT
|
||||
|
||||
self.call(action_ex_cmd.GetOutput, app_args=['id'])
|
||||
|
||||
self.assertDictEqual(
|
||||
ACTION_EX_RESULT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_get_input(self):
|
||||
self.client.action_executions.get.return_value = ACTION_EX_WITH_INPUT
|
||||
|
||||
self.call(action_ex_cmd.GetInput, app_args=['id'])
|
||||
|
||||
self.assertDictEqual(
|
||||
ACTION_EX_INPUT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(action_ex_cmd.Delete, app_args=['id'])
|
||||
|
||||
self.client.action_executions.delete.assert_called_once_with('id')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(action_ex_cmd.Delete, app_args=['id1', 'id2'])
|
||||
|
||||
self.assertEqual(2, self.client.action_executions.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('id1'), mock.call('id2')],
|
||||
self.client.action_executions.delete.call_args_list
|
||||
)
|
|
@ -1,199 +0,0 @@
|
|||
# Copyright 2014 Mirantis, 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 mock
|
||||
import six
|
||||
|
||||
from mistralclient.api.v2 import actions
|
||||
from mistralclient.commands.v2 import actions as action_cmd
|
||||
from mistralclient.commands.v2 import base as cmd_base
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
ACTION_DICT = {
|
||||
'id': '1234-4567-7894-7895',
|
||||
'name': 'a',
|
||||
'is_system': True,
|
||||
'input': "param1",
|
||||
'description': 'My cool action',
|
||||
'tags': ['test'],
|
||||
'created_at': '1',
|
||||
'updated_at': '1'
|
||||
}
|
||||
|
||||
ACTION_DEF = """
|
||||
---
|
||||
version: '2.0'
|
||||
|
||||
base: std.echo
|
||||
base-parameters:
|
||||
output: "<% $.str1 %><% $.str2 %>"
|
||||
output: "<% $ %><% $ %>"
|
||||
"""
|
||||
|
||||
ACTION_WITH_DEF_DICT = ACTION_DICT.copy()
|
||||
ACTION_WITH_DEF_DICT.update({'definition': ACTION_DEF})
|
||||
ACTION = actions.Action(mock, ACTION_DICT)
|
||||
ACTION_WITH_DEF = actions.Action(mock, ACTION_WITH_DEF_DICT)
|
||||
|
||||
|
||||
class TestCLIActionsV2(base.BaseCommandTest):
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create(self, mock_open):
|
||||
self.client.actions.create.return_value = [ACTION]
|
||||
|
||||
result = self.call(action_cmd.Create, app_args=['1.txt'])
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create_public(self, mock_open):
|
||||
self.client.actions.create.return_value = [ACTION]
|
||||
|
||||
result = self.call(
|
||||
action_cmd.Create,
|
||||
app_args=['1.txt', '--public']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
'public',
|
||||
self.client.actions.create.call_args[1]['scope']
|
||||
)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create_long_input(self, mock_open):
|
||||
action_long_input_dict = ACTION_DICT.copy()
|
||||
long_input = ', '.join(
|
||||
['var%s' % i for i in six.moves.xrange(10)]
|
||||
)
|
||||
action_long_input_dict['input'] = long_input
|
||||
workflow_long_input = actions.Action(
|
||||
mock.Mock(),
|
||||
action_long_input_dict
|
||||
)
|
||||
self.client.actions.create.return_value = [workflow_long_input]
|
||||
|
||||
result = self.call(action_cmd.Create, app_args=['1.txt'])
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, cmd_base.cut(long_input),
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_update(self, mock_open):
|
||||
self.client.actions.update.return_value = [ACTION]
|
||||
|
||||
result = self.call(action_cmd.Update, app_args=['my_action.yaml'])
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_update_public(self, mock_open):
|
||||
self.client.actions.update.return_value = [ACTION]
|
||||
|
||||
result = self.call(
|
||||
action_cmd.Update,
|
||||
app_args=['my_action.yaml', '--public']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
'public',
|
||||
self.client.actions.update.call_args[1]['scope']
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.actions.list.return_value = [ACTION]
|
||||
|
||||
result = self.call(action_cmd.List)
|
||||
|
||||
self.assertEqual(
|
||||
[('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.actions.get.return_value = ACTION
|
||||
|
||||
result = self.call(action_cmd.Get, app_args=['name'])
|
||||
|
||||
self.assertEqual(
|
||||
('1234-4567-7894-7895', 'a', True, "param1",
|
||||
'My cool action', 'test', '1', '1'),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(action_cmd.Delete, app_args=['name'])
|
||||
|
||||
self.client.actions.delete.assert_called_once_with('name')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(action_cmd.Delete, app_args=['name1', 'name2'])
|
||||
|
||||
self.assertEqual(2, self.client.actions.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('name1'), mock.call('name2')],
|
||||
self.client.actions.delete.call_args_list
|
||||
)
|
||||
|
||||
def test_get_definition(self):
|
||||
self.client.actions.get.return_value = ACTION_WITH_DEF
|
||||
|
||||
self.call(action_cmd.GetDefinition, app_args=['name'])
|
||||
|
||||
self.app.stdout.write.assert_called_with(ACTION_DEF)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_validate(self, mock_open):
|
||||
self.client.actions.validate.return_value = {'valid': True}
|
||||
|
||||
result = self.call(action_cmd.Validate, app_args=['action.yaml'])
|
||||
|
||||
self.assertEqual((True, None), result[1])
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_validate_failed(self, mock_open):
|
||||
self.client.actions.validate.return_value = {
|
||||
'valid': False,
|
||||
'error': 'Invalid DSL...'
|
||||
}
|
||||
|
||||
result = self.call(action_cmd.Validate, app_args=['action.yaml'])
|
||||
|
||||
self.assertEqual((False, 'Invalid DSL...'), result[1])
|
|
@ -1,23 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 mistralclient.tests.unit.base_shell_test as base
|
||||
|
||||
|
||||
class TestCLIBashCompletionV2(base.BaseShellTests):
|
||||
def test_bash_completion(self):
|
||||
bash_completion, stderr = self.shell('bash-completion')
|
||||
|
||||
self.assertIn('bash-completion', bash_completion)
|
||||
self.assertFalse(stderr)
|
|
@ -1,178 +0,0 @@
|
|||
# Copyright 2014 Mirantis, 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 mock
|
||||
|
||||
from mistralclient.api.v2 import cron_triggers
|
||||
from mistralclient.commands.v2 import cron_triggers as cron_triggers_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
TRIGGER_DICT = {
|
||||
'name': 'my_trigger',
|
||||
'workflow_name': 'flow1',
|
||||
'workflow_input': {},
|
||||
'workflow_params': {},
|
||||
'pattern': '* * * * *',
|
||||
'next_execution_time': '4242-12-20 13:37',
|
||||
'remaining_executions': 5,
|
||||
'created_at': '1',
|
||||
'updated_at': '1'
|
||||
}
|
||||
|
||||
TRIGGER = cron_triggers.CronTrigger(mock, TRIGGER_DICT)
|
||||
|
||||
|
||||
class TestCLITriggersV2(base.BaseCommandTest):
|
||||
@mock.patch('mistralclient.commands.v2.cron_triggers.Create.'
|
||||
'_convert_time_string_to_utc')
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create(self, mock_open, mock_convert):
|
||||
self.client.cron_triggers.create.return_value = TRIGGER
|
||||
mock_open.return_value = mock.MagicMock(spec=open)
|
||||
|
||||
result = self.call(
|
||||
cron_triggers_cmd.Create,
|
||||
app_args=['my_trigger', 'flow1', '--pattern', '* * * * *',
|
||||
'--params', '{}', '--count', '5', '--first-time',
|
||||
'4242-12-20 13:37', '--utc']
|
||||
)
|
||||
|
||||
mock_convert.assert_not_called()
|
||||
self.assertEqual(
|
||||
(
|
||||
'my_trigger', 'flow1', {}, '* * * * *',
|
||||
'4242-12-20 13:37', 5, '1', '1'
|
||||
),
|
||||
result[1]
|
||||
)
|
||||
|
||||
@mock.patch('mistralclient.commands.v2.cron_triggers.Create.'
|
||||
'_convert_time_string_to_utc')
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create_no_utc(self, mock_open, mock_convert):
|
||||
self.client.cron_triggers.create.return_value = TRIGGER
|
||||
mock_open.return_value = mock.MagicMock(spec=open)
|
||||
mock_convert.return_value = '4242-12-20 18:37'
|
||||
|
||||
result = self.call(
|
||||
cron_triggers_cmd.Create,
|
||||
app_args=['my_trigger', 'flow1', '--pattern', '* * * * *',
|
||||
'--params', '{}', '--count', '5', '--first-time',
|
||||
'4242-12-20 13:37']
|
||||
)
|
||||
|
||||
mock_convert.assert_called_once_with('4242-12-20 13:37')
|
||||
self.client.cron_triggers.create.assert_called_once_with(
|
||||
'my_trigger', 'flow1', {}, {}, '* * * * *', '4242-12-20 18:37', 5)
|
||||
self.assertEqual(
|
||||
(
|
||||
'my_trigger', 'flow1', {}, '* * * * *',
|
||||
'4242-12-20 13:37', 5, '1', '1'
|
||||
),
|
||||
result[1]
|
||||
)
|
||||
|
||||
@mock.patch('mistralclient.commands.v2.cron_triggers.time')
|
||||
def test_convert_time_string_to_utc_from_utc(self, mock_time):
|
||||
cmd = cron_triggers_cmd.Create(self.app, None)
|
||||
|
||||
mock_time.daylight = 0
|
||||
mock_time.altzone = 0
|
||||
mock_time.timezone = 0
|
||||
mock_localtime = mock.Mock()
|
||||
mock_localtime.tm_isdst = 0
|
||||
mock_time.localtime.return_value = mock_localtime
|
||||
|
||||
utc_value = cmd._convert_time_string_to_utc('4242-12-20 13:37')
|
||||
|
||||
expected_time = '4242-12-20 13:37'
|
||||
|
||||
self.assertEqual(expected_time, utc_value)
|
||||
|
||||
@mock.patch('mistralclient.commands.v2.cron_triggers.time')
|
||||
def test_convert_time_string_to_utc_from_dst(self, mock_time):
|
||||
cmd = cron_triggers_cmd.Create(self.app, None)
|
||||
|
||||
mock_time.daylight = 1
|
||||
mock_time.altzone = (4 * 60 * 60)
|
||||
mock_time.timezone = (5 * 60 * 60)
|
||||
mock_localtime = mock.Mock()
|
||||
mock_localtime.tm_isdst = 1
|
||||
mock_time.localtime.return_value = mock_localtime
|
||||
|
||||
utc_value = cmd._convert_time_string_to_utc('4242-12-20 13:37')
|
||||
|
||||
expected_time = '4242-12-20 17:37'
|
||||
|
||||
self.assertEqual(expected_time, utc_value)
|
||||
|
||||
@mock.patch('mistralclient.commands.v2.cron_triggers.time')
|
||||
def test_convert_time_string_to_utc_no_dst(self, mock_time):
|
||||
cmd = cron_triggers_cmd.Create(self.app, None)
|
||||
|
||||
mock_time.daylight = 1
|
||||
mock_time.altzone = (4 * 60 * 60)
|
||||
mock_time.timezone = (5 * 60 * 60)
|
||||
mock_localtime = mock.Mock()
|
||||
mock_localtime.tm_isdst = 0
|
||||
mock_time.localtime.return_value = mock_localtime
|
||||
|
||||
utc_value = cmd._convert_time_string_to_utc('4242-12-20 13:37')
|
||||
|
||||
expected_time = '4242-12-20 18:37'
|
||||
|
||||
self.assertEqual(expected_time, utc_value)
|
||||
|
||||
def test_list(self):
|
||||
self.client.cron_triggers.list.return_value = [TRIGGER]
|
||||
|
||||
result = self.call(cron_triggers_cmd.List)
|
||||
|
||||
self.assertEqual(
|
||||
[(
|
||||
'my_trigger', 'flow1', {}, '* * * * *',
|
||||
'4242-12-20 13:37', 5, '1', '1'
|
||||
)],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.cron_triggers.get.return_value = TRIGGER
|
||||
|
||||
result = self.call(cron_triggers_cmd.Get, app_args=['name'])
|
||||
|
||||
self.assertEqual(
|
||||
(
|
||||
'my_trigger', 'flow1', {}, '* * * * *',
|
||||
'4242-12-20 13:37', 5, '1', '1'
|
||||
),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(cron_triggers_cmd.Delete, app_args=['name'])
|
||||
|
||||
self.client.cron_triggers.delete.assert_called_once_with('name')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(cron_triggers_cmd.Delete, app_args=['name1', 'name2'])
|
||||
|
||||
self.assertEqual(2, self.client.cron_triggers.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('name1'), mock.call('name2')],
|
||||
self.client.cron_triggers.delete.call_args_list
|
||||
)
|
|
@ -1,125 +0,0 @@
|
|||
# Copyright 2015 - StackStorm, 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 copy
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
import yaml
|
||||
|
||||
from mistralclient.api.v2 import environments
|
||||
from mistralclient.commands.v2 import environments as environment_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
ENVIRONMENT_DICT = {
|
||||
'name': 'env1',
|
||||
'description': 'Test Environment #1',
|
||||
'scope': 'private',
|
||||
'variables': {
|
||||
'server': 'localhost',
|
||||
'database': 'test',
|
||||
'timeout': 600,
|
||||
'verbose': True
|
||||
},
|
||||
'created_at': str(datetime.datetime.utcnow()),
|
||||
'updated_at': str(datetime.datetime.utcnow())
|
||||
}
|
||||
|
||||
ENVIRONMENT = environments.Environment(mock, ENVIRONMENT_DICT)
|
||||
EXPECTED_RESULT = (ENVIRONMENT_DICT['name'],
|
||||
ENVIRONMENT_DICT['description'],
|
||||
json.dumps(ENVIRONMENT_DICT['variables'], indent=4),
|
||||
ENVIRONMENT_DICT['scope'],
|
||||
ENVIRONMENT_DICT['created_at'],
|
||||
ENVIRONMENT_DICT['updated_at'])
|
||||
|
||||
|
||||
class TestCLIEnvironmentsV2(base.BaseCommandTest):
|
||||
|
||||
def _test_create(self, content):
|
||||
self.client.environments.create.return_value = ENVIRONMENT
|
||||
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(content.encode('utf-8'))
|
||||
f.flush()
|
||||
file_path = os.path.abspath(f.name)
|
||||
result = self.call(environment_cmd.Create, app_args=[file_path])
|
||||
self.assertEqual(EXPECTED_RESULT, result[1])
|
||||
|
||||
def test_create_from_json(self):
|
||||
self._test_create(json.dumps(ENVIRONMENT_DICT, indent=4))
|
||||
|
||||
def test_create_from_yaml(self):
|
||||
yml = yaml.dump(ENVIRONMENT_DICT, default_flow_style=False)
|
||||
self._test_create(yml)
|
||||
|
||||
def _test_update(self, content):
|
||||
self.client.environments.update.return_value = ENVIRONMENT
|
||||
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(content.encode('utf-8'))
|
||||
f.flush()
|
||||
file_path = os.path.abspath(f.name)
|
||||
result = self.call(environment_cmd.Update, app_args=[file_path])
|
||||
self.assertEqual(EXPECTED_RESULT, result[1])
|
||||
|
||||
def test_update_from_json(self):
|
||||
env = copy.deepcopy(ENVIRONMENT_DICT)
|
||||
del env['created_at']
|
||||
del env['updated_at']
|
||||
self._test_update(json.dumps(env, indent=4))
|
||||
|
||||
def test_update_from_yaml(self):
|
||||
env = copy.deepcopy(ENVIRONMENT_DICT)
|
||||
del env['created_at']
|
||||
del env['updated_at']
|
||||
yml = yaml.dump(env, default_flow_style=False)
|
||||
self._test_update(yml)
|
||||
|
||||
def test_list(self):
|
||||
self.client.environments.list.return_value = [ENVIRONMENT]
|
||||
expected = (ENVIRONMENT_DICT['name'],
|
||||
ENVIRONMENT_DICT['description'],
|
||||
ENVIRONMENT_DICT['scope'],
|
||||
ENVIRONMENT_DICT['created_at'],
|
||||
ENVIRONMENT_DICT['updated_at'])
|
||||
|
||||
result = self.call(environment_cmd.List)
|
||||
|
||||
self.assertListEqual([expected], result[1])
|
||||
|
||||
def test_get(self):
|
||||
self.client.environments.get.return_value = ENVIRONMENT
|
||||
|
||||
result = self.call(environment_cmd.Get, app_args=['name'])
|
||||
|
||||
self.assertEqual(EXPECTED_RESULT, result[1])
|
||||
|
||||
def test_delete(self):
|
||||
self.call(environment_cmd.Delete, app_args=['name'])
|
||||
|
||||
self.client.environments.delete.assert_called_once_with('name')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(environment_cmd.Delete, app_args=['name1', 'name2'])
|
||||
|
||||
self.assertEqual(2, self.client.environments.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('name1'), mock.call('name2')],
|
||||
self.client.environments.delete.call_args_list
|
||||
)
|
|
@ -1,100 +0,0 @@
|
|||
# Copyright 2014 Mirantis, 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 mock
|
||||
|
||||
from mistralclient.api.v2 import event_triggers
|
||||
from mistralclient.commands.v2 import event_triggers as event_triggers_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
TRIGGER_DICT = {
|
||||
'id': '456',
|
||||
'name': 'my_trigger',
|
||||
'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
|
||||
'workflow_input': {},
|
||||
'workflow_params': {},
|
||||
'exchange': 'dummy_exchange',
|
||||
'topic': 'dummy_topic',
|
||||
'event': 'event.dummy',
|
||||
'created_at': '1',
|
||||
'updated_at': '1'
|
||||
}
|
||||
|
||||
TRIGGER = event_triggers.EventTrigger(mock, TRIGGER_DICT)
|
||||
|
||||
|
||||
class TestCLITriggersV2(base.BaseCommandTest):
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create(self, mock_open):
|
||||
self.client.event_triggers.create.return_value = TRIGGER
|
||||
mock_open.return_value = mock.MagicMock(spec=open)
|
||||
|
||||
result = self.call(
|
||||
event_triggers_cmd.Create,
|
||||
app_args=['my_trigger', '123e4567-e89b-12d3-a456-426655440000',
|
||||
'dummy_exchange', 'dummy_topic', 'event.dummy',
|
||||
'--params', '{}']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
(
|
||||
'456', 'my_trigger', '123e4567-e89b-12d3-a456-426655440000',
|
||||
{}, 'dummy_exchange', 'dummy_topic', 'event.dummy', '1', '1'
|
||||
),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.event_triggers.list.return_value = [TRIGGER]
|
||||
|
||||
result = self.call(event_triggers_cmd.List)
|
||||
|
||||
self.assertEqual(
|
||||
[(
|
||||
'456', 'my_trigger', '123e4567-e89b-12d3-a456-426655440000',
|
||||
{}, 'dummy_exchange', 'dummy_topic', 'event.dummy', '1', '1'
|
||||
)],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.event_triggers.get.return_value = TRIGGER
|
||||
|
||||
result = self.call(event_triggers_cmd.Get, app_args=['id'])
|
||||
|
||||
self.assertEqual(
|
||||
(
|
||||
'456', 'my_trigger', '123e4567-e89b-12d3-a456-426655440000',
|
||||
{}, 'dummy_exchange', 'dummy_topic', 'event.dummy', '1', '1'
|
||||
),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(event_triggers_cmd.Delete, app_args=['id'])
|
||||
|
||||
self.client.event_triggers.delete.assert_called_once_with('id')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(event_triggers_cmd.Delete, app_args=['id1', 'id2'])
|
||||
|
||||
self.assertEqual(2, self.client.event_triggers.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('id1'), mock.call('id2')],
|
||||
self.client.event_triggers.delete.call_args_list
|
||||
)
|
|
@ -1,289 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, Inc.
|
||||
# Copyright 2016 - Brocade Communications Systems, 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 mock
|
||||
import pkg_resources as pkg
|
||||
import six
|
||||
import sys
|
||||
|
||||
from mistralclient.api.v2 import executions
|
||||
from mistralclient.commands.v2 import executions as execution_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
EXEC = executions.Execution(
|
||||
mock,
|
||||
{
|
||||
'id': '123',
|
||||
'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
|
||||
'workflow_name': 'some',
|
||||
'description': '',
|
||||
'state': 'RUNNING',
|
||||
'state_info': None,
|
||||
'created_at': '1',
|
||||
'updated_at': '1',
|
||||
'task_execution_id': None
|
||||
}
|
||||
)
|
||||
|
||||
SUB_WF_EXEC = executions.Execution(
|
||||
mock,
|
||||
{
|
||||
'id': '456',
|
||||
'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
|
||||
'workflow_name': 'some_sub_wf',
|
||||
'description': '',
|
||||
'state': 'RUNNING',
|
||||
'state_info': None,
|
||||
'created_at': '1',
|
||||
'updated_at': '1',
|
||||
'task_execution_id': 'abc'
|
||||
}
|
||||
)
|
||||
|
||||
EX_RESULT = (
|
||||
'123',
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
'some',
|
||||
'',
|
||||
'<none>',
|
||||
'RUNNING',
|
||||
None,
|
||||
'1',
|
||||
'1'
|
||||
)
|
||||
|
||||
SUB_WF_EX_RESULT = (
|
||||
'456',
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
'some_sub_wf',
|
||||
'',
|
||||
'abc',
|
||||
'RUNNING',
|
||||
None,
|
||||
'1',
|
||||
'1'
|
||||
)
|
||||
|
||||
|
||||
class TestCLIExecutionsV2(base.BaseCommandTest):
|
||||
|
||||
stdout = six.moves.StringIO()
|
||||
stderr = six.moves.StringIO()
|
||||
|
||||
def setUp(self):
|
||||
super(TestCLIExecutionsV2, self).setUp()
|
||||
|
||||
# Redirect stdout and stderr so it doesn't pollute the test result.
|
||||
sys.stdout = self.stdout
|
||||
sys.stderr = self.stderr
|
||||
|
||||
def tearDown(self):
|
||||
super(TestCLIExecutionsV2, self).tearDown()
|
||||
|
||||
# Reset to original stdout and stderr.
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
|
||||
def test_create_wf_input_string(self):
|
||||
self.client.executions.create.return_value = EXEC
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Create,
|
||||
app_args=['id', '{ "context": true }']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_create_wf_input_file(self):
|
||||
self.client.executions.create.return_value = EXEC
|
||||
|
||||
path = pkg.resource_filename(
|
||||
'mistralclient',
|
||||
'tests/unit/resources/ctx.json'
|
||||
)
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Create,
|
||||
app_args=['id', path]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_create_with_description(self):
|
||||
self.client.executions.create.return_value = EXEC
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Create,
|
||||
app_args=['id', '{ "context": true }', '-d', '']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_update_state(self):
|
||||
states = ['RUNNING', 'SUCCESS', 'PAUSED', 'ERROR', 'CANCELLED']
|
||||
|
||||
for state in states:
|
||||
self.client.executions.update.return_value = executions.Execution(
|
||||
mock,
|
||||
{
|
||||
'id': '123',
|
||||
'workflow_id': '123e4567-e89b-12d3-a456-426655440000',
|
||||
'workflow_name': 'some',
|
||||
'description': '',
|
||||
'state': state,
|
||||
'state_info': None,
|
||||
'created_at': '1',
|
||||
'updated_at': '1',
|
||||
'task_execution_id': None
|
||||
}
|
||||
)
|
||||
|
||||
ex_result = list(EX_RESULT)
|
||||
ex_result[5] = state
|
||||
ex_result = tuple(ex_result)
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Update,
|
||||
app_args=['id', '-s', state]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
ex_result,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_update_invalid_state(self):
|
||||
states = ['IDLE', 'WAITING', 'DELAYED']
|
||||
|
||||
for state in states:
|
||||
self.assertRaises(
|
||||
SystemExit,
|
||||
self.call,
|
||||
execution_cmd.Update,
|
||||
app_args=['id', '-s', state]
|
||||
)
|
||||
|
||||
def test_resume_update_env(self):
|
||||
self.client.executions.update.return_value = EXEC
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Update,
|
||||
app_args=['id', '-s', 'RUNNING', '--env', '{"k1": "foobar"}']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_update_description(self):
|
||||
self.client.executions.update.return_value = EXEC
|
||||
|
||||
result = self.call(
|
||||
execution_cmd.Update,
|
||||
app_args=['id', '-d', 'foobar']
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.executions.list.return_value = [EXEC, SUB_WF_EXEC]
|
||||
|
||||
result = self.call(execution_cmd.List)
|
||||
|
||||
self.assertEqual(
|
||||
[EX_RESULT, SUB_WF_EX_RESULT],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_list_with_pagination(self):
|
||||
self.client.executions.list.return_value = [EXEC]
|
||||
|
||||
self.call(execution_cmd.List)
|
||||
self.client.executions.list.assert_called_once_with(
|
||||
limit=100,
|
||||
marker='',
|
||||
sort_dirs='asc',
|
||||
sort_keys='created_at',
|
||||
task=None
|
||||
)
|
||||
|
||||
self.call(
|
||||
execution_cmd.List,
|
||||
app_args=[
|
||||
'--limit', '5',
|
||||
'--sort_dirs', 'id, Workflow',
|
||||
'--sort_keys', 'desc',
|
||||
'--marker', 'abc'
|
||||
]
|
||||
)
|
||||
|
||||
self.client.executions.list.assert_called_with(
|
||||
limit=5,
|
||||
marker='abc',
|
||||
sort_dirs='id, Workflow',
|
||||
sort_keys='desc',
|
||||
task=None
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.executions.get.return_value = EXEC
|
||||
|
||||
result = self.call(execution_cmd.Get, app_args=['id'])
|
||||
|
||||
self.assertEqual(
|
||||
EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get_sub_wf_ex(self):
|
||||
self.client.executions.get.return_value = SUB_WF_EXEC
|
||||
|
||||
result = self.call(execution_cmd.Get, app_args=['id'])
|
||||
|
||||
self.assertEqual(
|
||||
SUB_WF_EX_RESULT,
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(execution_cmd.Delete, app_args=['id'])
|
||||
|
||||
self.client.executions.delete.assert_called_once_with('id')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(execution_cmd.Delete, app_args=['id1', 'id2'])
|
||||
|
||||
self.assertEqual(2, self.client.executions.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('id1'), mock.call('id2')],
|
||||
self.client.executions.delete.call_args_list
|
||||
)
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright 2016 Catalyst IT Limited
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from mistralclient.api.v2 import members
|
||||
from mistralclient.commands.v2 import members as member_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
MEMBER_DICT = {
|
||||
'id': '123',
|
||||
'resource_id': '456',
|
||||
'resource_type': 'workflow',
|
||||
'project_id': '1111',
|
||||
'member_id': '2222',
|
||||
'status': 'pending',
|
||||
'created_at': '1',
|
||||
'updated_at': '1'
|
||||
}
|
||||
|
||||
MEMBER = members.Member(mock, MEMBER_DICT)
|
||||
|
||||
|
||||
class TestCLIWorkflowMembers(base.BaseCommandTest):
|
||||
def test_create(self):
|
||||
self.client.members.create.return_value = MEMBER
|
||||
|
||||
result = self.call(
|
||||
member_cmd.Create,
|
||||
app_args=[MEMBER_DICT['resource_id'], MEMBER_DICT['resource_type'],
|
||||
MEMBER_DICT['member_id']]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
('456', 'workflow', '1111', '2222', 'pending', '1', '1'),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_update(self):
|
||||
self.client.members.update.return_value = MEMBER
|
||||
|
||||
result = self.call(
|
||||
member_cmd.Update,
|
||||
app_args=[MEMBER_DICT['resource_id'], MEMBER_DICT['resource_type'],
|
||||
'-m', MEMBER_DICT['member_id']]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
('456', 'workflow', '1111', '2222', 'pending', '1', '1'),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_list(self):
|
||||
self.client.members.list.return_value = [MEMBER]
|
||||
|
||||
result = self.call(
|
||||
member_cmd.List,
|
||||
app_args=[MEMBER_DICT['resource_id'], MEMBER_DICT['resource_type']]
|
||||
)
|
||||
|
||||
self.assertListEqual(
|
||||
[('456', 'workflow', '1111', '2222', 'pending', '1', '1')],
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
self.client.members.get.return_value = MEMBER
|
||||
|
||||
result = self.call(
|
||||
member_cmd.Get,
|
||||
app_args=[MEMBER_DICT['resource_id'], MEMBER_DICT['resource_type'],
|
||||
'-m', MEMBER_DICT['member_id']]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
('456', 'workflow', '1111', '2222', 'pending', '1', '1'),
|
||||
result[1]
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
self.call(
|
||||
member_cmd.Delete,
|
||||
app_args=[MEMBER_DICT['resource_id'], MEMBER_DICT['resource_type'],
|
||||
MEMBER_DICT['member_id']]
|
||||
)
|
||||
|
||||
self.client.members.delete.assert_called_once_with(
|
||||
MEMBER_DICT['resource_id'],
|
||||
MEMBER_DICT['resource_type'],
|
||||
MEMBER_DICT['member_id']
|
||||
)
|
|
@ -1,37 +0,0 @@
|
|||
# Copyright 2015 Huawei Technologies Co., Ltd.
|
||||
#
|
||||
# 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 mock
|
||||
|
||||
from mistralclient.api.v2 import services
|
||||
from mistralclient.commands.v2 import services as service_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
SERVICE_DICT = {
|
||||
'name': 'service_name',
|
||||
'type': 'service_type',
|
||||
}
|
||||
|
||||
SERVICE = services.Service(mock, SERVICE_DICT)
|
||||
|
||||
|
||||
class TestCLIServicesV2(base.BaseCommandTest):
|
||||
def test_list(self):
|
||||
self.client.services.list.return_value = [SERVICE]
|
||||
expected = (SERVICE_DICT['name'], SERVICE_DICT['type'],)
|
||||
|
||||
result = self.call(service_cmd.List)
|
||||
|
||||
self.assertListEqual([expected], result[1])
|
|
@ -1,128 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 json
|
||||
|
||||
import mock
|
||||
|
||||
from mistralclient.api.v2 import tasks
|
||||
from mistralclient.commands.v2 import tasks as task_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
TASK_DICT = {
|
||||
'id': '123',
|
||||
'name': 'some',
|
||||
'workflow_name': 'thing',
|
||||
'workflow_execution_id': '321',
|
||||
'state': 'RUNNING',
|
||||
'state_info': None,
|
||||
'created_at': '1',
|
||||
'updated_at': '1',
|
||||
}
|
||||
|
||||
TASK_RESULT = {"test": "is", "passed": "successfully"}
|
||||
TASK_PUBLISHED = {"bar1": "val1", "var2": 2}
|
||||
|
||||
TASK_WITH_RESULT_DICT = TASK_DICT.copy()
|
||||
TASK_WITH_RESULT_DICT.update({'result': json.dumps(TASK_RESULT)})
|
||||
TASK_WITH_PUBLISHED_DICT = TASK_DICT.copy()
|
||||
TASK_WITH_PUBLISHED_DICT.update({'published': json.dumps(TASK_PUBLISHED)})
|
||||
|
||||
TASK = tasks.Task(mock, TASK_DICT)
|
||||
TASK_WITH_RESULT = tasks.Task(mock, TASK_WITH_RESULT_DICT)
|
||||
TASK_WITH_PUBLISHED = tasks.Task(mock, TASK_WITH_PUBLISHED_DICT)
|
||||
|
||||
EXPECTED_TASK_RESULT = (
|
||||
'123', 'some', 'thing', '321', 'RUNNING', None, '1', '1'
|
||||
)
|
||||
|
||||
|
||||
class TestCLITasksV2(base.BaseCommandTest):
|
||||
def test_list(self):
|
||||
self.client.tasks.list.return_value = [TASK]
|
||||
|
||||
result = self.call(task_cmd.List)
|
||||
|
||||
self.assertEqual([EXPECTED_TASK_RESULT], result[1])
|
||||
|
||||
def test_list_with_workflow_execution(self):
|
||||
self.client.tasks.list.return_value = [TASK]
|
||||
|
||||
result = self.call(task_cmd.List, app_args=['workflow_execution'])
|
||||
|
||||
self.assertEqual([EXPECTED_TASK_RESULT], result[1])
|
||||
|
||||
def test_get(self):
|
||||
self.client.tasks.get.return_value = TASK
|
||||
|
||||
result = self.call(task_cmd.Get, app_args=['id'])
|
||||
|
||||
self.assertEqual(EXPECTED_TASK_RESULT, result[1])
|
||||
|
||||
def test_get_result(self):
|
||||
self.client.tasks.get.return_value = TASK_WITH_RESULT
|
||||
|
||||
self.call(task_cmd.GetResult, app_args=['id'])
|
||||
|
||||
self.assertDictEqual(
|
||||
TASK_RESULT,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_get_published(self):
|
||||
self.client.tasks.get.return_value = TASK_WITH_PUBLISHED
|
||||
|
||||
self.call(task_cmd.GetPublished, app_args=['id'])
|
||||
|
||||
self.assertDictEqual(
|
||||
TASK_PUBLISHED,
|
||||
json.loads(self.app.stdout.write.call_args[0][0])
|
||||
)
|
||||
|
||||
def test_rerun(self):
|
||||
self.client.tasks.rerun.return_value = TASK
|
||||
|
||||
result = self.call(task_cmd.Rerun, app_args=['id'])
|
||||
|
||||
self.assertEqual(EXPECTED_TASK_RESULT, result[1])
|
||||
|
||||
def test_rerun_no_reset(self):
|
||||
self.client.tasks.rerun.return_value = TASK
|
||||
|
||||
result = self.call(task_cmd.Rerun, app_args=['id', '--resume'])
|
||||
|
||||
self.assertEqual(EXPECTED_TASK_RESULT, result[1])
|
||||
|
||||
def test_rerun_update_env(self):
|
||||
self.client.tasks.rerun.return_value = TASK
|
||||
|
||||
result = self.call(
|
||||
task_cmd.Rerun,
|
||||
app_args=['id', '--env', '{"k1": "foobar"}']
|
||||
)
|
||||
|
||||
self.assertEqual(EXPECTED_TASK_RESULT, result[1])
|
||||
|
||||
def test_rerun_no_reset_update_env(self):
|
||||
self.client.tasks.rerun.return_value = TASK
|
||||
|
||||
result = self.call(
|
||||
task_cmd.Rerun,
|
||||
app_args=['id', '--resume', '--env', '{"k1": "foobar"}']
|
||||
)
|
||||
|
||||
self.assertEqual(EXPECTED_TASK_RESULT, result[1])
|
|
@ -1,119 +0,0 @@
|
|||
# Copyright 2014 - Mirantis, Inc.
|
||||
# Copyright 2015 - StackStorm, 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 mock
|
||||
|
||||
from mistralclient.api.v2 import workbooks
|
||||
from mistralclient.commands.v2 import workbooks as workbook_cmd
|
||||
from mistralclient.tests.unit import base
|
||||
|
||||
|
||||
WORKBOOK_DICT = {
|
||||
'name': 'a',
|
||||
'tags': ['a', 'b'],
|
||||
'created_at': '1',
|
||||
'updated_at': '1'
|
||||
}
|
||||
|
||||
|
||||
WB_DEF = """
|
||||
---
|
||||
version: '2.0
|
||||
|
||||
name: wb
|
||||
|
||||
workflows:
|
||||
wf1:
|
||||
tasks:
|
||||
task1:
|
||||
action: nova.servers_get server="1"
|
||||
"""
|
||||
|
||||
WB_WITH_DEF_DICT = WORKBOOK_DICT.copy()
|
||||
WB_WITH_DEF_DICT.update({'definition': WB_DEF})
|
||||
WORKBOOK = workbooks.Workbook(mock, WORKBOOK_DICT)
|
||||
WORKBOOK_WITH_DEF = workbooks.Workbook(mock, WB_WITH_DEF_DICT)
|
||||
|
||||
|
||||
class TestCLIWorkbooksV2(base.BaseCommandTest):
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_create(self, mock_open):
|
||||
self.client.workbooks.create.return_value = WORKBOOK
|
||||
|
||||
result = self.call(workbook_cmd.Create, app_args=['wb.yaml'])
|
||||
|
||||
self.assertEqual(('a', 'a, b', '1', '1'), result[1])
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_update(self, mock_open):
|
||||
self.client.workbooks.update.return_value = WORKBOOK
|
||||
|
||||
result = self.call(workbook_cmd.Update, app_args=['definition'])
|
||||
|
||||
self.assertEqual(('a', 'a, b', '1', '1'), result[1])
|
||||
|
||||
def test_list(self):
|
||||
self.client.workbooks.list.return_value = [WORKBOOK]
|
||||
|
||||
result = self.call(workbook_cmd.List)
|
||||
|
||||
self.assertEqual([('a', 'a, b', '1', '1')], result[1])
|
||||
|
||||
def test_get(self):
|
||||
self.client.workbooks.get.return_value = WORKBOOK
|
||||
|
||||
result = self.call(workbook_cmd.Get, app_args=['name'])
|
||||
|
||||
self.assertEqual(('a', 'a, b', '1', '1'), result[1])
|
||||
|
||||
def test_delete(self):
|
||||
self.call(workbook_cmd.Delete, app_args=['name'])
|
||||
|
||||
self.client.workbooks.delete.assert_called_once_with('name')
|
||||
|
||||
def test_delete_with_multi_names(self):
|
||||
self.call(workbook_cmd.Delete, app_args=['name1', 'name2'])
|
||||
|
||||
self.assertEqual(2, self.client.workbooks.delete.call_count)
|
||||
self.assertEqual(
|
||||
[mock.call('name1'), mock.call('name2')],
|
||||
self.client.workbooks.delete.call_args_list
|
||||
)
|
||||
|
||||
def test_get_definition(self):
|
||||
self.client.workbooks.get.return_value = WORKBOOK_WITH_DEF
|
||||
|
||||
self.call(workbook_cmd.GetDefinition, app_args=['name'])
|
||||
|
||||
self.app.stdout.write.assert_called_with(WB_DEF)
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_validate(self, mock_open):
|
||||
self.client.workbooks.validate.return_value = {'valid': True}
|
||||
|
||||
result = self.call(workbook_cmd.Validate, app_args=['wb.yaml'])
|
||||
|
||||
self.assertEqual((True, None), result[1])
|
||||
|
||||
@mock.patch('argparse.open', create=True)
|
||||
def test_validate_failed(self, mock_open):
|
||||
self.client.workbooks.validate.return_value = {
|
||||
'valid': False,
|
||||
'error': 'Invalid DSL...'
|
||||
}
|
||||
|
||||
result = self.call(workbook_cmd.Validate, app_args=['wb.yaml'])
|
||||
|
||||
self.assertEqual((False, 'Invalid DSL...'), result[1])
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue