././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/0000775000175000017500000000000000000000000015331 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/.zuul.yaml0000664000175000017500000000056300000000000017276 0ustar00zuulzuul00000000000000- 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/AUTHORS0000664000175000017500000000225400000000000016404 0ustar00zuulzuul0000000000000098k <18552437190@163.com> Akihiro Motoki Andreas Jaeger Corey Bryant Ghanshyam Mann Hervé Beraud Ivan Kolodyazhny Mark Goddard Michiel Piscaer Nguyen Hai OpenStack Release Bot Pavlo Shchelokovskyy Radosław Piliszek Sean McGinnis Takashi Natsume Vadym Markov XinxinShen ZhongShengping chung00-lee dengzhaosen huang.zhiping jacky06 manchandavishal niraj singh nirajsingh nitesh.vanarase openstack openstack sunjia suzhengwei wu.shiming zhouguowei ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/CONTRIBUTING.rst0000664000175000017500000000117600000000000017777 0ustar00zuulzuul00000000000000The 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/ChangeLog0000664000175000017500000000611400000000000017105 0ustar00zuulzuul00000000000000CHANGES ======= 11.0.0 ------ * Update master for stable/2024.1 10.0.0 ------ * 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 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/HACKING.rst0000664000175000017500000000025000000000000017124 0ustar00zuulzuul00000000000000masakari-dashboard Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/LICENSE0000664000175000017500000002363700000000000016351 0ustar00zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/MANIFEST.in0000664000175000017500000000024700000000000017072 0ustar00zuulzuul00000000000000include AUTHORS include ChangeLog exclude .gitignore exclude .gitreview include setup.py recursive-include masakaridashboard *.js *.html *.scss global-exclude *.pyc ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/PKG-INFO0000664000175000017500000000677200000000000016442 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: masakari-dashboard Version: 11.0.0 Summary: Horizon plugin for masakari Home-page: https://opendev.org/openstack/masakari-dashboard Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: =============================== 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/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/README.rst0000664000175000017500000000422400000000000017022 0ustar00zuulzuul00000000000000=============================== 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/ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/doc/0000775000175000017500000000000000000000000016076 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/doc/Makefile0000664000175000017500000001276300000000000017547 0ustar00zuulzuul00000000000000# 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." ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/doc/requirements.txt0000664000175000017500000000121400000000000021360 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # Order matters to the pip dependency resolver, so sorting this file # changes how packages are installed. New dependencies should be # added in alphabetical order, however, some dependencies may need to # be installed in a specific order. # Docs Requirements sphinx>=2.0.0,!=2.1.0 # BSD openstackdocstheme>=2.2.1 # Apache-2.0 oslosphinx>=4.7.0 # Apache-2.0 reno>=3.1.0 # Apache-2.0 docutils>=0.11 # OSI-Approved Open Source, Public Domain ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/doc/source/0000775000175000017500000000000000000000000017376 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/doc/source/conf.py0000664000175000017500000002413200000000000020677 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/doc/source/contributor/0000775000175000017500000000000000000000000021750 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000356000000000000025215 0ustar00zuulzuul00000000000000============================ 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 `_. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/doc/source/index.rst0000664000175000017500000000453000000000000021241 0ustar00zuulzuul00000000000000=============================== 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/manage.py0000664000175000017500000000154100000000000017134 0ustar00zuulzuul00000000000000#!/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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/0000775000175000017500000000000000000000000022622 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/PKG-INFO0000664000175000017500000000677200000000000023733 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: masakari-dashboard Version: 11.0.0 Summary: Horizon plugin for masakari Home-page: https://opendev.org/openstack/masakari-dashboard Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: =============================== 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/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/SOURCES.txt0000664000175000017500000001077700000000000024522 0ustar00zuulzuul00000000000000.zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst manage.py 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/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/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././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/dependency_links.txt0000664000175000017500000000000100000000000026670 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/not-zip-safe0000664000175000017500000000000100000000000025050 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/pbr.json0000664000175000017500000000005600000000000024301 0ustar00zuulzuul00000000000000{"git_version": "30139f0", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/requires.txt0000664000175000017500000000010500000000000025216 0ustar00zuulzuul00000000000000PyYAML>=3.12 horizon>=17.1.0 openstacksdk>=0.26.0 pbr!=2.1.0,>=2.0.0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866874.0 masakari-dashboard-11.0.0/masakari_dashboard.egg-info/top_level.txt0000664000175000017500000000002200000000000025346 0ustar00zuulzuul00000000000000masakaridashboard ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/0000775000175000017500000000000000000000000020771 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/__init__.py0000664000175000017500000000000000000000000023070 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/api/0000775000175000017500000000000000000000000021542 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/api/__init__.py0000664000175000017500000000000000000000000023641 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/api/api.py0000664000175000017500000001561600000000000022676 0ustar00zuulzuul00000000000000# 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') 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, 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/conf/0000775000175000017500000000000000000000000021716 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/conf/masakari_policy.yaml0000664000175000017500000000055100000000000025752 0ustar00zuulzuul00000000000000--- 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/dashboard.py0000664000175000017500000000367500000000000023305 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/default/0000775000175000017500000000000000000000000022415 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/default/__init__.py0000664000175000017500000000000000000000000024514 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/default/panel.py0000664000175000017500000000143000000000000024064 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/default/templates/0000775000175000017500000000000000000000000024413 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/default/templates/default/0000775000175000017500000000000000000000000026037 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/default/templates/default/base.html0000664000175000017500000000034700000000000027643 0ustar00zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block css %} {% include "_stylesheets.html" %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/default/templates/default/table.html0000664000175000017500000000024100000000000030011 0ustar00zuulzuul00000000000000{% extends 'masakaridashboard/default/base.html' %} {% block main %}
{{ table.render }}
{% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/handle_errors.py0000664000175000017500000000570200000000000024176 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/hosts/0000775000175000017500000000000000000000000022131 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/__init__.py0000664000175000017500000000000000000000000024230 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/forms.py0000664000175000017500000000512700000000000023636 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/panel.py0000664000175000017500000000144200000000000023603 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/tables.py0000664000175000017500000000632300000000000023761 0ustar00zuulzuul00000000000000# 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( u"Delete Host", u"Delete Hosts", count ) @staticmethod def action_past(count): return ngettext_lazy( u"Deleted Host", u"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,) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/tabs.py0000664000175000017500000000173700000000000023444 0ustar00zuulzuul00000000000000# 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,) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/0000775000175000017500000000000000000000000024127 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/0000775000175000017500000000000000000000000025267 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/_detail_overview.html0000664000175000017500000000145700000000000031513 0ustar00zuulzuul00000000000000{% 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 }}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/_update.html0000664000175000017500000000036300000000000027600 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/detail.html0000664000175000017500000000224600000000000027423 0ustar00zuulzuul00000000000000 {% 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 %}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/index.html0000664000175000017500000000036500000000000027270 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/templates/hosts/update.html0000664000175000017500000000030200000000000027432 0ustar00zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Host" %}{% endblock %} {% block main %} {% include 'masakaridashboard/hosts/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/tests.py0000664000175000017500000001417700000000000023657 0ustar00zuulzuul00000000000000# 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 ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/urls.py0000664000175000017500000000161500000000000023473 0ustar00zuulzuul00000000000000# 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'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/hosts/views.py0000664000175000017500000001137000000000000023642 0ustar00zuulzuul00000000000000# 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} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/local/0000775000175000017500000000000000000000000022063 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/local/__init__.py0000664000175000017500000000000000000000000024162 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/local/enabled/0000775000175000017500000000000000000000000023455 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/local/enabled/_50_masakaridashboard.py0000664000175000017500000000160000000000000030127 0ustar00zuulzuul00000000000000# 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, } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/local/enabled/__init__.py0000664000175000017500000000000000000000000025554 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/local/local_settings.d/0000775000175000017500000000000000000000000025317 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/local/local_settings.d/_50_masakari.py0000664000175000017500000000135100000000000030124 0ustar00zuulzuul00000000000000# 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'}) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/notifications/0000775000175000017500000000000000000000000023642 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/__init__.py0000664000175000017500000000000000000000000025741 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/panel.py0000664000175000017500000000150000000000000025307 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/tables.py0000664000175000017500000000467400000000000025501 0ustar00zuulzuul00000000000000# 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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/tabs.py0000664000175000017500000000657100000000000025156 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/0000775000175000017500000000000000000000000025640 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6906583 masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/0000775000175000017500000000000000000000000030511 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/_detail_overview.html 22 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/_detail_overview.h0000664000175000017500000000200500000000000034206 0ustar00zuulzuul00000000000000{% 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 }}
././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/_progress_detail.html 22 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/_progress_detail.h0000664000175000017500000000065400000000000034214 0ustar00zuulzuul00000000000000{{ table.render }} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/templates/notifications/index.html0000664000175000017500000000040500000000000032505 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/tests.py0000664000175000017500000001310100000000000025352 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/urls.py0000664000175000017500000000156000000000000025203 0ustar00zuulzuul00000000000000# 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'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/notifications/views.py0000664000175000017500000001045400000000000025355 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/segments/0000775000175000017500000000000000000000000022616 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/__init__.py0000664000175000017500000000000000000000000024715 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/forms.py0000664000175000017500000001610500000000000024321 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/panel.py0000664000175000017500000000154300000000000024272 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/tables.py0000664000175000017500000000703200000000000024444 0ustar00zuulzuul00000000000000# 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( u"Delete Segment", u"Delete Segments", count ) @staticmethod def action_past(count): return ngettext_lazy( u"Deleted Segment", u"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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/tabs.py0000664000175000017500000000335400000000000024126 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/0000775000175000017500000000000000000000000024614 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/0000775000175000017500000000000000000000000026441 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/_addhost.html0000664000175000017500000000110400000000000031110 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/_create.html0000664000175000017500000000207400000000000030734 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/_detail_overview.html0000664000175000017500000000170200000000000032656 0ustar00zuulzuul00000000000000{% 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 }}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/_update.html0000664000175000017500000000035100000000000030747 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/addhost.html0000664000175000017500000000030200000000000030750 0ustar00zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Add Host" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segment/_addhost.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/create.html0000664000175000017500000000030300000000000030566 0ustar00zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Validate Segment" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segments/_create.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/index.html0000664000175000017500000000037300000000000030441 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/templates/segments/update.html0000664000175000017500000000030000000000000030602 0ustar00zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update Segment" %}{% endblock %} {% block main %} {% include 'masakaridashboard/segment/_update.html' %} {% endblock %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/tests.py0000664000175000017500000002302700000000000024336 0ustar00zuulzuul00000000000000# 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 ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/urls.py0000664000175000017500000000221200000000000024152 0ustar00zuulzuul00000000000000# 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'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/segments/views.py0000664000175000017500000002017200000000000024327 0ustar00zuulzuul00000000000000# 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/static/0000775000175000017500000000000000000000000022260 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/static/masakaridashboard/0000775000175000017500000000000000000000000025720 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/static/masakaridashboard/css/0000775000175000017500000000000000000000000026510 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/static/masakaridashboard/css/style.css0000664000175000017500000000126500000000000030366 0ustar00zuulzuul00000000000000.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; } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/test/0000775000175000017500000000000000000000000021750 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/__init__.py0000664000175000017500000000000000000000000024047 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/helpers.py0000664000175000017500000000231200000000000023762 0ustar00zuulzuul00000000000000# 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/settings.py0000664000175000017500000000307500000000000024167 0ustar00zuulzuul00000000000000# 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'] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/test/test_data/0000775000175000017500000000000000000000000023720 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/test_data/__init__.py0000664000175000017500000000000000000000000026017 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/test_data/masakari_data.py0000664000175000017500000000770100000000000027060 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/test_data/utils.py0000664000175000017500000000217100000000000025433 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/test/uuidsentinel.py0000664000175000017500000000214200000000000025031 0ustar00zuulzuul00000000000000# 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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/version.py0000664000175000017500000000130400000000000023026 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/vmoves/0000775000175000017500000000000000000000000022310 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/__init__.py0000664000175000017500000000000000000000000024407 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/panel.py0000664000175000017500000000144700000000000023767 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/tables.py0000664000175000017500000000410000000000000024127 0ustar00zuulzuul00000000000000# 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,) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/tabs.py0000664000175000017500000000174700000000000023624 0ustar00zuulzuul00000000000000# 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,) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/masakaridashboard/vmoves/templates/0000775000175000017500000000000000000000000024306 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/masakaridashboard/vmoves/templates/vmoves/0000775000175000017500000000000000000000000025625 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/templates/vmoves/_detail_overview.html0000664000175000017500000000203500000000000032042 0ustar00zuulzuul00000000000000{% 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 }}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/templates/vmoves/detail.html0000664000175000017500000000273600000000000027765 0ustar00zuulzuul00000000000000 {% 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 %}
././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/templates/vmoves/index.html0000664000175000017500000000036700000000000027630 0ustar00zuulzuul00000000000000{% 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 %} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/tests.py0000664000175000017500000000412200000000000024023 0ustar00zuulzuul00000000000000# 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') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/urls.py0000664000175000017500000000151100000000000023645 0ustar00zuulzuul00000000000000# 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'), ] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/masakaridashboard/vmoves/views.py0000664000175000017500000000645400000000000024030 0ustar00zuulzuul00000000000000# 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) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6866581 masakari-dashboard-11.0.0/releasenotes/0000775000175000017500000000000000000000000020022 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/releasenotes/notes/0000775000175000017500000000000000000000000021152 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000023423 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/blueprint-add-vmoves-37992efec8403393.yaml0000664000175000017500000000032700000000000030203 0ustar00zuulzuul00000000000000--- features: - | Adds support for Masakari VMove API in microversion 1.3. `Blueprint vm-evacuations-for-host-recovery `__ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/blueprint-enable-to-segment-efdff66078dab752.yaml0000664000175000017500000000031200000000000031732 0ustar00zuulzuul00000000000000--- features: - | Adds support for API microversion 1.2 with ``enabled`` flag for segments. `Blueprint enable-to-segment `__ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/bug-1944679-0df043f17a8bbaff.yaml0000664000175000017500000000037500000000000026113 0ustar00zuulzuul00000000000000--- 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 `__ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/drop-masakariclient-dep-054a456a5bf2b941.yaml0000664000175000017500000000013700000000000030664 0ustar00zuulzuul00000000000000--- other: - | Masakari Dashboard no longer depends on the Masakari Client (OSC plugin). ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/drop-py-2-7-a5322c1cb7c74c61.yaml0000664000175000017500000000034200000000000026134 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/fix-cacert-023407ba61a4bb7a.yaml0000664000175000017500000000021200000000000026246 0ustar00zuulzuul00000000000000--- fixes: - | Fixes an issue where the dashboard fails to load if communication with the API requires a custom CA certificate. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/notes/yamlify-policy-169e72bc8abd93a1.yaml0000664000175000017500000000044200000000000027302 0ustar00zuulzuul00000000000000--- 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. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/releasenotes/source/0000775000175000017500000000000000000000000021322 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/2023.1.rst0000664000175000017500000000020200000000000022573 0ustar00zuulzuul00000000000000=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/2023.2.rst0000664000175000017500000000020200000000000022574 0ustar00zuulzuul00000000000000=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/2024.1.rst0000664000175000017500000000020200000000000022574 0ustar00zuulzuul00000000000000=========================== 2024.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2024.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/conf.py0000664000175000017500000002211400000000000022621 0ustar00zuulzuul00000000000000# 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/'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/index.rst0000664000175000017500000000041300000000000023161 0ustar00zuulzuul00000000000000========================================== masakaridashboard Release Notes ========================================== .. toctree:: :maxdepth: 1 unreleased 2024.1 2023.2 2023.1 zed yoga xena wallaby victoria ussuri train rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000023176 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/train.rst0000664000175000017500000000017600000000000023175 0ustar00zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000016000000000000024200 0ustar00zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/ussuri.rst0000664000175000017500000000020200000000000023400 0ustar00zuulzuul00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/victoria.rst0000664000175000017500000000021200000000000023667 0ustar00zuulzuul00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: stable/victoria ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/wallaby.rst0000664000175000017500000000020600000000000023505 0ustar00zuulzuul00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: stable/wallaby ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/xena.rst0000664000175000017500000000017200000000000023007 0ustar00zuulzuul00000000000000========================= Xena Series Release Notes ========================= .. release-notes:: :branch: stable/xena ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/yoga.rst0000664000175000017500000000020000000000000023003 0ustar00zuulzuul00000000000000========================= Yoga Series Release Notes ========================= .. release-notes:: :branch: unmaintained/yoga ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/releasenotes/source/zed.rst0000664000175000017500000000016600000000000022641 0ustar00zuulzuul00000000000000======================== Zed Series Release Notes ======================== .. release-notes:: :branch: stable/zed ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/requirements.txt0000664000175000017500000000115100000000000020613 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # Order matters to the pip dependency resolver, so sorting this file # changes how packages are installed. New dependencies should be # added in alphabetical order, however, some dependencies may need to # be installed in a specific order. # # PBR should always appear first 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 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1727866874.6946588 masakari-dashboard-11.0.0/setup.cfg0000664000175000017500000000156400000000000017160 0ustar00zuulzuul00000000000000[metadata] name = masakari-dashboard summary = Horizon plugin for masakari description_file = README.rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://opendev.org/openstack/masakari-dashboard python_requires = >=3.8 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 [files] packages = masakaridashboard [build_sphinx] all_files = 1 build_dir = doc/build source_dir = doc/source warning_is_error = 1 [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/setup.py0000664000175000017500000000174500000000000017052 0ustar00zuulzuul00000000000000# 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/test-requirements.txt0000664000175000017500000000127200000000000021574 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # Order matters to the pip dependency resolver, so sorting this file # changes how packages are installed. New dependencies should be # added in alphabetical order, however, some dependencies may need to # be installed in a specific order. # # Hacking should appear first in case something else depends on pep8 hacking>=3.0.1,<3.1.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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1727866850.0 masakari-dashboard-11.0.0/tox.ini0000664000175000017500000000576400000000000016660 0ustar00zuulzuul00000000000000[tox] envlist = pep8,py36,py39 minversion = 3.18.0 ignore_basepython_conflict = True [testenv] basepython = python3 usedevelop = True setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master CLIENT_NAME=masakari-dashboard NOSE_WITH_OPENSTACK=1 NOSE_OPENSTACK_COLOR=1 NOSE_OPENSTACK_RED=0.05 NOSE_OPENSTACK_YELLOW=0.025 NOSE_OPENSTACK_SHOW_ELAPSED=1 deps = -c{env:TOX_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -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] # We need to install horizon dependencies to build module references deps = -c{env:TOX_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt -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 = -c{env:TOX_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/doc/requirements.txt 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