././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.383735 masakari_dashboard-14.0.0/0000775000175000017500000000000015163173704014224 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/.zuul.yaml0000664000175000017500000000056315163173622016170 0ustar00zuulzuul- project: templates: - check-requirements - horizon-non-primary-django-jobs - openstack-python3-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 - periodic-stable-jobs - periodic-jobs-with-oslo-master check: jobs: - openstack-tox-linters gate: jobs: - openstack-tox-linters ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/AUTHORS0000664000175000017500000000256015163173704015277 0ustar00zuulzuul98k <18552437190@163.com> Akihiro Motoki Andreas Jaeger Corey Bryant Ghanshyam Mann Hervé Beraud Ivan Anfimov Ivan Kolodyazhny Kevin Schwerdtfeger Mark Goddard Michiel Piscaer Nguyen Hai OpenStack Release Bot Pavlo Shchelokovskyy Radosław Piliszek Sean McGinnis Stephen Finucane Takashi Kajinami Takashi Natsume Vadym Markov XinxinShen ZhongShengping chung00-lee dengzhaosen huang.zhiping jacky06 manchandavishal niraj singh nirajsingh nitesh.vanarase niuke openstack openstack sunjia suzhengwei wu.shiming zhouguowei ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/CONTRIBUTING.rst0000664000175000017500000000117615163173622016671 0ustar00zuulzuulThe source repository for this project can be found at: https://opendev.org/openstack/masakari-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/masakari For more specific information about contributing to this repository, see the masakari-dashboard contributor guide: https://docs.openstack.org/masakari-dashboard/latest/contributor/contributing.html ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/ChangeLog0000664000175000017500000000755115163173704016006 0ustar00zuulzuulCHANGES ======= 14.0.0 ------ * Drop environments for nose * tox: Drop basepython * Add project badge * reno: Update master for unmaintained/2024.1 * Bump hacking * Remove retired oslosphinx * Migrate setup configuration to pyproject.toml * Update master for stable/2025.2 * Remove unicode prefix from code 13.0.0 ------ * Remove Python 3.8 and 3.9 support * Remove unused MANIFEST.in * Update master for stable/2025.1 * reno: Update master for unmaintained/2023.1 12.0.0 ------ * Update master for stable/2024.2 * Support region\_name option during connection * reno: Update master for unmaintained/zed 11.0.0 ------ * Update master for stable/2024.1 10.0.0 ------ * reno: Update master for unmaintained/xena * reno: Update master for unmaintained/wallaby * reno: Update master for unmaintained/victoria * support for Masakari VMoves * reno: Update master for unmaintained/yoga * support for Masakari VMove * Django 4.x: fix some import error * Update python classifier in setup.cfg * Update master for stable/2023.1 * Fix auth\_url to request.user.endpoint * Update master for stable/2023.2 * Use py3 as the default runtime for tox * Cleanup py27 support 9.0.0 ----- * remove duplicated code 8.0.0 ----- * Update tox.ini for tox4 * Switch to 2023.1 Python3 unit tests and generic template name * Update master for stable/zed 7.0.0 ----- * Drop Python 3.6 and 3.7 support * Address RemovedInDjango40Warning * Add Python3 zed unit tests * Update master for stable/yoga 6.0.0 ----- * Updating python testing classifier as per Yoga testing runtime * Use service list instead of hypervisor list * Add Python3 yoga unit tests * Update master for stable/xena 5.0.0.0rc1 ---------- * [CI] Run bandit * Update policy file to yaml * [docs] IRC moved to OFTC * Change minversion of tox to 3.18.0 * [community goal] Update contributor documentation * Replace getargspec with getfullargspec * setup.cfg: Replace dashes with underscores * Add Python3 xena unit tests * Update master for stable/wallaby 4.0.0 ----- * Add 'is\_enabled' attr to segment * Drop dep on python-masakariclient * Disable dashboard when Masakari is not available * Revert "remove py37" * remove unicode from code * remove py37 * Update TOX\_CONSTRAINTS\_FILE * [CI] Add periodic jobs * Add py38 package metadata * Fix links * Publish the missing docs * Update home-page * Add Python3 wallaby unit tests * Update master for stable/victoria 3.0.0 ----- * Fix CA file for API client * Cleanup for Refactor-error-messages * Add OPENSTACK\_ENDPOINT\_TYPE to the connection * Remove mox3 from test-requirements * Stop to use the \_\_future\_\_ module * Switch to newer openstackdocstheme and reno versions * Update hacking for Python3 * Add Python3 victoria unit tests * Update master for stable/ussuri 2.0.0 ----- * Use unittest.mock instead of third party mock * s/assertItemsEqual/assertCountEqual/g * Drop Django 1.11 support * Change the CONTRIBUTING.rst to the right pattern * Fix the wrong links * [ussuri][goal] Drop python 2.7 support and testing * Use Horizon project template for django jobs * Update master for stable/train 1.0.0 ----- * Add Python 3 Train unit tests * Chang Glance to masakari dashboard * OpenDev Migration Patch 0.3.0 ----- * Implement real time data for recovery workflow details * Render progress details for notification * Run all jobs by default using python3 * Fix tox for releasenotes * Change openstack-dev to openstack-discuss * Django 2.0 support * Drop nose dependencies * import zuul job settings from project-config * fix tox python3 overrides * Update reno for stable/rocky * Fix incorrect message for host update 0.2.0 ----- * Added notification panel * Implement update host * Implement host detail * Implement delete host * Add host panel 0.1.0 ----- * Implement update segment * Implement segment detail tab * Implement delete segment * Add segment panel * Initial UI-Cookiecutter commit * Added .gitreview ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/HACKING.rst0000664000175000017500000000025015163173622016016 0ustar00zuulzuulmasakari-dashboard Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/LICENSE0000664000175000017500000002363715163173622015243 0ustar00zuulzuul 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. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.383735 masakari_dashboard-14.0.0/PKG-INFO0000644000175000017500000000672015163173704015324 0ustar00zuulzuulMetadata-Version: 2.4 Name: masakari-dashboard Version: 14.0.0 Summary: Horizon plugin for masakari Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/masakari-dashboard/latest/ Project-URL: Repository, https://opendev.org/openstack/masakari-dashboard Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pbr!=2.1.0,>=2.0.0 Requires-Dist: horizon>=17.1.0 Requires-Dist: openstacksdk>=0.26.0 Requires-Dist: PyYAML>=3.12 Dynamic: license-file Dynamic: requires-dist ================== Masakari dashboard ================== .. image:: https://governance.openstack.org/tc/badges/masakari-dashboard.svg .. Change things from this point on Horizon plugin for Masakari * Free software: Apache license * Source: https://opendev.org/openstack/masakari-dashboard * Bugs: https://bugs.launchpad.net/masakari-dashboard Enabling in DevStack -------------------- Add this repo as an external repository into your ``local.conf`` file:: [[local|localrc]] enable_plugin masakaridashboard https://opendev.org/openstack/masakari-dashboard Manual Installation ------------------- Begin by cloning the Horizon and Masakari dashboard repositories:: git clone https://opendev.org/openstack/horizon git clone https://opendev.org/openstack/masakari-dashboard Create a virtual environment and install Horizon dependencies:: cd horizon python3 tools/install_venv.py Set up your ``local_settings.py`` file:: cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py Open up the copied ``local_settings.py`` file in your preferred text editor. You will want to customize several settings: - ``OPENSTACK_HOST`` should be configured with the hostname of your OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your environment. (They should be correct unless you modified your OpenStack server to change them.) Install Masakari dashboard with all dependencies in your virtual environment:: tools/with_venv.sh pip install -e ../masakari-dashboard/ And enable it in Horizon:: ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.yaml openstack_dashboard/conf To run horizon with the newly enabled Masakari dashboard plugin run:: ./run_tests.sh --runserver 0.0.0.0:8080 to have the application start on port 8080 and the horizon dashboard will be available in your browser at http://localhost:8080/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/README.rst0000664000175000017500000000432615163173622015717 0ustar00zuulzuul================== Masakari dashboard ================== .. image:: https://governance.openstack.org/tc/badges/masakari-dashboard.svg .. Change things from this point on Horizon plugin for Masakari * Free software: Apache license * Source: https://opendev.org/openstack/masakari-dashboard * Bugs: https://bugs.launchpad.net/masakari-dashboard Enabling in DevStack -------------------- Add this repo as an external repository into your ``local.conf`` file:: [[local|localrc]] enable_plugin masakaridashboard https://opendev.org/openstack/masakari-dashboard Manual Installation ------------------- Begin by cloning the Horizon and Masakari dashboard repositories:: git clone https://opendev.org/openstack/horizon git clone https://opendev.org/openstack/masakari-dashboard Create a virtual environment and install Horizon dependencies:: cd horizon python3 tools/install_venv.py Set up your ``local_settings.py`` file:: cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py Open up the copied ``local_settings.py`` file in your preferred text editor. You will want to customize several settings: - ``OPENSTACK_HOST`` should be configured with the hostname of your OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your environment. (They should be correct unless you modified your OpenStack server to change them.) Install Masakari dashboard with all dependencies in your virtual environment:: tools/with_venv.sh pip install -e ../masakari-dashboard/ And enable it in Horizon:: ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.yaml openstack_dashboard/conf To run horizon with the newly enabled Masakari dashboard plugin run:: ./run_tests.sh --runserver 0.0.0.0:8080 to have the application start on port 8080 and the horizon dashboard will be available in your browser at http://localhost:8080/ ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.364732 masakari_dashboard-14.0.0/doc/0000775000175000017500000000000015163173704014771 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/doc/Makefile0000664000175000017500000001276315163173622016441 0ustar00zuulzuul# Makefile for Sphinx documentation # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Masakari dashboard.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Masakari dashboard.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Masakari dashboard" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Masakari dashboard" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/doc/requirements.txt0000664000175000017500000000022515163173622020253 0ustar00zuulzuulsphinx>=2.0.0,!=2.1.0 # BSD openstackdocstheme>=2.2.1 # Apache-2.0 reno>=3.1.0 # Apache-2.0 docutils>=0.11 # OSI-Approved Open Source, Public Domain ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.364732 masakari_dashboard-14.0.0/doc/source/0000775000175000017500000000000015163173704016271 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/doc/source/conf.py0000664000175000017500000002413215163173622017571 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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. # # Horizon documentation build configuration file, created by # sphinx-quickstart on Thu Oct 27 11:38:59 2011. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys import django BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT) # This is required for ReadTheDocs.org, but isn't a bad idea anyway. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'masakaridashboard.test.settings') # Starting in Django 1.7, standalone scripts, such as a sphinx build # require that django.setup() be called first. # https://docs.djangoproject.com/en/1.8/releases/1.7/#standalone-scripts django.setup() # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. # They can be extensions coming with Sphinx (named 'sphinx.ext.*') # or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'openstackdocstheme', ] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Masakari dashboard' copyright = '2017, OpenStack Foundation' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['**/#*', '**~', '**/#*#'] # The reST default role (used for this markup: `text`) # to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] primary_domain = 'py' nitpicky = False # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # openstackdocstheme options openstackdocs_repo_name = 'openstack/masakari-dashboard' openstackdocs_auto_name = False openstackdocs_bug_project = 'masakari-dashboard' openstackdocs_bug_tag = '' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Masakari dashboarddoc' # -- Options for LaTeX output ------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'Masakari dashboard.tex', 'Masakari dashboard Documentation', 'OpenStack Foundation', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'Masakari dashboard Documentation', 'Documentation for the Masakari dashboard plugin to the OpenStack\ Dashboard (Horizon)', ['OpenStack'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Masakari dashboard', 'Masakari dashboard Documentation', 'OpenStack', 'Masakari dashboard', 'Horizon plugin for masakari', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # -- Options for Epub output -------------------------------------------------- # Bibliographic Dublin Core info. epub_title = 'Masakari dashboard' epub_author = 'OpenStack' epub_publisher = 'OpenStack' epub_copyright = '2017, OpenStack' # The language of the text. It defaults to the language option # or en if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be an ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. # epub_exclude_files = [] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3657322 masakari_dashboard-14.0.0/doc/source/contributor/0000775000175000017500000000000015163173704020643 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000356015163173622024107 0ustar00zuulzuul============================ So You Want to Contribute... ============================ For general information on contributing to OpenStack, please check out the `contributor guide `_ to get started. It covers all the basics that are common to all OpenStack projects: the accounts you need, the basics of interacting with our Gerrit review system, how we communicate as a community, etc. Below will cover the more project specific information you need to get started with masakari-dashboard. Communication ~~~~~~~~~~~~~ * IRC channel #openstack-masakari at OFTC * Mailing list (prefix subjects with ``[masakari]`` for faster responses) http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss Contacting the Core Team ~~~~~~~~~~~~~~~~~~~~~~~~ Please refer the `masakari-dashboard Core Team `_ contacts. New Feature Planning ~~~~~~~~~~~~~~~~~~~~ masakari-dashboard features are tracked on `Launchpad `_. Task Tracking ~~~~~~~~~~~~~ We track our tasks in `Launchpad `_. If you're looking for some smaller, easier work item to pick up and get started on, search for the 'low-hanging-fruit' tag. Reporting a Bug ~~~~~~~~~~~~~~~ You found an issue and want to make sure we are aware of it? You can do so on `Launchpad `_. Getting Your Patch Merged ~~~~~~~~~~~~~~~~~~~~~~~~~ All changes proposed to the masakari-dashboard project require one or two +2 votes from masakari-dashboard core reviewers before one of the core reviewers can approve patch by giving ``Workflow +1`` vote. Project Team Lead Duties ~~~~~~~~~~~~~~~~~~~~~~~~ All common PTL duties are enumerated in the `PTL guide `_. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/doc/source/index.rst0000664000175000017500000000453015163173622020133 0ustar00zuulzuul=============================== Masakari dashboard =============================== Horizon plugin for masakari * Free software: Apache license * Source: https://opendev.org/openstack/masakari-dashboard * Bugs: https://bugs.launchpad.net/masakari-dashboard Features -------- * TODO Enabling in DevStack -------------------- Add this repo as an external repository into your ``local.conf`` file:: [[local|localrc]] enable_plugin masakaridashboard https://github.com/openstack/masakari-dashboard Manual Installation ------------------- Begin by cloning the Horizon and Masakari dashboard repositories:: git clone https://github.com/openstack/horizon git clone https://github.com/openstack/masakari-dashboard Create a virtual environment and install Horizon dependencies:: cd horizon python tools/install_venv.py Set up your ``local_settings.py`` file:: cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py Open up the copied ``local_settings.py`` file in your preferred text editor. You will want to customize several settings: - ``OPENSTACK_HOST`` should be configured with the hostname of your OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your environment. (They should be correct unless you modified your OpenStack server to change them.) Install Masakari dashboard with all dependencies in your virtual environment:: tools/with_venv.sh pip install -e ../masakari-dashboard/ And enable it in Horizon:: ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.yaml openstack_dashboard/conf To run horizon with the newly enabled Masakari dashboard plugin run:: ./run_tests.sh --runserver 0.0.0.0:8080 to have the application start on port 8080 and the horizon dashboard will be available in your browser at http://localhost:8080/ For Contributors ================ * If you are a new contributor to Masakari Dashboard please refer: :doc:`contributor/contributing` .. toctree:: :hidden: contributor/contributing ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/manage.py0000664000175000017500000000154115163173622016026 0ustar00zuulzuul#!/usr/bin/env python # Copyright (c) 2018 NTT DATA # # 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 if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "masakaridashboard.test.settings") execute_from_command_line(sys.argv) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.383735 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/0000775000175000017500000000000015163173704021515 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/PKG-INFO0000644000175000017500000000672015163173704022615 0ustar00zuulzuulMetadata-Version: 2.4 Name: masakari-dashboard Version: 14.0.0 Summary: Horizon plugin for masakari Author-email: OpenStack License: Apache-2.0 Project-URL: Homepage, https://docs.openstack.org/masakari-dashboard/latest/ Project-URL: Repository, https://opendev.org/openstack/masakari-dashboard Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE Requires-Dist: pbr!=2.1.0,>=2.0.0 Requires-Dist: horizon>=17.1.0 Requires-Dist: openstacksdk>=0.26.0 Requires-Dist: PyYAML>=3.12 Dynamic: license-file Dynamic: requires-dist ================== Masakari dashboard ================== .. image:: https://governance.openstack.org/tc/badges/masakari-dashboard.svg .. Change things from this point on Horizon plugin for Masakari * Free software: Apache license * Source: https://opendev.org/openstack/masakari-dashboard * Bugs: https://bugs.launchpad.net/masakari-dashboard Enabling in DevStack -------------------- Add this repo as an external repository into your ``local.conf`` file:: [[local|localrc]] enable_plugin masakaridashboard https://opendev.org/openstack/masakari-dashboard Manual Installation ------------------- Begin by cloning the Horizon and Masakari dashboard repositories:: git clone https://opendev.org/openstack/horizon git clone https://opendev.org/openstack/masakari-dashboard Create a virtual environment and install Horizon dependencies:: cd horizon python3 tools/install_venv.py Set up your ``local_settings.py`` file:: cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py Open up the copied ``local_settings.py`` file in your preferred text editor. You will want to customize several settings: - ``OPENSTACK_HOST`` should be configured with the hostname of your OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your environment. (They should be correct unless you modified your OpenStack server to change them.) Install Masakari dashboard with all dependencies in your virtual environment:: tools/with_venv.sh pip install -e ../masakari-dashboard/ And enable it in Horizon:: ln -s ../masakari-dashboard/masakaridashboard/local/enabled/_50_masakaridashboard.py openstack_dashboard/local/enabled ln -s ../masakari-dashboard/masakaridashboard/local/local_settings.d/_50_masakari.py openstack_dashboard/local/local_settings.d ln -s ../masakari-dashboard/masakaridashboard/conf/masakari_policy.yaml openstack_dashboard/conf To run horizon with the newly enabled Masakari dashboard plugin run:: ./run_tests.sh --runserver 0.0.0.0:8080 to have the application start on port 8080 and the horizon dashboard will be available in your browser at http://localhost:8080/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/SOURCES.txt0000664000175000017500000001121515163173704023401 0ustar00zuulzuul.zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst manage.py pyproject.toml requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/Makefile doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/contributor/contributing.rst masakari_dashboard.egg-info/PKG-INFO masakari_dashboard.egg-info/SOURCES.txt masakari_dashboard.egg-info/dependency_links.txt masakari_dashboard.egg-info/not-zip-safe masakari_dashboard.egg-info/pbr.json masakari_dashboard.egg-info/requires.txt masakari_dashboard.egg-info/top_level.txt masakaridashboard/__init__.py masakaridashboard/dashboard.py masakaridashboard/handle_errors.py masakaridashboard/version.py masakaridashboard/api/__init__.py masakaridashboard/api/api.py masakaridashboard/conf/masakari_policy.yaml masakaridashboard/default/__init__.py masakaridashboard/default/panel.py masakaridashboard/default/templates/default/base.html masakaridashboard/default/templates/default/table.html masakaridashboard/hosts/__init__.py masakaridashboard/hosts/forms.py masakaridashboard/hosts/panel.py masakaridashboard/hosts/tables.py masakaridashboard/hosts/tabs.py masakaridashboard/hosts/tests.py masakaridashboard/hosts/urls.py masakaridashboard/hosts/views.py masakaridashboard/hosts/templates/hosts/_detail_overview.html masakaridashboard/hosts/templates/hosts/_update.html masakaridashboard/hosts/templates/hosts/detail.html masakaridashboard/hosts/templates/hosts/index.html masakaridashboard/hosts/templates/hosts/update.html masakaridashboard/local/__init__.py masakaridashboard/local/enabled/_50_masakaridashboard.py masakaridashboard/local/enabled/__init__.py masakaridashboard/local/local_settings.d/_50_masakari.py masakaridashboard/notifications/__init__.py masakaridashboard/notifications/panel.py masakaridashboard/notifications/tables.py masakaridashboard/notifications/tabs.py masakaridashboard/notifications/tests.py masakaridashboard/notifications/urls.py masakaridashboard/notifications/views.py masakaridashboard/notifications/templates/notifications/_detail_overview.html masakaridashboard/notifications/templates/notifications/_progress_detail.html masakaridashboard/notifications/templates/notifications/index.html masakaridashboard/segments/__init__.py masakaridashboard/segments/forms.py masakaridashboard/segments/panel.py masakaridashboard/segments/tables.py masakaridashboard/segments/tabs.py masakaridashboard/segments/tests.py masakaridashboard/segments/urls.py masakaridashboard/segments/views.py masakaridashboard/segments/templates/segments/_addhost.html masakaridashboard/segments/templates/segments/_create.html masakaridashboard/segments/templates/segments/_detail_overview.html masakaridashboard/segments/templates/segments/_update.html masakaridashboard/segments/templates/segments/addhost.html masakaridashboard/segments/templates/segments/create.html masakaridashboard/segments/templates/segments/index.html masakaridashboard/segments/templates/segments/update.html masakaridashboard/static/masakaridashboard/css/style.css masakaridashboard/test/__init__.py masakaridashboard/test/helpers.py masakaridashboard/test/settings.py masakaridashboard/test/uuidsentinel.py masakaridashboard/test/test_data/__init__.py masakaridashboard/test/test_data/masakari_data.py masakaridashboard/test/test_data/utils.py masakaridashboard/vmoves/__init__.py masakaridashboard/vmoves/panel.py masakaridashboard/vmoves/tables.py masakaridashboard/vmoves/tabs.py masakaridashboard/vmoves/tests.py masakaridashboard/vmoves/urls.py masakaridashboard/vmoves/views.py masakaridashboard/vmoves/templates/vmoves/_detail_overview.html masakaridashboard/vmoves/templates/vmoves/detail.html masakaridashboard/vmoves/templates/vmoves/index.html releasenotes/notes/.placeholder releasenotes/notes/blueprint-add-vmoves-37992efec8403393.yaml releasenotes/notes/blueprint-enable-to-segment-efdff66078dab752.yaml releasenotes/notes/bug-1944679-0df043f17a8bbaff.yaml releasenotes/notes/drop-masakariclient-dep-054a456a5bf2b941.yaml releasenotes/notes/drop-py-2-7-a5322c1cb7c74c61.yaml releasenotes/notes/drop-python-38-and-39.yaml releasenotes/notes/fix-cacert-023407ba61a4bb7a.yaml releasenotes/notes/yamlify-policy-169e72bc8abd93a1.yaml releasenotes/source/2023.1.rst releasenotes/source/2023.2.rst releasenotes/source/2024.1.rst releasenotes/source/2024.2.rst releasenotes/source/2025.1.rst releasenotes/source/2025.2.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/rocky.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././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/dependency_links.txt0000664000175000017500000000000115163173704025563 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/not-zip-safe0000664000175000017500000000000115163173704023743 0ustar00zuulzuul ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/pbr.json0000664000175000017500000000005615163173704023174 0ustar00zuulzuul{"git_version": "d68e479", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/requires.txt0000664000175000017500000000010515163173704024111 0ustar00zuulzuulpbr!=2.1.0,>=2.0.0 horizon>=17.1.0 openstacksdk>=0.26.0 PyYAML>=3.12 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040452.0 masakari_dashboard-14.0.0/masakari_dashboard.egg-info/top_level.txt0000664000175000017500000000002215163173704024241 0ustar00zuulzuulmasakaridashboard ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3667324 masakari_dashboard-14.0.0/masakaridashboard/0000775000175000017500000000000015163173704017664 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/__init__.py0000664000175000017500000000000015163173622021762 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3677325 masakari_dashboard-14.0.0/masakaridashboard/api/0000775000175000017500000000000015163173704020435 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/api/__init__.py0000664000175000017500000000000015163173622022533 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/api/api.py0000664000175000017500000001636715163173622021574 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import 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 keystoneauth1.identity.generic import token from keystoneauth1 import session as ks_session from openstack import connection from openstack_dashboard.api import nova as nova_api from masakaridashboard.handle_errors import handle_errors @memoized.memoized def openstack_connection(request, version=None): interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') region_name = request.COOKIES.get('services_region') if not region_name: default_service_regions = getattr(settings, 'DEFAULT_SERVICE_REGIONS', {}) region_name = default_service_regions.get('*') auth = token.Token( auth_url=request.user.endpoint, token=request.user.token.id, project_name=request.user.project_name, project_id=request.user.tenant_id) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT') session = ks_session.Session(auth=auth, verify=cacert or True) conn = connection.Connection(session=session, interface=interface, region_name=region_name, ha_api_version=version) return conn.instance_ha def get_compute_service_list(request): return nova_api.service_list(request, binary="nova-compute") @handle_errors(_("Unable to retrieve segments"), []) def get_segment_list(request, marker='', paginate=False, filters=None): """Returns segments as per page size.""" page_size = utils.get_page_size(request) client = openstack_connection(request) kwargs = get_request_param(marker, paginate, filters, page_size) entities_iter = client.segments(**kwargs) has_prev_data = has_more_data = False if paginate: entities, has_more_data, has_prev_data = pagination_process( entities_iter, kwargs['limit'], page_size, marker) else: entities = list(entities_iter) return entities, has_more_data, has_prev_data def get_request_param(marker, paginate, filters, page_size): limit = getattr(settings, 'API_RESULT_LIMIT', 100) if paginate: request_size = page_size + 1 else: request_size = limit kwargs = {"marker": marker, "limit": request_size } if filters is not None: kwargs.update(filters) return kwargs def pagination_process(data, request_size, page_size, marker): """Retrieve a listing of specific entity and handles pagination. :param request: Request data :param marker: Pagination marker for large data sets: entity id :param paginate: If true will perform pagination based on settings. Default:False """ prev_data = more_data = False entities = list(itertools.islice(data, request_size)) # first and middle page condition if len(entities) > page_size: entities.pop() more_data = True # middle page condition if marker is not None: prev_data = True elif marker is not None: prev_data = True return entities, more_data, prev_data @handle_errors(_("Unable to retrieve segments"), []) def segment_list(request): return list(openstack_connection(request).segments()) def segment_create(request, data): """Create segment.""" return openstack_connection(request).create_segment(**data) @handle_errors(_("Unable to retrieve segment"), []) def get_segment(request, segment_id): """Returns segment by id""" return openstack_connection(request).get_segment(segment_id) @handle_errors(_("Unable to delete segment"), []) def segment_delete(request, segment_id, ignore_missing=True): return openstack_connection(request).delete_segment( segment_id, ignore_missing) @handle_errors(_("Unable to update segment"), []) def segment_update(request, segment_id, fields_to_update): """Update segment.""" return openstack_connection(request).update_segment( segment_id, **fields_to_update) def create_host(request, data): """Create Host.""" attrs = {'name': data['name'], 'reserved': data['reserved'], 'type': data['type'], 'control_attributes': data['control_attributes'], 'on_maintenance': data['on_maintenance']} return openstack_connection(request).create_host( data['segment_id'], **attrs) @handle_errors(_("Unable to get host list"), []) def get_host_list(request, segment_id, filters): """Returns host list.""" return openstack_connection(request).hosts(segment_id, **filters) def delete_host(request, host_id, segment_id): return openstack_connection(request).delete_host( host_id, segment_id, False) def update_host(request, host_uuid, failover_segment_id, fields_to_update): return openstack_connection(request).update_host( host_uuid, failover_segment_id, **fields_to_update) def get_host(request, host_id, segment_id): """return single host """ return openstack_connection(request).get_host(host_id, segment_id) def notification_list(request, filters=None, marker='', paginate=False): """return notifications list """ page_size = utils.get_page_size(request) kwargs = get_request_param(marker, paginate, filters, page_size) entities_iter = openstack_connection(request).notifications(**kwargs) has_prev_data = has_more_data = False if paginate: entities, has_more_data, has_prev_data = pagination_process( entities_iter, kwargs['limit'], page_size, marker) else: entities = list(entities_iter) return entities, has_more_data, has_prev_data def get_notification_list(request): """return notifications list """ return list(openstack_connection(request).notifications()) def get_notification(request, notification_id): """return single notifications""" return openstack_connection(request).get_notification(notification_id) def get_notification_with_progress_details(request, notification_id): return openstack_connection( request, version='1.1').get_notification( notification_id) @handle_errors(_("Unable to get vmoves list"), []) def get_vmoves_list(request, notification_id, filters): """return vmoves list """ return openstack_connection( request, version='1.3').vmoves(notification_id, **filters) def get_vmove(request, notification_id, vmove_id): """return single vmove""" return openstack_connection( request, version='1.3').get_vmove(vmove_id, notification_id) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3677325 masakari_dashboard-14.0.0/masakaridashboard/conf/0000775000175000017500000000000015163173704020611 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/conf/masakari_policy.yaml0000664000175000017500000000055115163173622024644 0ustar00zuulzuul--- admin_api: is_admin:True context_is_admin: role:admin admin_or_owner: is_admin:True or project_id:%(project_id)s default: rule:admin_api os_masakari_api:extensions: rule:admin_api os_masakari_api:segments: rule:admin_api os_masakari_api:os-hosts: rule:admin_api os_masakari_api:notifications: rule:admin_api os_masakari_api:vmoves: rule:admin_api ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/dashboard.py0000664000175000017500000000367515163173622022177 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from django.utils.translation import gettext_lazy as _ import horizon from masakaridashboard.api import api from masakaridashboard.default import panel LOG = logging.getLogger(__name__) class MasakariDashboard(horizon.Dashboard): slug = "masakaridashboard" name = _("Instance-ha") panels = ('default', 'segments', 'hosts', 'notifications', 'vmoves') default_panel = 'default' policy_rules = (('instance-ha', 'context_is_admin'),) def allowed(self, context): # disable whole dashboard if masakari # is not present in the service catalog try: # NOTE(pas-ha) this method tries to construct keystoneauth.Adapter # for the Instance-HA service, # which will fail if the service is absent api.openstack_connection(context['request']) except Exception as e: # catch all errors and log them, # no need to totally fail on e.g. HTTP connect failure LOG.warning(f"Failed to find suitable endpoint for Instance HA " f"service, Masakari Dashboard will not be displayed. " f"Error was: {e}") return False return super().allowed(context) horizon.register(MasakariDashboard) MasakariDashboard.register(panel.Default) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3677325 masakari_dashboard-14.0.0/masakaridashboard/default/0000775000175000017500000000000015163173704021310 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/default/__init__.py0000664000175000017500000000000015163173622023406 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/default/panel.py0000664000175000017500000000143015163173622022756 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.utils.translation import gettext_lazy as _ import horizon class Default(horizon.Panel): name = _("Default") slug = 'default' nav = False ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3597312 masakari_dashboard-14.0.0/masakaridashboard/default/templates/0000775000175000017500000000000015163173704023306 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3677325 masakari_dashboard-14.0.0/masakaridashboard/default/templates/default/0000775000175000017500000000000015163173704024732 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/default/templates/default/base.html0000664000175000017500000000034715163173622026535 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block css %} {% include "_stylesheets.html" %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/default/templates/default/table.html0000664000175000017500000000024115163173622026703 0ustar00zuulzuul{% extends 'masakaridashboard/default/base.html' %} {% block main %}
{{ table.render }}
{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/handle_errors.py0000664000175000017500000000570215163173622023070 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import inspect import horizon.exceptions def handle_errors(error_message, error_default=None, request_arg=None): """A decorator for adding default error handling to API calls. It wraps the original method in a try-except block, with horizon's error handling added. Note: it should only be used on functions or methods that take request as their argument (it has to be named "request", or ``request_arg`` has to be provided, indicating which argument is the request). The decorated method accepts a number of additional parameters: :param _error_handle: whether to handle the errors in this call :param _error_message: override the error message :param _error_default: override the default value returned on error :param _error_redirect: specify a redirect url for errors :param _error_ignore: ignore known errors """ def decorator(func): if request_arg is None: _request_arg = 'request' if _request_arg not in inspect.getfullargspec(func).args: raise RuntimeError( "The handle_errors decorator requires 'request' as " "an argument of the function or method being decorated") else: _request_arg = request_arg @functools.wraps(func) def wrapper(*args, **kwargs): _error_handle = kwargs.pop('_error_handle', True) _error_message = kwargs.pop('_error_message', error_message) _error_default = kwargs.pop('_error_default', error_default) _error_redirect = kwargs.pop('_error_redirect', None) _error_ignore = kwargs.pop('_error_ignore', False) if not _error_handle: return func(*args, **kwargs) try: return func(*args, **kwargs) except Exception as e: callargs = inspect.getcallargs(func, *args, **kwargs) request = callargs[_request_arg] _error_message += ': ' + str(e) horizon.exceptions.handle(request, _error_message, ignore=_error_ignore, redirect=_error_redirect) return _error_default wrapper.wrapped = func return wrapper return decorator ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3697329 masakari_dashboard-14.0.0/masakaridashboard/hosts/0000775000175000017500000000000015163173704021024 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/__init__.py0000664000175000017500000000000015163173622023122 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/forms.py0000664000175000017500000000512715163173622022530 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 horizon import exceptions from horizon import forms from horizon import messages from masakaridashboard.api import api class UpdateHostForm(forms.SelfHandlingForm): uuid = forms.CharField(widget=forms.HiddenInput()) failover_segment_id = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField( label=_('Host Name'), widget=forms.TextInput(attrs={'readonly': 'readonly'})) reserved = forms.ChoiceField( label=_('Reserved'), choices=[('False', 'False'), ('True', 'True')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'available host'}), required=False) type = forms.CharField( label=_('Type'), widget=forms.TextInput(attrs={'maxlength': 255})) control_attributes = forms.CharField( label=_('Control Attribute'), widget=forms.TextInput()) on_maintenance = forms.ChoiceField( label=_('On Maintenance'), choices=[('False', 'False'), ('True', 'True')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'available host'}), required=False ) def handle(self, request, data): try: attrs = {'name': data['name'], 'reserved': data['reserved'], 'type': data['type'], 'control_attributes': data['control_attributes'], 'on_maintenance': data['on_maintenance']} api.update_host(request, data['uuid'], data["failover_segment_id"], attrs) msg = _('Successfully updated host.') messages.success(request, msg) except Exception: msg = _('Failed to update host.') redirect = reverse('horizon:masakaridashboard:hosts:index') exceptions.handle(request, msg, redirect=redirect) return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/panel.py0000664000175000017500000000144215163173622022475 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 masakaridashboard import dashboard class Hosts(horizon.Panel): name = _("Hosts") slug = 'hosts' dashboard.MasakariDashboard.register(Hosts) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/tables.py0000664000175000017500000000631715163173622022656 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 exceptions from horizon import tables from masakaridashboard.api import api HOST_FILTER_CHOICES = ( ('failover_segment_id', _("Segment Id ="), True), ('type', _("Type ="), True), ('on_maintenance', _("On Maintenance ="), True), ('reserved', _("Reserved ="), True), ) class HostFilterAction(tables.FilterAction): filter_type = "server" filter_choices = HOST_FILTER_CHOICES class DeleteHost(tables.DeleteAction): @staticmethod def action_present(count): return ngettext_lazy( "Delete Host", "Delete Hosts", count ) @staticmethod def action_past(count): return ngettext_lazy( "Deleted Host", "Deleted Hosts", count ) def delete(self, request, data): row_data = data.split(',') segment_uuid = row_data[1] host_uuid = row_data[0] try: api.delete_host(request, host_uuid, segment_uuid) except Exception: msg = _('Unable to delete host.') redirect = reverse('horizon:masakaridashboard:hosts:index') exceptions.handle(self.request, msg, redirect=redirect) class UpdateHost(tables.LinkAction): name = "update" verbose_name = _("Update Host") classes = ("ajax-modal",) def get_link_url(self, datum): host_id = datum.uuid + ',' + datum.failover_segment_id url = "horizon:masakaridashboard:hosts:update" return reverse(url, args=[host_id]) class HostTable(tables.DataTable): name = tables.Column('name', verbose_name=_("Name"), link="horizon:masakaridashboard:hosts:detail") uuid = tables.Column('uuid', verbose_name=_("UUID")) reserved = tables.Column( 'reserved', verbose_name=_("Reserved")) type = tables.WrappingColumn('type', verbose_name=_("Type")) control_attributes = tables.Column( 'control_attributes', verbose_name=_( "Control Attribute"), truncate=40) on_maintenance = tables.Column( 'on_maintenance', verbose_name=_("On Maintenance")) failover_segment_id = tables.Column( 'failover_segment_id', verbose_name=_("Failover Segment"), link="horizon:masakaridashboard:segments:detail") def get_object_id(self, datum): return datum.uuid + ',' + datum.failover_segment_id class Meta(object): name = "host" verbose_name = _("Host") table_actions = (HostFilterAction, DeleteHost) row_actions = (UpdateHost,) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/tabs.py0000664000175000017500000000173715163173622022336 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 tabs class OverviewTab(tabs.Tab): name = _("Hosts") slug = "hosts" template_name = ("masakaridashboard/hosts/_detail_overview.html") def get_context_data(self, request): return {"host": self.tab_group.kwargs['host']} class HostDetailTabs(tabs.DetailTabsGroup): slug = "host_details" tabs = (OverviewTab,) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3597312 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/0000775000175000017500000000000015163173704023022 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.370733 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/0000775000175000017500000000000015163173704024162 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/_detail_overview.html0000664000175000017500000000145715163173622030405 0ustar00zuulzuul{% load i18n sizeformat parse_date %}

