././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7801814
mistral_dashboard-20.0.0/ 0000775 0001750 0001750 00000000000 00000000000 015276 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/.stestr.conf 0000664 0001750 0001750 00000000071 00000000000 017545 0 ustar 00zuul zuul 0000000 0000000 [DEFAULT]
test_path=./mistraldashboard/tests
top_dir=./
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/.zuul.yaml 0000664 0001750 0001750 00000000312 00000000000 017233 0 ustar 00zuul zuul 0000000 0000000 - project:
templates:
- check-requirements
- horizon-non-primary-django-jobs
- openstack-python3-jobs-horizon
- publish-openstack-docs-pti
- release-notes-jobs-python3
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/AUTHORS 0000664 0001750 0001750 00000005322 00000000000 016350 0 ustar 00zuul zuul 0000000 0000000 Adriano Petrich
Akihiro Motoki
Anastasia Kuznetsova
Andreas Jaeger
Andreas Jaeger
Arnaud M
Bo Tran
Brad P. Crochet
Cao Xuan Hoang
Charles Short
Christian Berendt
Chuck Short
Corey Bryant
Doug Hellmann
Dougal Matthews
Eyal
Flavio Percoco
Gal Margalit
Gal Margalit
Ghanshyam Mann
Hanxi Liu
Hardik Parekh
Hervé Beraud
Ivan Kolodyazhny
Jeffrey Zhang
Jeremy Liu
Jeremy Stanley
Kirill Izotov
Liat Fried
Lingxian Kong
Lucky samadhiya
Mateusz Kowalski
Matthias Runge
Nguyen Hai
Nguyen Hai Truong
Nguyen Hung Phuong
Nikolay Mahotkin
OpenStack Release Bot
Pedro Henrique
Rajiv Kumar
Renat Akhmerov
Rinat Sabitov
Sean McGinnis
Sharat
Sharat Sharma
Sharat Sharma
Takashi Kajinami
Takashi Kajinami
Tobias Urdin
Tobias Urdin
Tony Breeds
Vieri <15050873171@163.com>
Vu Cong Tuan
Zhenguo Niu
Zhenguo Niu
hardik
hparekh
jiaqi07
lawrancejing
manchandavishal
melissaml
pengyuesheng
privaterookie <996514515@qq.com>
ricolin
sanu madhavan
shubham
songwenping
sunjia
venkatamahesh
wu.shiming
zhangguoqing
zhouyunfeng
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/CONTRIBUTING.rst 0000664 0001750 0001750 00000001171 00000000000 017737 0 ustar 00zuul zuul 0000000 0000000 The source repository for this project can be found at:
https://opendev.org/openstack/mistral-dashboard
Pull requests submitted through GitHub are not monitored.
To start contributing to OpenStack, follow the steps in the contribution guide
to set up and use Gerrit:
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
Bugs should be filed on Launchpad:
https://bugs.launchpad.net/mistral
For more specific information about contributing to this repository, see the
Mistral Dashboard contributor guide:
https://docs.openstack.org/mistral/latest/developer/contributor/contributing.html
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/ChangeLog 0000664 0001750 0001750 00000032765 00000000000 017065 0 ustar 00zuul zuul 0000000 0000000 CHANGES
=======
20.0.0
------
* Skip installation to speed up pep8
* reno: Update master for unmaintained/2023.1
* Changed minversion in tox to 3.18.0
* docs: Fix inconsistent format of commands
* Add missing core job templates
* Bump hacking
* Update master for stable/2023.2
* Update master for stable/2023.1
* Update master for stable/2024.2
* reno: Update master for unmaintained/zed
* Replace retired oslosphinx
* reno: Update master for unmaintained/xena
* reno: Update master for unmaintained/wallaby
* reno: Update master for unmaintained/victoria
19.0.0
------
* Update master for stable/2024.1
* reno: Update master for unmaintained/yoga
18.0.0
------
* Update python classifier in setup.cfg
* Update the workflow language syntax documentation url
17.0.0
------
* Use wf\_identifier when creating a execution
* Revert "Use workflow\_id when creating a execution"
* Use workflow\_id when creating a execution
16.0.0.0b1
----------
* Fix tox4 error
* Switch to 2023.1 Python3 unit tests and generic template name
* Update master for stable/zed
15.0.0
------
* remove unicode from code
* Use TOX\_CONSTRAINTS\_FILE
* remove unicode from code
* Replace deprecated ugettext\_lazy and ungettext\_lazy
* Update python testing as per zed cycle testing runtime
* Update master for stable/yoga
* Address RemovedInDjango40Warning
* Update TOX\_CONSTRAINTS\_FILE
* Update master for stable/xena
* Enforce usage of raw definitions
* Replace deprecated inspect.getargspec
14.0.0
------
* [community goal] Update contributor documentation
* setup.cfg: Replace dashes with underscores
* Use py3 as the default runtime for tox
* Add Python3 xena unit tests
* Update master for stable/wallaby
* Setting DEFAULT to False
* Fix gate jobs
12.0.0
------
* Add Python3 wallaby unit tests
* Update master for stable/victoria
11.0.0
------
* [goal] Migrate testing to ubuntu focal
* Cleanup for Refactor-error-messages
* Use unittest.mock instead of mock
* Switch to newer openstackdocstheme and reno versions
* Bump default tox env from py37 to py38
* Add py38 package metadata
* Add Python3 victoria unit tests
* Update master for stable/ussuri
10.0.0
------
* s/assertItemsEqual/assertCountEqual/g
* Remove six usage
* Drop Django 1.11 support
10.0.0.0b1
----------
* [ussuri][goal] Drop python 2.7 support and testing
* Switch to Ussuri jobs
* Use Horizon project template for django jobs
* Update master for stable/train
9.0.0.0rc1
----------
* Fix error when use keystone federation
9.0.0.0b1
---------
* Refactor error messages
* Add Python 3 Train unit tests
* Remove redundant exception handling
* Remove the ErrorHandleTests class
* Modify the name of action\_executions panel
* Follow the new PTI for document build
* Add the unit test for executions
* Add the unit test for cron\_triggers
* Add the unit test for update\_action\_execution
* Add the unit test for create and update action
* Add the unit test for executions detail
* Add the unit test for workflows detail
* Add the unit test for workbooks detail
* Add the unit test for tasks detail
* Add the unit test for cron\_triggers detail
* Add the unit test for action\_executions detail
* Add the unit test for actions detail
* Modify the url of upper\_constraints\_file
* fix the Bug of test in workflows panel
* fix the Bug of test in workbooks panel
* fix the Bug of test in tasks panel
* fix the Bug of test in executions panel
* fix the Bug of test in action\_executions panel
* fix the Bug of test in cron\_triggers panel
* fix the Bug of test in actions panel
* Use openstack-python3-train-jobs for python3 test runtime
* Add python 3.7 classifier to setup.cfg
* Blacklist sphinx 2.1.0 (autodoc bug)
* Correct error messages on update action execution form
* The columns out of page range
* Unified button style on Execute form in workflow panel
* Change action\_execution to action\_executions in url
* Indent four spaces
* Update sphinx dependency
* Remove try: ... finally: pass
* Add asterisk mark for the required field
* Add python37 job
* Translate executions state
* Add asterisk mark for the required field
* Switch to stestr
* Dropping the py35 testing
* Replace git.openstack.org URLs with opendev.org URLs
* OpenDev Migration Patch
* Fix the bug of the py27 test
* Update master for stable/stein
8.0.0
-----
* Change openstack-dev to openstack-discuss
* Update min tox version to 2.0
* Remove setup.py check from pep8 job
* Increment versioning with pbr instruction
* Update links in README
* add python 3.6 unit test job
* import zuul job settings from project-config
* Drop nose dependencies
* fix mistral dashbaord workflow execution form bug
* Update reno for stable/rocky
7.0.0
-----
* Drop mox, no longer needed
* Add the restructuredtext check to the flake8 job
* fix tox python3 overrides
* Ignore the .eggs directory
7.0.0.0b2
---------
* Fix the README formatting
* Django 2.0 support
* support py3
7.0.0.0b1
---------
* Fix workflow language link
* Add py35 to tox.ini
* Updated from global requirements
* Remove mox3 from test-requiremens.txt
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Update reno for stable/queens
6.0.0
-----
* Updated from global requirements
6.0.0.0b2
---------
* Drop django\_openstack\_auth from requirements.txt
6.0.0.0b1
---------
* Revert "Migrate mistral-dashboard to zuul v3"
* Migrate mistral-dashboard to zuul v3
* Updated from global requirements
* Fix CSS for "Cancel" button in workflow execution
* Updated from global requirements
* Expand parameter list for workflow execution
* Updated from global requirements
* Updated from global requirements
* Update reno for stable/pike
* Update the URLs
5.0.0
-----
* Updated from global requirements
* Updated from global requirements
* Change author
* Updated from global requirements
* Remove newlines from test data
5.0.0.0b2
---------
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
5.0.0.0b1
---------
* Updated from global requirements
* Optimize the link address
* Make the definition section of the wb/wf create section editable
* Added link to Create Action form Edit
* Fix "Workflow" section of "Action Execution Details" view
* Change the Next button to Validate
* Added test for mistral-dashboard cron\_triggers
* Fix oslo\_debug\_helper not running
* Updated from global requirements
* Add test for mistral-dashboard action\_executions
* Update test requirement
* Update .gitignore
* Reorganize docs
* Fix 'tox -ereleasenotes' Error
* Replace github with git.openstack.org
* Updated from global requirements
* Update reno for stable/ocata
4.0.0.0rc1
----------
* Updated from global requirements
* Updated from global requirements
* Removed unnecessary utf-8 encoding
* Task table - type column would refer to workflow executions accordingly
* Remove django<1.8 from tests
* Move \_50\_mistral.py file into enabled folder
* Remove the dependency on horizon repo
* Fixed: Dashboard: "Run action" functionality doesn't work
* add CONTRIBUTING.rst
* Add Constraints support
* Updated from global requirements
* Workflow list - added missing fields
* Added links to DSL docs in create/update views
* Fix docs-gate for mistral-dashboard
* Changed "Task Details" to "Task Execution Details"
4.0.0.0b2
---------
* mistral-dashboard:fixed boolean field design bug
* mistral-dashboard: added action executions screens
* Updated from global requirements
* Show team and repo badges on README
4.0.0.0b1
---------
* Fixed action screen "run" button CSS issue
* Remove mox in test-requirement.txt
* Added reno for stable/mitaka, stable/liberty and stable/newton
* Adding files to .gitignore
* Cleanup unused files
* mistral-dashboard: entities name change for system consistency
* Updated from global requirements
* Delete \*openstack/common\* in flake8 exclude list
* Updated from global requirements
* Updated from global requirements
* Enable release notes translation
* Maintain releasenotes for mistral dashboard
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Added sphinix config to setup.cfg
3.0.0.0rc1
----------
* Clean imports in code
* Remove .mailmap and AUTHORS file since it's no longer needed
* Clean imports in code
3.0.0.0b3
---------
* Updated from global requirements
* Remove requirements satisfied by horizon
* Removed 'pull\_right' option
* Fixed unit tests issue
* Updated from global requirements
3.0.0.0b2
---------
* Updated from global requirements
* Updated from global requirements
2.0.0.0rc1
----------
* Updated from global requirements
2.0.0.0b3
---------
* Fix [ui] Actions screen pagination supports only descending order
* UI: Actions screen improvements
* Fix gate-mistral-dashboard-python34
* UI: fixed workflow execution don't work
* UI: table pagination logic encapsulation
* Updated from global requirements
* Update URLs to Django 1.8 style
* UI: Cron trigger create modal
* Updated from global requirements
* Updated from global requirements
* Updated from global requirements
* Mistral-dashboard: Actions screen
2.0.0.0b2
---------
* Updated from global requirements
* UI: Enable CSS use in mistral dashboard
* UI: Cron Trigger screens - list and overview
* UI: Execution & task screens - state info tooltip
* Remove "builtins = \_" from tox.ini
* UI: Task screen auto refresh
* Use the default \`url\` tag instead
* UI: Execution screen auto refresh
* Delete python bytecode before every test run
* Remove version from setup.cfg
* UI: Execution pagination on client side
2.0.0.0b1
---------
* Updated from global requirements
* UI: Execution update - update description modal
* UI: Execution update - table actions
* UI:Actions list:remove desc column+details screen
* UI: Task & Execution screen refactored
* UI: Task & Execution screen refactored
* Updated from global requirements
* UI: Execution Overview screen refactored
* executions overview screen: workflow link
* Added home-page link in setup.cfg file
* Set version for Mitaka
* mistral-dashboard: refer dashboard debug instructions to the updated mistral trbouelshooting page
* Add .mailmap for pbr AUTHORS generation
* Update AUTHORS file
* mistral-dashboard: refer dashboard debug instructions to the updated mistral trbouelshooting page
* Add .mailmap for pbr AUTHORS generation
* Update AUTHORS file
* Changed titles of modal forms
1.0.0.0rc1
----------
* Updated from global requirements
* Updated from global requirements
* Mistral-dashboard: Tasks list-addition of Execution detail screen
1.0.0.0b3
---------
* Updated from global requirements
* Remove the test cases for handle\_errors to fix the py27 gate issue
* Updated from global requirements
* mistral-dashboard: added Task details overview screen infrastructure
* Rename Mistral dashboard to Workflow
* Add tables filter action support
* Restrict the pbr version to sync with global requirement
* Add actions and executions unit tests
* Add unit tests for tasks
* mistral-dashboard: executions input & output modal header - added corresponding name
* mistral-dashboard executions list table: input & output sections content via a modal
* Update AUTHORS
* mistral-dashboard - fixed the installation readme: fixed an issue where horizon was not showing mistral dashboard upon installation
* Add unit tests for workbooks
* mistral-dashboard Execution enhancements
* Tasks list - removal and addition of a column
1.0.0.0b2
---------
* Fixed bug in error handing
* Correct wrong argument name in workbook\_delete()
* Add delete action support
* Add update action support
* Add create action support
* Add mistral APIException as a Recoverable Exception
* Add 'update' and 'delete' unit tests for workflows
* mistral-dashboard documentation
* Add 'index' and 'create' unit tests for workflows
* Added execution get method to mistral-dashboard api
* Show Action definition
* Add actions panel
* Add test helpers and test data
* Add \*.lock and .secret\_key\_store to .gitignore
* Added delete button to execution screen ui on mistral-dashboard
* added git clone explanation
* Add update workbook support
* Show task result when click the id
* Fix errors when user click execution id
* Add delete workbook support
* Add update workflow support
* Add create workbook support
* Remove H101, H803, H238 from ignore list
* Remove H302 check
* Add delete workflow support
* Add create workflow support
* Fix H306 errors
* Show workbook definition
* Refactor workbook table fields
* Show workflow definition
* Adding info on how to install mistraldashboard module in venv
* Enable Unit Test
* Refactor workflow table fields
* Reduce API client object in a request
* Add handle\_error decorator to API calls
* Ignore swap files from getting into repository
* Wrap mistralclient code in api.py
* Not compressing the mistral specific css
* Switch to v2 mistral api
1.0.0.0b1
---------
* Removing redundant header from setup.py
* Update .gitreview file for project rename
2015.1.0
--------
* Updating AUTHORS file
0.1.1
-----
* Making style changes and adding AUTHORS file
* Fix showing all statuses in UI
* Update requirements according to global requirements (master)
0.1
---
* Fix missing static folder
* Reenable Task view inside Executions
* Modify to use API v2
* Enable hacking check H306
* Fixing readme file
* Fixing dependency to Mistral client
0.0.4
-----
* Readme updates
* Make use of /executions endpoint API
* Horizon plugin system compatibility
* Color statuses for both executions and tasks
* Add Task's output and parameters columns
* Move dashboard from python-mistralclient
* Add .gitreview
* Initial commit
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/LICENSE 0000664 0001750 0001750 00000023636 00000000000 016315 0 ustar 00zuul zuul 0000000 0000000
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.
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7801814
mistral_dashboard-20.0.0/PKG-INFO 0000644 0001750 0001750 00000006445 00000000000 016402 0 ustar 00zuul zuul 0000000 0000000 Metadata-Version: 2.1
Name: mistral-dashboard
Version: 20.0.0
Summary: Mistral dashboard
Home-page: https://docs.openstack.org/mistral/latest/
Author: OpenStack
Author-email: openstack-discuss@lists.openstack.org
License: Apache License, Version 2.0
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Environment :: OpenStack
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.8
License-File: LICENSE
Requires-Dist: pbr!=2.1.0,>=2.0.0
Requires-Dist: iso8601>=0.1.11
Requires-Dist: python-mistralclient>=4.3.0
Requires-Dist: PyYAML>=3.12
Requires-Dist: horizon>=17.1.0
========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/tc/badges/mistral-dashboard.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
=================
Mistral Dashboard
=================
Horizon plugin for Mistral.
Setup Instructions
==================
This instruction assumes that Horizon is already installed and it's installation
folder is . Detailed information on how to install Horizon can be
found at https://docs.openstack.org/horizon/latest/contributor/quickstart.html#setup.
The installation folder of Mistral Dashboard will be referred to as .
The following should get you started. Clone the repository into your local
OpenStack directory:
.. code:: console
$ git clone https://opendev.org/openstack/mistral-dashboard.git
Install mistral-dashboard:
.. code:: console
$ sudo pip install -e
Or if you're planning to run Horizon server in a virtual environment (see below):
.. code:: console
$ tox -evenv -- pip install -e ../mistral-dashboard/
and then:
.. code:: console
$ cp -b /mistraldashboard/enabled/_50_mistral.py /openstack_dashboard/local/enabled/_50_mistral.py
Since Mistral only supports Identity v3, you must ensure that the dashboard
points the proper OPENSTACK_KEYSTONE_URL in /openstack_dashboard/local/local_settings.py file::
OPENSTACK_API_VERSIONS = {
"identity": 3,
}
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
Also, make sure you have changed OPENSTACK_HOST to point to your Keystone
server and check all endpoints are accessible. You may want to change
OPENSTACK_ENDPOINT_TYPE to "publicURL" if some of them are not.
When you're ready, you would need to either restart your apache:
.. code:: console
$ sudo service apache2 restart
or run the development server (in case you have decided to use local horizon):
.. code:: console
$ cd ../horizon/
$ tox -evenv -- python manage.py runserver
Mistral-Dashboard Debug Instructions
------------------------------------
For debug instructions refer to `OpenStack Mistral Troubleshooting
`_
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/README.rst 0000664 0001750 0001750 00000004456 00000000000 016776 0 ustar 00zuul zuul 0000000 0000000 ========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/tc/badges/mistral-dashboard.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
=================
Mistral Dashboard
=================
Horizon plugin for Mistral.
Setup Instructions
==================
This instruction assumes that Horizon is already installed and it's installation
folder is . Detailed information on how to install Horizon can be
found at https://docs.openstack.org/horizon/latest/contributor/quickstart.html#setup.
The installation folder of Mistral Dashboard will be referred to as .
The following should get you started. Clone the repository into your local
OpenStack directory:
.. code:: console
$ git clone https://opendev.org/openstack/mistral-dashboard.git
Install mistral-dashboard:
.. code:: console
$ sudo pip install -e
Or if you're planning to run Horizon server in a virtual environment (see below):
.. code:: console
$ tox -evenv -- pip install -e ../mistral-dashboard/
and then:
.. code:: console
$ cp -b /mistraldashboard/enabled/_50_mistral.py /openstack_dashboard/local/enabled/_50_mistral.py
Since Mistral only supports Identity v3, you must ensure that the dashboard
points the proper OPENSTACK_KEYSTONE_URL in /openstack_dashboard/local/local_settings.py file::
OPENSTACK_API_VERSIONS = {
"identity": 3,
}
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
Also, make sure you have changed OPENSTACK_HOST to point to your Keystone
server and check all endpoints are accessible. You may want to change
OPENSTACK_ENDPOINT_TYPE to "publicURL" if some of them are not.
When you're ready, you would need to either restart your apache:
.. code:: console
$ sudo service apache2 restart
or run the development server (in case you have decided to use local horizon):
.. code:: console
$ cd ../horizon/
$ tox -evenv -- python manage.py runserver
Mistral-Dashboard Debug Instructions
------------------------------------
For debug instructions refer to `OpenStack Mistral Troubleshooting
`_
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7441814
mistral_dashboard-20.0.0/doc/ 0000775 0001750 0001750 00000000000 00000000000 016043 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/doc/requirements.txt 0000664 0001750 0001750 00000000225 00000000000 021326 0 ustar 00zuul zuul 0000000 0000000 openstackdocstheme>=2.2.1 # Apache-2.0
sphinx>=2.0.0,!=2.1.0 # BSD
reno>=3.1.0 # Apache-2.0
docutils>=0.11 # OSI-Approved Open Source, Public Domain
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7441814
mistral_dashboard-20.0.0/doc/source/ 0000775 0001750 0001750 00000000000 00000000000 017343 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/doc/source/conf.py 0000664 0001750 0001750 00000004261 00000000000 020645 0 ustar 00zuul zuul 0000000 0000000 # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration ----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'openstackdocstheme',
]
# autodoc generation is a bit aggressive and a nuisance when doing heavy
# text edit cycles.
# execute "export SPHINX_DEBUG=1" in your terminal to disable
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'mistral-dashboard'
copyright = '2014, OpenStack Foundation'
# -- Options for openstackdocstheme -------------------------------------------
openstackdocs_repo_name = 'openstack/mistral-dashboard'
openstackdocs_bug_project = 'mistral'
openstackdocs_bug_tag = 'doc'
# If true, '()' will be appended to :func: etc. cross-reference text.
add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
add_module_names = True
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'native'
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
# html_theme_path = ["."]
html_theme = 'openstackdocs'
# html_static_path = []
# Output file base name for HTML help builder.
htmlhelp_basename = '%sdoc' % project
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/doc/source/contributing.rst 0000664 0001750 0001750 00000000113 00000000000 022577 0 ustar 00zuul zuul 0000000 0000000 ============
Contributing
============
.. include:: ../../CONTRIBUTING.rst
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/doc/source/index.rst 0000664 0001750 0001750 00000000375 00000000000 021211 0 ustar 00zuul zuul 0000000 0000000
Welcome to Mistral Dashboard's documentation!
=============================================
Contents:
.. toctree::
:maxdepth: 1
readme
contributing
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/doc/source/readme.rst 0000664 0001750 0001750 00000000035 00000000000 021330 0 ustar 00zuul zuul 0000000 0000000 .. include:: ../../README.rst ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/manage.py 0000775 0001750 0001750 00000001510 00000000000 017100 0 ustar 00zuul zuul 0000000 0000000 #!/usr/bin/env python
# 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 django.core.management import execute_from_command_line # noqa
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
"mistraldashboard.test.settings")
execute_from_command_line(sys.argv)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7801814
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/ 0000775 0001750 0001750 00000000000 00000000000 022452 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/PKG-INFO 0000644 0001750 0001750 00000006445 00000000000 023556 0 ustar 00zuul zuul 0000000 0000000 Metadata-Version: 2.1
Name: mistral-dashboard
Version: 20.0.0
Summary: Mistral dashboard
Home-page: https://docs.openstack.org/mistral/latest/
Author: OpenStack
Author-email: openstack-discuss@lists.openstack.org
License: Apache License, Version 2.0
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Environment :: OpenStack
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Requires-Python: >=3.8
License-File: LICENSE
Requires-Dist: pbr!=2.1.0,>=2.0.0
Requires-Dist: iso8601>=0.1.11
Requires-Dist: python-mistralclient>=4.3.0
Requires-Dist: PyYAML>=3.12
Requires-Dist: horizon>=17.1.0
========================
Team and repository tags
========================
.. image:: https://governance.openstack.org/tc/badges/mistral-dashboard.svg
:target: https://governance.openstack.org/tc/reference/tags/index.html
.. Change things from this point on
=================
Mistral Dashboard
=================
Horizon plugin for Mistral.
Setup Instructions
==================
This instruction assumes that Horizon is already installed and it's installation
folder is . Detailed information on how to install Horizon can be
found at https://docs.openstack.org/horizon/latest/contributor/quickstart.html#setup.
The installation folder of Mistral Dashboard will be referred to as .
The following should get you started. Clone the repository into your local
OpenStack directory:
.. code:: console
$ git clone https://opendev.org/openstack/mistral-dashboard.git
Install mistral-dashboard:
.. code:: console
$ sudo pip install -e
Or if you're planning to run Horizon server in a virtual environment (see below):
.. code:: console
$ tox -evenv -- pip install -e ../mistral-dashboard/
and then:
.. code:: console
$ cp -b /mistraldashboard/enabled/_50_mistral.py /openstack_dashboard/local/enabled/_50_mistral.py
Since Mistral only supports Identity v3, you must ensure that the dashboard
points the proper OPENSTACK_KEYSTONE_URL in /openstack_dashboard/local/local_settings.py file::
OPENSTACK_API_VERSIONS = {
"identity": 3,
}
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
Also, make sure you have changed OPENSTACK_HOST to point to your Keystone
server and check all endpoints are accessible. You may want to change
OPENSTACK_ENDPOINT_TYPE to "publicURL" if some of them are not.
When you're ready, you would need to either restart your apache:
.. code:: console
$ sudo service apache2 restart
or run the development server (in case you have decided to use local horizon):
.. code:: console
$ cd ../horizon/
$ tox -evenv -- python manage.py runserver
Mistral-Dashboard Debug Instructions
------------------------------------
For debug instructions refer to `OpenStack Mistral Troubleshooting
`_
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/SOURCES.txt 0000664 0001750 0001750 00000016014 00000000000 024340 0 ustar 00zuul zuul 0000000 0000000 .stestr.conf
.zuul.yaml
AUTHORS
CONTRIBUTING.rst
ChangeLog
LICENSE
README.rst
manage.py
requirements.txt
run_tests.sh
setup.cfg
setup.py
test-requirements.txt
tox.ini
doc/requirements.txt
doc/source/conf.py
doc/source/contributing.rst
doc/source/index.rst
doc/source/readme.rst
mistral_dashboard.egg-info/PKG-INFO
mistral_dashboard.egg-info/SOURCES.txt
mistral_dashboard.egg-info/dependency_links.txt
mistral_dashboard.egg-info/not-zip-safe
mistral_dashboard.egg-info/pbr.json
mistral_dashboard.egg-info/requires.txt
mistral_dashboard.egg-info/top_level.txt
mistraldashboard/__init__.py
mistraldashboard/api.py
mistraldashboard/dashboard.py
mistraldashboard/exceptions.py
mistraldashboard/forms.py
mistraldashboard/handle_errors.py
mistraldashboard/action_executions/__init__.py
mistraldashboard/action_executions/forms.py
mistraldashboard/action_executions/panel.py
mistraldashboard/action_executions/tables.py
mistraldashboard/action_executions/tests.py
mistraldashboard/action_executions/urls.py
mistraldashboard/action_executions/views.py
mistraldashboard/action_executions/templates/action_executions/_update.html
mistraldashboard/action_executions/templates/action_executions/detail.html
mistraldashboard/action_executions/templates/action_executions/filtered.html
mistraldashboard/action_executions/templates/action_executions/index.html
mistraldashboard/action_executions/templates/action_executions/update.html
mistraldashboard/actions/__init__.py
mistraldashboard/actions/forms.py
mistraldashboard/actions/panel.py
mistraldashboard/actions/tables.py
mistraldashboard/actions/tests.py
mistraldashboard/actions/urls.py
mistraldashboard/actions/views.py
mistraldashboard/actions/templates/actions/_create.html
mistraldashboard/actions/templates/actions/_run.html
mistraldashboard/actions/templates/actions/_update.html
mistraldashboard/actions/templates/actions/create.html
mistraldashboard/actions/templates/actions/detail.html
mistraldashboard/actions/templates/actions/index.html
mistraldashboard/actions/templates/actions/run.html
mistraldashboard/actions/templates/actions/update.html
mistraldashboard/cron_triggers/__init__.py
mistraldashboard/cron_triggers/forms.py
mistraldashboard/cron_triggers/panel.py
mistraldashboard/cron_triggers/tables.py
mistraldashboard/cron_triggers/tests.py
mistraldashboard/cron_triggers/urls.py
mistraldashboard/cron_triggers/views.py
mistraldashboard/cron_triggers/templates/cron_triggers/_create.html
mistraldashboard/cron_triggers/templates/cron_triggers/create.html
mistraldashboard/cron_triggers/templates/cron_triggers/detail.html
mistraldashboard/cron_triggers/templates/cron_triggers/index.html
mistraldashboard/default/__init__.py
mistraldashboard/default/panel.py
mistraldashboard/default/smart_cell.py
mistraldashboard/default/utils.py
mistraldashboard/default/templates/default/_booleanfield.html
mistraldashboard/default/templates/default/_code.html
mistraldashboard/default/templates/default/_humantime.html
mistraldashboard/default/templates/default/_label.html
mistraldashboard/default/templates/default/_preprint.html
mistraldashboard/default/templates/default/_prettyprint.html
mistraldashboard/default/templates/default/base.html
mistraldashboard/default/templates/default/table.html
mistraldashboard/enabled/_50_mistral.py
mistraldashboard/enabled/__init__.py
mistraldashboard/executions/__init__.py
mistraldashboard/executions/forms.py
mistraldashboard/executions/panel.py
mistraldashboard/executions/tables.py
mistraldashboard/executions/tests.py
mistraldashboard/executions/urls.py
mistraldashboard/executions/views.py
mistraldashboard/executions/templates/executions/_update_description.html
mistraldashboard/executions/templates/executions/detail.html
mistraldashboard/executions/templates/executions/index.html
mistraldashboard/executions/templates/executions/index_filtered_task.html
mistraldashboard/executions/templates/executions/update_description.html
mistraldashboard/static/mistraldashboard/css/style.css
mistraldashboard/tasks/__init__.py
mistraldashboard/tasks/panel.py
mistraldashboard/tasks/tables.py
mistraldashboard/tasks/tests.py
mistraldashboard/tasks/urls.py
mistraldashboard/tasks/views.py
mistraldashboard/tasks/templates/tasks/detail.html
mistraldashboard/tasks/templates/tasks/filtered.html
mistraldashboard/tasks/templates/tasks/index.html
mistraldashboard/test/__init__.py
mistraldashboard/test/helpers.py
mistraldashboard/test/settings.py
mistraldashboard/test/urls.py
mistraldashboard/test/test_data/__init__.py
mistraldashboard/test/test_data/mistral_data.py
mistraldashboard/test/test_data/utils.py
mistraldashboard/workbooks/__init__.py
mistraldashboard/workbooks/forms.py
mistraldashboard/workbooks/panel.py
mistraldashboard/workbooks/tables.py
mistraldashboard/workbooks/tests.py
mistraldashboard/workbooks/urls.py
mistraldashboard/workbooks/views.py
mistraldashboard/workbooks/templates/workbooks/_create.html
mistraldashboard/workbooks/templates/workbooks/_select_definition.html
mistraldashboard/workbooks/templates/workbooks/_update.html
mistraldashboard/workbooks/templates/workbooks/create.html
mistraldashboard/workbooks/templates/workbooks/detail.html
mistraldashboard/workbooks/templates/workbooks/index.html
mistraldashboard/workbooks/templates/workbooks/select_definition.html
mistraldashboard/workbooks/templates/workbooks/update.html
mistraldashboard/workflows/__init__.py
mistraldashboard/workflows/forms.py
mistraldashboard/workflows/panel.py
mistraldashboard/workflows/tables.py
mistraldashboard/workflows/tests.py
mistraldashboard/workflows/urls.py
mistraldashboard/workflows/views.py
mistraldashboard/workflows/templates/workflows/_create.html
mistraldashboard/workflows/templates/workflows/_execute.html
mistraldashboard/workflows/templates/workflows/_select_definition.html
mistraldashboard/workflows/templates/workflows/_update.html
mistraldashboard/workflows/templates/workflows/create.html
mistraldashboard/workflows/templates/workflows/detail.html
mistraldashboard/workflows/templates/workflows/execute.html
mistraldashboard/workflows/templates/workflows/index.html
mistraldashboard/workflows/templates/workflows/select_definition.html
mistraldashboard/workflows/templates/workflows/update.html
releasenotes/notes/.placeholder
releasenotes/notes/bug-1931558-4674cdde721dfab8.yaml
releasenotes/notes/drop-py-2-7-022d0dd59feb8b07.yaml
releasenotes/notes/drop-python-3-6-and-3-7-ae37bc21f97de767.yaml
releasenotes/source/2023.1.rst
releasenotes/source/2023.2.rst
releasenotes/source/2024.1.rst
releasenotes/source/2024.2.rst
releasenotes/source/conf.py
releasenotes/source/index.rst
releasenotes/source/liberty.rst
releasenotes/source/mitaka.rst
releasenotes/source/newton.rst
releasenotes/source/ocata.rst
releasenotes/source/pike.rst
releasenotes/source/queens.rst
releasenotes/source/rocky.rst
releasenotes/source/stein.rst
releasenotes/source/train.rst
releasenotes/source/unreleased.rst
releasenotes/source/ussuri.rst
releasenotes/source/victoria.rst
releasenotes/source/wallaby.rst
releasenotes/source/xena.rst
releasenotes/source/yoga.rst
releasenotes/source/zed.rst
releasenotes/source/_static/.placeholder
releasenotes/source/_templates/.placeholder ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 00000000000 026520 0 ustar 00zuul zuul 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 00000000000 024700 0 ustar 00zuul zuul 0000000 0000000
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/pbr.json 0000664 0001750 0001750 00000000056 00000000000 024131 0 ustar 00zuul zuul 0000000 0000000 {"git_version": "5e3e024", "is_release": true} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/requires.txt 0000664 0001750 0001750 00000000134 00000000000 025050 0 ustar 00zuul zuul 0000000 0000000 pbr!=2.1.0,>=2.0.0
iso8601>=0.1.11
python-mistralclient>=4.3.0
PyYAML>=3.12
horizon>=17.1.0
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591352.0
mistral_dashboard-20.0.0/mistral_dashboard.egg-info/top_level.txt 0000664 0001750 0001750 00000000021 00000000000 025175 0 ustar 00zuul zuul 0000000 0000000 mistraldashboard
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7481816
mistral_dashboard-20.0.0/mistraldashboard/ 0000775 0001750 0001750 00000000000 00000000000 020621 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/__init__.py 0000664 0001750 0001750 00000000000 00000000000 022720 0 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7481816
mistral_dashboard-20.0.0/mistraldashboard/action_executions/ 0000775 0001750 0001750 00000000000 00000000000 024344 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/__init__.py 0000664 0001750 0001750 00000000000 00000000000 026443 0 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/forms.py 0000664 0001750 0001750 00000006736 00000000000 026060 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
# 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 django.utils.translation import gettext_lazy as _
from horizon import forms
from mistraldashboard import api
from mistraldashboard.handle_errors import handle_errors
class UpdateForm(forms.SelfHandlingForm):
action_execution_id = forms.CharField(label=_("Action Execution ID"),
widget=forms.HiddenInput(),
required=False)
output_source = forms.ChoiceField(
label=_('Output'),
help_text=_('Content for output. '
'Select either file, raw content or Null value.'),
choices=[('null', _(' (sends empty value)')),
('file', _('File')),
('raw', _('Direct Input'))],
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'outputsource'}
),
required=False
)
output_upload = forms.FileField(
label=_('Output File'),
help_text=_('A local output to upload'),
widget=forms.FileInput(
attrs={'class': 'switched',
'data-switch-on': 'outputsource',
'data-outputsource-file': _('Output File')}
),
required=False
)
output_data = forms.CharField(
label=_('Output Data'),
help_text=_('The raw content for output'),
widget=forms.widgets.Textarea(
attrs={'class': 'switched',
'data-switch-on': 'outputsource',
'data-outputsource-raw': _('Output Data'),
'rows': 4}
),
required=False
)
state = forms.ChoiceField(
label=_('State'),
help_text=_('Select state to update'),
choices=[('null', _(' (sends empty value)')),
('SUCCESS', _('Success')),
('ERROR', _('Error'))],
widget=forms.Select(
attrs={'class': 'switchable'}
),
required=False
)
def clean(self):
cleaned_data = super(UpdateForm, self).clean()
cleaned_data['output'] = None
if cleaned_data.get('output_upload'):
files = self.request.FILES
cleaned_data['output'] = files['output_upload'].read()
elif cleaned_data.get('output_data'):
cleaned_data['output'] = cleaned_data['output_data']
elif cleaned_data.get('output_source') == 'null':
cleaned_data['output'] = None
del cleaned_data['output_upload']
del cleaned_data['output_data']
del cleaned_data['output_source']
if cleaned_data['state'] == 'null':
cleaned_data['state'] = None
return cleaned_data
@handle_errors(_("Unable to update Action Execution"), [])
def handle(self, request, data):
return api.action_execution_update(
request,
data['action_execution_id'],
data['state'],
data['output'],
)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/panel.py 0000664 0001750 0001750 00000001511 00000000000 026013 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.utils.translation import gettext_lazy as _
import horizon
from mistraldashboard import dashboard
class ActionExecutions(horizon.Panel):
name = _("Action Executions")
slug = 'action_executions'
dashboard.MistralDashboard.register(ActionExecutions)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/tables.py 0000664 0001750 0001750 00000010306 00000000000 026170 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from horizon import tables
from mistraldashboard import api
from mistraldashboard.default import smart_cell
from mistraldashboard.default import utils
smart_cell.init()
class UpdateRow(tables.Row):
ajax = True
def get_data(self, request, id):
instance = api.action_execution_get(request, id)
return instance
class DeleteActionExecution(tables.DeleteAction):
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Action Execution",
u"Delete Action Executions",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Action Execution",
u"Deleted Action Executions",
count
)
def delete(self, request, action_execution_id):
api.action_execution_delete(request, action_execution_id)
class UpdateActionExecution(tables.LinkAction):
name = "updateAE"
verbose_name = _("Update")
url = "horizon:mistral:action_executions:update"
classes = ("ajax-modal",)
class TaskExecutionIDColumn(tables.Column):
def get_link_url(self, datum):
task_url = "horizon:mistral:tasks:detail"
obj_id = datum.task_execution_id
return reverse(task_url, args=[obj_id])
class WorkflowNameColumn(tables.Column):
def get_link_url(self, datum):
workflow_url = "horizon:mistral:workflows:detail"
obj_id = datum.workflow_name
return reverse(workflow_url, args=[obj_id])
class ActionExecutionsTable(tables.DataTable):
def getHoverHelp(data):
if hasattr(data, 'state_info') and data.state_info:
return {'title': data.state_info}
STATE_STATUS_CHOICES = (
("success", True),
("error", False),
("idle", None),
("running", None),
("canceled", None),
)
id = tables.Column(
"id",
verbose_name=_("ID"),
link="horizon:mistral:action_executions:detail"
)
name = tables.Column(
"name",
verbose_name=_("Name")
)
tags = tables.Column(
"tags",
verbose_name=_("Tags")
)
workflow_name = WorkflowNameColumn(
"workflow_name",
verbose_name=_("Workflow Name"),
link=True
)
task_execution_id = TaskExecutionIDColumn(
"task_execution_id",
verbose_name=_("Task Execution ID"),
link=True
)
task_name = tables.Column(
"task_name",
verbose_name=_("Task name")
)
description = tables.Column(
"description",
verbose_name=_("Description")
)
created_at = tables.Column(
"created_at",
verbose_name=_("Created at"),
filters=[utils.humantime]
)
updated_at = tables.Column(
"updated_at",
verbose_name=_("Updated at"),
filters=[utils.humantime]
)
accepted = tables.Column(
"accepted",
verbose_name=_("Accepted"),
filters=[utils.booleanfield],
)
state = tables.Column(
"state",
status=True,
status_choices=STATE_STATUS_CHOICES,
verbose_name=_("State"),
filters=[utils.label],
cell_attributes_getter=getHoverHelp
)
class Meta(object):
name = "actionExecutions"
verbose_name = _("Action Executions")
status_columns = ["state"]
row_class = UpdateRow
table_actions = (
tables.FilterAction,
DeleteActionExecution
)
row_actions = (UpdateActionExecution, DeleteActionExecution)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7361815
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/ 0000775 0001750 0001750 00000000000 00000000000 026342 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7521815
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/ 0000775 0001750 0001750 00000000000 00000000000 032065 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/_update.html0000664 0001750 0001750 00000001022 00000000000 034367 0 ustar 00zuul zuul 0000000 0000000 {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
{% trans "Description:" %}
{% trans "Enter new output and or state to update the corresponding Action Execution." %}
{% trans "For more info refer to:" %}
{% trans "Mistral documentation - Action Executions" %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/detail.html 0000664 0001750 0001750 00000007063 00000000000 034223 0 ustar 00zuul zuul 0000000 0000000
{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Action Execution Details" %}{% endblock %}
{% block page_header %}
{% trans "Action Execution Details" %}
{% endblock page_header %}
{% block main %}
{% load i18n sizeformat %}
{% trans "Overview" %}
- {% trans "Name" %}
- {{ action_execution.name }}
- {% trans "ID" %}
- {{ action_execution.id }}
{% if action_execution.description %}
- {% trans "Description" %}
- {{ action_execution.description }}
{% endif %}
- {% trans "State" %}
- {{ action_execution.state }}
{% if action_execution.state_info %}
- {% trans "State Info" %}
- {{ action_execution.state_info }}
{% endif %}
- {% trans "Accepted" %}
- {{ action_execution.accepted }}
- {% trans "Tags" %}
- {{ action_execution.tags }}
- {% trans "Creation Date" %}
- {{ action_execution.created_at|parse_isotime}}
- {% trans "Time Since Created" %}
- {{ action_execution.created_at|parse_isotime|timesince }}
- {% trans "Update Date" %}
- {{ action_execution.updated_at|parse_isotime}}
- {% trans "Time Since Updated" %}
- {{ action_execution.updated_at|parse_isotime|timesince }}
- {% trans "Input" %}
- {{ action_execution.input }}
- {% trans "Output" %}
- {{ action_execution.output }}
{% if action_execution.workflow_url %}
{% endif %}
{% if action_execution.task_execution_url %}
{% endif %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000205 00000000000 011452 x ustar 00 0000000 0000000 111 path=mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/filtered.html
22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/filtered.htm0000664 0001750 0001750 00000000522 00000000000 034374 0 ustar 00zuul zuul 0000000 0000000 {% extends 'mistral/default/table.html' %}
{% load i18n %}
{% block title %}
{% trans "Action Executions" %}
{{ task_id }}
{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Action Executions of Task ID:") %}
{{ task_id }}
{% endblock page_header %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/index.html 0000664 0001750 0001750 00000000410 00000000000 034055 0 ustar 00zuul zuul 0000000 0000000 {% extends 'mistral/default/table.html' %}
{% load i18n %}
{% block title %}
{% trans "Action Executions" %}
{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Action Executions")%}
{% endblock page_header %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/templates/action_executions/update.html 0000664 0001750 0001750 00000000405 00000000000 034234 0 ustar 00zuul zuul 0000000 0000000 {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" %}
{% endblock page_header %}
{% block main %}
{% include 'mistral/executions/_update.html' %}
{% endblock %} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/tests.py 0000664 0001750 0001750 00000005311 00000000000 026060 0 ustar 00zuul zuul 0000000 0000000 # 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 django.urls import reverse
from openstack_dashboard.test import helpers
from mistraldashboard import api
from mistraldashboard.test import helpers as test
INDEX_URL = reverse('horizon:mistral:action_executions:index')
class ActionExecutionsTest(test.TestCase):
@helpers.create_mocks({api: ('action_executions_list',)})
def test_index(self):
self.mock_action_executions_list.return_value =\
self.mistralclient_action_executions.list()
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'mistral/action_executions/index.html')
self.mock_action_executions_list.assert_called_once_with(
helpers.IsHttpRequest())
@helpers.create_mocks({api: ('action_execution_update',)})
def test_update_post(self):
action_execution = self.mistralclient_action_executions.first()
self.mock_action_execution_update.return_value = action_execution
form_data = {"action_execution_id": action_execution.id,
"state": action_execution.state,
"output_source": "raw",
"output_data": action_execution.output}
res = self.client.post(
reverse('horizon:mistral:action_executions:update',
args=(action_execution.id,)),
form_data)
self.assertNoFormErrors(res)
self.mock_action_execution_update.assert_called_once_with(
helpers.IsHttpRequest(), action_execution.id,
action_execution.state, action_execution.output)
@helpers.create_mocks({api: ('action_execution_get',)})
def test_detail(self):
action_execution = self.mistralclient_action_executions.list()[0]
self.mock_action_execution_get.return_value = action_execution
url = reverse('horizon:mistral:action_executions:detail',
args=[action_execution.id])
res = self.client.get(url)
self.assertTemplateUsed(res, 'mistral/action_executions/detail.html')
self.mock_action_execution_get.assert_called_once_with(
helpers.IsHttpRequest(), action_execution.id)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/urls.py 0000664 0001750 0001750 00000002506 00000000000 025706 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.urls import re_path
from mistraldashboard.action_executions import views
ACTION_EXECUTIONS = r'^(?P[^/]+)/%s$'
TASKS = r'^(?P[^/]+)/%s$'
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(ACTION_EXECUTIONS % 'detail', views.OverviewView.as_view(),
name='detail'),
re_path(ACTION_EXECUTIONS % 'input', views.CodeView.as_view(),
{'column': 'input'}, name='input'),
re_path(ACTION_EXECUTIONS % 'output', views.CodeView.as_view(),
{'column': 'output'}, name='output'),
re_path(ACTION_EXECUTIONS % 'update', views.UpdateView.as_view(),
name='update'),
re_path(TASKS % 'task', views.FilteredByTaskView.as_view(),
name='task')
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/action_executions/views.py 0000664 0001750 0001750 00000012555 00000000000 026063 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import generic
from horizon import forms
from horizon import tables
from mistraldashboard.action_executions import forms as action_execution_forms
from mistraldashboard.action_executions import tables as mistral_tables
from mistraldashboard import api
from mistraldashboard.default import utils
from mistraldashboard import forms as mistral_forms
def get_single_action_execution_data(request, **kwargs):
action_execution_id = kwargs['action_execution_id']
action_execution = api.action_execution_get(
request,
action_execution_id
)
return action_execution
class OverviewView(generic.TemplateView):
template_name = 'mistral/action_executions/detail.html'
page_title = _("Action Execution Details")
workflow_url = 'horizon:mistral:workflows:detail'
task_execution_url = 'horizon:mistral:tasks:detail'
def get_context_data(self, **kwargs):
context = super(OverviewView, self).get_context_data(**kwargs)
action_execution = get_single_action_execution_data(
self.request,
**kwargs
)
if action_execution.workflow_name:
action_execution.workflow_url = reverse(
self.workflow_url,
args=[action_execution.workflow_name])
if action_execution.task_execution_id:
action_execution.task_execution_url = reverse(
self.task_execution_url,
args=[action_execution.task_execution_id]
)
if action_execution.input:
action_execution.input = utils.prettyprint(action_execution.input)
if action_execution.output:
action_execution.output = utils.prettyprint(
action_execution.output
)
if action_execution.state:
action_execution.state = utils.label(action_execution.state)
action_execution.accepted = utils.booleanfield(
action_execution.accepted
)
breadcrumb = [(action_execution.id, reverse(
'horizon:mistral:action_executions:detail',
args=[action_execution.id]
))]
context["custom_breadcrumb"] = breadcrumb
context['action_execution'] = action_execution
return context
class CodeView(forms.ModalFormView):
template_name = 'mistral/default/code.html'
modal_header = _("Code view")
form_id = "code_view"
form_class = mistral_forms.EmptyForm
cancel_label = "OK"
cancel_url = reverse_lazy("horizon:mistral:action_executions:index")
page_title = _("Code view")
def get_context_data(self, **kwargs):
context = super(CodeView, self).get_context_data(**kwargs)
column = self.kwargs['column']
action_execution = get_single_action_execution_data(
self.request,
**self.kwargs
)
io = {}
if column == 'input':
io['name'] = _('Input')
io['value'] = utils.prettyprint(action_execution.input)
elif column == 'output':
io['name'] = _('Output')
io['value'] = (
utils.prettyprint(action_execution.output)
if action_execution.output
else _("No available output yet")
)
context['io'] = io
return context
class IndexView(tables.DataTableView):
table_class = mistral_tables.ActionExecutionsTable
template_name = 'mistral/action_executions/index.html'
def get_data(self):
return api.action_executions_list(self.request)
class UpdateView(forms.ModalFormView):
template_name = 'mistral/action_executions/update.html'
modal_header = _("Update Action Execution")
form_id = "update_action_execution"
form_class = action_execution_forms.UpdateForm
submit_label = _("Update")
success_url = reverse_lazy("horizon:mistral:action_executions:index")
submit_url = "horizon:mistral:action_executions:update"
cancel_url = "horizon:mistral:action_executions:index"
page_title = _("Update Action Execution")
def get_initial(self):
return {"action_execution_id": self.kwargs["action_execution_id"]}
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
context['submit_url'] = reverse(
self.submit_url,
args=[self.kwargs["action_execution_id"]]
)
return context
class FilteredByTaskView(tables.DataTableView):
table_class = mistral_tables.ActionExecutionsTable
template_name = 'mistral/action_executions/filtered.html'
data = {}
def get_data(self, **kwargs):
task_id = self.kwargs['task_id']
data = api.action_executions_list(self.request, task_id)
return data
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7521815
mistral_dashboard-20.0.0/mistraldashboard/actions/ 0000775 0001750 0001750 00000000000 00000000000 022261 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/__init__.py 0000664 0001750 0001750 00000000000 00000000000 024360 0 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/forms.py 0000664 0001750 0001750 00000014676 00000000000 023777 0 ustar 00zuul zuul 0000000 0000000 # 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 json
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from mistraldashboard import api
class RunForm(forms.SelfHandlingForm):
action_name = forms.CharField(
label=_("Action"),
required=True,
widget=forms.TextInput(attrs={'readonly': 'readonly'})
)
input = forms.CharField(
label=_("Input"),
required=False,
initial="{}",
widget=forms.widgets.Textarea()
)
save_result = forms.CharField(
label=_("Save result to DB"),
required=False,
widget=forms.CheckboxInput()
)
def handle(self, request, data):
try:
input = json.loads(data['input'])
except Exception as e:
msg = _('Action input is invalid JSON: %s') % str(e)
messages.error(request, msg)
return False
try:
params = {"save_result": data['save_result'] == 'True'}
action = api.action_run(
request,
data['action_name'],
input,
params
)
msg = _('Run action has been created with name '
'"%s".') % action.name
messages.success(request, msg)
return True
except Exception as e:
# In case of a failure, keep the dialog open and show the error
msg = _('Failed to run action "%(action_name)s"'
' %(e)s:') % {'action_name': data['action_name'],
'e': str(e)
}
messages.error(request, msg)
return False
class CreateForm(forms.SelfHandlingForm):
definition_source = forms.ChoiceField(
label=_('Definition Source'),
choices=[('file', _('File')),
('raw', _('Direct Input'))],
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'definitionsource'})
)
definition_upload = forms.FileField(
label=_('Definition File'),
help_text=_('A local definition to upload.'),
widget=forms.FileInput(
attrs={'class': 'switched',
'data-switch-on': 'definitionsource',
'data-required-when-shown': 'true',
'data-definitionsource-file': _('Definition File')}
),
required=False
)
definition_data = forms.CharField(
label=_('Definition Data'),
help_text=_('The raw contents of the definition.'),
widget=forms.widgets.Textarea(
attrs={'class': 'switched',
'data-switch-on': 'definitionsource',
'data-required-when-shown': 'true',
'data-definitionsource-raw': _('Definition Data'),
'rows': 4}
),
required=False
)
def clean(self):
cleaned_data = super(CreateForm, self).clean()
if cleaned_data.get('definition_upload'):
files = self.request.FILES
cleaned_data['definition'] = files['definition_upload'].read()
elif cleaned_data.get('definition_data'):
cleaned_data['definition'] = cleaned_data['definition_data']
else:
raise forms.ValidationError(
_('You must specify the definition source.'))
return cleaned_data
def handle(self, request, data):
try:
api.action_create(request, data['definition'])
msg = _('Successfully created action.')
messages.success(request, msg)
return True
except Exception:
msg = _('Failed to create action.')
redirect = reverse('horizon:mistral:actions:index')
exceptions.handle(request, msg, redirect=redirect)
class UpdateForm(forms.SelfHandlingForm):
definition_source = forms.ChoiceField(
label=_('Definition Source'),
choices=[('file', _('File')),
('raw', _('Direct Input'))],
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'definitionsource'})
)
definition_upload = forms.FileField(
label=_('Definition File'),
help_text=_('A local definition to upload.'),
widget=forms.FileInput(
attrs={'class': 'switched',
'data-switch-on': 'definitionsource',
'data-definitionsource-file': _('Definition File')}
),
required=False
)
definition_data = forms.CharField(
label=_('Definition Data'),
help_text=_('The raw contents of the definition.'),
widget=forms.widgets.Textarea(
attrs={'class': 'switched',
'data-switch-on': 'definitionsource',
'data-definitionsource-raw': _('Definition Data'),
'rows': 4}
),
required=False
)
def clean(self):
cleaned_data = super(UpdateForm, self).clean()
if cleaned_data.get('definition_upload'):
files = self.request.FILES
cleaned_data['definition'] = files['definition_upload'].read()
elif cleaned_data.get('definition_data'):
cleaned_data['definition'] = cleaned_data['definition_data']
else:
raise forms.ValidationError(
_('You must specify the definition source.'))
return cleaned_data
def handle(self, request, data):
try:
api.action_update(request, data['definition'])
msg = _('Successfully updated action.')
messages.success(request, msg)
return True
except Exception:
msg = _('Failed to update action.')
redirect = reverse('horizon:mistral:actions:index')
exceptions.handle(request, msg, redirect=redirect)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/panel.py 0000664 0001750 0001750 00000001471 00000000000 023735 0 ustar 00zuul zuul 0000000 0000000 # 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 django.utils.translation import gettext_lazy as _
import horizon
from mistraldashboard import dashboard
class Actions(horizon.Panel):
name = _("Actions")
slug = 'actions'
dashboard.MistralDashboard.register(Actions)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/tables.py 0000664 0001750 0001750 00000007005 00000000000 024107 0 ustar 00zuul zuul 0000000 0000000 # 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 django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from horizon import tables
from horizon.utils import filters
from mistraldashboard import api
from mistraldashboard.default import smart_cell
from mistraldashboard.default import utils
smart_cell.init()
class CreateAction(tables.LinkAction):
name = "create"
verbose_name = _("Create Action")
url = "horizon:mistral:actions:create"
classes = ("ajax-modal",)
icon = "plus"
class UpdateAction(tables.LinkAction):
name = "update"
verbose_name = _("Update Action")
url = "horizon:mistral:actions:update"
classes = ("ajax-modal",)
icon = "pencil"
class DeleteAction(tables.DeleteAction):
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Action",
u"Delete Actions",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Action",
u"Deleted Actions",
count
)
def delete(self, request, action_name):
api.action_delete(request, action_name)
def allowed(self, request, action=None):
if action:
return not action.is_system
else:
return True
def tags_to_string(action):
return ', '.join(action.tags) if action.tags else None
def cut(action, length=100):
inputs = action.input
if inputs and len(inputs) > length:
return "%s..." % inputs[:length]
else:
return inputs
class RunAction(tables.LinkAction):
name = "run"
verbose_name = _("Run")
classes = ("ajax-modal",)
def get_link_url(self, datum):
obj_id = datum.name
url = "horizon:mistral:actions:run"
return reverse(url, args=[obj_id])
class ActionsTable(tables.DataTable):
name = tables.Column(
"name",
verbose_name=_("Name"),
link="horizon:mistral:actions:detail"
)
is_system = tables.Column(
"is_system",
verbose_name=_("Is System"),
filters=[utils.booleanfield]
)
tags = tables.Column(
tags_to_string,
verbose_name=_("Tags")
)
inputs = tables.Column(
cut,
verbose_name=_("Input")
)
created = tables.Column(
"created_at",
verbose_name=_("Created"),
filters=(
filters.parse_isotime,
filters.timesince_or_never
)
)
updated = tables.Column(
"updated_at",
verbose_name=_("Updated"),
filters=(
filters.parse_isotime,
filters.timesince_or_never
)
)
class Meta(object):
name = "actions"
verbose_name = _("Actions")
table_actions = (
CreateAction,
UpdateAction,
DeleteAction,
tables.FilterAction,
)
row_actions = (RunAction, DeleteAction)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7361815
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/ 0000775 0001750 0001750 00000000000 00000000000 024257 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7521815
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/ 0000775 0001750 0001750 00000000000 00000000000 025717 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/_create.html 0000664 0001750 0001750 00000001140 00000000000 030203 0 ustar 00zuul zuul 0000000 0000000 {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-body-right %}
{% trans "Description:" %}
{% trans "Use one of the available definition source options to specify the definition to be used in creating this action." %}
{% trans "Refer"%}
Mistral Workflow Language
{% trans " documentation for syntax details." %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/_run.html 0000664 0001750 0001750 00000000317 00000000000 027551 0 ustar 00zuul zuul 0000000 0000000 {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
{% trans "Description:" %}
{% trans "From here you can run an action." %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/_update.html 0000664 0001750 0001750 00000001140 00000000000 030222 0 ustar 00zuul zuul 0000000 0000000 {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-body-right %}
{% trans "Description:" %}
{% trans "Use one of the available definition source options to specify the definition to be used in updating this action." %}
{% trans "Refer"%}
Mistral Workflow Language
{% trans " documentation for syntax details." %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/create.html 0000664 0001750 0001750 00000000265 00000000000 030053 0 ustar 00zuul zuul 0000000 0000000 {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Action" %}{% endblock %}
{% block main %}
{% include 'mistral/actions/_create.html' %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/detail.html 0000664 0001750 0001750 00000002517 00000000000 030054 0 ustar 00zuul zuul 0000000 0000000
{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Action Definition" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Action Definition") %}
{% endblock page_header %}
{% block main %}
{% trans "Overview" %}
- {% trans "Name" %}
- {{ action.name }}
- {% trans "ID" %}
- {{ action.id }}
- {% trans "Tags" %}
- {{ action.tags }}
- {% trans "Created at" %}
- {{ action.created_at }}
- {% trans "Is system" %}
- {{ action.is_system }}
- {% trans "Updated at" %}
- {{ action.updated_at }}
- {% trans "Scope" %}
- {{ action.scope }}
- {% trans "Input" %}
- {{ action.input }}
- {% trans "Description" %}
- {{ action.description }}
- {% trans "Definition" %}
{{ action.definition }}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/index.html 0000664 0001750 0001750 00000000356 00000000000 027720 0 ustar 00zuul zuul 0000000 0000000 {% extends 'mistral/default/table.html' %}
{% load i18n %}
{% block title %}{% trans "Actions" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=_("Actions") %}
{% endblock page_header %} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/run.html 0000664 0001750 0001750 00000000376 00000000000 027417 0 ustar 00zuul zuul 0000000 0000000 {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" %}
{% endblock page_header %}
{% block main %}
{% include 'mistral/actions/_run.html' %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/templates/actions/update.html 0000664 0001750 0001750 00000000265 00000000000 030072 0 ustar 00zuul zuul 0000000 0000000 {% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Update Action" %}{% endblock %}
{% block main %}
{% include 'mistral/actions/_update.html' %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/tests.py 0000664 0001750 0001750 00000006042 00000000000 023777 0 ustar 00zuul zuul 0000000 0000000 # 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 django.urls import reverse
from openstack_dashboard.test import helpers as horizon_test
from mistraldashboard import api
from mistraldashboard.test import helpers as test
INDEX_URL = reverse('horizon:mistral:actions:index')
class ActionsTest(test.TestCase):
@horizon_test.create_mocks({api: ('pagination_list',)})
def test_index(self):
self.mock_pagination_list.return_value =\
[self.mistralclient_actions.list(), False, False]
res = self.client.get(INDEX_URL)
self.assertTemplateUsed(res, 'mistral/actions/index.html')
self.mock_pagination_list.assert_called_once_with(
entity="actions", request=horizon_test.IsHttpRequest(),
marker=None, sort_keys='name', sort_dirs='desc',
paginate=True, reversed_order=True)
@horizon_test.create_mocks({api: ('action_create',)})
def test_create_post(self):
action = self.mistralclient_actions.first()
self.mock_action_create.return_value = action
url = reverse("horizon:mistral:actions:create")
form_data = {
'definition_source': 'raw',
'definition_data': action.definition
}
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.mock_action_create.assert_called_once_with(
horizon_test.IsHttpRequest(),
action.definition)
@horizon_test.create_mocks({api: ('action_update',)})
def test_update_post(self):
action = self.mistralclient_actions.first()
self.mock_action_update.return_value = action
url = reverse("horizon:mistral:actions:update")
form_data = {
'definition_source': 'raw',
'definition_data': action.definition
}
res = self.client.post(url, form_data)
self.assertNoFormErrors(res)
self.mock_action_update.assert_called_once_with(
horizon_test.IsHttpRequest(),
action.definition)
@horizon_test.create_mocks({api: ('action_get',)})
def test_detail(self):
action = self.mistralclient_actions.list()[0]
self.mock_action_get.return_value = action
url = reverse('horizon:mistral:actions:detail',
args=[action.id])
res = self.client.get(url)
self.assertTemplateUsed(res, 'mistral/actions/detail.html')
self.mock_action_get.assert_called_once_with(
horizon_test.IsHttpRequest(), action.id)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/urls.py 0000664 0001750 0001750 00000002055 00000000000 023622 0 ustar 00zuul zuul 0000000 0000000 # 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 django.urls import re_path
from mistraldashboard.actions import views
ACTIONS = r'^(?P[^/]+)/%s$'
urlpatterns = [
re_path(r'^$', views.IndexView.as_view(), name='index'),
re_path(ACTIONS % 'detail', views.DetailView.as_view(), name='detail'),
re_path(ACTIONS % 'run', views.RunView.as_view(), name='run'),
re_path(r'^create$', views.CreateView.as_view(), name='create'),
re_path(r'^update$', views.UpdateView.as_view(), name='update'),
]
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/actions/views.py 0000664 0001750 0001750 00000012023 00000000000 023766 0 ustar 00zuul zuul 0000000 0000000 # 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 django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views import generic
from horizon import exceptions
from horizon import forms
from horizon import tables
from mistraldashboard.actions import forms as mistral_forms
from mistraldashboard.actions import tables as mistral_tables
from mistraldashboard import api
from mistraldashboard.default import utils
class CreateView(forms.ModalFormView):
template_name = 'mistral/actions/create.html'
modal_header = _("Create Action")
form_id = "create_action"
form_class = mistral_forms.CreateForm
submit_label = _("Create")
submit_url = reverse_lazy("horizon:mistral:actions:create")
success_url = reverse_lazy('horizon:mistral:actions:index')
page_title = _("Create Action")
class UpdateView(forms.ModalFormView):
template_name = 'mistral/actions/update.html'
modal_header = _("Update Action")
form_id = "update_action"
form_class = mistral_forms.UpdateForm
submit_label = _("Update")
submit_url = reverse_lazy("horizon:mistral:actions:update")
success_url = reverse_lazy('horizon:mistral:actions:index')
page_title = _("Update Action")
class IndexView(tables.DataTableView):
table_id = "workflow_action"
table_class = mistral_tables.ActionsTable
template_name = 'mistral/actions/index.html'
def has_prev_data(self, table):
return self._prev
def has_more_data(self, table):
return self._more
def get_data(self):
actions = []
prev_marker = self.request.GET.get(
mistral_tables.ActionsTable._meta.prev_pagination_param,
None
)
if prev_marker is not None:
sort_dir = 'asc'
marker = prev_marker
else:
sort_dir = 'desc'
marker = self.request.GET.get(
mistral_tables.ActionsTable._meta.pagination_param,
None
)
try:
actions, self._more, self._prev = api.pagination_list(
entity="actions",
request=self.request,
marker=marker,
sort_keys='name',
sort_dirs=sort_dir,
paginate=True,
reversed_order=True
)
if prev_marker is not None:
actions = sorted(
actions,
key=lambda action: getattr(
action, 'name'
),
reverse=False
)
except Exception:
self._prev = False
self._more = False
msg = _('Unable to retrieve actions list.')
exceptions.handle(self.request, msg)
return actions
class DetailView(generic.TemplateView):
template_name = 'mistral/actions/detail.html'
page_title = _("Action Definition")
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
action = self.get_data(self.request, **kwargs)
action.is_system = utils.booleanfield(action.is_system)
breadcrumb = [(action.name, reverse(
'horizon:mistral:actions:detail',
args=[action.id]
))]
context["custom_breadcrumb"] = breadcrumb
context['action'] = action
return context
def get_data(self, request, **kwargs):
try:
action_name = kwargs['action_name']
action = api.action_get(request, action_name)
except Exception:
msg = _('Unable to get action "%s".') % action_name
redirect = reverse('horizon:mistral:actions:index')
exceptions.handle(self.request, msg, redirect=redirect)
return action
class RunView(forms.ModalFormView):
form_class = mistral_forms.RunForm
template_name = 'mistral/actions/run.html'
form_id = "run_action"
success_url = reverse_lazy("horizon:mistral:actions:index")
submit_label = _("Run")
modal_header = _("Run Action")
page_title = _("Run Action")
submit_url = "horizon:mistral:actions:run"
def get_initial(self, **kwargs):
return {'action_name': self.kwargs['action_name']}
def get_context_data(self, **kwargs):
context = super(RunView, self).get_context_data(**kwargs)
context['submit_url'] = reverse(
self.submit_url,
args=[self.kwargs["action_name"]]
)
return context
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/api.py 0000664 0001750 0001750 00000027761 00000000000 021761 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2014 - 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 itertools
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from horizon.utils import functions as utils
from horizon.utils import memoized
from mistralclient.api import client as mistral_client
from mistraldashboard.handle_errors import handle_errors
from openstack_dashboard.api import base
SERVICE_TYPE = 'workflowv2'
@memoized.memoized
def mistralclient(request):
return mistral_client.client(
username=request.user.username,
auth_token=request.user.token.id,
project_id=request.user.tenant_id,
# We can't use auth_url param in here if we config
# and use keystone federation
mistral_url=base.url_for(request, 'workflowv2'),
# Todo: add SECONDARY_ENDPOINT_TYPE support
endpoint_type=getattr(
settings,
'OPENSTACK_ENDPOINT_TYPE',
'internalURL'
),
service_type=SERVICE_TYPE,
# We should not treat definition as file path or uri otherwise
# we allow access to contents in internal servers
enforce_raw_definition=False
)
@handle_errors(_("Unable to retrieve list"), [])
def pagination_list(entity, request, marker='', sort_keys='', sort_dirs='asc',
paginate=False, reversed_order=False, selector=None):
"""Retrieve a listing of specific entity and handles pagination.
:param entity: Requested entity (String)
:param request: Request data
:param marker: Pagination marker for large data sets: entity id
:param sort_keys: Columns to sort results by
:param sort_dirs: Sorting Directions (asc/desc). Default:asc
:param paginate: If true will perform pagination based on settings.
Default:False
:param reversed_order: flag to reverse list. Default:False
:param selector: additional selector to allow further server filtering
"""
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
page_size = utils.get_page_size(request)
if paginate:
request_size = page_size + 1
else:
request_size = limit
if reversed_order:
sort_dirs = 'desc' if sort_dirs == 'asc' else 'asc'
api = mistralclient(request)
entities_iter = (
getattr(api, entity).list(
selector,
marker=marker,
limit=limit,
sort_keys=sort_keys,
sort_dirs=sort_dirs
) if selector else (
getattr(api, entity).list(
marker=marker,
limit=limit,
sort_keys=sort_keys,
sort_dirs=sort_dirs
)
)
)
has_prev_data = has_more_data = False
if paginate:
entities = list(itertools.islice(entities_iter, request_size))
# first and middle page condition
if len(entities) > page_size:
entities.pop(-1)
has_more_data = True
# middle page condition
if marker is not None:
has_prev_data = True
# first page condition when reached via prev back
elif reversed_order and marker is not None:
has_more_data = True
# last page condition
elif marker is not None:
has_prev_data = True
# restore the original ordering here
if reversed_order:
entities = sorted(entities,
key=lambda ent:
(getattr(ent, sort_keys) or '').lower(),
reverse=(sort_dirs == 'desc')
)
else:
entities = list(entities_iter)
return entities, has_more_data, has_prev_data
def execution_create(request, **data):
"""Creates new execution."""
return mistralclient(request).executions.create(**data)
def execution_get(request, execution_id):
"""Get specific execution.
:param execution_id: Execution ID
"""
return mistralclient(request).executions.get(execution_id)
def execution_update(request, execution_id, field, value):
"""update specific execution field, either state or description.
:param request: Request data
:param execution_id: Execution ID
:param field: flag - either Execution state or description
:param value: new update value
"""
if field == "state":
return mistralclient(request).\
executions.update(execution_id, value)
elif field == "description":
return mistralclient(request).\
executions.update(execution_id, None, value)
def execution_delete(request, execution_name):
"""Delete execution.
:param execution_name: Execution name
"""
return mistralclient(request).executions.delete(execution_name)
@handle_errors(_("Unable to retrieve tasks."), [])
def task_list(request, execution_id=None):
"""Returns all tasks.
:param execution_id: Workflow execution ID associated with list of tasks
"""
return mistralclient(request).tasks.list(execution_id)
def task_get(request, task_id=None):
"""Get specific task.
:param task_id: Task ID
"""
return mistralclient(request).tasks.get(task_id)
@handle_errors(_("Unable to retrieve workflows"), [])
def workflow_list(request):
"""Returns all workflows."""
return mistralclient(request).workflows.list()
def workflow_get(request, workflow_name):
"""Get specific workflow.
:param workflow_name: Workflow name
"""
return mistralclient(request).workflows.get(workflow_name)
def workflow_create(request, workflows_definition):
"""Create workflow.
:param workflows_definition: Workflows definition
"""
return mistralclient(request).workflows.create(workflows_definition)
def workflow_validate(request, workflow_definition):
"""Validate workflow.
:param workflow_definition: Workflow definition
"""
return mistralclient(request).workflows.validate(workflow_definition)
def workflow_delete(request, workflow_name):
"""Delete workflow.
:param workflow_name: Workflow name
"""
return mistralclient(request).workflows.delete(workflow_name)
def workflow_update(request, workflows_definition):
"""Update workflow.
:param workflows_definition: Workflows definition
"""
return mistralclient(request).workflows.update(workflows_definition)
@handle_errors(_("Unable to retrieve workbooks."), [])
def workbook_list(request):
"""Returns all workbooks."""
return mistralclient(request).workbooks.list()
def workbook_get(request, workbook_name):
"""Get specific workbook.
:param workbook_name: Workbook name
"""
return mistralclient(request).workbooks.get(workbook_name)
def workbook_create(request, workbook_definition):
"""Create workbook.
:param workbook_definition: Workbook definition
"""
return mistralclient(request).workbooks.create(workbook_definition)
def workbook_validate(request, workbook_definition):
"""Validate workbook.
:param workbook_definition: Workbook definition
"""
return mistralclient(request).workbooks.validate(workbook_definition)
def workbook_delete(request, workbook_name):
"""Delete workbook.
:param workbook_name: Workbook name
"""
return mistralclient(request).workbooks.delete(workbook_name)
def workbook_update(request, workbook_definition):
"""Update workbook.
:param workbook_definition: Workbook definition
"""
return mistralclient(request).workbooks.update(workbook_definition)
@handle_errors(_("Unable to retrieve actions."), [])
def action_list(request):
"""Returns all actions."""
return mistralclient(request).actions.list()
def action_get(request, action_name):
"""Get specific action.
:param action_name: Action name
"""
return mistralclient(request).actions.get(action_name)
def action_create(request, action_definition):
"""Create action.
:param action_definition: Action definition
"""
return mistralclient(request).actions.create(action_definition)
def action_update(request, action_definition):
"""Update action.
:param action_definition: Action definition
"""
return mistralclient(request).actions.update(action_definition)
def action_run(request, action_name, input, params):
"""Run specific action execution.
:param action_name: Action name
:param input: input
:param params: params
"""
return mistralclient(request).action_executions.create(
action_name,
input,
**params
)
def action_delete(request, action_name):
"""Delete action.
:param action_name: Action name
"""
return mistralclient(request).actions.delete(action_name)
@handle_errors(_("Unable to retrieve action executions list"), [])
def action_executions_list(request, task_execution_id=None):
"""Returns all actions executions.
:param request: Request data
:param task_execution_id: (Optional) Task Execution ID to filter by
"""
return mistralclient(request).action_executions.list(task_execution_id)
@handle_errors(_("Unable to retrieve action execution"), [])
def action_execution_get(request, action_execution_id):
"""Get specific action execution.
:param action_execution_id: Action Execution ID
"""
return mistralclient(request).action_executions.get(action_execution_id)
@handle_errors(_("Unable to delete action execution/s"), [])
def action_execution_delete(request, action_execution_id):
"""Delete action execution.
:param action_execution_id: Action execution ID
"""
return mistralclient(request).action_executions.delete(action_execution_id)
def action_execution_update(request, id, state=None, output=None):
"""Update action execution output and or state.
:param id: action execution id
:param output: action execution output
:param state: action execution state
"""
return mistralclient(request).action_executions.update(id, state, output)
@handle_errors(_("Unable to retrieve cron trigger list"), [])
def cron_trigger_list(request):
"""Returns all cron triggers.
:param request: Request data
"""
return mistralclient(request).cron_triggers.list()
@handle_errors(_("Unable to retrieve cron trigger"), [])
def cron_trigger_get(request, cron_trigger_name):
"""Get specific cron trigger.
:param request: Request data
:param cron_trigger_name: Cron trigger name
"""
return mistralclient(request).cron_triggers.get(cron_trigger_name)
@handle_errors(_("Unable to delete cron trigger/s"), [])
def cron_trigger_delete(request, cron_trigger_name):
"""Delete Cron Trigger.
:param request: Request data
:param cron_trigger_name: Cron Trigger name
"""
return mistralclient(request).cron_triggers.delete(cron_trigger_name)
def cron_trigger_create(
request,
cron_trigger_name,
workflow_ID,
workflow_input,
workflow_params,
pattern,
first_time,
count
):
"""Create Cron Trigger.
:param request: Request data
:param cron_trigger_name: Cron Trigger name
:param workflow_ID: Workflow ID
:param workflow_input: Workflow input
:param workflow_params: Workflow params <* * * * *>
:param pattern: <* * * * *>
:param first_time:
Date and time of the first execution
:param count: Number of wanted executions
"""
return mistralclient(request).cron_triggers.create(
cron_trigger_name,
workflow_ID,
workflow_input,
workflow_params,
pattern,
first_time,
count
)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7561815
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/ 0000775 0001750 0001750 00000000000 00000000000 023470 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/__init__.py 0000664 0001750 0001750 00000000000 00000000000 025567 0 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/forms.py 0000664 0001750 0001750 00000016061 00000000000 025174 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.utils.translation import gettext_lazy as _
from horizon import forms
from horizon import messages
from mistraldashboard import api
from mistraldashboard.default.utils import convert_empty_string_to_none
from mistraldashboard.handle_errors import handle_errors
class CreateForm(forms.SelfHandlingForm):
name = forms.CharField(
max_length=255,
label=_("Name"),
help_text=_('Cron Trigger name.'),
required=True
)
workflow_id = forms.ChoiceField(
label=_('Workflow ID'),
help_text=_('Select Workflow ID.'),
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'workflow_select'}
)
)
input_source = forms.ChoiceField(
label=_('Input'),
help_text=_('JSON of input values defined in the workflow. '
'Select either file or raw content.'),
choices=[('file', _('File')),
('raw', _('Direct Input'))],
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'inputsource'}
)
)
input_upload = forms.FileField(
label=_('Input File'),
help_text=_('A local input to upload.'),
widget=forms.FileInput(
attrs={'class': 'switched',
'data-switch-on': 'inputsource',
'data-required-when-shown': 'true',
'data-inputsource-file': _('Input File')}
),
required=False
)
input_data = forms.CharField(
label=_('Input Data'),
help_text=_('The raw contents of the input.'),
widget=forms.widgets.Textarea(
attrs={'class': 'switched',
'data-switch-on': 'inputsource',
'data-required-when-shown': 'true',
'data-inputsource-raw': _('Input Data'),
'rows': 4}
),
required=False
)
params_source = forms.ChoiceField(
label=_('Params'),
help_text=_('JSON of params values defined in the workflow. '
'Select either file or raw content.'),
choices=[('file', _('File')),
('raw', _('Direct Input'))],
widget=forms.Select(
attrs={'class': 'switchable',
'data-slug': 'paramssource'}
)
)
params_upload = forms.FileField(
label=_('Params File'),
help_text=_('A local input to upload.'),
widget=forms.FileInput(
attrs={'class': 'switched',
'data-switch-on': 'paramssource',
'data-required-when-shown': 'true',
'data-paramssource-file': _('Params File')}
),
required=False
)
params_data = forms.CharField(
label=_('Params Data'),
help_text=_('The raw contents of the params.'),
widget=forms.widgets.Textarea(
attrs={'class': 'switched',
'data-switch-on': 'paramssource',
'data-required-when-shown': 'true',
'data-paramssource-raw': _('Params Data'),
'rows': 4}
),
required=False
)
first_time = forms.CharField(
label=_('First Time (YYYY-MM-DD HH:MM)'),
help_text=_('Date and time of the first execution.'),
widget=forms.widgets.TextInput(),
required=False
)
schedule_count = forms.CharField(
label=_('Count'),
help_text=_('Number of desired executions.'),
widget=forms.widgets.TextInput(),
required=False
)
schedule_pattern = forms.CharField(
label=_('Pattern (* * * * *)'),
help_text=_('Cron Trigger pattern, mind the space between each char.'),
widget=forms.widgets.TextInput(),
required=False
)
def __init__(self, request, *args, **kwargs):
super(CreateForm, self).__init__(request, *args, **kwargs)
workflow_list = api.workflow_list(request)
workflow_id_list = []
for wf in workflow_list:
workflow_id_list.append(
(wf.id, "{id} ({name})".format(id=wf.id, name=wf.name))
)
self.fields['workflow_id'].choices = workflow_id_list
def clean(self):
cleaned_data = super(CreateForm, self).clean()
cleaned_data['input'] = ""
cleaned_data['params'] = ""
if cleaned_data.get('input_upload'):
files = self.request.FILES
cleaned_data['input'] = files['input_upload'].read()
elif cleaned_data.get('input_data'):
cleaned_data['input'] = cleaned_data['input_data']
del cleaned_data['input_upload']
del cleaned_data['input_data']
if len(cleaned_data['input']) > 0:
try:
cleaned_data['input'] = json.loads(cleaned_data['input'])
except Exception as e:
msg = _('Input is invalid JSON: %s') % str(e)
raise forms.ValidationError(msg)
if cleaned_data.get('params_upload'):
files = self.request.FILES
cleaned_data['params'] = files['params_upload'].read()
elif cleaned_data.get('params_data'):
cleaned_data['params'] = cleaned_data['params_data']
del cleaned_data['params_upload']
del cleaned_data['params_data']
if len(cleaned_data['params']) > 0:
try:
cleaned_data['params'] = json.loads(cleaned_data['params'])
except Exception as e:
msg = _('Params is invalid JSON: %s') % str(e)
raise forms.ValidationError(msg)
return cleaned_data
@handle_errors(_("Unable to create Cron Trigger"), [])
def handle(self, request, data):
data['input'] = convert_empty_string_to_none(data['input'])
data['params'] = convert_empty_string_to_none(data['params'])
data['schedule_pattern'] = convert_empty_string_to_none(
data['schedule_pattern']
)
data['first_time'] = convert_empty_string_to_none(data['first_time'])
data['schedule_count'] = convert_empty_string_to_none(
data['schedule_count']
)
api.cron_trigger_create(
request,
data['name'],
data['workflow_id'],
data['input'],
data['params'],
data['schedule_pattern'],
data['first_time'],
data['schedule_count'],
)
msg = _('Successfully created Cron Trigger.')
messages.success(request, msg)
return True
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/panel.py 0000664 0001750 0001750 00000001471 00000000000 025144 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.utils.translation import gettext_lazy as _
import horizon
from mistraldashboard import dashboard
class CronTriggers(horizon.Panel):
name = _("Cron Triggers")
slug = 'cron_triggers'
dashboard.MistralDashboard.register(CronTriggers)
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/tables.py 0000664 0001750 0001750 00000006173 00000000000 025323 0 ustar 00zuul zuul 0000000 0000000 # Copyright 2016 - Nokia.
#
# 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 django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext_lazy
from horizon import tables
from mistraldashboard import api
from mistraldashboard.default.utils import humantime
class CreateCronTrigger(tables.LinkAction):
name = "create"
verbose_name = _("Create Cron Trigger")
url = "horizon:mistral:cron_triggers:create"
classes = ("ajax-modal",)
icon = "plus"
class DeleteCronTrigger(tables.DeleteAction):
@staticmethod
def action_present(count):
return ngettext_lazy(
u"Delete Cron Trigger",
u"Delete Cron Triggers",
count
)
@staticmethod
def action_past(count):
return ngettext_lazy(
u"Deleted Cron Trigger",
u"Deleted Cron Triggers",
count
)
def delete(self, request, cron_trigger_name):
api.cron_trigger_delete(request, cron_trigger_name)
class WorkflowColumn(tables.Column):
def get_link_url(self, datum):
workflow_url = "horizon:mistral:workflows:detail"
obj_id = datum.workflow_name
return reverse(workflow_url, args=[obj_id])
class CronTriggersTable(tables.DataTable):
id = tables.Column(
"id",
verbose_name=_("ID"),
link="horizon:mistral:cron_triggers:detail"
)
name = tables.Column(
"name",
verbose_name=_("Name")
)
workflow_name = WorkflowColumn(
"workflow_name",
verbose_name=_("Workflow"),
link=True
)
pattern = tables.Column(
"pattern",
verbose_name=_("Pattern"),
)
next_execution_time = tables.Column(
"next_execution_time",
verbose_name=_("Next Execution Time"),
)
remaining_executions = tables.Column(
"remaining_executions",
verbose_name=_("Remaining Executions"),
)
first_execution_time = tables.Column(
"first_execution_time",
verbose_name=_("First Execution Time"),
)
created_at = tables.Column(
"created_at",
verbose_name=_("Created at"),
filters=[humantime]
)
updated_at = tables.Column(
"updated_at",
verbose_name=_("Updated at"),
filters=[humantime]
)
def get_object_id(self, datum):
return datum.name
class Meta(object):
name = "cron trigger"
verbose_name = _("Cron Trigger")
table_actions = (
tables.FilterAction,
CreateCronTrigger,
DeleteCronTrigger
)
row_actions = (DeleteCronTrigger,)
././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7361815
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/templates/ 0000775 0001750 0001750 00000000000 00000000000 025466 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 011452 x ustar 00 0000000 0000000 28 mtime=1743591352.7561815
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/templates/cron_triggers/ 0000775 0001750 0001750 00000000000 00000000000 030335 5 ustar 00zuul zuul 0000000 0000000 ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/templates/cron_triggers/_create.html 0000664 0001750 0001750 00000002712 00000000000 032627 0 ustar 00zuul zuul 0000000 0000000 {% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
{% block modal-body-right %}
{% trans "Description" %}:
{% blocktrans %}
Cron Trigger is an object allowing to run workflow on a schedule.
{% endblocktrans %}
{% blocktrans %}
Using Cron Triggers it is possible to run workflows according to
specific rules: periodically setting a pattern
or on external events like Ceilometer alarm.
{% endblocktrans %}
{% trans "For more info" %}:
{% trans "Please note" %}:
{% blocktrans %}
Name, Workflow ID and Pattern are mandatory fields.
{% endblocktrans %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/templates/cron_triggers/create.html 0000664 0001750 0001750 00000000224 00000000000 032464 0 ustar 00zuul zuul 0000000 0000000 {% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block main %}
{% include 'mistral/cron_triggers/_create.html' %}
{% endblock %}
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 011453 x ustar 00 0000000 0000000 22 mtime=1743591311.0
mistral_dashboard-20.0.0/mistraldashboard/cron_triggers/templates/cron_triggers/detail.html 0000664 0001750 0001750 00000006504 00000000000 032472 0 ustar 00zuul zuul 0000000 0000000
{% extends 'mistral/default/base.html' %}
{% load i18n %}
{% block title %}{% trans "Cron Trigger Details" %}{% endblock %}
{% block page_header %}
{% trans "Cron Trigger Details" %}
-
Cron Triggers
-
{{ cron_trigger.name }}
{% endblock page_header %}
{% block main %}
{% load i18n sizeformat %}
{% trans "Overview" %}
- {% trans "Name" %}
- {{ cron_trigger.name }}
- {% trans "ID" %}
- {{ cron_trigger.id }}
- {% trans "Pattern" %}
- {{ cron_trigger.pattern }}
- {% trans "Creation Date" %}
- {{ cron_trigger.created_at|parse_isotime }}
- {% trans "Time Since Created" %}
- {{ cron_trigger.created_at|parse_isotime|timesince }}
- {% trans "First Execution" %}
-
{% with time=cron_trigger.first_execution_time %}
{{ time|parse_isotime|default:"Empty" }}
{% endwith %}
- {% trans "Time Since first Execution" %}
-
{% with time=cron_trigger.first_execution_time %}
{{ time|parse_isotime|timesince|default:"Empty" }}
{% endwith %}
- {% trans "Update Date" %}
-
{{ cron_trigger.updated_at|parse_isotime|default:"Empty" }}
- {% trans "Time Since Updated" %}
-
{% with time=cron_trigger.updated_at %}
{{ time|parse_isotime|timesince|default:"Empty" }}
{% endwith %}
- {% trans "Next Execution Time" %}
-
{% with time=cron_trigger.next_execution_time %}
{{ time|parse_isotime|default:"Empty" }}
{% endwith %}
- {% trans "Time Until Next Execution" %}
-
{% with time=cron_trigger.next_execution_time %}
{{ time|parse_isotime|timeuntil|default:"Empty" }}
{% endwith %}
- {% trans "Remaining Executions" %}
- {{ cron_trigger.remaining_executions|default:"0"}}
{% endblock %}