{% trans "Host Detail" %}


{% trans "Name" %}
{{ host.name }}
{% trans "UUID" %}
{{ host.uuid }}
{% trans "Failover Segment" %}
{{ host.failover_segment_id }}
{% trans "Reserved" %}
{{ host.reserved }}
{% trans "On Maintenance" %}
{{ host.on_maintenance }}
{% trans "Type" %}
{{ host.type }}
{% trans "Control Attribute" %}
{{ host.control_attributes }}
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/_update.html0000664000175000017500000000036315163173622026472 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Modify reserved, type, on_maintenance and control_attributes of a host." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/detail.html0000664000175000017500000000224615163173622026315 0ustar00zuulzuul {% extends 'masakaridashboard/default/base.html' %} {% load i18n %} {% block title %}{% trans "Host Detail" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Host Detail") %} {% endblock page_header %} {% block main %} {% load i18n sizeformat parse_date %}

{% trans "Host Detail" %}


{% trans "UUID" %}
{{ host.uuid }}
{% trans "Name" %}
{{ host.name }}
{% trans "Reserved" %}
{{ host.reserved }}
{% trans "Type" %}
{{ host.type }}
{% trans "Control Attribute" %}
{{ host.control_attributes }}
{% trans "On Maintenance" %}
{{ host.on_maintenance }}
{% trans "Failover Segment" %}
{{ host.failover_segment_id }}
{% endblock %}
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/index.html0000664000175000017500000000036515163173622026162 0ustar00zuulzuul{% extends 'masakaridashboard/default/table.html' %} {% load i18n %} {% block title %}{% trans "Hosts" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Hosts") %} {% endblock page_header %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/templates/hosts/update.html0000664000175000017500000000030215163173622026324 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Host" %}{% endblock %} {% block main %} {% include 'masakaridashboard/hosts/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/tests.py0000664000175000017500000001417715163173622022551 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from django.urls import reverse from masakaridashboard.test import helpers as test INDEX_URL = reverse('horizon:masakaridashboard:hosts:index') class HostTest(test.TestCase): def test_index(self): hosts = self.masakari_host.list() segments = self.masakari_segment.list() with mock.patch('masakaridashboard.api.api.segment_list', return_value=segments), mock.patch( 'masakaridashboard.api.api.get_segment', return_value=segments[0]), mock.patch( 'masakaridashboard.api.api.get_host_list', return_value=hosts): res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'masakaridashboard/hosts/index.html') self.assertEqual(res.status_code, 200) def test_create_post(self): segment = self.masakari_segment.list() host = self.masakari_host.list()[0] compute_services = self.compute_services.list() create_url = reverse('horizon:masakaridashboard:segments:addhost', args=[segment[0].uuid]) form_data = { 'segment_id': host.failover_segment_id, 'segment_name': segment[0].name, 'name': host.name, 'type': host.type, 'reserved': host.reserved, 'control_attributes': host.control_attributes, 'on_maintenance': host.on_maintenance } with mock.patch('masakaridashboard.api.api.segment_list', return_value=segment), mock.patch( 'masakaridashboard.api.api.get_host_list', return_value=[]), mock.patch( 'masakaridashboard.api.api.get_compute_service_list', return_value=compute_services), mock.patch( 'masakaridashboard.api.api.get_segment', return_value=segment[0]), mock.patch( 'masakaridashboard.api.api.create_host', return_value=host) as mocked_create: res = self.client.post(create_url, form_data) self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) self.assertRedirectsNoFollow(res, INDEX_URL) mocked_create.assert_called_once_with( mock.ANY, form_data ) def test_delete_ok(self): host = self.masakari_host.list()[0] data = {'object_ids': host.uuid + ',' + host.failover_segment_id, 'action': 'host__delete'} with mock.patch( 'masakaridashboard.api.api.segment_list', return_value=[self.masakari_segment.first( )]), mock.patch( 'masakaridashboard.api.api.get_host_list', return_value=self.masakari_host.list()), mock.patch( 'masakaridashboard.api.api.delete_host', return_value=None ) as mocked_delete: res = self.client.post(INDEX_URL, data) self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) mocked_delete.assert_called_once_with( mock.ANY, host.uuid, host.failover_segment_id, ) def test_detail(self): host = self.masakari_host.list()[0] id_to_update = host.uuid + ',' + host.failover_segment_id detail_url = reverse('horizon:masakaridashboard:hosts:detail', args=[id_to_update]) with mock.patch('masakaridashboard.api.api.get_host', return_value=self.masakari_host.list()[0]): res = self.client.get(detail_url) self.assertNoFormErrors(res) self.assertEqual(200, res.status_code) self.assertTemplateUsed(res, 'horizon/common/_detail.html') self.assertTemplateUsed( res, 'masakaridashboard/hosts/_detail_overview.html') def test_update(self): host_to_update = self.masakari_host.list()[0] id_to_update = ( host_to_update.uuid + ',' + host_to_update.failover_segment_id) update_url = reverse('horizon:masakaridashboard:hosts:update', args=[id_to_update]) host_to_update.control_attributes = 'fake' form_data = { 'failover_segment_id': host_to_update.failover_segment_id, 'uuid': host_to_update.uuid, 'name': host_to_update.name, 'type': host_to_update.type, 'reserved': host_to_update.reserved, 'control_attributes': host_to_update.control_attributes, 'on_maintenance': host_to_update.on_maintenance } with mock.patch( 'masakaridashboard.api.api.get_host', return_value=self.masakari_host.list()[0]), mock.patch( 'masakaridashboard.api.api.update_host', return_value=host_to_update) as mocked_update: res = self.client.post(update_url, form_data) self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) self.assertRedirectsNoFollow(res, INDEX_URL) fields_to_update = { 'name': host_to_update.name, 'type': host_to_update.type, 'reserved': host_to_update.reserved, 'control_attributes': host_to_update.control_attributes, 'on_maintenance': host_to_update.on_maintenance } mocked_update.assert_called_once_with( mock.ANY, host_to_update.uuid, host_to_update.failover_segment_id, fields_to_update ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/urls.py0000664000175000017500000000161515163173622022365 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 masakaridashboard.hosts import views HOST = r'^(?P[^/]+)/%s$' urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(HOST % 'detail', views.DetailView.as_view(), name='detail'), re_path(HOST % 'update', views.UpdateView.as_view(), name='update'), ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/hosts/views.py0000664000175000017500000001137015163173622022534 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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.conf import settings from django.urls import reverse from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import tables from horizon import tabs from horizon.utils import memoized from masakaridashboard.api import api from masakaridashboard.hosts import forms as host_forms from masakaridashboard.hosts import tables as masakari_tab from masakaridashboard.hosts import tabs as host_tab class IndexView(tables.DataTableView): table_class = masakari_tab.HostTable template_name = 'masakaridashboard/hosts/index.html' page_title = _("Hosts") def needs_filter_first(self, table): return self._needs_filter_first def get_data(self): segments = api.segment_list(self.request) host_list = [] filters = self.get_filters() self._needs_filter_first = True filter_first = getattr(settings, 'FILTER_DATA_FIRST', {}) if filter_first.get('masakaridashboard.hosts', False) and len( filters) == 0: self._needs_filter_first = True self._more = False return host_list for segment in segments: host_gen = api.get_host_list(self.request, segment.uuid, filters) for item in host_gen: host_list.append(item) return host_list class DetailView(tabs.TabbedTableView): tab_group_class = host_tab.HostDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ host.name|default:host.id }}" def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) host = self.get_data() table = masakari_tab.HostTable(self.request) context["host"] = host context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(host) return context @memoized.memoized_method def get_data(self): try: row_data = self.kwargs['host_id'].split(',') segment_id = row_data[1] host_id = row_data[0] host = api.get_host(self.request, host_id, segment_id) except Exception: msg = _('Unable to get host "%s".') % host_id redirect = reverse('horizon:masakaridashboard:hosts:index') exceptions.handle(self.request, msg, redirect=redirect) return host def get_redirect_url(self): return reverse('horizon:masakaridashboard:hosts:index') def get_tabs(self, request, *args, **kwargs): host = self.get_data() return self.tab_group_class(request, host=host, **kwargs) class UpdateView(forms.ModalFormView): template_name = 'masakaridashboard/hosts/update.html' modal_header = _("Update Host") form_id = "update_host" form_class = host_forms.UpdateHostForm submit_label = _("Update") submit_url = "horizon:masakaridashboard:hosts:update" success_url = reverse_lazy("horizon:masakaridashboard:hosts:index") page_title = _("Update Host") @memoized.memoized_method def get_object(self): try: row_data = self.kwargs['host_id'].split(',') host_id = row_data[0] segment_id = row_data[1] host = api.get_host(self.request, host_id, segment_id) return host except Exception: msg = _('Unable to retrieve host.') redirect = reverse('horizon:masakaridashboard:hosts:index') exceptions.handle(self.request, msg, redirect=redirect) def get_context_data(self, **kwargs): context = super(UpdateView, self).get_context_data(**kwargs) context['submit_url'] = reverse( self.submit_url, args=[self.kwargs['host_id']] ) return context def get_initial(self, **kwargs): host = self.get_object() return {'name': host.name, 'reserved': host.reserved, 'type': host.type, 'control_attributes': host.control_attributes, 'on_maintenance': host.on_maintenance, 'uuid': host.uuid, 'failover_segment_id': host.failover_segment_id} ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.370733 masakari_dashboard-14.0.0/masakaridashboard/local/0000775000175000017500000000000015163173704020756 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/local/__init__.py0000664000175000017500000000000015163173622023054 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.370733 masakari_dashboard-14.0.0/masakaridashboard/local/enabled/0000775000175000017500000000000015163173704022350 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/local/enabled/_50_masakaridashboard.py0000664000175000017500000000160015163173622027021 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from openstack_dashboard import exceptions DASHBOARD = 'masakaridashboard' ADD_INSTALLED_APPS = ['masakaridashboard'] ADD_EXCEPTIONS = { 'recoverable': exceptions.RECOVERABLE, 'not_found': exceptions.NOT_FOUND, 'unauthorized': exceptions.UNAUTHORIZED, } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/local/enabled/__init__.py0000664000175000017500000000000015163173622024446 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.370733 masakari_dashboard-14.0.0/masakaridashboard/local/local_settings.d/0000775000175000017500000000000015163173704024212 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/local/local_settings.d/_50_masakari.py0000664000175000017500000000135115163173622027016 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from openstack_dashboard.settings import POLICY_FILES POLICY_FILES.update({'instance-ha': 'masakari_policy.yaml'}) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3717332 masakari_dashboard-14.0.0/masakaridashboard/notifications/0000775000175000017500000000000015163173704022535 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/__init__.py0000664000175000017500000000000015163173622024633 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/panel.py0000664000175000017500000000150015163173622024201 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 masakaridashboard import dashboard class Notification(horizon.Panel): name = _("Notifications") slug = 'notifications' dashboard.MasakariDashboard.register(Notification) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/tables.py0000664000175000017500000000467415163173622024373 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 tables from horizon.utils import filters NOTIFICATION_FILTER_CHOICES = ( ('source_host_uuid', _("Source Host UUID ="), True), ('type', _("Type ="), True), ('status', _("Status ="), True), ('generated_since', _("Generated Since ="), True), ) class NotificationFilterAction(tables.FilterAction): filter_type = "server" filter_choices = NOTIFICATION_FILTER_CHOICES class ProgressDetailsItem(object): def __init__(self, id, action, timestamp, message): self.id = id self.action = action self.timestamp = timestamp self.message = message class NotificationsTable(tables.DataTable): source_host_uuid = tables.Column( 'source_host_uuid', verbose_name=_("Host")) notification_uuid = tables.Column( 'notification_uuid', verbose_name=_("UUID"), link="horizon:masakaridashboard:notifications:detail") type = tables.Column('type', verbose_name=_("Type")) status = tables.Column('status', verbose_name=_("Status")) payload = tables.Column( 'payload', verbose_name=_("Payload"), truncate=40) def get_object_id(self, datum): return datum.notification_uuid class Meta(object): name = "notifications" verbose_name = _("Notifications") table_actions = (NotificationFilterAction,) class NotificationProgressDetailsTable(tables.DataTable): action = tables.Column('action', verbose_name=_('Action')) timestamp = tables.Column('timestamp', verbose_name=_('Timestamp'), filters=[filters.parse_isotime]) message = tables.Column('message', verbose_name=_('Message')) id = tables.Column('id', verbose_name=_('ID'), hidden=True) class Meta(object): name = "notification_progress_details" verbose_name = _("NotificationProgressDetails") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/tabs.py0000664000175000017500000000657115163173622024050 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 exceptions from horizon import tabs from masakaridashboard.api import api from masakaridashboard.notifications import tables as notification_tab from masakaridashboard.vmoves import tables as vmove_table class OverviewTab(tabs.Tab): name = _("Notifications") slug = "notifications" template_name = ("masakaridashboard/notifications/_detail_overview.html") def get_context_data(self, request): return {"notification": self.tab_group.kwargs['notification']} class NotificationProgressDetailsTab(tabs.TableTab): table_classes = (notification_tab.NotificationProgressDetailsTable,) name = _("Progress Details") slug = "notification_progress_details" template_name = "masakaridashboard/notifications/_progress_detail.html" preload = False def get_notification_progress_details_data(self): try: id = 0 notification = self.tab_group.kwargs['notification'] notification_obj = \ api.get_notification_with_progress_details( self.request, notification.notification_uuid) progress_detail_list = [] for progress_detail in notification_obj.recovery_workflow_details: # Retrieve progress name from detailed name. action = progress_detail['name'] for task in progress_detail['progress_details']: id = id + 1 progress_obj = notification_tab.ProgressDetailsItem( id, action, task['timestamp'], task['message'] ) progress_detail_list.append(progress_obj) return progress_detail_list except Exception: error_message = (_("Failed to get progress details for " "notification '%s'.") % notification.notification_uuid) exceptions.handle(self.request, error_message) return [] class VMoveTab(tabs.TableTab): table_classes = (vmove_table.VMoveTable,) name = _("VMoves") slug = "vmove_tab" template_name = "horizon/common/_detail_table.html" preload = False def get_vmove_data(self): notification = self.tab_group.kwargs['notification'] if notification.type != "COMPUTE_HOST": return [] vmove_list = [] vmove_gen = api.get_vmoves_list( self.request, notification.notification_uuid, filters={}) for item in vmove_gen: vmove_list.append(item) return vmove_list class NotificationDetailTabs(tabs.DetailTabsGroup): slug = "notification_details" tabs = (OverviewTab, NotificationProgressDetailsTab, VMoveTab) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3607314 masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/0000775000175000017500000000000015163173704024533 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.372733 masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/0000775000175000017500000000000015163173704027404 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/_detail_overview.html 22 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/_detail_overview.h0000664000175000017500000000200515163173622033100 0ustar00zuulzuul{% load i18n sizeformat parse_date %}

{% trans "Notification Detail" %}


{% trans "Notification UUID" %}
{{ notification.notification_uuid }}
{% trans "Source Host UUID" %}
{{ notification.source_host_uuid }}
{% trans "Type" %}
{{ notification.type }}
{% trans "Status" %}
{{ notification.status }}
{% trans "Generated Time" %}
{{ notification.generated_time|parse_isotime }}
{% trans "Created At" %}
{{ notification.created_at|parse_isotime }}
{% trans "Updated At" %}
{{ notification.updated_at|parse_isotime }}
{% trans "Payload" %}
{{ notification.payload }}
././@PaxHeader0000000000000000000000000000020700000000000010214 xustar00113 path=masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/_progress_detail.html 22 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/_progress_detail.h0000664000175000017500000000065415163173622033106 0ustar00zuulzuul{{ table.render }} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/templates/notifications/index.html0000664000175000017500000000040515163173622031377 0ustar00zuulzuul{% extends 'masakaridashboard/default/table.html' %} {% load i18n %} {% block title %}{% trans "Notifications" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Notifications") %} {% endblock page_header %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/tests.py0000664000175000017500000001310115163173622024244 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 unittest import mock from urllib import parse from django.conf import settings from django.test.utils import override_settings from django.urls import reverse from masakaridashboard.notifications import tables as notification_tab from masakaridashboard.test import helpers as test INDEX_URL = reverse('horizon:masakaridashboard:notifications:index') class NotificationTest(test.TestCase): def test_index(self): notifications = self.masakari_notification.list() with mock.patch( 'masakaridashboard.api.api.notification_list', return_value=[ notifications, False, False]) as mock_notification_list: res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'masakaridashboard/notifications/index.html') notifications_from_res = res.context['notifications_table'].data self.assertCountEqual(notifications_from_res, notifications) self.assertEqual(res.status_code, 200) mock_notification_list.assert_called_once_with( mock.ANY, filters={}, marker=None, paginate=True) def _test_notifications_index_paginated( self, filters, marker, notifications, url, has_more, has_prev): with mock.patch( 'masakaridashboard.api.api.notification_list', return_value=[notifications, has_more, has_prev]) as mock_notification_list: res = self.client.get(parse.unquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'masakaridashboard/notifications/index.html') mock_notification_list.assert_called_once_with( mock.ANY, filters=filters, marker=marker, paginate=True) return res @override_settings(API_RESULT_PAGE_SIZE=1) @mock.patch('masakaridashboard.api.api.get_notification') def test_notifications_index_paginated(self, mock_get_notification): get_single_notification = self.masakari_notification.list()[0] mock_get_notification.return_value = get_single_notification notification_list = self.masakari_notification.list() size = settings.API_RESULT_PAGE_SIZE base_url = INDEX_URL next = notification_tab.NotificationsTable._meta.pagination_param # get first page expected_notifications = notification_list[:size] res = self._test_notifications_index_paginated( filters={}, marker=None, notifications=expected_notifications, url=base_url, has_more=True, has_prev=False) notifications = res.context['notifications_table'].data self.assertCountEqual(notifications, expected_notifications) # get second page expected_notifications = notification_list[size:2 * size] marker = expected_notifications[0].id url = base_url + "?%s=%s" % (next, marker) res = self._test_notifications_index_paginated( filters={}, marker=marker, notifications=expected_notifications, url=url, has_more=True, has_prev=True) notifications = res.context['notifications_table'].data self.assertCountEqual(notifications, expected_notifications) # get last page expected_notifications = notification_list[-size:] marker = expected_notifications[0].id url = base_url + "?%s=%s" % (next, marker) res = self._test_notifications_index_paginated( filters={}, marker=marker, notifications=expected_notifications, url=url, has_more=False, has_prev=True) notifications = res.context['notifications_table'].data self.assertCountEqual(notifications, expected_notifications) @override_settings(API_RESULT_PAGE_SIZE=1) def test_notifications_index_paginated_prev_page(self): notification_list = self.masakari_notification.list() size = settings.API_RESULT_PAGE_SIZE base_url = INDEX_URL prev = notification_tab.NotificationsTable._meta.prev_pagination_param # prev from some page expected_notifications = notification_list[size:2 * size] marker = expected_notifications[0].id url = base_url + "?%s=%s" % (prev, marker) res = self._test_notifications_index_paginated( filters={}, marker=marker, notifications=expected_notifications, url=url, has_more=True, has_prev=True) notifications = res.context['notifications_table'].data self.assertCountEqual(notifications, expected_notifications) # back to first page expected_notifications = notification_list[:size] marker = expected_notifications[0].id url = base_url + "?%s=%s" % (prev, marker) res = self._test_notifications_index_paginated( filters={}, marker=marker, notifications=expected_notifications, url=url, has_more=True, has_prev=False) notifications = res.context['notifications_table'].data self.assertCountEqual(notifications, expected_notifications) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/urls.py0000664000175000017500000000156015163173622024075 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 masakaridashboard.notifications import views NOTIFICATION = r'^(?P[^/]+)/%s$' urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(NOTIFICATION % 'detail', views.DetailView.as_view(), name='detail'), ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/notifications/views.py0000664000175000017500000001045415163173622024247 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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.conf import settings from django.urls import reverse from django.utils.translation import gettext_lazy as _ from horizon import exceptions from horizon import tables from horizon import tabs from horizon.utils import memoized from masakaridashboard.api import api from masakaridashboard.notifications import tables as notification_tab from masakaridashboard.notifications import tabs as not_tab class IndexView(tables.DataTableView): table_class = notification_tab.NotificationsTable template_name = 'masakaridashboard/notifications/index.html' page_title = _("Notifications") _more = False _prev = False def needs_filter_first(self, table): return self._needs_filter_first def has_more_data(self, table): return self._more def has_prev_data(self, table): return self._prev def get_data(self): notification_list = [] marker = self.request.GET.get( notification_tab.NotificationsTable._meta.pagination_param, None ) if marker is not None: try: notification = api.get_notification(self.request, marker) marker = notification.id except Exception: msg = _('Unable to get notification "%s".') % marker redirect = reverse( 'horizon:masakaridashboard:notifications:index') exceptions.handle(self.request, msg, redirect=redirect) filters = self.get_filters() self._needs_filter_first = True filter_first = getattr(settings, 'FILTER_DATA_FIRST', {}) if filter_first.get('masakaridashboard.notifications', False) and len( filters) == 0: self._needs_filter_first = True self._more = False return notification_list try: notification_list, self._more, self._prev = api.notification_list( self.request, filters=filters, marker=marker, paginate=True) except Exception: self._prev = False self._more = False msg = _('Unable to retrieve notification list.') exceptions.handle(self.request, msg) return notification_list class DetailView(tabs.TabbedTableView): tab_group_class = not_tab.NotificationDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ notification.notification_uuid }}" def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) notification = self.get_data() table = notification_tab.NotificationsTable(self.request) context["notification"] = notification context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(notification) return context @memoized.memoized_method def get_data(self): try: notification_data = self.kwargs['notification_id'] if len(notification_data.split(',')) > 1: notification_id = notification_data.split(',')[0] else: notification_id = notification_data notification = api.get_notification(self.request, notification_id) except Exception: msg = _('Unable to get notification "%s".') % notification_id redirect = reverse('horizon:masakaridashboard:notifications:index') exceptions.handle(self.request, msg, redirect=redirect) return notification def get_redirect_url(self): return reverse('horizon:masakaridashboard:notifications:index') def get_tabs(self, request, *args, **kwargs): notification = self.get_data() return self.tab_group_class( request, notification=notification, **kwargs) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3737333 masakari_dashboard-14.0.0/masakaridashboard/segments/0000775000175000017500000000000015163173704021511 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/__init__.py0000664000175000017500000000000015163173622023607 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/forms.py0000664000175000017500000001610515163173622023213 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from 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 masakaridashboard.api import api class CreateSegmentForm(forms.SelfHandlingForm): name = forms.CharField( label=_('Segment Name'), widget=forms.TextInput(attrs={'maxlength': 255}), help_text=_('The segment name.')) recovery_method = forms.ChoiceField( label=_('Recovery Method'), choices=[('auto', 'auto'), ('auto_priority', 'auto_priority'), ('reserved_host', 'reserved_host'), ('rh_priority', 'rh_priority')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'recovery_method'}), required=True, help_text=_('Type of recovery if any host in this segment goes down.') ) service_type = forms.CharField( label=_('Service Type'), help_text=_('The name of service which will be deployed in this' ' segment. As of now user can mention COMPUTE as service' ' type.'), widget=forms.TextInput(attrs={ 'readonly': 'readonly', 'value': 'compute'})) description = forms.CharField( label=_("Description"), widget=forms.Textarea( attrs={'rows': 4}), required=False) is_enabled = forms.BooleanField( label=_("Enabled"), required=False, initial=True, ) def __init__(self, *args, **kwargs): super(CreateSegmentForm, self).__init__(*args, **kwargs) def handle(self, request, data): try: api.segment_create(request, data) msg = _('Successfully created segment') messages.success(request, msg) except Exception as exc: if exc.status_code == 409: msg = _('Segment with name "%s" already exists') % data["name"] else: msg = _('Failed to create segment') redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(request, msg, redirect=redirect) return True class UpdateForm(forms.SelfHandlingForm): uuid = forms.CharField(widget=forms.HiddenInput()) name = forms.CharField( label=_('Segment Name'), widget=forms.TextInput(attrs={'maxlength': 255})) recovery_method = forms.ChoiceField( label=_('Recovery Method'), choices=[('auto', 'auto'), ('auto_priority', 'auto_priority'), ('reserved_host', 'reserved_host'), ('rh_priority', 'rh_priority')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'recovery_method'}), required=False ) description = forms.CharField( label=_('Description'), widget=forms.Textarea( attrs={'width': "100%", 'cols': "80", 'rows': "5", }), required=False ) is_enabled = forms.BooleanField( label=_("Enabled"), required=False, ) def handle(self, request, data): try: fields_to_update = { 'name': data['name'], 'recovery_method': data['recovery_method'], 'description': data['description'], 'is_enabled': data['is_enabled'], } api.segment_update(request, data['uuid'], fields_to_update) msg = _('Successfully updated segment.') messages.success(request, msg) except Exception: msg = _('Failed to update segment.') redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(request, msg, redirect=redirect) return True class AddHostForm(forms.SelfHandlingForm): segment_id = forms.CharField(widget=forms.HiddenInput()) segment_name = forms.CharField( label=_('Segment Name'), widget=forms.TextInput( attrs={'readonly': 'readonly'}), required=False) name = forms.ChoiceField(label=_('Host Name'), choices=[]) reserved = forms.ChoiceField( label=_('Reserved'), choices=[('False', 'False'), ('True', 'True')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'available host'}), required=False, help_text=_("A boolean indicating whether this host is reserved or" " not. Default value is set to False.")) type = forms.CharField( label=_('Type'), widget=forms.TextInput(attrs={'maxlength': 255}), help_text=_("Type of host.")) control_attributes = forms.CharField( label=_('Control Attribute'), widget=forms.TextInput(), help_text=_("Attributes to control host.")) on_maintenance = forms.ChoiceField( label=_('On Maintenance'), choices=[('False', 'False'), ('True', 'True')], widget=forms.Select( attrs={'class': 'switchable', 'data-slug': 'available host'}), required=False, help_text=_("A boolean indicating whether this host is on maintenance" " or not. Default value is set to False.")) def __init__(self, *args, **kwargs): super(AddHostForm, self).__init__(*args, **kwargs) # Populate candidate name choices available_host_list = kwargs.get('initial', {}).get( "available_host_list", []) host_candidate_list = [] # NOTE(pas-ha) available_host_list contains # novaclient v2 Service objects for service in available_host_list: host_candidate_list.append( (service.host, '%(name)s (%(id)s)' % {"name": service.host, "id": service.id})) if host_candidate_list: host_candidate_list.insert(0, ("", _("Select a host"))) else: host_candidate_list.insert(0, ("", _("No host available"))) self.fields['name'].choices = host_candidate_list def handle(self, request, data): try: api.create_host(request, data) msg = _('Host created successfully.') messages.success(request, msg) except Exception: msg = _('Failed to create host.') redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(request, msg, redirect=redirect) return True ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/panel.py0000664000175000017500000000154315163173622023164 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.utils.translation import gettext_lazy as _ import horizon from masakaridashboard import dashboard class Segment(horizon.Panel): name = _("Segments") slug = 'segments' dashboard.MasakariDashboard.register(Segment) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/tables.py0000664000175000017500000000702615163173622023341 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.template import defaultfilters as filters from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.utils.translation import ngettext_lazy from masakaridashboard.api import api from horizon import tables class AddHost(tables.LinkAction): name = "add_host" verbose_name = _("Add Host") classes = ("ajax-modal",) def get_link_url(self, datum): obj_id = datum.uuid url = "horizon:masakaridashboard:segments:addhost" return reverse(url, args=[obj_id]) class CreateSegment(tables.LinkAction): name = "create" verbose_name = _("Create Segment") url = "horizon:masakaridashboard:segments:create_segment" classes = ("ajax-modal",) icon = "plus" SEGMENT_FILTER_CHOICES = ( ('recovery_method', _("Recovery Method ="), True), ('service_type', _("Service Type ="), True), ('is_enabled', _("Enabled ="), True, _('e.g. Yes/No')), ) class SegmentFilterAction(tables.FilterAction): filter_type = "server" filter_choices = SEGMENT_FILTER_CHOICES class DeleteSegment(tables.DeleteAction): @staticmethod def action_present(count): return ngettext_lazy( "Delete Segment", "Delete Segments", count ) @staticmethod def action_past(count): return ngettext_lazy( "Deleted Segment", "Deleted Segments", count ) def delete(self, request, segment_uuid): api.segment_delete(request, segment_uuid, ignore_missing=True) class UpdateSegment(tables.LinkAction): name = "update" verbose_name = _("Update Segment") classes = ("ajax-modal",) def get_link_url(self, datum): obj_id = datum.uuid url = "horizon:masakaridashboard:segments:update" return reverse(url, args=[obj_id]) class FailoverSegmentTable(tables.DataTable): name = tables.WrappingColumn( 'name', verbose_name=_("Name"), link="horizon:masakaridashboard:segments:detail", truncate=40) uuid = tables.Column('uuid', verbose_name=_("UUID"), link="horizon:masakaridashboard:segments:detail") recovery_method = tables.Column( 'recovery_method', verbose_name=_("Recovery Method")) service_type = tables.Column( 'service_type', verbose_name=_("Service Type")) description = tables.WrappingColumn( 'description', verbose_name=_("Description"), truncate=40) is_enabled = tables.Column('is_enabled', verbose_name=_('Enabled'), status=True, filters=(filters.yesno, filters.capfirst)) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "failover_segment" verbose_name = _("FailoverSegment") table_actions = (DeleteSegment, CreateSegment, SegmentFilterAction) row_actions = (UpdateSegment, AddHost) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/tabs.py0000664000175000017500000000335415163173622023020 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 tabs from masakaridashboard.api import api from masakaridashboard.hosts import tables as host_table class OverviewTab(tabs.Tab): name = _("Segment") slug = "segment" template_name = ("masakaridashboard/segments/_detail_overview.html") def get_context_data(self, request): return {"segment": self.tab_group.kwargs['segment']} class HostTab(tabs.TableTab): table_classes = (host_table.HostTable,) name = _("Hosts") slug = "host_tab" template_name = "horizon/common/_detail_table.html" preload = False def get_host_data(self): segment_data = self.tab_group.kwargs['segment_id'] if len(segment_data.split(',')) > 1: segment_id = segment_data.split(',')[1] else: segment_id = segment_data host_list = [] host_gen = api.get_host_list(self.request, segment_id, filters={}) for item in host_gen: host_list.append(item) return host_list class SegmentDetailTabs(tabs.DetailTabsGroup): slug = "segment_details" tabs = (OverviewTab, HostTab) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3607314 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/0000775000175000017500000000000015163173704023507 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3747334 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/0000775000175000017500000000000015163173704025334 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/_addhost.html0000664000175000017500000000110415163173622030002 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Create a Host under a given segment with name, type and control_attributes."%}

{% trans "Reserved : User can set specific host as reserved by checking on reserved parameter. On this particular host, compute service must be disabled. Default value is set to False." %}

{% trans "On Maintenance: Boolean parameter indicating whether this host is under maintenance or not. Default value is set to False." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/_create.html0000664000175000017500000000207415163173622027626 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block form_attrs %}enctype="multipart/form-data"{% endblock %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Create a failover segment of hypervisor hosts." %}

{% trans "Recovery methods:" %}

{% trans "auto: Nova selects the new compute host for evacuation of instances running on a failed compute host" %}

{% trans "reserved_host : One of the reserved host configured in the segment will be used for evacuation of instances running on a failed compute host" %}

{% trans "auto_priority: First it will try 'auto' recovery method, if it's fails, then it will try using 'reserved_host' recovery method." %}

{% trans "rh_priority: It is exactly opposite of 'auto_priority' recovery method." %}

{% trans "Please note: " %} {% trans "Service Type is presently not used by Masakari but it's a mandatory field so the default value is set to 'compute' and it cannot be changed." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/_detail_overview.html0000664000175000017500000000170215163173622031550 0ustar00zuulzuul{% load i18n sizeformat parse_date %}

{% trans "Segment Detail" %}


{% trans "Name" %}
{{ segment.name }}
{% trans "ID" %}
{{ segment.id }}
{% trans "Description" %}
{{ segment.description }}
{% trans "Recovery Method" %}
{{ segment.recovery_method }}
{% trans "Service Type" %}
{{ segment.service_type }}
{% trans "Enabled" %}
{{ segment.is_enabled|yesno|capfirst }}
{% trans "Created at" %}
{{ segment.created_at|parse_isotime }}
{% trans "Updated at" %}
{{ segment.updated_at|parse_isotime }}
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/_update.html0000664000175000017500000000035115163173622027641 0ustar00zuulzuul{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Modify name, recovery_method and description of a failover segment." %}

{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/addhost.html0000664000175000017500000000030215163173622027642 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Add Host" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segment/_addhost.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/create.html0000664000175000017500000000030315163173622027460 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Validate Segment" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segments/_create.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/index.html0000664000175000017500000000037315163173622027333 0ustar00zuulzuul{% extends 'masakaridashboard/default/table.html' %} {% load i18n %} {% block title %}{% trans "Segments" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Segments") %} {% endblock page_header %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/templates/segments/update.html0000664000175000017500000000030015163173622027474 0ustar00zuulzuul{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Segment" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segment/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/tests.py0000664000175000017500000002302715163173622023230 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from urllib import parse from django.conf import settings from django.test.utils import override_settings from django.urls import reverse from openstack_dashboard.test import helpers from masakaridashboard.segments import tables as segment_table from masakaridashboard.test import helpers as test INDEX_URL = reverse('horizon:masakaridashboard:segments:index') CREATE_URL = reverse('horizon:masakaridashboard:segments:create_segment') class SegmentTest(test.TestCase): def test_index(self): with mock.patch( 'masakaridashboard.api.api.get_segment_list', return_value=[self.masakari_segment.list(), False, False]) as mock_get_segment_list: res = self.client.get(INDEX_URL) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html') mock_get_segment_list.assert_called_once_with( filters={}, marker=None, paginate=True, request=helpers.IsHttpRequest()) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, self.masakari_segment.list()) def test_create_get(self): res = self.client.get(CREATE_URL) self.assertTemplateUsed(res, 'masakaridashboard/segments/create.html') def test_create_post(self): segment = self.masakari_segment.list()[0] form_data = { 'name': segment.name, 'recovery_method': segment.recovery_method, 'service_type': segment.service_type, 'description': segment.description, 'is_enabled': segment.is_enabled, } with mock.patch('masakaridashboard.api.api.segment_create', return_value=segment) as mocked_create: res = self.client.post(CREATE_URL, form_data) self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) self.assertRedirectsNoFollow(res, INDEX_URL) mocked_create.assert_called_once_with( helpers.IsHttpRequest(), form_data ) def _test_segments_index_paginated( self, filters, marker, segments, url, has_more, has_prev): with mock.patch( 'masakaridashboard.api.api.get_segment_list', return_value=[segments, has_more, has_prev]) as mock_get_segment_list: res = self.client.get(parse.unquote(url)) self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html') mock_get_segment_list.assert_called_once_with( filters=filters, marker=marker, paginate=True, request=helpers.IsHttpRequest()) return res @override_settings(API_RESULT_PAGE_SIZE=1) @mock.patch('masakaridashboard.api.api.get_segment') def test_segments_index_paginated(self, mock_get_segment): mock_get_segment.return_value = self.masakari_segment.list()[0] segment_list = self.masakari_segment.list() size = settings.API_RESULT_PAGE_SIZE base_url = INDEX_URL next = segment_table.FailoverSegmentTable._meta.pagination_param # get first page expected_segments = segment_list[:size] res = self._test_segments_index_paginated(filters={}, marker=None, segments=expected_segments, url=base_url, has_more=True, has_prev=False) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, expected_segments) # get second page expected_segments = segment_list[size:2 * size] marker = expected_segments[0].id url = base_url + "?%s=%s" % (next, marker) res = self._test_segments_index_paginated(filters={}, marker=marker, segments=expected_segments, url=url, has_more=True, has_prev=True) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, expected_segments) # get last page expected_segments = segment_list[-size:] marker = expected_segments[0].id url = base_url + "?%s=%s" % (next, marker) res = self._test_segments_index_paginated(filters={}, marker=marker, segments=expected_segments, url=url, has_more=False, has_prev=True) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, expected_segments) @override_settings(API_RESULT_PAGE_SIZE=1) def test_segments_index_paginated_prev_page(self): segment_list = self.masakari_segment.list() size = settings.API_RESULT_PAGE_SIZE base_url = INDEX_URL prev = segment_table.FailoverSegmentTable._meta.prev_pagination_param # prev from some page expected_segments = segment_list[size:2 * size] marker = expected_segments[0].id url = base_url + "?%s=%s" % (prev, marker) res = self._test_segments_index_paginated(filters={}, marker=marker, segments=expected_segments, url=url, has_more=True, has_prev=True) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, expected_segments) # back to first page expected_segments = segment_list[:size] marker = expected_segments[0].id url = base_url + "?%s=%s" % (prev, marker) res = self._test_segments_index_paginated( filters={}, marker=marker, segments=expected_segments, url=url, has_more=True, has_prev=False) segments = res.context['failover_segment_table'].data self.assertCountEqual(segments, expected_segments) def test_delete_ok(self): segment = self.masakari_segment.list()[0] data = {'object_ids': [segment.uuid], 'action': 'failover_segment__delete'} with mock.patch( 'masakaridashboard.api.api.get_segment_list', return_value=(self.masakari_segment.list(), True, True) ), mock.patch( 'masakaridashboard.api.api.segment_delete', return_value=None ) as mocked_delete: res = self.client.post(INDEX_URL, data) self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) mocked_delete.assert_called_once_with( helpers.IsHttpRequest(), segment.uuid, ignore_missing=True ) def test_detail(self): segment = self.masakari_segment.list()[0] detail_url = reverse('horizon:masakaridashboard:segments:detail', args=[segment.uuid]) with mock.patch('masakaridashboard.api.api.get_segment', return_value=segment): res = self.client.get(detail_url) self.assertNoFormErrors(res) self.assertEqual(200, res.status_code) self.assertEqual(segment.uuid, res.context['segment'].uuid) self.assertTemplateUsed(res, 'horizon/common/_detail.html') self.assertTemplateUsed( res, 'masakaridashboard/segments/_detail_overview.html') def test_update(self): segment_obj = self.masakari_segment.list()[0] update_url = reverse('horizon:masakaridashboard:segments:update', args=[segment_obj.uuid]) segment_obj.name = 'fake' form_data = { 'uuid': segment_obj.uuid, 'name': segment_obj.name, 'recovery_method': segment_obj.recovery_method, 'description': segment_obj.description, 'is_enabled': segment_obj.is_enabled, } with mock.patch( 'masakaridashboard.api.api.get_segment', return_value=self.masakari_segment.list()[0]), mock.patch( 'masakaridashboard.api.api.segment_update', return_value=segment_obj) as mocked_update: res = self.client.post(update_url, form_data) self.assertNoFormErrors(res) self.assertEqual(res.status_code, 302) self.assertRedirectsNoFollow(res, INDEX_URL) data_to_update = { 'name': segment_obj.name, 'recovery_method': segment_obj.recovery_method, 'description': segment_obj.description, 'is_enabled': segment_obj.is_enabled, } mocked_update.assert_called_once_with( helpers.IsHttpRequest(), segment_obj.uuid, data_to_update ) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/urls.py0000664000175000017500000000221215163173622023044 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.urls import re_path from masakaridashboard.segments import views SEGMENT = r'^(?P[^/]+)/%s$' urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(r'^create_segment$', views.CreateSegmentView.as_view(), name='create_segment'), re_path(SEGMENT % 'detail', views.DetailView.as_view(), name='detail'), re_path(SEGMENT % 'update', views.UpdateView.as_view(), name='update'), re_path(SEGMENT % 'addhost', views.AddHostView.as_view(), name='addhost'), ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/segments/views.py0000664000175000017500000002017215163173622023221 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.conf import settings from django.urls import reverse from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from horizon import tables from masakaridashboard.api import api from masakaridashboard.segments import tables as masakari_tab from horizon import exceptions from horizon import forms from horizon.utils import memoized from masakaridashboard.segments import forms as segment_forms from horizon import tabs from masakaridashboard.segments import tabs as seg_tab class IndexView(tables.DataTableView): table_class = masakari_tab.FailoverSegmentTable template_name = 'masakaridashboard/segments/index.html' page_title = _("Segments") _more = False _prev = False def needs_filter_first(self, table): return self._needs_filter_first def has_more_data(self, table): return self._more def has_prev_data(self, table): return self._prev def get_data(self): segments = [] marker = self.request.GET.get( masakari_tab.FailoverSegmentTable._meta.pagination_param, None ) if marker is not None: segment = api.get_segment(self.request, marker) marker = segment.id filters = self.get_filters() self._needs_filter_first = True filter_first = getattr(settings, 'FILTER_DATA_FIRST', {}) if filter_first.get('masakaridashboard.segments', False) and len( filters) == 0: self._needs_filter_first = True self._more = False return segments try: segments, self._more, self._prev = api.get_segment_list( request=self.request, marker=marker, filters=filters, paginate=True ) except Exception: self._prev = False self._more = False msg = _('Unable to retrieve segment list.') exceptions.handle(self.request, msg) return segments class CreateSegmentView(forms.ModalFormView): template_name = 'masakaridashboard/segments/create.html' modal_header = _("Create Segment") form_id = "create_segment" form_class = segment_forms.CreateSegmentForm submit_label = _("Create") submit_url = reverse_lazy( "horizon:masakaridashboard:segments:create_segment") success_url = reverse_lazy("horizon:masakaridashboard:segments:index") page_title = _("Create Segment") def get_form_kwargs(self): kwargs = super(CreateSegmentView, self).get_form_kwargs() return kwargs class DetailView(tabs.TabbedTableView): tab_group_class = seg_tab.SegmentDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ segment.name|default:segment.id }}" def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) segment = self.get_data() table = masakari_tab.FailoverSegmentTable(self.request) context["segment"] = segment context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(segment) return context @memoized.memoized_method def get_data(self): try: segment_data = self.kwargs['segment_id'] if len(segment_data.split(',')) > 1: segment_id = segment_data.split(',')[1] else: segment_id = segment_data segment = api.get_segment(self.request, segment_id) except Exception: msg = _('Unable to get segment "%s".') % segment_id redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(self.request, msg, redirect=redirect) return segment def get_redirect_url(self): return reverse('horizon:masakaridashboard:segments:index') def get_tabs(self, request, *args, **kwargs): segment = self.get_data() return self.tab_group_class(request, segment=segment, **kwargs) class UpdateView(forms.ModalFormView): template_name = 'masakaridashboard/segments/update.html' modal_header = _("Update Segment") form_id = "update_segment" form_class = segment_forms.UpdateForm submit_label = _("Update") submit_url = "horizon:masakaridashboard:segments:update" success_url = reverse_lazy("horizon:masakaridashboard:segments:index") page_title = _("Update Segment") @memoized.memoized_method def get_object(self): try: segment = api.get_segment(self.request, self.kwargs['segment_id']) return segment except Exception: msg = _('Unable to retrieve segment.') redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(self.request, msg, redirect=redirect) def get_context_data(self, **kwargs): context = super(UpdateView, self).get_context_data(**kwargs) context['submit_url'] = reverse( self.submit_url, args=[self.kwargs["segment_id"]] ) return context def get_initial(self, **kwargs): segment = self.get_object() return { 'uuid': self.kwargs['segment_id'], 'name': segment.name, 'recovery_method': segment.recovery_method, 'description': segment.description, 'is_enabled': segment.is_enabled, } class AddHostView(forms.ModalFormView): template_name = 'masakaridashboard/segments/addhost.html' modal_header = _("Add Host") form_id = "add_host" form_class = segment_forms.AddHostForm submit_label = _("Add Host") submit_url = "horizon:masakaridashboard:segments:addhost" success_url = reverse_lazy("horizon:masakaridashboard:hosts:index") page_title = _("Add Host") @memoized.memoized_method def get_object(self): segments = api.segment_list(self.request) host_list = [] for segment in segments: host_gen = api.get_host_list( self.request, segment.uuid, filters={}) for item in host_gen: host_list.append(item.name) try: available_host_list = [] service_list = api.get_compute_service_list(self.request) for service in service_list: if service.host not in host_list: available_host_list.append(service) return available_host_list except Exception: msg = _('Unable to retrieve host list.') redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(self.request, msg, redirect=redirect) def get_context_data(self, **kwargs): context = super(AddHostView, self).get_context_data(**kwargs) context['submit_url'] = reverse( self.submit_url, args=[self.kwargs["segment_id"]] ) return context def get_initial(self): available_host_list = self.get_object() segment_name = api.get_segment( self.request, self.kwargs['segment_id']).name initial = {'segment_id': self.kwargs['segment_id'], 'segment_name': segment_name, 'available_host_list': available_host_list, 'reserved': self.kwargs.get('reserved'), 'type': self.kwargs.get('service_type'), 'control_attributes': self.kwargs.get('control_attributes'), 'on_maintenance': self.kwargs.get('on_maintenance') } return initial ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3607314 masakari_dashboard-14.0.0/masakaridashboard/static/0000775000175000017500000000000015163173704021153 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3607314 masakari_dashboard-14.0.0/masakaridashboard/static/masakaridashboard/0000775000175000017500000000000015163173704024613 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3757336 masakari_dashboard-14.0.0/masakaridashboard/static/masakaridashboard/css/0000775000175000017500000000000015163173704025403 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/static/masakaridashboard/css/style.css0000664000175000017500000000126515163173622027260 0ustar00zuulzuul.masakari-wrapper.list{ list-style: inherit; } .masakari-wrapper #actions{ width:100%; } .masakari-wrapper #actions a.btn{ width:initial; } .masakari-wrapper.detail-screen .page-breadcrumb ol li{ max-width: inherit; } .masakari-wrapper.detail-screen .page-breadcrumb li:last-child{ display:none; } .masakari-wrapper .navbar-brand{ padding: 6px 10px; } .boolfield{ font-style: italic; } .boolfield i{ padding-right: .2em; } .boolfield i.green{ color: green; } .boolfield i.red{ color: red; } .line-space{ margin: .3em 0; } .line-space dd{ display:inline-block; margin-left: 1.5em; } ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3757336 masakari_dashboard-14.0.0/masakaridashboard/test/0000775000175000017500000000000015163173704020643 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/__init__.py0000664000175000017500000000000015163173622022741 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/helpers.py0000664000175000017500000000231215163173622022654 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from openstack_dashboard.test import helpers from masakaridashboard.test.test_data import utils class MasakariTestsMixin(object): def _setup_test_data(self): super(MasakariTestsMixin, self)._setup_test_data() utils.load_test_data(self) class TestCase(MasakariTestsMixin, helpers.TestCase): def setUp(self): allowed_patch = mock.patch( "masakaridashboard.dashboard.MasakariDashboard.allowed", return_value=True) allowed_patch.start() self.addCleanup(mock.patch.stopall) super().setUp() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/settings.py0000664000175000017500000000307515163173622023061 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Default to Horizons test settings to avoid any missing keys from horizon.test.settings import * # noqa: F403,H303 from openstack_dashboard.test.settings import * # noqa: F403,H303 # pop these keys to avoid log warnings about deprecation # update_dashboards will populate them anyway HORIZON_CONFIG.pop('dashboards', None) HORIZON_CONFIG.pop('default_dashboard', None) # Update the dashboards with masakaridashboard from masakaridashboard.local import enabled # noqa import openstack_dashboard.enabled # noqa from openstack_dashboard.utils import settings # noqa settings.update_dashboards( [ enabled, openstack_dashboard.enabled, ], HORIZON_CONFIG, INSTALLED_APPS ) # Ensure any duplicate apps are removed after the update_dashboards call INSTALLED_APPS = list(set(INSTALLED_APPS)) NOSE_ARGS = ['--nocapture', '--nologcapture', '--cover-package=masakaridashboard', '--cover-inclusive'] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3767338 masakari_dashboard-14.0.0/masakaridashboard/test/test_data/0000775000175000017500000000000015163173704022613 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/test_data/__init__.py0000664000175000017500000000000015163173622024711 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/test_data/masakari_data.py0000664000175000017500000000770115163173622025752 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime from oslo_utils import timeutils from openstack.instance_ha.v1 import host from openstack.instance_ha.v1 import notification from openstack.instance_ha.v1 import segment from openstack.instance_ha.v1 import vmove from openstack_dashboard.test.test_data import utils as test_data_utils from masakaridashboard.test import uuidsentinel from novaclient.v2.services import Service from novaclient.v2.services import ServiceManager NOW = timeutils.utcnow().replace(microsecond=0) def data(TEST): TEST.masakari_segment = test_data_utils.TestDataContainer() segment1 = segment.Segment( uuid=uuidsentinel.segment1, name='test', recovery_method='auto', service_type='service', description='demo', is_enabled=True, ) segment2 = segment.Segment( uuid=uuidsentinel.segment2, name='test2', recovery_method='auto', service_type='service', description='demo', is_enabled=False, ) segment3 = segment.Segment( uuid=uuidsentinel.segment3, name='test3', recovery_method='auto', service_type='service', description='demo', is_enabled=True, ) TEST.masakari_segment.add(segment1) TEST.masakari_segment.add(segment2) TEST.masakari_segment.add(segment3) TEST.masakari_host = test_data_utils.TestDataContainer() host1 = host.Host(uuid=uuidsentinel.host1, name="test", reserved='True', type='service', control_attributes='test', failover_segment_id=uuidsentinel.segment1, on_maintenance='False') TEST.masakari_host.add(host1) TEST.compute_services = test_data_utils.TestDataContainer() service1 = Service( ServiceManager, { "id": 1, "host": "test", } ) TEST.compute_services.add(service1) TEST.masakari_notification = test_data_utils.TestDataContainer() notification1 = notification.Notification( notification_uuid=uuidsentinel.notification1, status='new', generated_time=(NOW - datetime.timedelta(seconds=2)), payload='test', type='type1', source_host_uuid=uuidsentinel.host1) notification2 = notification.Notification( notification_uuid=uuidsentinel.notification2, status='running', generated_time=(NOW - datetime.timedelta(seconds=3)), payload='test', type='type2', source_host_uuid=uuidsentinel.host2) notification3 = notification.Notification( notification_uuid=uuidsentinel.notification3, status='error', generated_time=(NOW - datetime.timedelta(seconds=4)), payload='test', type='type3', source_host_uuid=uuidsentinel.host3) TEST.masakari_notification.add(notification1) TEST.masakari_notification.add(notification2) TEST.masakari_notification.add(notification3) TEST.masakari_vmove = test_data_utils.TestDataContainer() vmove1 = vmove.VMove( uuid=uuidsentinel.vmove1, notification_id=uuidsentinel.notification1, server_id=uuidsentinel.server1, server_name="vm", source_host='host1', dest_host='host2', start_time=(NOW - datetime.timedelta(seconds=3)), end_time=(NOW - datetime.timedelta(seconds=1)), status='succeeded', type='evacuation') TEST.masakari_vmove.add(vmove1) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/test_data/utils.py0000664000175000017500000000217115163173622024325 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from openstack_dashboard.test.test_data import utils def load_test_data(load_onto=None): from masakaridashboard.test.test_data import masakari_data from openstack_dashboard.test.test_data import exceptions # The order of these loaders matters, some depend on others. loaders = ( exceptions.data, masakari_data.data, ) if load_onto: for data_func in loaders: data_func(load_onto) return load_onto else: return utils.TestData(*loaders) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/test/uuidsentinel.py0000664000175000017500000000214215163173622023723 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import sys class UUIDSentinels(object): def __init__(self): from oslo_utils import uuidutils self._uuid_module = uuidutils self._sentinels = {} def __getattr__(self, name): if name.startswith('_'): raise ValueError('Sentinels must not start with _') if name not in self._sentinels: self._sentinels[name] = self._uuid_module.generate_uuid() return self._sentinels[name] sys.modules[__name__] = UUIDSentinels() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/version.py0000664000175000017500000000130415163173622021720 0ustar00zuulzuul# Copyright (C) 2018 NTT DATA # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version version_info = pbr.version.VersionInfo('masakaridashboard') ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.377734 masakari_dashboard-14.0.0/masakaridashboard/vmoves/0000775000175000017500000000000015163173704021203 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/__init__.py0000664000175000017500000000000015163173622023301 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/panel.py0000664000175000017500000000144715163173622022661 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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 masakaridashboard import dashboard class VMoves(horizon.Panel): name = _("VMoves") slug = 'vmoves' dashboard.MasakariDashboard.register(VMoves) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/tables.py0000664000175000017500000000410015163173622023021 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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 tables VMOVE_FILTER_CHOICES = ( ('notification_uuid', _("Notification UUId ="), True), ('type', _("Type ="), True), ('status', _("Status ="), True), ) class VMoveFilterAction(tables.FilterAction): filter_type = "server" filter_choices = VMOVE_FILTER_CHOICES class VMoveTable(tables.DataTable): uuid = tables.Column('uuid', verbose_name=_("UUID"), link="horizon:masakaridashboard:vmoves:detail") notification_id = tables.Column( 'notification_id', verbose_name=_("Notification UUID"), link="horizon:masakaridashboard:notifications:detail") server_id = tables.Column( 'server_id', verbose_name=_("Server ID")) server_name = tables.Column( 'server_name', verbose_name=_("Server Name")) type = tables.Column('type', verbose_name=_("Type")) source_host = tables.Column( 'source_host', verbose_name=_("Source Host")) dest_host = tables.Column( 'dest_host', verbose_name=_("Dest Host")) start_time = tables.Column( 'start_time', verbose_name=_("Start Time")) end_time = tables.Column( 'end_time', verbose_name=_("End Time")) status = tables.Column( 'status', verbose_name=_("Status")) def get_object_id(self, datum): return datum.notification_id + ',' + datum.uuid class Meta(object): name = "vmove" verbose_name = _("VMove") table_actions = (VMoveFilterAction,) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/tabs.py0000664000175000017500000000174715163173622022516 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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 tabs class OverviewTab(tabs.Tab): name = _("VMoves") slug = "vmoves" template_name = ("masakaridashboard/vmoves/_detail_overview.html") def get_context_data(self, request): return {"vmove": self.tab_group.kwargs['vmove']} class VMoveDetailTabs(tabs.DetailTabsGroup): slug = "vmove_details" tabs = (OverviewTab,) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3617315 masakari_dashboard-14.0.0/masakaridashboard/vmoves/templates/0000775000175000017500000000000015163173704023201 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.378734 masakari_dashboard-14.0.0/masakaridashboard/vmoves/templates/vmoves/0000775000175000017500000000000015163173704024520 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/templates/vmoves/_detail_overview.html0000664000175000017500000000203515163173622030734 0ustar00zuulzuul{% load i18n sizeformat parse_date %}

{% trans "VMove Detail" %}


{% trans "UUID" %}
{{ vmove.uuid }}
{% trans "Server ID" %}
{{ vmove.server_id }}
{% trans "Server Name" %}
{{ vmove.server_name }}
{% trans "Type" %}
{{ vmove.type }}
{% trans "Source Host" %}
{{ vmove.source_host }}
{% trans "Dest Host" %}
{{ vmove.dest_host }}
{% trans "Start Time" %}
{{ vmove.start_time }}
{% trans "End Time" %}
{{ vmove.end_time }}
{% trans "Status" %}
{{ vmove.status }}
{% trans "message" %}
{{ vmove.message }}
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/templates/vmoves/detail.html0000664000175000017500000000273615163173622026657 0ustar00zuulzuul {% extends 'masakaridashboard/default/base.html' %} {% load i18n %} {% block title %}{% trans "VMove Detail" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("VMove Detail") %} {% endblock page_header %} {% block main %} {% load i18n sizeformat parse_date %}

{% trans "VMove Detail" %}


{% trans "UUID" %}
{{ vmove.uuid }}
{% trans "Notification UUID" %}
{{ vmove.notification_uuid }}
{% trans "Server ID" %}
{{ vmove.server_id }}
{% trans "Server Name" %}
{{ vmove.server_name }}
{% trans "Type" %}
{{ vmove.type }}
{% trans "Source Host" %}
{{ vmove.source_host }}
{% trans "Dest Host" %}
{{ vmove.dest_host }}
{% trans "Start Time" %}
{{ vmove.start_time }}
{% trans "End Time" %}
{{ vmove.end_time }}
{% trans "Status" %}
{{ vmove.status }}
{% trans "Message" %}
{{ vmove.message }}
{% endblock %}
././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/templates/vmoves/index.html0000664000175000017500000000036715163173622026522 0ustar00zuulzuul{% extends 'masakaridashboard/default/table.html' %} {% load i18n %} {% block title %}{% trans "VMoves" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("VMoves") %} {% endblock page_header %} ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/tests.py0000664000175000017500000000412215163173622022715 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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 unittest import mock from django.urls import reverse from masakaridashboard.test import helpers as test INDEX_URL = reverse('horizon:masakaridashboard:vmoves:index') class VMoveTest(test.TestCase): def test_index(self): vmoves = self.masakari_vmove.list() notifications = self.masakari_notification.list() with mock.patch('masakaridashboard.api.api.get_notification_list', return_value=notifications), mock.patch( 'masakaridashboard.api.api.get_notification', return_value=notifications[0]), mock.patch( 'masakaridashboard.api.api.get_vmoves_list', return_value=vmoves): res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'masakaridashboard/vmoves/index.html') self.assertEqual(res.status_code, 200) def test_detail(self): vmove = self.masakari_vmove.list()[0] id_to_update = vmove.uuid + ',' + vmove.notification_id detail_url = reverse('horizon:masakaridashboard:vmoves:detail', args=[id_to_update]) with mock.patch('masakaridashboard.api.api.get_vmove', return_value=self.masakari_vmove.list()[0]): res = self.client.get(detail_url) self.assertNoFormErrors(res) self.assertEqual(200, res.status_code) self.assertTemplateUsed(res, 'horizon/common/_detail.html') self.assertTemplateUsed( res, 'masakaridashboard/vmoves/_detail_overview.html') ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/urls.py0000664000175000017500000000151115163173622022537 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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 masakaridashboard.vmoves import views VMOVE = r'^(?P[^/]+)/%s$' urlpatterns = [ re_path(r'^$', views.IndexView.as_view(), name='index'), re_path(VMOVE % 'detail', views.DetailView.as_view(), name='detail'), ] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/masakaridashboard/vmoves/views.py0000664000175000017500000000645415163173622022722 0ustar00zuulzuul# Copyright(c) 2022 Inspur # # 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.conf import settings from django.urls import reverse from django.utils.translation import gettext_lazy as _ from horizon import exceptions from horizon import tables from horizon import tabs from horizon.utils import memoized from masakaridashboard.api import api from masakaridashboard.vmoves import tables as masakari_tab from masakaridashboard.vmoves import tabs as vmove_tab class IndexView(tables.DataTableView): table_class = masakari_tab.VMoveTable template_name = 'masakaridashboard/vmoves/index.html' page_title = _("VMoves") def needs_filter_first(self, table): return self._needs_filter_first def get_data(self): notifications = api.get_notification_list(self.request) vmove_list = [] filters = self.get_filters() self._needs_filter_first = True filter_first = getattr(settings, 'FILTER_DATA_FIRST', {}) if filter_first.get('masakaridashboard.vmoves', False) and len( filters) == 0: self._needs_filter_first = True self._more = False return vmove_list for notification in notifications: if notification.type != "COMPUTE_HOST": continue vmove_gen = api.get_vmoves_list( self.request, notification.notification_uuid, filters) for item in vmove_gen: vmove_list.append(item) return vmove_list class DetailView(tabs.TabbedTableView): tab_group_class = vmove_tab.VMoveDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ vmove.server_name|default:vmove.server_name }}" def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) vmove = self.get_data() table = masakari_tab.VMoveTable(self.request) context["vmove"] = vmove context["url"] = self.get_redirect_url() context["actions"] = table.render_row_actions(vmove) return context @memoized.memoized_method def get_data(self): try: row_data = self.kwargs['vmove_id'].split(',') notification_id = row_data[1] vmove_id = row_data[0] vmove = api.get_vmove(self.request, vmove_id, notification_id) except Exception: msg = _('Unable to get vmove "%s".') % vmove_id redirect = reverse('horizon:masakaridashboard:vmoves:index') exceptions.handle(self.request, msg, redirect=redirect) return vmove def get_redirect_url(self): return reverse('horizon:masakaridashboard:vmoves:index') def get_tabs(self, request, *args, **kwargs): vmove = self.get_data() return self.tab_group_class(request, vmove=vmove, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/pyproject.toml0000664000175000017500000000227315163173622017143 0ustar00zuulzuul[build-system] requires = ["pbr>=6.1.1"] build-backend = "pbr.build" [project] name = "masakari-dashboard" description = "Horizon plugin for masakari" authors = [ {name = "OpenStack", email = "openstack-discuss@lists.openstack.org"}, ] readme = {file = "README.rst", content-type = "text/x-rst"} license = {text = "Apache-2.0"} dynamic = ["version", "dependencies"] requires-python = ">=3.10" classifiers = [ "Environment :: OpenStack", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", ] [project.urls] Homepage = "https://docs.openstack.org/masakari-dashboard/latest/" Repository = "https://opendev.org/openstack/masakari-dashboard" [tool.setuptools] packages = [ "masakaridashboard" ] ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3617315 masakari_dashboard-14.0.0/releasenotes/0000775000175000017500000000000015163173704016715 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1775040452.3797343 masakari_dashboard-14.0.0/releasenotes/notes/0000775000175000017500000000000015163173704020045 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000015163173622022315 0ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/blueprint-add-vmoves-37992efec8403393.yaml0000664000175000017500000000032715163173622027075 0ustar00zuulzuul--- features: - | Adds support for Masakari VMove API in microversion 1.3. `Blueprint vm-evacuations-for-host-recovery `__ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/blueprint-enable-to-segment-efdff66078dab752.yaml0000664000175000017500000000031215163173622030624 0ustar00zuulzuul--- features: - | Adds support for API microversion 1.2 with ``enabled`` flag for segments. `Blueprint enable-to-segment `__ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/bug-1944679-0df043f17a8bbaff.yaml0000664000175000017500000000037515163173622025005 0ustar00zuulzuul--- fixes: - | Fixed an issue with retreiving candidates for hosts. Now they are retreived using compute service list API, the same as used during host validation in Masakari itself. `LP#1944679 `__ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/drop-masakariclient-dep-054a456a5bf2b941.yaml0000664000175000017500000000013715163173622027556 0ustar00zuulzuul--- other: - | Masakari Dashboard no longer depends on the Masakari Client (OSC plugin). ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/drop-py-2-7-a5322c1cb7c74c61.yaml0000664000175000017500000000034215163173622025026 0ustar00zuulzuul--- upgrade: - | Python 2.7 support has been dropped. Last release of Masakari-dashboard to support python 2.7 is OpenStack Train. The minimum version of Python now supported by Masakari-dashboard is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/drop-python-38-and-39.yaml0000664000175000017500000000011015163173622024424 0ustar00zuulzuul--- upgrade: - | Support for Python 3.8 and 3.9 has been dropped. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/fix-cacert-023407ba61a4bb7a.yaml0000664000175000017500000000021215163173622025140 0ustar00zuulzuul--- fixes: - | Fixes an issue where the dashboard fails to load if communication with the API requires a custom CA certificate. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/notes/yamlify-policy-169e72bc8abd93a1.yaml0000664000175000017500000000044215163173622026174 0ustar00zuulzuul--- upgrade: - | Since Victoria, release JSON policy files are obsolete. In Masakari they were deprecated in Wallaby. The old JSON policy file is a valid YAML, but at least changing the file extension is needed. The JSON file is no longer used by default nor distributed. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.383735 masakari_dashboard-14.0.0/releasenotes/source/0000775000175000017500000000000015163173704020215 5ustar00zuulzuul././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2023.1.rst0000664000175000017500000000021015163173622021464 0ustar00zuulzuul=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2023.2.rst0000664000175000017500000000020215163173622021466 0ustar00zuulzuul=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2024.1.rst0000664000175000017500000000021015163173622021465 0ustar00zuulzuul=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: unmaintained/2024.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2024.2.rst0000664000175000017500000000020215163173622021467 0ustar00zuulzuul=========================== 2024.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2025.1.rst0000664000175000017500000000020215163173622021467 0ustar00zuulzuul=========================== 2025.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.1 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/2025.2.rst0000664000175000017500000000020215163173622021470 0ustar00zuulzuul=========================== 2025.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2025.2 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/conf.py0000664000175000017500000002211415163173622021513 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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. # Masakari dashboard Release Notes documentation build configuration file, # created by sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. # templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Masakari dashboard Release Notes' copyright = '2017, OpenStack Foundation' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # Release notes are version independent, no need to set version and release. version = '' # The full version, including alpha/beta/rc tags. release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # openstackdocstheme options openstackdocs_repo_name = 'openstack/masakari-dashboard' openstackdocs_auto_name = False openstackdocs_bug_project = 'masakari' openstackdocs_bug_tag = '' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Masakari dashboardReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'Masakari dashboardReleaseNotes.tex', 'Masakari dashboard Release Notes Documentation', 'Masakari dashboard Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'masakaridashboardreleasenotes', 'Masakari dashboard Release Notes Documentation', ['Masakari dashboard Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Masakari dashboardReleaseNotes', 'Masakari dashboard Release Notes Documentation', 'Masakari dashboard Developers', 'Masakari dashboardReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040403.0 masakari_dashboard-14.0.0/releasenotes/source/index.rst0000664000175000017500000000045115163173623022056 0ustar00zuulzuul========================================== masakaridashboard Release Notes ========================================== .. toctree:: :maxdepth: 1 unreleased 2025.2 2025.1 2024.2 2024.1 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train rocky ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022115163173622022070 0ustar00zuulzuul=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/train.rst0000664000175000017500000000017615163173622022067 0ustar00zuulzuul========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000016015163173622023072 0ustar00zuulzuul============================== Current Series Release Notes ============================== .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/ussuri.rst0000664000175000017500000000020215163173622022272 0ustar00zuulzuul=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/victoria.rst0000664000175000017500000000022015163173622022560 0ustar00zuulzuul============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: unmaintained/victoria ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/wallaby.rst0000664000175000017500000000021415163173622022376 0ustar00zuulzuul============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: unmaintained/wallaby ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/xena.rst0000664000175000017500000000020015163173622021671 0ustar00zuulzuul========================= Xena Series Release Notes ========================= .. release-notes:: :branch: unmaintained/xena ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/yoga.rst0000664000175000017500000000020015163173622021675 0ustar00zuulzuul========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/releasenotes/source/zed.rst0000664000175000017500000000017415163173622021532 0ustar00zuulzuul======================== Zed Series Release Notes ======================== .. release-notes:: :branch: unmaintained/zed ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/requirements.txt0000664000175000017500000000054615163173622017514 0ustar00zuulzuul# Requirements lower bounds listed here are our best effort to keep them up to # date but we do not test them so no guarantee of having them all correct. If # you find any incorrect lower bounds, let us know or propose a fix. pbr!=2.1.0,>=2.0.0 # Apache-2.0 # Horizon Core Requirements horizon>=17.1.0 # Apache-2.0 openstacksdk>=0.26.0 PyYAML>=3.12 # MIT ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1775040452.384735 masakari_dashboard-14.0.0/setup.cfg0000664000175000017500000000011415163173704016041 0ustar00zuulzuul[metadata] name = masakari-dashboard [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/setup.py0000664000175000017500000000123015163173622015731 0ustar00zuulzuul# Copyright (c) 2018 NTT DATA # # 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 setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/test-requirements.txt0000664000175000017500000000026015163173622020462 0ustar00zuulzuulhacking>=6.1.0,<6.2.0 # Apache-2.0 # Testing Requirements coverage!=4.4,>=4.0 # Apache-2.0 nodeenv>=0.9.4 # BSD selenium>=2.50.1 # Apache-2.0 xvfbwrapper>=0.1.3 #license: MIT ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1775040402.0 masakari_dashboard-14.0.0/tox.ini0000664000175000017500000000463415163173622015545 0ustar00zuulzuul[tox] envlist = pep8,py3 minversion = 3.18.0 [testenv] usedevelop = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python manage.py test {posargs} --settings=masakaridashboard.test.settings --verbosity 2 [testenv:linters] skip_install = True deps = {[testenv:pep8]deps} {[testenv:bandit]deps} commands = {[testenv:pep8]commands} {[testenv:bandit]commands} [testenv:pep8] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} hacking commands = flake8 {posargs} [testenv:bandit] skip_install = True deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} bandit commands = bandit -r masakaridashboard [testenv:venv] commands = {posargs} [testenv:cover] commands = coverage erase coverage run {toxinidir}/manage.py test masakaridashboard --settings=masakaridashboard.test.settings {posargs} coverage xml --omit '.tox/cover/*' -o 'cover/coverage.xml' coverage html --omit '.tox/cover/*' -d 'cover/htmlcov' [testenv:eslint] allowlist_externals = npm commands = npm install npm run postinstall npm run lint [testenv:karma] # NOTE(shu-mutou): Until PhantomJS setup get reliable, we use # Chromium for JS test. And npm can't launch Chromium via tox. #allowlist_externals = npm #commands = # npm install # npm run postinstall # npm run test allowlist_externals = echo commands = echo "npm can't launch Chromium via tox." echo "nexecute `npm run test`" [testenv:docs] deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] # There is no need to install horizon. skip_install = True deps = {[testenv:docs]deps} commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [flake8] # F405 TEMPLATES may be undefined, or defined from star imports # (because it is not easy to avoid this in openstack_dashboard.test.settings) # (W503 and W504 are incompatible and we need to choose one of them. # Existing codes follows W503, so we disable W504.) ignore = F405,W504 exclude = .venv,.git,.tox,dist,*lib/python*,*egg,build,node_modules max-complexity = 20