watcher-dashboard-3.0.0/0000775000175000017500000000000013656752140015127 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/doc/0000775000175000017500000000000013656752140015674 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/doc/requirements.txt0000664000175000017500000000042713656752043021165 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. openstackdocstheme>=1.31.2 # Apache-2.0 sphinx>=1.8.0,!=2.1.0 # BSD watcher-dashboard-3.0.0/doc/source/0000775000175000017500000000000013656752140017174 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/doc/source/conf.py0000775000175000017500000000746113656752043020510 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'openstackdocstheme', ] wsme_protocols = ['restjson'] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Watcher Dashboard' copyright = u'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. # # The short X.Y version. # The full version, including alpha/beta/rc tags. # release = # The short X.Y version. # version = watcher_version.version_info.version_string() # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['watcher.'] exclude_patterns = [ # The man directory includes some snippet files that are included # in other documents during the build but that should not be # included in the toctree themselves, so tell Sphinx to ignore # them when scanning for input files. 'man/footer.rst', 'man/general-options.rst', ] # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for man page output -------------------------------------------- # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' man_pages = [] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] # html_theme_options = {'incubating': True} # html_theme_options = {"show_other_versions": "True"} html_theme = 'openstackdocs' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'doc-watcher-dashboard.tex', u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 latex_use_xindy = False latex_domain_indices = False latex_elements = { 'makeindex': '', 'printindex': '', 'preamble': r'\setcounter{tocdepth}{3}', } # Example configuration for intersphinx: refer to the Python standard library. # intersphinx_mapping = {'http://docs.python.org/': None} # openstackdocstheme options repository_name = 'openstack/watcher-dashboard' bug_project = 'watcher-dashboard' bug_tag = '' watcher-dashboard-3.0.0/doc/source/install/0000775000175000017500000000000013656752140020642 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/doc/source/install/installation.rst0000664000175000017500000001063613656752043024105 0ustar zuulzuul00000000000000Installation ------------ First off, create a virtual environment and install the Horizon dependencies:: $ git clone https://github.com/openstack/horizon $ cd horizon $ python tools/install_venv.py We will refer to the folder you are now in as ````. If you want more details on how to install Horizon, you can have a look at the `Horizon documentation`_, especially their `quickstart tutorial`_. Then, you need to install Watcher Dashboard on the server running Horizon. To do so, you can issue the following commands:: $ git clone https://opendev.org/openstack/watcher-dashboard $ cd watcher-dashboard $ pip install -e . We will refer to the folder you are now in as ````. The next step is now to register the Watcher Dashboard plugins against your Horizon. To do so, you can execute the ``tools/register_plugin.sh``:: $ cd $ ./tools/register_plugin.sh . This script will then create the needed symlinks within Horizon so that it can load the Watcher plugin when it starts. If you wish to have Horizon running being an Apache server, do not forget to start the service via the following command:: $ sudo service apache2 restart For more details on how to configure Horizon for a production environment, you can refer to their online `installation guide`_. .. _Horizon documentation: https://docs.openstack.org/horizon/latest .. _quickstart tutorial: https://docs.openstack.org/horizon/latest/contributor/quickstart.html .. _installation guide: https://docs.openstack.org/horizon/latest/install/index.html DevStack setup -------------- Add the following to your DevStack ``local.conf`` file :: enable_plugin watcher-dashboard https://opendev.org/openstack/watcher-dashboard Unit testing ------------ First of all, you have to create an environment to run your tests in. This step is actually part of the ``run_tests.sh`` script which creates and maintains a clean virtual environment. Here below is the basic command to run Watcher Dashboard tests:: $ ./run_tests.sh The first time you will issue the command above, you will be asked if you want to create a virtual environment. So unless you have installed everything manually (in which case you should use the ``-N`` flag), you need to accept Integration testing ------------------- Before being able to run integration tests, you need to have a Horizon server running with Watcher Dashboard plugin configured. To do so, you can run a test server using the following command:: $ ./run_tests.sh --runserver 0.0.0.0:8000 By default, integration tests expect to find a running Horizon server at ``http://localhost:8000/`` but this can be customized by editing the ``watcher_dashboard/test/integration_tests/horizon.conf`` configuration file. Likewise, this Horizon will be looking, by default, for a Keystone backend at ``http://localhost:5000/v2.0``. So in order to customize its location, you will have to edit ``watcher_dashboard/test/settings.py`` by updating the ``OPENSTACK_KEYSTONE_URL`` variable. To run integration tests:: $ ./run_tests.sh --integration You can use PhantomJS as a headless browser to execute your integration tests. On an Ubuntu distribution you can install it via the following command:: $ sudo apt-get install phantomjs Then you can run your integration tests like this:: $ ./run_tests.sh --integration --selenium-headless Please note that these commands are also available via ``tox``. .. note:: As of the Mitaka release, the dashboard for watcher is now maintained outside of the Horizon codebase, in this repository. Policies -------- You can enable policies on Watcher ``Optimization`` panel, by updating in the ``/openstack_dashboard/settings.py`` configuration file the following parameters POLICY_FILES = { ... 'infra-optim': 'watcher_policy.json', } You can also update the file ``/openstack_dashboard/conf/watcher_policy.conf`` to customize your policies. Links ----- Watcher project: https://opendev.org/openstack/watcher/ Watcher at github: https://github.com/openstack/watcher Watcher at wiki.openstack.org: https://wiki.openstack.org/wiki/Watcher Launchpad project: https://launchpad.net/watcher Join us on IRC (Internet Relay Chat):: Network: Freenode (irc.freenode.net/watcher) Channel: #openstack-watcher Or send an email to openstack-discuss@lists.openstack.org using [watcher] in object watcher-dashboard-3.0.0/doc/source/install/index.rst0000664000175000017500000000005613656752043022506 0ustar zuulzuul00000000000000.. toctree:: :maxdepth: 1 installation watcher-dashboard-3.0.0/doc/source/index.rst0000664000175000017500000000342213656752043021040 0ustar zuulzuul00000000000000.. Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. You can view the license at: https://creativecommons.org/licenses/by/3.0/ ========================================== Welcome to Watcher Dashboard documentation ========================================== OpenStack Watcher provides a flexible and scalable resource optimization service for multi-tenant OpenStack-based clouds. Watcher provides a complete optimization loop—including everything from a metrics receiver, complex event processor and profiler, optimization processor and an action plan applier. This provides a robust framework to realize a wide range of cloud optimization goals, including the reduction of data center operating costs, increased system performance via intelligent virtual machine migration, increased energy efficiency and more! Watcher project consists of several source code repositories: * `watcher`_ - is the main repository. It contains code for Watcher API server, Watcher Decision Engine and Watcher Applier. * `python-watcherclient`_ - Client library and CLI client for Watcher. * `watcher-dashboard`_ - Watcher Horizon plugin. The documentation provided here is continually kept up-to-date based on the latest code, and may not represent the state of the project at any specific prior release. .. _watcher: https://opendev.org/openstack/watcher/ .. _python-watcherclient: https://opendev.org/openstack/python-watcherclient/ .. _watcher-dashboard: https://opendev.org/openstack/watcher-dashboard/ Install Guide ============= .. toctree:: :maxdepth: 1 install/index Developer Guide =============== .. toctree:: :maxdepth: 1 contributor/index Indices and tables ================== * :ref:`genindex` * :ref:`search` watcher-dashboard-3.0.0/doc/source/contributor/0000775000175000017500000000000013656752140021546 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000406613656752043025017 0ustar zuulzuul00000000000000.. Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. You can view the license at: https://creativecommons.org/licenses/by/3.0/ .. _contributing: ======================= Contributing to Watcher ======================= If you're interested in contributing to the Watcher project, the following will help get you started. Contributor License Agreement ----------------------------- .. index:: single: license; agreement In order to contribute to the Watcher project, you need to have signed OpenStack's contributor's agreement. .. seealso:: * https://docs.openstack.org/infra/manual/developers.html * https://wiki.openstack.org/wiki/CLA LaunchPad Project ----------------- Most of the tools used for OpenStack depend on a launchpad.net ID for authentication. After signing up for a launchpad account, join the "openstack" team to have access to the mailing list and receive notifications of important events. .. seealso:: * https://launchpad.net/ * https://launchpad.net/watcher * https://launchpad.net/watcher-dashboard * https://launchpad.net/~openstack Project Hosting Details ----------------------- Bug tracker https://launchpad.net/watcher-dashboard Blueprints https://blueprints.launchpad.net/watcher-dashboard Mailing list (prefix subjects with ``[watcher]`` for faster responses) http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss Wiki https://wiki.openstack.org/wiki/Watcher Code Hosting https://opendev.org/openstack/watcher-dashboard Code Review https://review.opendev.org/#/q/status:open+project:openstack/watcher-dashboard,n,z IRC Channel ``#openstack-watcher`` (changelog_) Weekly Meetings On Wednesdays at 14:00 UTC on even weeks in the ``#openstack-meeting-4`` IRC channel, 13:00 UTC on odd weeks in the ``#openstack-meeting-alt`` IRC channel (`meetings logs`_) .. _changelog: http://eavesdrop.openstack.org/irclogs/%23openstack-watcher/ .. _meetings logs: http://eavesdrop.openstack.org/meetings/watcher/ watcher-dashboard-3.0.0/doc/source/contributor/index.rst0000664000175000017500000000005613656752043023412 0ustar zuulzuul00000000000000.. toctree:: :maxdepth: 1 contributing watcher-dashboard-3.0.0/requirements.txt0000664000175000017500000000051613656752043020417 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 horizon>=18.2.0 # Apache-2.0 PyYAML>=3.12 # MIT python-watcherclient>=1.1.0 # Apache-2.0 watcher-dashboard-3.0.0/babel-django.cfg0000664000175000017500000000012313656752043020113 0ustar zuulzuul00000000000000[python: watcher_dashboard/**.py] [django: watcher_dashboard/**/templates/**.html] watcher-dashboard-3.0.0/setup.cfg0000664000175000017500000000200513656752140016745 0ustar zuulzuul00000000000000[metadata] name = watcher-dashboard summary = Watcher Management Dashboard description-file = README.rst author = OpenStack author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/watcher-dashboard/latest python-requires = >=3.6 classifier = Development Status :: 5 - Production/Stable Environment :: OpenStack Framework :: Django Intended Audience :: Developers Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: OS Independent Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Topic :: Internet :: WWW/HTTP [files] packages = watcher_dashboard [nosetests] verbosity = 2 detailed-errors = 1 [egg_info] tag_build = tag_date = 0 watcher-dashboard-3.0.0/tox.ini0000664000175000017500000000416013656752043016445 0ustar zuulzuul00000000000000[tox] minversion = 2.0 envlist = py37,py36,pep8 skipsdist = True [testenv] basepython = python3 usedevelop = True setenv = VIRTUAL_ENV={envdir} DJANGO_SETTINGS_MODULE=watcher_dashboard.test.settings # Note the hash seed is set to 0 until horizon can be tested with a # random hash seed successfully. PYTHONHASHSEED=0 whitelist_externals = /bin/bash rm find deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = rm -f .testrepository/times.dbm find . -type f -name "*.pyc" -delete find . -type d -name "__pycache__" -delete python manage.py test --settings=watcher_dashboard.test.settings \ --exclude-tag integration \ watcher_dashboard [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:docs] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} whitelist_externals = rm make commands = rm -rf doc/build/pdf sphinx-build -W --keep-going -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:debug] commands = oslo_debug_helper {posargs} [flake8] # F405 TEMPLATES may be undefined, or defined from star imports # (because it is not easy to avoid this in openstack_dashboard.test.settings) # W504 line break after binary operator ignore = F405,W504 show-source = True builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,.ropeproject,tools [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt watcher-dashboard-3.0.0/ChangeLog0000664000175000017500000001525013656752140016704 0ustar zuulzuul00000000000000CHANGES ======= 3.0.0 ----- * Cleanup py27 support * Fix pyScss version in lower-constraints.txt * s/assertItemsEqual/assertCountEqual/g * Drop Django 1.11 support * translation: drop babel extractor definitions * Drop python 2.7 support and testing * tox: Keeping going with docs and cleanup setup.cfg * Switch to official Ussuri jobs * Use Horizon project template for django jobs * TypeError exception when tests with Django22 * Update master for stable/train 2.0.0 ----- * Build pdf docs * Add blueprints link for README * Refactor error messages * Remove the unused code * Blacklist sphinx 2.1.0 (autodoc bug) * Add py37 in tox.ini and setup.cfg file * Switch to the new canonical constraints URL on master * Sync Sphinx requirement * Replace git.openstack.org URLs with opendev.org URLs * OpenDev Migration Patch * Remove py35 * Update master for stable/stein * Add python 3.7 unit test job for watcher-dashboard * Replace openstack.org git:// URLs with https:// 1.12.0 ------ * Update hacking version * Use template for lower-constraints * Update devel info: mailing list * Change openstack-dev to openstack-discuss * Removed older version of python added 3.5 * Update min tox version to 2.0 * Fix Invalid filter: parse\_isotime * switch documentation job to new PTI * import zuul job settings from project-config * Imported Translations from Zanata * Switch test runner to django default runner * Update reno for stable/rocky 1.11.0 ------ * fix audit delete failure: add allow func to filter audit * fix indicator exception * fix audit delete failure * Add name for audit: check if audit\_name already exists 1.10.0 ------ * Add name for audit * Set interval parameter to optional and add validation before audit create * Drop mox3 from test-requirements.txt * Add release note in README * Django 2.0 support and fix lower-constraints * fix tox python3 overrides * Explicitly use django\_nose.NoseTestSuiteRunner 1.9.0 ----- * Remove mox usage from api tests * Remove mox usage from audittemplate tests * Remove mox usage from strategy tests * Remove mox usage from goal tests * Updated from global requirements * add lower-constraints job * Delete the unnecessary '-' * Install horizon directly from pypi * Update links in README * Updated from global requirements * Imported Translations from Zanata * Fix url links and spelling error in docs * Imported Translations from Zanata * Update reno for stable/queens 1.8.0 ----- * Update unreachable link in installation doc 1.7.0 ----- * Updated from global requirements * Updated from global requirements * Cleanup test-requirements 1.6.0 ----- * Update audit\_template create help message * Remove setting of version/release from releasenotes * Updated from global requirements * Drop django\_openstack\_auth from requirements.txt * removed horizon from test\_requestent.txt * Remove the dulpicate description * Imported Translations from Zanata 1.5.0 ----- * Updated from global requirements * enable watcher-dashboard service in devstack * Add formatting marks in README.rst * Fix to use "." to source script files * Updated from global requirements * Add support for cron syntax * Update reno for stable/pike 1.4.0 ----- * Update url in README.rst * Update the documentation link for doc migration * Imported Translations from Zanata * Updated from global requirements 1.3.0 ----- * Updated from global requirements * Imported Translations from Zanata * Update docs to the new standards * Remove load url from future and fix gate * Add DS\_Store to gitignore * switch to openstackdocstheme * Add rm to whitelist\_externals in tox.ini 1.2.0 ----- * Updated from global requirements * Fix watcher-dashboard deployment error * Fixing Tox and optimize the link address * Updated from global requirements 1.1.0 ----- * Updated from global requirements * Add a button to create audit template in audit creat form * Fix exception error when creating audit without audit template * Adding horizon time filters to format the time in detail page * Replace default:"—" with default:\_("-") * Using enable\_plugin instend of enable\_service * Remove the extra required=True * Remove the empty file form.py * Updated from global requirements * [Fix gate]Update test requirement * Remove unused logging import * Updated from global requirements * Update the path of watcher dashboard enable file * Refactor the watcher dashboard enable file * Update reno for stable/ocata 1.0.0 ----- * Add [watcher] tag when using OpenStack ML 0.8.0 ----- * Updated from global requirements * Remove link to modindex * 'next\_uuid' Action field is replaced by 'parents' * Updated from global requirements * Remove support for py26 * Replaces yaml.load() with yaml.safe\_load() * Fix bad parsing of HTTP response * Removed unnecessary utf-8 encoding * Remove useless ddt requirements * Remove useless pytz requirements * Add page title for some panel table * Remove the pep8 ingore * Add reno for release notes management * Remove the 'MANIFEST.in' * Add Constraints support * Add auto\_trigger option in Audit creation form 0.7.0 ----- * Show team and repo badges on README * Moved README.rst content to installation.rst doc * Remove unnecessary watcher\_dashboard/models.py file * Updated from global requirements * ceilometer has been removed from openstack-dashboard 0.6.0 ----- * Updated from global requirements * Added the audit scope field in dashboard * Updated from global requirements 0.5.0 ----- * Added action details * Updated from global requirements * Clean the code by renaming 'type' parameters * Drop \*openstack/common\* in flake8 exclude * Add new audit fields in dashboard tables * Update table's actions * Fix py27 commands failed * Replace UUID reference by name * modify the home-page info with the developer documentation * interval param should be set to None by default * Replace audit\_filter by audit * Remove 'Go to Audit Template' from Audit view 0.4.0 ----- * Remove discover from test-requirements * Updated from global requirements * Updated from global requirements * Add policies for API access control to watcher-dashboard * Do not pass interval parameter for ONESHOT audit * Add support continuously-optimization * Revert "Initializes EfficacyIndicator attributes" * Initializes EfficacyIndicator attributes * Modify error message from 'action\_plan' to 'audit' * Rename type keyword across the project * Replaced UUID of goal with name * Added efficacy-related fields to the dashboard 0.3.0 ----- * Added Goals and Strategies to Dashboard * Add fix for ONESHOT type * Removed unused 'alarm' field * Fix api test about AuditTemplate * update test-requirements to get horizon faster 0.2.0 ----- * Renamed 'TRIGGERED' state to 'PENDING' 0.1.0 ----- * Watcher Dashboard - Initial commit * Added .gitreview watcher-dashboard-3.0.0/HACKING.rst0000664000175000017500000000445513656752043016737 0ustar zuulzuul00000000000000Contributing ============ The code repository is located at `OpenStack `__. Please go there if you want to check it out: git clone https://github.com/openstack/watcher-dashboard.git The list of bugs and blueprints is on Launchpad: ``__ We use OpenStack's Gerrit for the code contributions: ``__ and we follow the `OpenStack Gerrit Workflow `__. If you're interested in the code, here are some key places to start: * `watcher_dashboard/api.py `_ - This file contains all the API calls made to the Watcher API (through python-watcherclient). * `watcher_dashboard/infra_optim `_ - The Watcher Dashboard code is contained within this directory. Running tests ============= There are several ways to run tests for watcher-dashboard. Using ``tox``: This is the easiest way to run tests. When run, tox installs dependencies, prepares the virtual python environment, then runs test commands. The gate tests in gerrit usually also use tox to run tests. For available tox environments, see ``tox.ini``. By running ``run_tests.sh``: Tests can also be run using the ``run_tests.sh`` script, to see available options, run it with the ``--help`` option. It handles preparing the virtual environment and executing tests, but in contrast with tox, it does not install all dependencies, e.g. ``jshint`` must be installed before running the jshint testcase. Manual tests: To manually check watcher-dashboard, it is possible to run a development server for watcher-dashboard by running ``run_tests.sh --runserver``. To run the server with the settings used by the test environment: ``run_tests.sh --runserver 0.0.0.0:8000`` OpenStack Style Commandments ============================ - Step 1: Read https://www.python.org/dev/peps/pep-0008/ - Step 2: Read https://www.python.org/dev/peps/pep-0008/ again - Step 3: Read https://github.com/openstack/hacking/blob/master/HACKING.rst watcher-dashboard-3.0.0/releasenotes/0000775000175000017500000000000013656752140017620 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/notes/0000775000175000017500000000000013656752140020750 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000013656752043023223 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/notes/drop-py-2-7-198cca7f72d16655.yaml0000664000175000017500000000033313656752043025701 0ustar zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. Last release of watcher-dashboard to support py2.7 is OpenStack Train. The minimum version of Python now supported by watcher-dashboard is Python 3.6. watcher-dashboard-3.0.0/releasenotes/source/0000775000175000017500000000000013656752140021120 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/train.rst0000664000175000017500000000017613656752043022775 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train watcher-dashboard-3.0.0/releasenotes/source/_static/0000775000175000017500000000000013656752140022546 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000013656752043025021 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/conf.py0000664000175000017500000002166213656752043022430 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # Watcher Release Notes documentation build configuration file, created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Watcher Dashboard Release Notes' copyright = u'2017, Watcher Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # openstackdocstheme options repository_name = 'openstack/watcher-dashboard' bug_project = 'watcher-dashboard' 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 = {"show_other_versions": "True"} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # 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 = 'WatcherDashboardReleaseNotesdoc' # -- 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', 'WatcherDashboardReleaseNotes.tex', u'Watcher Dashboard Release Notes Documentation', u'Watcher 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', 'watcherdashboardreleasenotes', u'Watcher Dashboard Release Notes Documentation', [u'Watcher 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', 'WatcherDashboardReleaseNotes', u'Watcher Dashboard Release Notes Documentation', u'Watcher Developers', 'WatcherDashboardReleaseNotes', '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/'] watcher-dashboard-3.0.0/releasenotes/source/stein.rst0000664000175000017500000000022113656752043022771 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein watcher-dashboard-3.0.0/releasenotes/source/queens.rst0000664000175000017500000000022313656752043023151 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens watcher-dashboard-3.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000016013656752043024000 0ustar zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: watcher-dashboard-3.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022113656752043022776 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky watcher-dashboard-3.0.0/releasenotes/source/index.rst0000664000175000017500000000030313656752043022757 0ustar zuulzuul00000000000000=============================== Watcher Dashboard Release Notes =============================== .. toctree:: :maxdepth: 1 unreleased train stein rocky queens pike ocata watcher-dashboard-3.0.0/releasenotes/source/ocata.rst0000664000175000017500000000023013656752043022736 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata watcher-dashboard-3.0.0/releasenotes/source/_templates/0000775000175000017500000000000013656752140023255 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013656752043025530 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/0000775000175000017500000000000013656752140022357 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/en_GB/0000775000175000017500000000000013656752140023331 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013656752140025116 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000664000175000017500000000177313656752043030161 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: watcher-dashboard\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-08-15 08:11+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-08-17 09:34+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Current Series Release Notes" msgstr "Current Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Queens Series Release Notes" msgstr "Queens Series Release Notes" msgid "Rocky Series Release Notes" msgstr "Rocky Series Release Notes" msgid "Watcher Dashboard Release Notes" msgstr "Watcher Dashboard Release Notes" watcher-dashboard-3.0.0/releasenotes/source/locale/ko_KR/0000775000175000017500000000000013656752140023364 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/ko_KR/LC_MESSAGES/0000775000175000017500000000000013656752140025151 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po0000664000175000017500000000126513656752043030210 0ustar zuulzuul00000000000000# minwook-shin , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: Watcher Dashboard Release Notes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-03-01 11:28+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-07-31 01:05+0000\n" "Last-Translator: minwook-shin \n" "Language-Team: Korean (South Korea)\n" "Language: ko_KR\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=1; plural=0\n" msgid "Current Series Release Notes" msgstr "í„재 ě‹śë¦¬ě¦ ë¦´ë¦¬ě¦ ë…¸íŠ¸" msgid "Ocata Series Release Notes" msgstr "Ocata ě‹śë¦¬ě¦ ë¦´ë¦¬ě¦ ë…¸íŠ¸" watcher-dashboard-3.0.0/releasenotes/source/pike.rst0000664000175000017500000000021713656752043022604 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike watcher-dashboard-3.0.0/manage.py0000775000175000017500000000150113656752043016733 0ustar zuulzuul00000000000000#!/usr/bin/env python # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import sys from django.core.management import execute_from_command_line if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "watcher_dashboard.test.settings") execute_from_command_line(sys.argv) watcher-dashboard-3.0.0/PKG-INFO0000664000175000017500000000407513656752140016232 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: watcher-dashboard Version: 3.0.0 Summary: Watcher Management Dashboard Home-page: https://docs.openstack.org/watcher-dashboard/latest Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/watcher-dashboard.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on OpenStack Dashboard plugin for Watcher project ============================================== The Watcher dashboard is a Horizon plugin that will allow users to realize a wide range of cloud optimization goals. * Free software: Apache license * Documentation: https://docs.openstack.org/watcher-dashboard/latest * Source: https://opendev.org/openstack/watcher-dashboard * Bugs: https://bugs.launchpad.net/watcher-dashboard * Release Notes: https://docs.openstack.org/releasenotes/watcher-dashboard * Blueprints: https://blueprints.launchpad.net/watcher-dashboard Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: OpenStack Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP Requires-Python: >=3.6 watcher-dashboard-3.0.0/test-requirements.txt0000664000175000017500000000126413656752043021375 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. # Hacking already pins down pep8, pyflakes and flake8 hacking>=3.0,<3.1.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 mock>=2.0.0 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD selenium>=2.50.1 # Apache-2.0 testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT # This also needs xvfb library installed on your OS xvfbwrapper>=0.1.3 #license: MIT # Doc requirements openstackdocstheme>=1.31.2 # Apache-2.0 sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2 # BSD reno>=2.5.0 # Apache-2.0 watcher-dashboard-3.0.0/lower-constraints.txt0000664000175000017500000000515613656752043021376 0ustar zuulzuul00000000000000alabaster==0.7.10 appdirs==1.4.3 asn1crypto==0.24.0 Babel==2.5.3 certifi==2018.1.18 cffi==1.11.5 chardet==3.0.4 cliff==2.11.0 cmd2==0.8.1 contextlib2==0.5.5 coverage==4.0 cryptography==2.1.4 debtcollector==1.19.0 decorator==4.2.1 deprecation==2.0 Django==2.2 django-appconf==1.0.2 django-babel==0.6.2 django-compressor==2.2 django-pyscss==2.0.2 docutils==0.14 dogpile.cache==0.6.5 dulwich==0.19.0 extras==1.0.0 fasteners==0.14.1 fixtures==3.0.0 futurist==1.6.0 horizon==18.2.0 httplib2==0.10.3 idna==2.6 imagesize==1.0.0 iso8601==0.1.12 Jinja2==2.10 jmespath==0.9.3 jsonpatch==1.21 jsonpointer==2.0 jsonschema==2.6.0 keystoneauth1==3.4.0 linecache2==1.0.0 MarkupSafe==1.0 mock==2.0.0 monotonic==1.4 msgpack==0.5.6 munch==2.2.0 netaddr==0.7.19 netifaces==0.10.6 openstackdocstheme==1.31.2 openstacksdk==0.12.0 os-client-config==1.29.0 os-service-types==1.2.0 osc-lib==1.10.0 oslo.concurrency==3.26.0 oslo.config==5.2.0 oslo.i18n==3.20.0 oslo.policy==1.34.0 oslo.serialization==2.25.0 oslo.utils==3.36.0 osprofiler==2.3.0 packaging==17.1 pbr==2.0.0 Pint==0.8.1 prettytable==0.7.2 pycparser==2.18 Pygments==2.2.0 pymongo==3.6.1 pyOpenSSL==17.5.0 pyparsing==2.2.0 pyperclip==1.6.0 pyScss==1.3.7 python-cinderclient==5.0.0 python-glanceclient==2.9.1 python-keystoneclient==3.22.0 python-mimeparse==1.6.0 python-neutronclient==6.7.0 python-novaclient==10.1.0 python-subunit==1.0.0 python-swiftclient==3.5.0 python-watcherclient==1.1.0 pytz==2018.3 PyYAML==3.12 rcssmin==1.0.6 reno==2.5.0 requests==2.18.4 requestsexceptions==1.4.0 rfc3986==1.1.0 rjsmin==1.0.12 selenium==2.50.1 semantic-version==2.6.0 simplejson==3.13.2 six==1.11.0 snowballstemmer==1.2.1 Sphinx==1.6.2 sphinxcontrib-websupport==1.0.1 stevedore==1.28.0 testscenarios==0.4 testtools==2.2.0 traceback2==1.4.0 unittest2==1.1.0 urllib3==1.22 warlock==1.3.0 WebOb==1.7.4 wrapt==1.10.11 XStatic==1.0.1 XStatic-Angular==1.5.8.0 XStatic-Angular-Bootstrap==2.2.0.0 XStatic-Angular-FileUpload==12.0.4.0 XStatic-Angular-Gettext==2.3.8.0 XStatic-Angular-lrdragndrop==1.0.2.2 XStatic-Angular-Schema-Form==0.8.13.0 XStatic-Bootstrap-Datepicker==1.3.1.0 XStatic-Bootstrap-SCSS==3.3.7.1 XStatic-bootswatch==3.3.7.0 XStatic-D3==3.5.17.0 XStatic-Font-Awesome==4.7.0.0 XStatic-Hogan==2.0.0.2 XStatic-Jasmine==2.4.1.1 XStatic-jQuery==1.10.2.1 XStatic-JQuery-Migrate==1.2.1.1 XStatic-jquery-ui==1.12.0.1 XStatic-JQuery.quicksearch==2.0.3.1 XStatic-JQuery.TableSorter==2.14.5.1 XStatic-JSEncrypt==2.3.1.1 XStatic-mdi==1.4.57.0 XStatic-objectpath==1.2.1.0 XStatic-Rickshaw==1.5.0.0 XStatic-roboto-fontface==0.5.0.0 XStatic-smart-table==1.4.13.2 XStatic-Spin==1.2.5.2 XStatic-term.js==0.0.7.0 XStatic-tv4==1.2.7.0 xvfbwrapper==0.1.3 watcher-dashboard-3.0.0/AUTHORS0000664000175000017500000000356513656752140016210 0ustar zuulzuul00000000000000Akihiro Motoki Alexander Chadin Andreas Jaeger Anh Tran Antoine Cabot Cao Xuan Hoang David TARDIVEL Doug Hellmann Erik Olof Gunnar Andersson Flavio Percoco Ghanshyam Mann Ian Wienand Ivan Kolodyazhny Kim Bao Long Larry Rensing Li Wei M V P Nitesh Michal Arbet Nguyen Hai OpenStack Release Bot Qian Min Chen Rajiv Kumar ShangXiao Swapnil Kulkarni (coolsvap) Tony Breeds Vincent Francoise Vladimir Ostroverkhov Yumeng Bao YumengBao Yumeng_Bao avnish caoyuan chenke gaofei gujin huang.zhiping jacky06 licanwei lingyongxu malei manchandavishal melissaml pengyuesheng petergurinov qingszhao rajat29 ricolin zhangguoqing zhulingjie zhurong zte-hanrong watcher-dashboard-3.0.0/tools/0000775000175000017500000000000013656752140016267 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/tools/install_venv_common.py0000664000175000017500000001343713656752043022727 0ustar zuulzuul00000000000000# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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. """Provides methods needed by installation script for OpenStack development virtual environments. Since this script is used to bootstrap a virtualenv from the system's Python environment, it should be kept strictly compatible with Python 2.6. Synced in from openstack-common """ import optparse import os import subprocess import sys class InstallVenv(object): def __init__(self, root, venv, requirements, test_requirements, py_version, project): self.root = root self.venv = venv self.requirements = requirements self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): if sys.version_info < (2, 6): self.die("Need Python Version >= 2.6") def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE else: stdout = None proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) output = proc.communicate()[0] if check_exit_code and proc.returncode != 0: self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) return (output, proc.returncode) def run_command(self, cmd, redirect_output=True, check_exit_code=True): return self.run_command_with_code(cmd, redirect_output, check_exit_code)[0] def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): return Fedora( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) else: return Distro( self.root, self.venv, self.requirements, self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() def create_virtualenv(self, no_site_packages=True): """Creates the virtual environment and installs PIP. Creates the virtual environment and installs PIP only into the virtual environment. """ if not os.path.isdir(self.venv): print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) print('done.') else: print("venv already exists...") pass def pip_install(self, *args): self.run_command(['tools/with_venv.sh', 'pip', 'install', '--upgrade'] + list(args), redirect_output=False) def install_dependencies(self): print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and # setuptools and pbr self.pip_install('pip>=1.4') self.pip_install('setuptools') self.pip_install('pbr') self.pip_install('-r', self.requirements, '-r', self.test_requirements) def parse_args(self, argv): """Parses command-line arguments.""" parser = optparse.OptionParser() parser.add_option('-n', '--no-site-packages', action='store_true', help="Do not inherit packages from global Python " "install") return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): def check_cmd(self, cmd): return bool(self.run_command(['which', cmd], check_exit_code=False).strip()) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if self.check_cmd('easy_install'): print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): print('Succeeded') return else: print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' ' favorite package management tool' % self.project) class Fedora(Distro): """This covers all Fedora-based distributions. Includes: Fedora, RHEL, CentOS, Scientific Linux """ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() watcher-dashboard-3.0.0/tools/install_venv.py0000664000175000017500000000453413656752043021355 0ustar zuulzuul00000000000000# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2010 OpenStack Foundation # Copyright 2013 IBM Corp. # # 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 import install_venv_common as install_venv # noqa def print_help(venv, root): help = """ OpenStack development environment setup is complete. OpenStack development uses virtualenv to track and manage Python dependencies while in development and testing. To activate the OpenStack virtualenv for the extent of your current shell session you can run: $ . %s/bin/activate Or, if you prefer, you can run commands in the virtualenv on a case by case basis by running: $ %s/tools/with_venv.sh Also, make test will automatically use the virtualenv. """ print(help % (venv, root)) def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) if os.environ.get('tools_path'): root = os.environ['tools_path'] venv = os.path.join(root, '.venv') if os.environ.get('venv'): venv = os.environ['venv'] pip_requires = os.path.join(root, 'requirements.txt') test_requires = os.path.join(root, 'test-requirements.txt') py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = 'OpenStack' install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, py_version, project) options = install.parse_args(argv) install.check_python_version() install.check_dependencies() install.create_virtualenv(no_site_packages=options.no_site_packages) install.install_dependencies() print_help(venv, root) if __name__ == '__main__': main(sys.argv) watcher-dashboard-3.0.0/tools/register_plugin.sh0000775000175000017500000000143313656752043022033 0ustar zuulzuul00000000000000#!/bin/bash src_path=`cd "$1"; pwd` dest_path=`cd "$2"; pwd` # echo "$src_path --> $dest_path" for filepath in $src_path/watcher_dashboard/local/enabled/*.py; do filename=$(basename $filepath) if [ $filename != "__init__.py" ]; then echo $filepath src_filepath="`cd "$(dirname $filepath)"; pwd`/$filename" dest_filepath="$dest_path/openstack_dashboard/local/enabled/$filename" echo "$src_filepath --> $dest_filepath" ln -s $src_filepath $dest_filepath fi done policy_file_name='watcher_policy.json' src_policy_filepath=$src_path'/watcher_dashboard/conf/'$policy_file_name dest_policy_file=$dest_path'/openstack_dashboard/conf/'$policy_file_name echo "$src_policy_filepath --> $dest_policy_file" ln -s $src_policy_filepath $dest_policy_file watcher-dashboard-3.0.0/tools/with_venv.sh0000775000175000017500000000077213656752043020647 0ustar zuulzuul00000000000000#!/bin/bash TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} VENV_DIR=${VENV_NAME:-/../.venv} TOOLS=${TOOLS_PATH} VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} HORIZON_DIR=${TOOLS%/tools} # This horrible mangling of the PYTHONPATH is required to get the # babel-angular-gettext extractor to work. To fix this the extractor needs to # be packaged on pypi and added to global requirements. That work is in progress. export PYTHONPATH="$HORIZON_DIR" source ${VENV}/bin/activate && "$@" watcher-dashboard-3.0.0/README.rst0000664000175000017500000000147613656752043016630 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/watcher-dashboard.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on OpenStack Dashboard plugin for Watcher project ============================================== The Watcher dashboard is a Horizon plugin that will allow users to realize a wide range of cloud optimization goals. * Free software: Apache license * Documentation: https://docs.openstack.org/watcher-dashboard/latest * Source: https://opendev.org/openstack/watcher-dashboard * Bugs: https://bugs.launchpad.net/watcher-dashboard * Release Notes: https://docs.openstack.org/releasenotes/watcher-dashboard * Blueprints: https://blueprints.launchpad.net/watcher-dashboard watcher-dashboard-3.0.0/babel-djangojs.cfg0000664000175000017500000000012513656752043020452 0ustar zuulzuul00000000000000[javascript: watcher_dashboard/**.js] [angular: watcher_dashboard/**/static/**.html] watcher-dashboard-3.0.0/.zuul.yaml0000664000175000017500000000037213656752043017074 0ustar zuulzuul00000000000000- project: templates: - check-requirements - horizon-non-primary-django-jobs - openstack-lower-constraints-jobs - openstack-python3-ussuri-jobs-horizon - publish-openstack-docs-pti - release-notes-jobs-python3 watcher-dashboard-3.0.0/setup.py0000664000175000017500000000127113656752043016644 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) watcher-dashboard-3.0.0/watcher_dashboard.egg-info/0000775000175000017500000000000013656752140022265 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard.egg-info/dependency_links.txt0000664000175000017500000000000113656752140026333 0ustar zuulzuul00000000000000 watcher-dashboard-3.0.0/watcher_dashboard.egg-info/requires.txt0000664000175000017500000000011413656752140024661 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 horizon>=18.2.0 PyYAML>=3.12 python-watcherclient>=1.1.0 watcher-dashboard-3.0.0/watcher_dashboard.egg-info/pbr.json0000664000175000017500000000005613656752140023744 0ustar zuulzuul00000000000000{"git_version": "30bb75d", "is_release": true}watcher-dashboard-3.0.0/watcher_dashboard.egg-info/PKG-INFO0000664000175000017500000000407513656752140023370 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: watcher-dashboard Version: 3.0.0 Summary: Watcher Management Dashboard Home-page: https://docs.openstack.org/watcher-dashboard/latest Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/watcher-dashboard.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on OpenStack Dashboard plugin for Watcher project ============================================== The Watcher dashboard is a Horizon plugin that will allow users to realize a wide range of cloud optimization goals. * Free software: Apache license * Documentation: https://docs.openstack.org/watcher-dashboard/latest * Source: https://opendev.org/openstack/watcher-dashboard * Bugs: https://bugs.launchpad.net/watcher-dashboard * Release Notes: https://docs.openstack.org/releasenotes/watcher-dashboard * Blueprints: https://blueprints.launchpad.net/watcher-dashboard Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: OpenStack Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP Requires-Python: >=3.6 watcher-dashboard-3.0.0/watcher_dashboard.egg-info/top_level.txt0000664000175000017500000000002213656752140025011 0ustar zuulzuul00000000000000watcher_dashboard watcher-dashboard-3.0.0/watcher_dashboard.egg-info/not-zip-safe0000664000175000017500000000000113656752140024513 0ustar zuulzuul00000000000000 watcher-dashboard-3.0.0/watcher_dashboard.egg-info/SOURCES.txt0000664000175000017500000001743513656752140024163 0ustar zuulzuul00000000000000.zuul.yaml AUTHORS ChangeLog HACKING.rst LICENSE README.rst babel-django.cfg babel-djangojs.cfg lower-constraints.txt manage.py requirements.txt run_tests.sh setup.cfg setup.py test-requirements.txt tox.ini devstack/local.conf.example devstack/plugin.sh devstack/settings doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/contributor/contributing.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/install/installation.rst releasenotes/notes/.placeholder releasenotes/notes/drop-py-2-7-198cca7f72d16655.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po tools/install_venv.py tools/install_venv_common.py tools/register_plugin.sh tools/with_venv.sh watcher_dashboard/__init__.py watcher_dashboard/version.py watcher_dashboard.egg-info/PKG-INFO watcher_dashboard.egg-info/SOURCES.txt watcher_dashboard.egg-info/dependency_links.txt watcher_dashboard.egg-info/not-zip-safe watcher_dashboard.egg-info/pbr.json watcher_dashboard.egg-info/requires.txt watcher_dashboard.egg-info/top_level.txt watcher_dashboard/api/__init__.py watcher_dashboard/api/watcher.py watcher_dashboard/common/__init__.py watcher_dashboard/common/exceptions.py watcher_dashboard/conf/watcher_policy.json watcher_dashboard/content/__init__.py watcher_dashboard/content/action_plans/__init__.py watcher_dashboard/content/action_plans/panel.py watcher_dashboard/content/action_plans/tables.py watcher_dashboard/content/action_plans/tabs.py watcher_dashboard/content/action_plans/urls.py watcher_dashboard/content/action_plans/views.py watcher_dashboard/content/actions/__init__.py watcher_dashboard/content/actions/panel.py watcher_dashboard/content/actions/tables.py watcher_dashboard/content/actions/tabs.py watcher_dashboard/content/actions/urls.py watcher_dashboard/content/actions/views.py watcher_dashboard/content/audit_templates/__init__.py watcher_dashboard/content/audit_templates/forms.py watcher_dashboard/content/audit_templates/panel.py watcher_dashboard/content/audit_templates/tables.py watcher_dashboard/content/audit_templates/tabs.py watcher_dashboard/content/audit_templates/tests.py watcher_dashboard/content/audit_templates/urls.py watcher_dashboard/content/audit_templates/views.py watcher_dashboard/content/audits/__init__.py watcher_dashboard/content/audits/forms.py watcher_dashboard/content/audits/panel.py watcher_dashboard/content/audits/tables.py watcher_dashboard/content/audits/tabs.py watcher_dashboard/content/audits/urls.py watcher_dashboard/content/audits/views.py watcher_dashboard/content/goals/__init__.py watcher_dashboard/content/goals/panel.py watcher_dashboard/content/goals/tables.py watcher_dashboard/content/goals/tabs.py watcher_dashboard/content/goals/tests.py watcher_dashboard/content/goals/urls.py watcher_dashboard/content/goals/views.py watcher_dashboard/content/strategies/__init__.py watcher_dashboard/content/strategies/forms.py watcher_dashboard/content/strategies/panel.py watcher_dashboard/content/strategies/tables.py watcher_dashboard/content/strategies/tabs.py watcher_dashboard/content/strategies/tests.py watcher_dashboard/content/strategies/urls.py watcher_dashboard/content/strategies/views.py watcher_dashboard/local/__init__.py watcher_dashboard/local/enabled/_31000_goals_panel.py watcher_dashboard/local/enabled/_31010_strategies_panel.py watcher_dashboard/local/enabled/_31020_watcher_panelgroup.py watcher_dashboard/local/enabled/_31030_audit_templates_panel.py watcher_dashboard/local/enabled/_31040_audits_panel.py watcher_dashboard/local/enabled/_31050_action_plans_panel.py watcher_dashboard/local/enabled/_31060_actions_panel.py watcher_dashboard/local/enabled/__init__.py watcher_dashboard/static/infra_optim/images/chevron.png watcher_dashboard/static/infra_optim/images/power.png watcher_dashboard/static/infra_optim/scss/infra_optim.scss watcher_dashboard/static/infra_optim/tests/formset_table.js watcher_dashboard/templates/client_side/_modal_chart.html watcher_dashboard/templates/client_side/templates.html watcher_dashboard/templates/formset_table/_row.html watcher_dashboard/templates/formset_table/_table.html watcher_dashboard/templates/formset_table/menu_formset.html watcher_dashboard/templates/horizon/common/_items_count_domain_page_header.html watcher_dashboard/templates/infra_optim/_fullscreen_workflow.html watcher_dashboard/templates/infra_optim/_fullscreen_workflow_base.html watcher_dashboard/templates/infra_optim/_performance_chart.html watcher_dashboard/templates/infra_optim/_performance_chart_box.html watcher_dashboard/templates/infra_optim/_top_5_box.html watcher_dashboard/templates/infra_optim/_top_5_chart.html watcher_dashboard/templates/infra_optim/_workflow_base.html watcher_dashboard/templates/infra_optim/base.html watcher_dashboard/templates/infra_optim/base_detail.html watcher_dashboard/templates/infra_optim/header_actions.html watcher_dashboard/templates/infra_optim/qunit.html watcher_dashboard/templates/infra_optim/action_plans/_details_overview.html watcher_dashboard/templates/infra_optim/action_plans/create.html watcher_dashboard/templates/infra_optim/action_plans/details.html watcher_dashboard/templates/infra_optim/action_plans/index.html watcher_dashboard/templates/infra_optim/actions/details.html watcher_dashboard/templates/infra_optim/actions/index.html watcher_dashboard/templates/infra_optim/actions_history/details.html watcher_dashboard/templates/infra_optim/actions_history/index.html watcher_dashboard/templates/infra_optim/audit_templates/_create.html watcher_dashboard/templates/infra_optim/audit_templates/create.html watcher_dashboard/templates/infra_optim/audit_templates/details.html watcher_dashboard/templates/infra_optim/audit_templates/index.html watcher_dashboard/templates/infra_optim/audits/_create.html watcher_dashboard/templates/infra_optim/audits/create.html watcher_dashboard/templates/infra_optim/audits/details.html watcher_dashboard/templates/infra_optim/audits/index.html watcher_dashboard/templates/infra_optim/goals/details.html watcher_dashboard/templates/infra_optim/goals/index.html watcher_dashboard/templates/infra_optim/logs/index.html watcher_dashboard/templates/infra_optim/strategies/details.html watcher_dashboard/templates/infra_optim/strategies/index.html watcher_dashboard/templatetags/__init__.py watcher_dashboard/test/__init__.py watcher_dashboard/test/helpers.py watcher_dashboard/test/selenium.py watcher_dashboard/test/settings.py watcher_dashboard/test/test_formset_table.py watcher_dashboard/test/urls.py watcher_dashboard/test/api_tests/__init__.py watcher_dashboard/test/api_tests/test_watcher.py watcher_dashboard/test/integration_tests/__init__.py watcher_dashboard/test/integration_tests/horizon.conf watcher_dashboard/test/integration_tests/pages/__init__.py watcher_dashboard/test/integration_tests/pages/admin/__init__.py watcher_dashboard/test/integration_tests/pages/admin/optimization/__init__.py watcher_dashboard/test/integration_tests/pages/admin/optimization/auditspage.py watcher_dashboard/test/integration_tests/pages/admin/optimization/audittemplatespage.py watcher_dashboard/test/integration_tests/tests/__init__.py watcher_dashboard/test/integration_tests/tests/test_audit_template_panel.py watcher_dashboard/test/test_data/__init__.py watcher_dashboard/test/test_data/exceptions.py watcher_dashboard/test/test_data/utils.py watcher_dashboard/test/test_data/watcher_data.py watcher_dashboard/utils/__init__.py watcher_dashboard/utils/errors.py watcher_dashboard/utils/tests.py watcher_dashboard/utils/utils.pywatcher-dashboard-3.0.0/watcher_dashboard/0000775000175000017500000000000013656752140020573 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/__init__.py0000664000175000017500000000000013656752043022674 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/0000775000175000017500000000000013656752140022245 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/__init__.py0000664000175000017500000000000013656752043024346 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/actions/0000775000175000017500000000000013656752140023705 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/actions/__init__.py0000664000175000017500000000000013656752043026006 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/actions/panel.py0000664000175000017500000000132213656752043025356 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class Actions(horizon.Panel): name = _("Actions ") slug = "actions" watcher-dashboard-3.0.0/watcher_dashboard/content/actions/urls.py0000664000175000017500000000153013656752043025245 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.actions import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/actions/tabs.py0000664000175000017500000000176213656752043025220 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/actions/_detail_overview.html" def get_context_data(self, request): return {"action": self.tab_group.kwargs['action']} class ActionDetailTabs(tabs.TabGroup): slug = "action_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/actions/views.py0000664000175000017500000000723513656752043025425 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 collections from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.tables import horizon.tabs from horizon.utils import memoized import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.actions import tables from watcher_dashboard.content.actions import tabs as wtabs class IndexView(horizon.tables.DataTableView): table_class = tables.ActionsTable template_name = 'infra_optim/actions/index.html' page_title = _("Actions") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context['audits_count'] = self.get_actions_count() return context def get_data(self): actions = [] search_opts = self.get_filters() try: actions = watcher.Action.list(self.request, **search_opts) except Exception as e: horizon.exceptions.handle( self.request, _("Unable to retrieve action information: %s") % str(e)) return actions def get_actions_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class DetailView(horizon.tables.MultiTableView): table_classes = [tables.ActionParametersTable] tab_group_class = wtabs.ActionDetailTabs template_name = 'infra_optim/actions/details.html' redirect_url = 'horizon:admin:actions:index' page_title = _("Action Details: {{ action.uuid }}") @memoized.memoized_method def _get_data(self): action_uuid = None try: action_uuid = self.kwargs['action_uuid'] action = watcher.Action.get(self.request, action_uuid) except Exception: msg = _('Unable to retrieve details for action "%s".') \ % action_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return action def get_parameters_data(self): action = self._get_data() parameter_cls = collections.namedtuple( 'Parameter', field_names=['name', 'value']) return [parameter_cls(name=name, value=value) for name, value in action.input_parameters.items()] def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) action = self._get_data() context["action"] = action return context def get_tabs(self, request, *args, **kwargs): action = self._get_data() # ports = self._get_ports() return self.tab_group_class(request, action=action, # ports=ports, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/actions/tables.py0000664000175000017500000001064713656752043025543 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.template.defaultfilters import title # noqa from django import urls from django.utils.translation import pgettext_lazy from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.messages import horizon.tables from horizon.utils import filters from watcher_dashboard.api import watcher LOG = logging.getLogger(__name__) ACTION_STATE_DISPLAY_CHOICES = ( ("NO STATE", pgettext_lazy("Power state of an Instance", u"No State")), ("ONGOING", pgettext_lazy("Power state of an Instance", u"On Going")), ("SUCCEEDED", pgettext_lazy("Power state of an Instance", u"Succeeded")), ("CANCELLED", pgettext_lazy("Power state of an Instance", u"Cancelled")), ("FAILED", pgettext_lazy("Power state of an Instance", u"Failed")), ("DELETED", pgettext_lazy("Power state of an Instance", u"Deleted")), ("PENDING", pgettext_lazy("Power state of an Instance", u"Pending")), ) class UpdateRow(horizon.tables.Row): ajax = True def get_data(self, request, action_id): action = None try: action = watcher.Action.get(request, action_id) except Exception: msg = _('Failed to get the action.') LOG.info(msg) horizon.messages.warning(request, msg) return action class ActionsFilterAction(horizon.tables.FilterAction): filter_type = "server" filter_choices = (('action_plan', _("Action Plan ID ="), True),) policy_rules = (("infra-optim", "action:detail"),) def get_action_plan_link(datum): try: return urls.reverse( "horizon:admin:action_plans:detail", kwargs={"action_plan_uuid": getattr( datum, "action_plan_uuid", None)}) except urls.NoReverseMatch: return None class ActionsTable(horizon.tables.DataTable): name = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:actions:detail") action_type = horizon.tables.Column( 'action_type', verbose_name=_('Type'), filters=(title, filters.replace_underscores)) state = horizon.tables.Column( 'state', verbose_name=_('State'), status_choices=ACTION_STATE_DISPLAY_CHOICES) action_plan = horizon.tables.Column( 'action_plan_uuid', verbose_name=_('Action Plan'), link=get_action_plan_link) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "wactions" verbose_name = _("Actions") table_actions = (ActionsFilterAction, ) row_class = UpdateRow class RelatedActionsTable(horizon.tables.DataTable): """Identical to the index table but with different Meta""" name = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:actions:detail") action_type = horizon.tables.Column( 'action_type', verbose_name=_('Type'), filters=(title, filters.replace_underscores)) state = horizon.tables.Column( 'state', verbose_name=_('State'), status_choices=ACTION_STATE_DISPLAY_CHOICES) action_plan = horizon.tables.Column( 'action_plan_uuid', verbose_name=_('Action Plan'), link=get_action_plan_link) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "related_wactions" verbose_name = _("Related Actions") hidden_title = False class ActionParametersTable(horizon.tables.DataTable): name = horizon.tables.Column( 'name', verbose_name=_("Parameter name")) value = horizon.tables.Column( 'value', verbose_name=_('Parameter value')) def get_object_id(self, datum): return datum.name class Meta(object): name = "parameters" verbose_name = _("Related parameters") hidden_title = False watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/0000775000175000017500000000000013656752140024717 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/__init__.py0000664000175000017500000000000013656752043027020 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/panel.py0000664000175000017500000000133713656752043026376 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class ActionPlans(horizon.Panel): name = _("Action Plans") slug = "action_plans" watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/urls.py0000664000175000017500000000167113656752043026265 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.action_plans import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), urls.url(r'^archive/$', views.ArchiveView.as_view(), name='archive'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/tabs.py0000664000175000017500000000201313656752043026220 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/action_plans/_detail_overview.html" def get_context_data(self, request): return {"action_plan": self.tab_group.kwargs['action_plans']} class ActionPlanDetailTabs(tabs.TabGroup): slug = "action_plan_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/views.py0000664000175000017500000001171513656752043026435 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.utils.translation import ugettext_lazy as _ import horizon.exceptions from horizon import forms import horizon.tables import horizon.tabs from horizon.utils import memoized import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.action_plans import tables from watcher_dashboard.content.actions import tables as action_tables from watcher_dashboard.content.audits import forms as wforms LOG = logging.getLogger(__name__) class IndexView(horizon.tables.DataTableView): table_class = tables.ActionPlansTable template_name = 'infra_optim/action_plans/index.html' page_title = _("Action Plans") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context['action_plans_count'] = self.get_action_plans_count() return context def get_data(self): action_plans = [] search_opts = self.get_filters() try: action_plans = watcher.ActionPlan.list( self.request, **search_opts) except Exception as exc: LOG.exception(exc) horizon.exceptions.handle( self.request, _("Unable to retrieve action_plan information.")) return action_plans def get_action_plans_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class ArchiveView(forms.ModalFormView): form_class = wforms.CreateForm form_id = "create_audit_form" modal_header = _("Create Audit") template_name = 'infra_optim/audits/create.html' page_title = _("Create Audit") submit_label = _("Create Audit") class DetailView(horizon.tables.MultiTableView): table_classes = ( action_tables.RelatedActionsTable, tables.RelatedEfficacyIndicatorsTable) template_name = 'infra_optim/action_plans/details.html' page_title = _("Action Plan Details: {{ action_plan.uuid }}") @memoized.memoized_method def _get_data(self): action_plan_uuid = None try: action_plan_uuid = self.kwargs['action_plan_uuid'] action_plan = watcher.ActionPlan.get( self.request, action_plan_uuid) except Exception as exc: LOG.exception(exc) msg = _('Unable to retrieve details for action_plan "%s".') \ % action_plan_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return action_plan def get_related_wactions_data(self): try: action_plan = self._get_data() actions = watcher.Action.list(self.request, action_plan=action_plan.uuid) except Exception as exc: LOG.exception(exc) actions = [] msg = _('Action list can not be retrieved: %s') % str(exc) horizon.exceptions.handle(self.request, msg) return actions def get_related_efficacy_indicators_data(self): try: action_plan = self._get_data() efficacy_indicators = [ watcher.EfficacyIndicator(indicator) for indicator in action_plan.efficacy_indicators] except Exception as exc: LOG.exception(exc) msg = _('Failed to get the efficacy indicators: %s') % str(exc) LOG.info(msg) horizon.messages.warning(self.request, msg) return efficacy_indicators def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) action_plan = self._get_data() context["action_plan"] = action_plan LOG.info('*********************************') LOG.info(action_plan) LOG.info('*********************************') return context def get_tabs(self, request, *args, **kwargs): action_plan = self._get_data() return self.tab_group_class( request, action_plan=action_plan, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/action_plans/tables.py0000664000175000017500000001674713656752043026564 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.template.defaultfilters import title # noqa from django import urls from django.utils.translation import pgettext_lazy from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy import horizon.exceptions import horizon.messages import horizon.tables from horizon.utils import filters from watcher_dashboard.api import watcher LOG = logging.getLogger(__name__) ACTION_PLAN_STATE_DISPLAY_CHOICES = ( ("NO STATE", pgettext_lazy("State of an action plan", u"No State")), ("ONGOING", pgettext_lazy("State of an action plan", u"On Going")), ("SUCCEEDED", pgettext_lazy("State of an action plan", u"Succeeded")), ("SUBMITTED", pgettext_lazy("State of an action plan", u"Submitted")), ("FAILED", pgettext_lazy("State of an action plan", u"Failed")), ("DELETED", pgettext_lazy("State of an action plan", u"Deleted")), ("RECOMMENDED", pgettext_lazy("State of an action plan", u"Recommended")), ) class ActionPlansFilterAction(horizon.tables.FilterAction): # server = choices query = text filter_type = "server" filter_choices = ( ('audit', _("Audit ="), True), ) policy_rules = (("infra-optim", "action_plan:detail"),) class ArchiveActionPlan(horizon.tables.DeleteAction): verbose_name = _("Archive Action Plans") policy_rules = (("infra-optim", "action_plan:delete"),) @staticmethod def action_present(count): return ungettext_lazy( u"Archive Action Plan", u"Archive Action Plans", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Action Plan archived", u"Action Plans archived", count ) def action(self, request, obj_id): watcher.ActionPlan.delete(request, obj_id) class StartActionPlan(horizon.tables.BatchAction): name = "start_action_plan" classes = ('btn-confirm',) policy_rules = (("infra-optim", "action_plan:update"),) help_text = _("Execute an action plan.") @staticmethod def action_present(count): return ungettext_lazy( u"Start Action Plan", u"Start Action Plans", count ) @staticmethod def action_past(count): return ungettext_lazy( u"Action Plan started", u"Action Plans started", count ) def action(self, request, action_plan_id): try: watcher.ActionPlan.start(request, action_plan_id) except Exception: msg = _('Failed to start the action plan.') LOG.info(msg) horizon.messages.warning(request, msg) def allowed(self, request, action_plan): return ((action_plan is None) or (action_plan.state in ("RECOMMENDED", "FAILED"))) class UpdateRow(horizon.tables.Row): ajax = True def get_data(self, request, action_plan_id): action_plan = None try: action_plan = watcher.Action.get(request, action_plan_id) except Exception: msg = _('Failed to get the action plan.') LOG.info(msg) horizon.messages.warning(request, msg) return action_plan def format_global_efficacy(action_plan): formatted_global_efficacy = None global_efficacy = watcher.EfficacyIndicator(action_plan.global_efficacy) if global_efficacy.value is not None and global_efficacy.unit: formatted_global_efficacy = "%(value)s %(unit)s" % dict( unit=global_efficacy.unit, value=global_efficacy.value) elif global_efficacy.value is not None: formatted_global_efficacy = global_efficacy.value return formatted_global_efficacy def get_audit_link(datum): try: return urls.reverse( "horizon:admin:audits:detail", kwargs={"audit_uuid": getattr(datum, "audit_uuid", None)}) except urls.NoReverseMatch: return None class ActionPlansTable(horizon.tables.DataTable): name = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:action_plans:detail") audit = horizon.tables.Column( 'audit_uuid', verbose_name=_('Audit'), link=get_audit_link) updated_at = horizon.tables.Column( 'updated_at', filters=(filters.parse_isotime, filters.timesince_sortable), verbose_name=_("Updated At")) status = horizon.tables.Column( 'state', verbose_name=_('State'), status=True, status_choices=ACTION_PLAN_STATE_DISPLAY_CHOICES) efficacy = horizon.tables.Column( transform=format_global_efficacy, verbose_name=_('Efficacy')) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "action_plans" verbose_name = _("Action Plans") table_actions = ( # CancelActionPlan, ActionPlansFilterAction, StartActionPlan, ArchiveActionPlan, ) row_actions = ( StartActionPlan, # CreateActionPlans, ArchiveActionPlan, # CreateActionPlans, # DeleteActionPlans, ) row_class = UpdateRow class RelatedActionPlansTable(horizon.tables.DataTable): name = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:action_plans:detail") audit = horizon.tables.Column( 'audit_uuid', verbose_name=_('Audit'), link=get_audit_link) updated_at = horizon.tables.Column( 'updated_at', filters=(filters.parse_isotime, filters.timesince_sortable), verbose_name=_("Updated At")) status = horizon.tables.Column( 'state', verbose_name=_('State'), status=True, status_choices=ACTION_PLAN_STATE_DISPLAY_CHOICES) efficacy = horizon.tables.Column( transform=format_global_efficacy, verbose_name=_('Efficacy')) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "related_action_plans" verbose_name = _("Related Action Plans") hidden_title = False row_actions = ( StartActionPlan, ArchiveActionPlan, ) row_class = UpdateRow class RelatedEfficacyIndicatorsTable(horizon.tables.DataTable): name = horizon.tables.Column( 'name', verbose_name=_("Name")) description = horizon.tables.Column( 'description', verbose_name=_("Description")) unit = horizon.tables.Column( 'unit', verbose_name=_("Unit")) value = horizon.tables.Column( 'value', verbose_name=_("Value")) def get_object_id(self, datum): return datum.name class Meta(object): name = "related_efficacy_indicators" verbose_name = _("Related Efficacy Indicators") hidden_title = False watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/0000775000175000017500000000000013656752140025431 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/__init__.py0000664000175000017500000000000013656752043027532 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/panel.py0000664000175000017500000000135013656752043027103 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class AuditTemplates(horizon.Panel): name = _("Audit Templates") slug = "audit_templates" watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/urls.py0000664000175000017500000000165713656752043027003 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.audit_templates import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^create/$', views.CreateView.as_view(), name='create'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/tabs.py0000664000175000017500000000203113656752043026732 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/audit_templates/_detail_overview.html" def get_context_data(self, request): return {"audit_template": self.tab_group.kwargs['audit_template']} class AuditTemplateDetailTabs(tabs.TabGroup): slug = "audit_template_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/tests.py0000664000175000017500000001051413656752043027150 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 import urls from watcher_dashboard import api from watcher_dashboard.test import helpers as test INDEX_URL = urls.reverse( 'horizon:admin:audit_templates:index') CREATE_URL = urls.reverse( 'horizon:admin:audit_templates:create') DETAILS_VIEW = 'horizon:admin:audit_templates:detail' class AuditTemplatesTest(test.BaseAdminViewTests): def setUp(self): super(AuditTemplatesTest, self).setUp() self.goal_list = self.goals.list() self.strategy_list = self.strategies.list() @mock.patch.object(api.watcher.AuditTemplate, 'list') def test_index(self, mock_list): mock_list.return_value = self.audit_templates.list() res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'infra_optim/audit_templates/index.html') audit_templates = res.context['audit_templates_table'].data self.assertCountEqual(audit_templates, self.audit_templates.list()) @mock.patch.object(api.watcher.AuditTemplate, 'list') def test_audit_template_list_unavailable(self, mock_list): mock_list.side_effect = self.exceptions.watcher resp = self.client.get(INDEX_URL) self.assertMessageCount(resp, error=1, warning=0) @mock.patch.object(api.watcher.Strategy, 'list') @mock.patch.object(api.watcher.Goal, 'list') def test_create_get(self, m_goal_list, m_strategy_list): m_goal_list.return_value = self.goal_list m_strategy_list.return_value = self.strategy_list res = self.client.get(CREATE_URL) self.assertTemplateUsed(res, 'infra_optim/audit_templates/create.html') @mock.patch.object(api.watcher.Strategy, 'list') @mock.patch.object(api.watcher.Goal, 'list') @mock.patch.object(api.watcher.AuditTemplate, 'create') def test_create_post(self, m_audit_create, m_goal_list, m_strategy_list): at = self.audit_templates.first() form_data = { 'name': at.name, 'goal': at.goal_uuid, 'strategy': at.strategy_uuid, 'description': at.description, 'scope': at.scope, } m_goal_list.return_value = self.goal_list m_strategy_list.return_value = self.strategy_list m_audit_create.return_value = at res = self.client.post(CREATE_URL, form_data) self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) @mock.patch.object(api.watcher.AuditTemplate, 'get') def test_details(self, m_get): at = self.audit_templates.first() at_id = at.uuid m_get.return_value = at DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[at_id]) res = self.client.get(DETAILS_URL) self.assertTemplateUsed(res, 'infra_optim/audit_templates/details.html') audit_templates = res.context['audit_template'] self.assertCountEqual([audit_templates], [at]) @mock.patch.object(api.watcher.AuditTemplate, 'get') def test_details_exception(self, m_get): at = self.audit_templates.first() at_id = at.uuid m_get.side_effect = self.exceptions.watcher DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[at_id]) res = self.client.get(DETAILS_URL) self.assertRedirectsNoFollow(res, INDEX_URL) @mock.patch.object(api.watcher.AuditTemplate, 'delete') @mock.patch.object(api.watcher.AuditTemplate, 'list') def test_delete(self, m_list, m_del): at_list = self.audit_templates.list() at = self.audit_templates.first() at_id = at.uuid m_list.return_value = at_list form_data = {'action': 'audit_templates__delete', 'object_ids': at_id} res = self.client.post(INDEX_URL, form_data) self.assertRedirectsNoFollow(res, INDEX_URL) watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/views.py0000664000175000017500000001143513656752043027146 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import json import logging from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ import horizon.exceptions from horizon import forms import horizon.tables import horizon.tabs import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.audit_templates import forms as wforms from watcher_dashboard.content.audit_templates import tables from watcher_dashboard.content.audit_templates import tabs as wtabs LOG = logging.getLogger(__name__) class IndexView(horizon.tables.DataTableView): table_class = tables.AuditTemplatesTable template_name = 'infra_optim/audit_templates/index.html' page_title = _("Audit Templates") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context['audit_templates_count'] = self.get_count() return context def get_data(self): audit_templates = [] search_opts = self.get_filters() try: audit_templates = watcher.AuditTemplate.list( self.request, **search_opts) except Exception as exc: LOG.exception(exc) horizon.exceptions.handle( self.request, _("Unable to retrieve audit template " "information: %s") % str(exc)) return audit_templates def get_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class CreateView(forms.ModalFormView): form_class = wforms.CreateForm form_id = "create_audit_templates_form" modal_header = _("Create Audit Template") template_name = 'infra_optim/audit_templates/create.html' success_url = reverse_lazy("horizon:admin:audit_templates:index") page_title = _("Create an Audit Template") submit_label = _("Create Audit Template") submit_url = reverse_lazy("horizon:admin:audit_templates:create") def get_object_id(self, obj): return obj.uuid class DetailView(horizon.tabs.TabbedTableView): tab_group_class = wtabs.AuditTemplateDetailTabs template_name = 'infra_optim/audit_templates/details.html' redirect_url = 'horizon:admin:audit_templates:index' page_title = _("Audit Template Details: {{ audit_template.name }}") def _get_data(self): audit_template_uuid = None try: LOG.info(self.kwargs) audit_template_uuid = self.kwargs['audit_template_uuid'] audit_template = watcher.AuditTemplate.get( self.request, audit_template_uuid) if audit_template.scope: audit_template.scope = json.dumps(audit_template.scope) except Exception as exc: LOG.exception(exc) msg = _('Unable to retrieve details for audit template "%s".') \ % audit_template_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return audit_template def get_related_audits_data(self): try: audit_template = self._get_data() audits = watcher.Audit.list( self.request, audit_template=audit_template.uuid) except Exception as exc: LOG.exception(exc) audits = [] msg = _('Audits list cannot be retrieved: %s') % str(exc) horizon.exceptions.handle(self.request, msg) return audits def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) audit_template = self._get_data() context["audit_template"] = audit_template return context def get_tabs(self, request, *args, **kwargs): audit_template = self._get_data() # ports = self._get_ports() return self.tab_group_class( request, audit_template=audit_template, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/tables.py0000664000175000017500000000707713656752043027272 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from django.utils.translation import ungettext_lazy import horizon.exceptions import horizon.messages import horizon.tables from watcher_dashboard.api import watcher class CreateAuditTemplates(horizon.tables.LinkAction): name = "create" verbose_name = _("Create Template") url = "horizon:admin:audit_templates:create" classes = ("ajax-modal", "btn-launch") policy_rules = (("infra-optim", "audit_template:create"),) class AuditTemplatesFilterAction(horizon.tables.FilterAction): filter_type = "server" filter_choices = ( ('goal', _("Goal ="), True), ('strategy', _("Strategy ="), True), ) policy_rules = (("infra-optim", "audit_template:detail"),) class LaunchAudit(horizon.tables.BatchAction): name = "launch_audit" verbose_name = _("Launch Audit") data_type_singular = _("Launch Audit") data_type_plural = _("Launch Audits") success_url = "horizon:admin:audits:index" policy_rules = (("infra-optim", "audit:create"),) @staticmethod def action_present(count): return ungettext_lazy( "Launch Audit", "Launch Audits", count ) @staticmethod def action_past(count): return ungettext_lazy( "Launched Audit", "Launched Audits", count ) def action(self, request, obj_id): params = {'audit_template_uuid': obj_id} params['audit_type'] = 'ONESHOT' params['auto_trigger'] = False watcher.Audit.create(request, **params) class ArchiveAuditTemplates(horizon.tables.DeleteAction): verbose_name = _("Archive Templates") policy_rules = (("infra-optim", "audit_template:delete"),) @staticmethod def action_present(count): return ungettext_lazy( "Archive Template", "Archive Templates", count ) @staticmethod def action_past(count): return ungettext_lazy( "Archived Template", "Archived Templates", count ) def delete(self, request, obj_id): watcher.AuditTemplate.delete(request, obj_id) class AuditTemplatesTable(horizon.tables.DataTable): name = horizon.tables.Column( 'name', verbose_name=_("Name"), link="horizon:admin:audit_templates:detail") goal = horizon.tables.Column( 'goal_name', verbose_name=_('Goal'), status=True, ) strategy = horizon.tables.Column( 'strategy_name', verbose_name=_('Strategy'), status=True, ) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "audit_templates" verbose_name = _("Audit Templates") table_actions = ( CreateAuditTemplates, AuditTemplatesFilterAction, LaunchAudit, ArchiveAuditTemplates, ) row_actions = ( LaunchAudit, ArchiveAuditTemplates, ) watcher-dashboard-3.0.0/watcher_dashboard/content/audit_templates/forms.py0000664000175000017500000001065213656752043027137 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Forms for starting Watcher Audit Templates. """ import logging from django.core import exceptions as core_exc from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages import yaml from watcher_dashboard.api import watcher LOG = logging.getLogger(__name__) class YamlValidator(object): message = _('Enter a valid YAML or JSON value.') code = 'invalid' def __init__(self, message=None): if message: self.message = message def __call__(self, value): try: yaml.safe_load(value) except Exception: raise core_exc.ValidationError(self.message, code=self.code) class CreateForm(forms.SelfHandlingForm): name = forms.CharField(max_length=255, label=_("Name")) description = forms.CharField(max_length=255, label=_("Description"), required=False) goal = forms.ChoiceField(label=_('Goal')) strategy = forms.DynamicChoiceField(label=_('Strategy'), required=False) scope = forms.CharField( label=_('Scope'), required=False, widget=forms.widgets.Textarea, validators=[YamlValidator()]) failure_url = 'horizon:admin:audit_templates:index' def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) goals = self._get_goal_list(request) strategies = self._get_strategy_list(request, goals) if goals: self.fields['goal'].choices = goals else: del self.fields['goal'] if strategies: self.fields['strategy'].choices = strategies else: del self.fields['strategy'] def _get_goal_list(self, request): try: goals = watcher.Goal.list(self.request) except Exception as exc: msg = _('Failed to get goals list: %s') % str(exc) LOG.info(msg) messages.warning(request, msg) messages.warning(request, exc) goals = [] choices = [ (goal.uuid, goal.display_name) for goal in goals ] if choices: choices.insert(0, ("", _("Select Goal"))) return choices def _get_strategy_list(self, request, goals): try: strategies = watcher.Strategy.list(self.request) except Exception as exc: msg = _('Failed to get the list of available strategies.') LOG.info(msg) messages.warning(request, msg) messages.warning(request, exc) strategies = [] _goals = {} for goal in goals: _goals[goal[0]] = goal[1] choices = [ (strategy.uuid, strategy.display_name + ' (GOAL: ' + _goals[strategy.goal_uuid] + ')') for strategy in strategies ] if choices: choices.insert(0, ("", _("Select Strategy"))) return choices def handle(self, request, data): try: params = {'name': data['name']} params['description'] = data['description'] params['goal'] = data['goal'] params['strategy'] = data['strategy'] or None params['scope'] = [] if not data['scope'] else yaml.safe_load( data['scope']) audit_tpl = watcher.AuditTemplate.create(request, **params) message = _('Audit Template was successfully created.') messages.success(request, message) return audit_tpl except Exception as exc: msg = _('Failed to create audit template: %s') % str(exc) LOG.info(exc) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect) return False watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/0000775000175000017500000000000013656752140024417 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/__init__.py0000664000175000017500000000000013656752043026520 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/panel.py0000664000175000017500000000133213656752043026071 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class Strategies(horizon.Panel): name = _("Strategies") slug = "strategies" watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/urls.py0000664000175000017500000000153513656752043025764 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.strategies import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/tabs.py0000664000175000017500000000177513656752043025736 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/strategies/_detail_overview.html" def get_context_data(self, request): return {"strategy": self.tab_group.kwargs['strategy']} class StrategyDetailTabs(tabs.TabGroup): slug = "strategy_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/tests.py0000664000175000017500000000503713656752043026142 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 import urls from watcher_dashboard import api from watcher_dashboard.test import helpers as test INDEX_URL = urls.reverse( 'horizon:admin:strategies:index') DETAILS_VIEW = 'horizon:admin:strategies:detail' class StrategiesTest(test.BaseAdminViewTests): goal_list = [ 'BASIC_CONSOLIDATION', 'MINIMIZE_ENERGY_CONSUMPTION', 'BALANCE_LOAD', 'MINIMIZE_LICENSING_COST', 'PREPARED_PLAN_OPERATION', ] @mock.patch.object(api.watcher.Strategy, 'list') def test_index(self, mock_list): mock_list.return_value = self.strategies.list() res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'infra_optim/strategies/index.html') strategies = res.context['strategies_table'].data self.assertCountEqual(strategies, self.strategies.list()) @mock.patch.object(api.watcher.Strategy, 'list') def test_strategy_list_unavailable(self, mock_list): mock_list.side_effect = self.exceptions.watcher resp = self.client.get(INDEX_URL) self.assertMessageCount(resp, error=1, warning=0) @mock.patch.object(api.watcher.Strategy, 'get') def test_details(self, mock_get): at = self.strategies.first() at_id = at.uuid mock_get.return_value = at DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[at_id]) res = self.client.get(DETAILS_URL) self.assertTemplateUsed(res, 'infra_optim/strategies/details.html') strategies = res.context['strategy'] self.assertCountEqual([strategies], [at]) @mock.patch.object(api.watcher.Strategy, 'get') def test_details_exception(self, mock_get): at = self.strategies.first() at_id = at.uuid mock_get.side_effect = self.exceptions.watcher DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[at_id]) res = self.client.get(DETAILS_URL) self.assertRedirectsNoFollow(res, INDEX_URL) watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/views.py0000664000175000017500000000657513656752043026145 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.tables import horizon.tabs from horizon.utils import memoized import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.strategies import tables from watcher_dashboard.content.strategies import tabs as wtabs LOG = logging.getLogger(__name__) class IndexView(horizon.tables.DataTableView): table_class = tables.StrategiesTable template_name = 'infra_optim/strategies/index.html' page_title = _("Strategies") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context['strategies_count'] = self.get_strategies_count() return context def get_data(self): strategies = [] search_opts = self.get_filters() try: strategies = watcher.Strategy.list(self.request, **search_opts) except Exception as exc: LOG.exception(exc) horizon.exceptions.handle( self.request, _("Unable to retrieve strategy information: %s") % str(exc)) return strategies def get_strategies_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class DetailView(horizon.tabs.TabbedTableView): tab_group_class = wtabs.StrategyDetailTabs template_name = 'infra_optim/strategies/details.html' redirect_url = 'horizon:admin:strategies:index' page_title = _("Strategy Details: {{ strategy.name }}") @memoized.memoized_method def _get_data(self): strategy_uuid = None try: strategy_uuid = self.kwargs['strategy_uuid'] strategy = watcher.Strategy.get(self.request, strategy_uuid) except Exception as exc: LOG.exception(exc) msg = _('Unable to retrieve details for strategy "%s".') \ % strategy_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return strategy def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) strategy = self._get_data() context["strategy"] = strategy return context def get_tabs(self, request, *args, **kwargs): strategy = self._get_data() return self.tab_group_class(request, strategy=strategy, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/tables.py0000664000175000017500000000441213656752043026246 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon.exceptions import horizon.messages import horizon.tables class StrategiesFilterAction(horizon.tables.FilterAction): # server = choices query = text filter_type = "server" filter_choices = ( ('goal', _("Goal ="), True), ) policy_rules = (("infra-optim", "strategy:detail"),) class StrategiesTable(horizon.tables.DataTable): uuid = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:strategies:detail") name = horizon.tables.Column( 'name', verbose_name=_('Name')) display_name = horizon.tables.Column( 'display_name', verbose_name=_('Verbose Name')) goal = horizon.tables.Column( 'goal_name', verbose_name=_("Goal"), ) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "strategies" verbose_name = _("Strategies") table_actions = ( StrategiesFilterAction, ) class RelatedStrategiesTable(horizon.tables.DataTable): uuid = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:strategies:detail") name = horizon.tables.Column( 'name', verbose_name=_('Name')) display_name = horizon.tables.Column( 'display_name', verbose_name=_('Verbose Name')) goal = horizon.tables.Column( 'goal_name', verbose_name=_("Goal"), ) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "related_strategies" verbose_name = _("Related strategies") hidden_title = False watcher-dashboard-3.0.0/watcher_dashboard/content/strategies/forms.py0000664000175000017500000000000013656752043026107 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/audits/0000775000175000017500000000000013656752140023536 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/audits/__init__.py0000664000175000017500000000000013656752043025637 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/audits/panel.py0000664000175000017500000000131613656752043025212 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class Audits(horizon.Panel): name = _("Audits") slug = "audits" watcher-dashboard-3.0.0/watcher_dashboard/content/audits/urls.py0000664000175000017500000000165213656752043025103 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.audits import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^create/$', views.CreateView.as_view(), name='create'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/audits/tabs.py0000664000175000017500000000175513656752043025053 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/audits/_detail_overview.html" def get_context_data(self, request): return {"audit": self.tab_group.kwargs['audit']} class AuditDetailTabs(tabs.TabGroup): slug = "audit_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/audits/views.py0000664000175000017500000001115713656752043025254 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.urls import reverse from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ import horizon.exceptions from horizon import forms import horizon.tables import horizon.tabs from horizon.utils import memoized import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.action_plans import tables as action_plan_tables from watcher_dashboard.content.audits import forms as wforms from watcher_dashboard.content.audits import tables from watcher_dashboard.content.audits import tabs as wtabs LOG = logging.getLogger(__name__) class IndexView(horizon.tables.DataTableView): table_class = tables.AuditsTable template_name = 'infra_optim/audits/index.html' page_title = _("Audits") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) create_action = { 'name': _("New Audit"), 'url': reverse('horizon:admin:audits:create'), 'icon': 'fa-plus', 'ajax_modal': True, } context['header_actions'] = [create_action] context['audits_count'] = self.get_audits_count() return context def get_data(self): audits = [] search_opts = self.get_filters() try: audits = watcher.Audit.list(self.request, **search_opts) except Exception as e: horizon.exceptions.handle( self.request, _("Unable to retrieve audit information: %s") % str(e)) return audits def get_audits_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class CreateView(forms.ModalFormView): form_class = wforms.CreateForm form_id = "create_audit_form" modal_header = _("Create Audit") template_name = 'infra_optim/audits/create.html' success_url = reverse_lazy("horizon:admin:audits:index") page_title = _("Create Audit") submit_label = _("Create Audit") submit_url = reverse_lazy("horizon:admin:audits:create") class DetailView(horizon.tables.MultiTableView): table_classes = (action_plan_tables.RelatedActionPlansTable,) tab_group_class = wtabs.AuditDetailTabs template_name = 'infra_optim/audits/details.html' redirect_url = 'horizon:admin:audits:index' page_title = _("Audit Details: {{ audit.uuid }}") @memoized.memoized_method def _get_data(self): audit_uuid = None try: audit_uuid = self.kwargs['audit_uuid'] audit = watcher.Audit.get(self.request, audit_uuid) except Exception: msg = _('Unable to retrieve details for audit "%s".') \ % audit_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return audit def get_related_action_plans_data(self): try: action_plan = self._get_data() audits = watcher.ActionPlan.list(self.request, audit=action_plan.uuid) except Exception as exc: LOG.exception(exc) audits = [] msg = _('Action plan list cannot be retrieved: %s') % str(exc) horizon.exceptions.handle(self.request, msg) return audits def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) audit = self._get_data() context["audit"] = audit return context def get_tabs(self, request, *args, **kwargs): audit = self._get_data() # ports = self._get_ports() return self.tab_group_class(request, audit=audit, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/audits/tables.py0000664000175000017500000001240213656752043025363 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 import shortcuts from django.template.defaultfilters import title # noqa from django import urls from django.utils.translation import pgettext_lazy from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy import horizon.exceptions import horizon.messages import horizon.tables from horizon.utils import filters from watcher_dashboard.api import watcher AUDIT_STATE_DISPLAY_CHOICES = ( ("NO STATE", pgettext_lazy("State of an audit", u"No State")), ("ONGOING", pgettext_lazy("State of an audit", u"On Going")), ("SUCCEEDED", pgettext_lazy("State of an audit", u"Succeeeded")), ("SUBMITTED", pgettext_lazy("State of an audit", u"Submitted")), ("FAILED", pgettext_lazy("State of an audit", u"Failed")), ("DELETED", pgettext_lazy("State of an audit", u"Deleted")), ("PENDING", pgettext_lazy("State of an audit", u"Pending")), ) class AuditsFilterAction(horizon.tables.FilterAction): # server = choices query = text filter_type = "server" filter_choices = ( ('audit_template', _("Audit Template ="), True), ) policy_rules = (("infra-optim", "audit:detail"),) class CreateAudit(horizon.tables.LinkAction): name = "create_audit" verbose_name = _("Create Audit") url = "horizon:admin:audits:create" classes = ("ajax-modal", "btn-launch") policy_rules = (("infra-optim", "audit:create"),) class GoToActionPlan(horizon.tables.Action): name = "go_to_action_plan" verbose_name = _("Go to Action Plan") url = "horizon:admin:action_plans:detail" policy_rules = (("infra-optim", "action_plan:detail"),) def allowed(self, request, audit): return audit or audit.state in ("SUCCEEDED", ) def single(self, table, request, audit_id): try: action_plans = watcher.ActionPlan.list( request, audit=audit_id) except Exception: horizon.exceptions.handle( request, _("Unable to retrieve action_plan information.")) return "javascript:void(0);" return shortcuts.redirect(urls.reverse( self.url, args=[action_plans[0].uuid])) class ArchiveAudits(horizon.tables.DeleteAction): verbose_name = _("Archive Audits") policy_rules = (("infra-optim", "audit:delete"),) def allowed(self, request, audit): return audit or audit.state not in ("ONGOING", "PENDING") @staticmethod def action_present(count): return ungettext_lazy( "Archive Audit", "Archive Audits", count ) @staticmethod def action_past(count): return ungettext_lazy( "Archived Audit", "Archived Audits", count ) def delete(self, request, obj_id): watcher.Audit.delete(request, obj_id) class AuditsTable(horizon.tables.DataTable): uuid = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:audits:detail") name = horizon.tables.Column( 'name', verbose_name=_("Name"), link="horizon:admin:audits:detail") goal = horizon.tables.Column( 'goal_name', verbose_name=_('Goal')) strategy = horizon.tables.Column( 'strategy_name', verbose_name=_('Strategy')) audit_type = horizon.tables.Column( 'audit_type', verbose_name=_('Audit Type')) auto_trigger = horizon.tables.Column( 'auto_trigger', verbose_name=_('Auto Trigger')) status = horizon.tables.Column( 'state', verbose_name=_('State'), status=True, status_choices=AUDIT_STATE_DISPLAY_CHOICES) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "audits" verbose_name = _("Audits") launch_actions = (CreateAudit,) table_actions = launch_actions + ( AuditsFilterAction, ) row_actions = ( GoToActionPlan, ArchiveAudits, ) class RelatedAuditsTable(horizon.tables.DataTable): name = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:audits:detail") audit_template = horizon.tables.Column( 'audit_template_name', verbose_name=_('Audit Template'), filters=(title, filters.replace_underscores)) status = horizon.tables.Column( 'state', verbose_name=_('State'), status=True, status_choices=AUDIT_STATE_DISPLAY_CHOICES) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "audits" verbose_name = _("Related audits") hidden_title = False watcher-dashboard-3.0.0/watcher_dashboard/content/audits/forms.py0000664000175000017500000001170413656752043025243 0ustar zuulzuul00000000000000# Copyright 2012, Nachi Ueno, NTT MCL, Inc. # All rights reserved. # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Forms for starting Watcher Audits. """ import logging from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages from watcher_dashboard.api import watcher LOG = logging.getLogger(__name__) ADD_AUDIT_TEMPLATES_URL = "horizon:admin:audit_templates:create" class CreateForm(forms.SelfHandlingForm): audit_template = forms.DynamicChoiceField( label=_("Audit Template"), add_item_link=ADD_AUDIT_TEMPLATES_URL) audit_name = forms.CharField(max_length=255, label=_("Name"), help_text=_("An audit name should not " "duplicate with existed audits' names."), required=False) audit_type = forms.ChoiceField(label=_("Audit Type"), choices=[(None, _("Select Audit Type")), ('oneshot', _('ONESHOT')), ('continuous', _('CONTINUOUS'))], widget=forms.Select(attrs={ 'class': 'switchable', 'data-slug': 'audit_type' })) interval = forms.CharField(label=_("Interval (in seconds or cron format)"), help_text=_("Interval in seconds or cron" "format for CONTINUOUS audit"), widget=forms.TextInput(attrs={ 'class': 'switched', 'data-switch-on': 'audit_type', 'data-audit_type-continuous': _("Interval (in seconds or cron" " format)")}), required=False) failure_url = 'horizon:admin:audits:index' auto_trigger = forms.BooleanField(label=_("Auto Trigger"), required=False) def __init__(self, request, *args, **kwargs): super(CreateForm, self).__init__(request, *args, **kwargs) audit_templates = self._get_audit_template_list(request) self.fields['audit_template'].choices = audit_templates def _get_audit_template_list(self, request): try: audit_templates = watcher.AuditTemplate.list(self.request) except Exception as e: msg = _('Failed to get audit template list: %s') % str(e) LOG.info(msg) messages.warning(request, msg) audit_templates = [] choices = [ (audit_template.uuid, audit_template.name or audit_template.uuid) for audit_template in audit_templates ] if choices: choices.insert(0, ("", _("Select Audit Template"))) else: choices.insert(0, ("", _("No Audit Template found"))) return choices def clean(self): cleaned_data = super(CreateForm, self).clean() audit_type = cleaned_data.get('audit_type') if audit_type == 'continuous' and not cleaned_data.get('interval'): msg = _('Please input an interval for continuous audit') raise forms.ValidationError(msg) return cleaned_data def handle(self, request, data): try: params = {'audit_template_uuid': data.get('audit_template')} params['audit_type'] = data['audit_type'].upper() params['auto_trigger'] = data['auto_trigger'] params['name'] = data['audit_name'] if data['audit_type'] == 'continuous': params['interval'] = data['interval'] else: params['interval'] = None audit = watcher.Audit.create(request, **params) message = _('Audit was successfully created.') messages.success(request, message) return audit except Exception as exc: if getattr(exc, 'http_status', None) == 409: msg = _('Error: Audit name already exists.') else: msg = _('Failed to create audit.') LOG.info(exc) redirect = reverse(self.failure_url) exceptions.handle(request, msg, redirect=redirect) return False watcher-dashboard-3.0.0/watcher_dashboard/content/goals/0000775000175000017500000000000013656752140023352 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/goals/__init__.py0000664000175000017500000000000013656752043025453 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/content/goals/panel.py0000664000175000017500000000131313656752043025023 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ import horizon class Goals(horizon.Panel): name = _("Goals") slug = "goals" watcher-dashboard-3.0.0/watcher_dashboard/content/goals/urls.py0000664000175000017500000000152413656752043024715 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 urls from watcher_dashboard.content.goals import views urlpatterns = [ urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^(?P[^/]+)/detail$', views.DetailView.as_view(), name='detail'), ] watcher-dashboard-3.0.0/watcher_dashboard/content/goals/tabs.py0000664000175000017500000000175013656752043024662 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 ugettext_lazy as _ from horizon import tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "infra_optim/goals/_detail_overview.html" def get_context_data(self, request): return {"goal": self.tab_group.kwargs['goal']} class GoalDetailTabs(tabs.TabGroup): slug = "goal_details" tabs = (OverviewTab,) sticky = True watcher-dashboard-3.0.0/watcher_dashboard/content/goals/tests.py0000664000175000017500000000445313656752043025076 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 import urls from watcher_dashboard import api from watcher_dashboard.test import helpers as test INDEX_URL = urls.reverse('horizon:admin:goals:index') DETAILS_VIEW = 'horizon:admin:goals:detail' class GoalsTest(test.BaseAdminViewTests): @mock.patch.object(api.watcher.Goal, 'list') def test_index(self, mock_list): mock_list.return_value = self.goals.list() res = self.client.get(INDEX_URL) self.assertTemplateUsed(res, 'infra_optim/goals/index.html') goals = res.context['goals_table'].data self.assertCountEqual(goals, self.goals.list()) @mock.patch.object(api.watcher.Goal, 'list') def test_goal_list_unavailable(self, mock_list): mock_list.side_effect = self.exceptions.watcher resp = self.client.get(INDEX_URL) self.assertMessageCount(resp, error=1, warning=0) @mock.patch.object(api.watcher.Strategy, 'list') @mock.patch.object(api.watcher.Goal, 'get') def test_details(self, mock_get, mock_list): goal = self.goals.first() goal_id = goal.uuid mock_get.return_value = goal DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[goal_id]) res = self.client.get(DETAILS_URL) self.assertTemplateUsed(res, 'infra_optim/goals/details.html') goals = res.context['goal'] self.assertCountEqual([goals], [goal]) @mock.patch.object(api.watcher.Goal, 'get') def test_details_exception(self, mock_get): at = self.goals.first() at_id = at.uuid mock_get.side_effect = self.exceptions.watcher DETAILS_URL = urls.reverse(DETAILS_VIEW, args=[at_id]) res = self.client.get(DETAILS_URL) self.assertRedirectsNoFollow(res, INDEX_URL) watcher-dashboard-3.0.0/watcher_dashboard/content/goals/views.py0000664000175000017500000001067213656752043025071 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.tables import horizon.tabs from horizon.utils import memoized import horizon.workflows from watcher_dashboard.api import watcher from watcher_dashboard.content.goals import tables from watcher_dashboard.content.goals import tabs as wtabs from watcher_dashboard.content.strategies import tables as strategies_tables LOG = logging.getLogger(__name__) class IndexView(horizon.tables.DataTableView): table_class = tables.GoalsTable template_name = 'infra_optim/goals/index.html' page_title = _("Goals") def get_context_data(self, **kwargs): context = super(IndexView, self).get_context_data(**kwargs) context['goals_count'] = self.get_goals_count() return context def get_data(self): goals = [] search_opts = self.get_filters() try: goals = watcher.Goal.list(self.request, **search_opts) except Exception as e: horizon.exceptions.handle( self.request, _("Unable to retrieve goal information: %s") % str(e)) return goals def get_goals_count(self): return len(self.get_data()) def get_filters(self): filters = {} filter_action = self.table._meta._filter_action if filter_action: filter_field = self.table.get_filter_field() if filter_action.is_api_filter(filter_field): filter_string = self.table.get_filter_string() if filter_field and filter_string: filters[filter_field] = filter_string return filters class DetailView(horizon.tables.MultiTableView): table_classes = (tables.EfficacySpecificationTable, strategies_tables.RelatedStrategiesTable) tab_group_class = wtabs.GoalDetailTabs template_name = 'infra_optim/goals/details.html' redirect_url = 'horizon:admin:goals:index' page_title = _("Goal Details: {{ goal.name }}") @memoized.memoized_method def _get_data(self): goal_uuid = None try: goal_uuid = self.kwargs['goal_uuid'] goal = watcher.Goal.get(self.request, goal_uuid) except Exception as exc: LOG.exception(exc) msg = _('Unable to retrieve details for goal "%s".') \ % goal_uuid horizon.exceptions.handle( self.request, msg, redirect=self.redirect_url) return goal def get_related_strategies_data(self): try: goal = self._get_data() strategies = watcher.Strategy.list(self.request, goal=goal.uuid) except Exception as exc: LOG.exception(exc) strategies = [] msg = _('Strategy list cannot be retrieved: %s') % str(exc) horizon.exceptions.handle(self.request, msg) return strategies def get_efficacy_specification_data(self): try: goal = self._get_data() indicators_spec = [watcher.EfficacyIndicatorSpec(spec) for spec in goal.efficacy_specification] except Exception as exc: LOG.exception(exc) indicators_spec = [] msg = _('Efficacy specification cannot be ' 'retrieved: %s') % str(exc) horizon.exceptions.handle(self.request, msg) return indicators_spec def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) goal = self._get_data() context["goal"] = goal return context def get_tabs(self, request, *args, **kwargs): goal = self._get_data() # ports = self._get_ports() return self.tab_group_class(request, goal=goal, # ports=ports, **kwargs) watcher-dashboard-3.0.0/watcher_dashboard/content/goals/tables.py0000664000175000017500000000354513656752043025207 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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.defaultfilters import title # noqa from django.utils.translation import ugettext_lazy as _ import horizon.exceptions import horizon.messages import horizon.tables class GoalsTable(horizon.tables.DataTable): uuid = horizon.tables.Column( 'uuid', verbose_name=_("UUID"), link="horizon:admin:goals:detail") name = horizon.tables.Column( 'name', verbose_name=_('Name')) display_name = horizon.tables.Column( 'display_name', verbose_name=_('Verbose Name')) def get_object_id(self, datum): return datum.uuid class Meta(object): name = "goals" verbose_name = _("Goals") class EfficacySpecificationTable(horizon.tables.DataTable): name = horizon.tables.Column( 'name', verbose_name=_("Name")) description = horizon.tables.Column( 'description', verbose_name=_("Description")) unit = horizon.tables.Column( 'unit', verbose_name=_("Unit")) schema = horizon.tables.Column( 'schema', verbose_name=_("Schema")) def get_object_id(self, datum): return datum.name class Meta(object): name = "efficacy_specification" verbose_name = _("Efficacy specification") hidden_title = False watcher-dashboard-3.0.0/watcher_dashboard/version.py0000664000175000017500000000135513656752043022640 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from pbr import version version_info = version.VersionInfo('watcher_dashboard') __version__ = version_info.cached_version_string() watcher-dashboard-3.0.0/watcher_dashboard/utils/0000775000175000017500000000000013656752140021733 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/utils/utils.py0000664000175000017500000000572513656752043023460 0ustar zuulzuul00000000000000# -*- coding: utf8 -*- # # 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 re CAMEL_RE = re.compile(r'([A-Z][a-z]+|[A-Z]+(?=[A-Z\s]|$))') def de_camel_case(text): """Convert CamelCase names to human-readable format.""" return ' '.join(w.strip() for w in CAMEL_RE.split(text) if w.strip()) def list_to_dict(object_list, key_attribute='id'): """Converts an object list to a dict :param object_list: list of objects to be put into a dict :type object_list: list :param key_attribute: object attribute used as index by dict :type key_attribute: str :return: dict containing the objects in the list :rtype: dict """ return dict((getattr(o, key_attribute), o) for o in object_list) def length(iterator): """A length function for iterators Returns the number of items in the specified iterator. Note that this function consumes the iterator in the process. """ return sum(1 for _item in iterator) def check_image_type(image, image_type): """Check if image 'type' property matches passed-in image_type. If image has no 'type' property' return True, as we cannot be sure what type of image it is. """ return (image.properties.get('type', image_type) == image_type) def filter_items(items, **kwargs): """Filters the list of items and returns the filtered list. Example usage: >>> class Item(object): ... def __init__(self, index): ... self.index = index ... def __repr__(self): ... return '' % self.index >>> items = [Item(i) for i in range(7)] >>> list(filter_items(items, index=1)) [] >>> list(filter_items(items, index__in=(1, 2, 3))) [, , ] >>> list(filter_items(items, index__not_in=(1, 2, 3))) [, , , ] """ for item in items: for name, value in kwargs.items(): if name.endswith('__in'): if getattr(item, name[:-len('__in')]) not in value: break elif name.endswith('__not_in'): if getattr(item, name[:-len('__not_in')]) in value: break else: if getattr(item, name) != value: break else: yield item def safe_int_cast(value): try: return int(value) except (TypeError, ValueError): return 0 watcher-dashboard-3.0.0/watcher_dashboard/utils/__init__.py0000664000175000017500000000000013656752043024034 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/utils/tests.py0000664000175000017500000000561313656752043023456 0ustar zuulzuul00000000000000# -*- coding: utf8 -*- # # 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 collections from watcher_dashboard.test import helpers from watcher_dashboard.utils import utils class UtilsTests(helpers.TestCase): def test_de_camel_case(self): ret = utils.de_camel_case('CamelCaseString') self.assertEqual(ret, 'Camel Case String') ret = utils.de_camel_case('SecureSSLConnection') self.assertEqual(ret, 'Secure SSL Connection') ret = utils.de_camel_case('xxXXxx') self.assertEqual(ret, 'xx X Xxx') ret = utils.de_camel_case('XXX') self.assertEqual(ret, 'XXX') ret = utils.de_camel_case('NON Camel Case') self.assertEqual(ret, 'NON Camel Case') def test_list_to_dict(self): Item = collections.namedtuple('Item', 'id') ret = utils.list_to_dict([Item('foo'), Item('bar'), Item('bar')]) self.assertEqual(ret, {'foo': Item('foo'), 'bar': Item('bar')}) def test_length(self): ret = utils.length(iter([])) self.assertEqual(ret, 0) ret = utils.length(iter([1, 2, 3])) self.assertEqual(ret, 3) def test_check_image_type(self): Image = collections.namedtuple('Image', 'properties') ret = utils.check_image_type(Image({'type': 'Picasso'}), 'Picasso') self.assertTrue(ret) ret = utils.check_image_type(Image({'type': 'Picasso'}), 'Van Gogh') self.assertFalse(ret) ret = utils.check_image_type(Image({}), 'Van Gogh') self.assertTrue(ret) def test_filter_items(self): Item = collections.namedtuple('Item', 'index') items = [Item(i) for i in range(7)] ret = list(utils.filter_items(items, index=1)) self.assertEqual(ret, [Item(1)]) ret = list(utils.filter_items(items, index__in=(1, 2, 3))) self.assertEqual(ret, [Item(1), Item(2), Item(3)]) ret = list(utils.filter_items(items, index__not_in=(1, 2, 3))) self.assertEqual(ret, [Item(0), Item(4), Item(5), Item(6)]) def test_safe_int_cast(self): ret = utils.safe_int_cast(1) self.assertEqual(ret, 1) ret = utils.safe_int_cast('1') self.assertEqual(ret, 1) ret = utils.safe_int_cast('') self.assertEqual(ret, 0) ret = utils.safe_int_cast(None) self.assertEqual(ret, 0) ret = utils.safe_int_cast(object()) self.assertEqual(ret, 0) watcher-dashboard-3.0.0/watcher_dashboard/utils/errors.py0000664000175000017500000000563713656752043023636 0ustar zuulzuul00000000000000# -*- coding: utf8 -*- # # 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): # XXX This is an ugly hack for finding the 'request' argument. if request_arg is None: for _request_arg, name in enumerate(inspect.getargspec(func).args): if name == 'request': break else: 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: request = args[_request_arg] horizon.exceptions.handle(request, _error_message, ignore=_error_ignore, redirect=_error_redirect) return _error_default wrapper.wrapped = func return wrapper return decorator watcher-dashboard-3.0.0/watcher_dashboard/templates/0000775000175000017500000000000013656752140022571 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/horizon/0000775000175000017500000000000013656752140024261 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/horizon/common/0000775000175000017500000000000013656752140025551 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/horizon/common/_items_count_domain_page_header.htmlwatcher-dashboard-3.0.0/watcher_dashboard/templates/horizon/common/_items_count_domain_page_header.h0000664000175000017500000000066113656752043034252 0ustar zuulzuul00000000000000{% load i18n %} {% block page_header %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/client_side/0000775000175000017500000000000013656752140025053 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/client_side/_modal_chart.html0000664000175000017500000000116613656752043030363 0ustar zuulzuul00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}modal_chart_template{% endblock %} {% block template %} {% jstemplate %} {% endjstemplate %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/client_side/templates.html0000664000175000017500000000005613656752043027742 0ustar zuulzuul00000000000000{% include "client_side/_modal_chart.html" %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/0000775000175000017500000000000013656752140025100 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions/0000775000175000017500000000000013656752140026540 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions/index.html0000664000175000017500000000056113656752043030541 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Actions' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Actions') items_count=actions_count %} {% endblock page_header %} {% block main %}
{{ wactions_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions/details.html0000664000175000017500000000241513656752043031057 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Actions: ' %}{{ action.uuid }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Actions: ')|add:action.uuid %} {% endblock page_header %} {% block main %}

{% trans "Action Info" %}

{% trans "UUID" %}
{{ action.uuid|default:_("-") }}
{% trans "Type" %}
{{ action.action_type|default:_("-") }}
{% url 'horizon:admin:action_plans:detail' action.action_plan_uuid as action_plan_url %}
{% trans "Action Plan" %}
{{ action.action_plan_uuid|default:_("-") }}
{% trans "State" %}
{{ action.state|default:_("-") }}
{% trans "Created At" %}
{{ action.created_at|parse_isotime|default:_("-") }}
{% trans "Update At" %}
{{ action.updated_at|parse_isotime|default:_("-") }}
{{ parameters_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/base_detail.html0000664000175000017500000000075013656752043030226 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% block main %}
{% block breadcrumbs %}{% endblock %}
{% block actions %}{% endblock %}

{% block name %}{% endblock %}

{% block overall_usage %}{% endblock %}
{{ tab_group.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_top_5_chart.html0000664000175000017500000000107613656752043030342 0ustar zuulzuul00000000000000{% load i18n %} {% load chart_helpers %}

{% trans 'Top 5 Nodes' %} ({{ top_5.label }}):

{% if top_5.data %} {% for d in top_5.data %} {% endfor %}
{{ d.node_uuid|truncatechars:6 }} {{ d.value}} {{ top_5.unit }} {%if d.direction %} {% endif %}
{% else %} {% trans 'No data available.' %} {% endif %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/action_plans/0000775000175000017500000000000013656752140027552 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/action_plans/create.html0000664000175000017500000000051613656752043031707 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans "Create Flavor" %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=_("Create Action Plan") %} {% endblock page_header %} {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/action_plans/index.html0000664000175000017500000000061213656752043031550 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Action Plans' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Action Plans') items_count=action_plans_count %} {% endblock page_header %} {% block main %}
{{ action_plans_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/action_plans/details.html0000664000175000017500000000070413656752043032070 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Action Plan Details"%}{% endblock %} {% block main %}
{% include "infra_optim/action_plans/_details_overview.html" %}
{{ related_efficacy_indicators_table.render }}
{{ related_wactions_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/action_plans/_details_overview.html0000664000175000017500000000141113656752043034151 0ustar zuulzuul00000000000000{% load i18n sizeformat %}

{% trans "Action Plan Overview" %}

{% trans "ID" %}
{{ action_plan.uuid|default:_("-") }}
{% url 'horizon:admin:audits:detail' action_plan.audit_uuid as audit_url %}
{% trans "Audit ID" %}
{{ action_plan.audit_uuid|default:_("-") }}
{% trans "State" %}
{{ action_plan.state|default:_("-") }}
{% trans "Created At" %}
{{ action_plan.created_at|parse_isotime|default:_("-") }}
{% trans "Update At" %}
{{ action_plan.updated_at|parse_isotime|default:_("-") }}
watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_top_5_box.html0000664000175000017500000000074413656752043030032 0ustar zuulzuul00000000000000
{% include "infra_optim/_top_5_chart.html" with top_5=top_5.fan%}
{% include "infra_optim/_top_5_chart.html" with top_5=top_5.voltage %}
{% include "infra_optim/_top_5_chart.html" with top_5=top_5.temperature%}
{% include "infra_optim/_top_5_chart.html" with top_5=top_5.current %}
watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_fullscreen_workflow.html0000664000175000017500000000317013656752043032224 0ustar zuulzuul00000000000000{% load i18n %} {% with workflow.get_entry_point as entry_point %}
{% csrf_token %} {% if REDIRECT_URL %}{% endif %}
{% block workflow-buttons %} {% endblock %}
{% block workflow-body %}
{% for step in workflow.steps %}
{{ step.render }}
{% if not forloop.last %} {% endif %} {% endfor %}
{% endblock %}
{% endwith %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audit_templates/0000775000175000017500000000000013656752140030264 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audit_templates/create.html0000664000175000017500000000030513656752043032415 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Template" %}{% endblock %} {% block main %} {% include 'infra_optim/audit_templates/_create.html' %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audit_templates/index.html0000664000175000017500000000060413656752043032263 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audit Templates' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Audit Templates') items_count=audit_templates_count %} {% endblock page_header %} {% block main %}
{{ audit_templates_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audit_templates/details.html0000664000175000017500000000263613656752043032610 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audit Templates: ' %}{{ audit_template.name }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Audit Templates: ')|add:audit_template.name %} {% endblock page_header %} {% block main %}

{% trans "Audit Template Info" %}

{% trans "Name" %}
{{ audit_template.name|default:_("-") }}
{% trans "UUID" %}
{{ audit_template.uuid|default:_("-") }}
{% trans "Goal" %}
{{ audit_template.goal_name|default:_("-") }}
{% trans "Strategy" %}
{{ audit_template.strategy_name|default:_("-") }}
{% trans "Audit Scope" %}
{{ audit_template.scope|linebreaks }}
{% trans "Created At" %}
{{ audit_template.created_at|parse_isotime|default:_("-") }}
{% trans "Update At" %}
{{ audit_template.updated_at|parse_isotime|default:_("-") }}
{% trans "Deleted At" %}
{{ audit_template.deleted_at|parse_isotime|default:_("-") }}
{{ table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audit_templates/_create.html0000664000175000017500000000301013656752043032550 0ustar zuulzuul00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Creates an audit template with specified parameters." %}

{% trans "Define the optimization goal to achieve, among those which are available." %} {% trans "Optionally, you can select the strategy used to achieve your goal. If not set, a strategy will be automatically selected among those which can be used for your goal" %}

{% trans "Audit scope:" %}

{% trans "Creates an audit template with specified parameters." %}

{% trans "YAML example:" %} --- - compute: - host_aggregates: - id: 1 - id: 2 - id: 3 - availability_zones: - name: AZ1 - name: AZ2 - exclude: - instances: - uuid: UUID1 - uuid: UUID2 - compute_nodes: - name: compute1

{% trans "JSON example:" %} [{'compute': [{'host_aggregates': [ {'id': 1}, {'id': 2}, {'id': 3}]}, {'availability_zones': [ {'name': 'AZ1'}, {'name': 'AZ2'}]}, {'exclude': [ {'instances': [ {'uuid': 'UUID1'}, {'uuid': 'UUID2'} ]}, {'compute_nodes': [ {'name': 'compute1'} ]} ]}] }]

{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/logs/0000775000175000017500000000000013656752140026044 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/logs/index.html0000664000175000017500000000060713656752043030046 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Logs' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Logs') items_count=flavors_count %} {% endblock page_header %} {% block main %}
{{ table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_workflow_base.html0000664000175000017500000000050013656752043030766 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans workflow.name %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=workflow.name %} {% endblock page_header %} {% block main %} {% include 'horizon/common/_workflow.html' %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/strategies/0000775000175000017500000000000013656752140027252 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/strategies/index.html0000664000175000017500000000054413656752043031254 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Strategies' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Strategies') items_count=strategy_count %} {% endblock page_header %} {% block main %}
{{ strategies_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/strategies/details.html0000664000175000017500000000222513656752043031570 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Strategies: ' %}{{ strategy.display_name }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Strategies: ')|add:strategy.display_name %} {% endblock page_header %} {% block main %}

{% trans "Strategy Info" %}

{% trans "UUID" %}
{{ strategy.uuid|default:_("-") }}
{% trans "Name" %}
{{ strategy.display_name|default:_("-") }}
{% trans "Goal" %}
{% url 'horizon:admin:goals:detail' strategy.goal_name as goal_url %}
{{ strategy.goal_name|default:_("-") }}
{% trans "Created At" %}
{{ strategy.created_at|parse_isotime|default:_("-") }}
{% trans "Update At" %}
{{ strategy.updated_at|parse_isotime|default:_("-") }}
{{ table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/header_actions.html0000664000175000017500000000061213656752043030737 0ustar zuulzuul00000000000000 watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_performance_chart_box.html0000664000175000017500000000514413656752043032465 0ustar zuulzuul00000000000000{% load i18n %} {% if meter_conf %}
{% trans "From" %}
{% trans "To" %}
{% for meter_label, url_part, y_max in meter_conf %}
{% include "infra_optim/_performance_chart.html" with label=meter_label y_max=y_max url=node_perf_url|add:"?"|add:url_part only %}
{% endfor %}
{% else %}

{% trans 'Metering service is not enabled.' %}

{% endif %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audits/0000775000175000017500000000000013656752140026371 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audits/create.html0000664000175000017500000000027013656752043030523 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Audit" %}{% endblock %} {% block main %} {% include 'infra_optim/audits/_create.html' %} {% endblock %}watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audits/index.html0000664000175000017500000000052713656752043030374 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audits' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Audits') items_count=audits_count %} {% endblock page_header %} {% block main %}
{{ audits_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audits/details.html0000664000175000017500000000260513656752043030711 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audits: ' %}{{ audit.uuid }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Audits: ')|add:audit.uuid %} {% endblock page_header %} {% block main %}

{% trans "Audit Info" %}

{% trans "UUID" %}
{{ audit.uuid|default:_("-") }}
{% trans "Goal" %}
{{ audit.goal_name|default:_("-") }}
{% trans "Strategy" %}
{{ audit.strategy_name|default:_("-") }}
{% trans "Type" %}
{{ audit.audit_type|default:_("-") }}
{% trans "Auto Trigger" %}
{{ audit.auto_trigger }}
{% url 'horizon:admin:audit_templates:detail' audit.audit_template_uuid as audit_template_url %}
{% trans "State" %}
{{ audit.state|default:_("-") }}
{% trans "Created At" %}
{{ audit.created_at|parse_isotime|default:_("-") }}
{% trans "Update At" %}
{{ audit.updated_at|parse_isotime|default:_("-") }}
{{ related_action_plans_table.render }}
{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/audits/_create.html0000664000175000017500000000053213656752043030663 0ustar zuulzuul00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Creates a audit with specified parameters." %}

{% trans "If you check 'Auto Trigger' option, the action plan, recommended by the audit, will be automatically started." %}

{% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/qunit.html0000664000175000017500000001572213656752043027137 0ustar zuulzuul00000000000000 Watcher QUnit Test Suite {% include "horizon/_conf.html" %} {% comment %}Load test modules here.{% endcomment %} {% comment %}End test modules.{% endcomment %} {% include "horizon/_scripts.html" %} {% include "infra_optim/_scripts.html" %}

Watcher JavaScript Tests

    Flavors

    Flavor Name VCPU RAM (MB) Root Disk (GB) Ephemeral Disk (GB) Swap Disk (MB) Max. VMs Delete
    -
    -
    -
    Displaying 3 items
    watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_performance_chart.html0000664000175000017500000000077613656752043031623 0ustar zuulzuul00000000000000

    {{ label }}

    watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/goals/0000775000175000017500000000000013656752140026205 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/goals/index.html0000664000175000017500000000052113656752043030202 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Goals' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Goals') items_count=goal_count %} {% endblock page_header %} {% block main %}
    {{ goals_table.render }}
    {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/goals/details.html0000664000175000017500000000204413656752043030522 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Goals: ' %}{{ goal.display_name }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Goals: ')|add:goal.display_name %} {% endblock page_header %} {% block main %}

    {% trans "Goal Info" %}

    {% trans "UUID" %}
    {{ goal.uuid|default:_("-") }}
    {% trans "Name" %}
    {{ goal.display_name|default:_("-") }}
    {% trans "Created At" %}
    {{ goal.created_at|parse_isotime|default:_("-") }}
    {% trans "Update At" %}
    {{ goal.updated_at|parse_isotime|default:_("-") }}
    {{ efficacy_specification_table.render }}
    {{ related_strategies_table.render }}
    {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/_fullscreen_workflow_base.html0000664000175000017500000000051013656752043033211 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans workflow.name %}{% endblock %} {% block page_header %} {% include "horizon/common/_page_header.html" with title=workflow.name %} {% endblock page_header %} {% block main %} {% include 'infra_optim/_fullscreen_workflow.html' %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions_history/0000775000175000017500000000000013656752140030321 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions_history/index.html0000664000175000017500000000164713656752043032330 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audits' %}{% endblock %} {% block page_header %} {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Audits') items_count=flavors_count %} {% endblock page_header %} {% block main %} {% if suggested_flavors_count %}
    {{ suggested_flavors_count }}× Suggested Flavor
    {{ suggested_flavors_table.render }}
    {% endif %}
    {{ audits_table.render }}
    {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/actions_history/details.html0000664000175000017500000000166113656752043032642 0ustar zuulzuul00000000000000{% extends 'infra_optim/base.html' %} {% load i18n %} {% block title %}{% trans 'Audits: ' %}{{ audits.name }}{% endblock %} {% block page_header %} {% include 'horizon/common/_page_header.html' with title=_('Audits: ')|add:audits.name %} {% endblock page_header %} {% block main %}

    {% trans "Hardware Info" %}

    {% trans "Severity" %}
    {{ audits.cpu_arch|default:_("-") }}
    {% trans "CPUs" %}
    {{ audits.vcpus|default:_("-") }}
    {% trans "Memory" %}
    {{ audits.ram_bytes|filesizeformat|default:_("-") }}
    {% trans "Disk" %}
    {{ audits.disk_bytes|filesizeformat|default:_("-") }}
    {{ table.render }}
    {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/infra_optim/base.html0000664000175000017500000000060213656752043026700 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% block css %} {{block.super}} {% load compress %} {% compress css %} {% endcompress %} {% endblock %} watcher-dashboard-3.0.0/watcher_dashboard/templates/formset_table/0000775000175000017500000000000013656752140025417 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templates/formset_table/_row.html0000664000175000017500000000131213656752043027252 0ustar zuulzuul00000000000000 {% for cell in row %} {% if cell.field %} {{ cell.field }} {% else %} {%if cell.wrap_list %}
      {% endif %}{{ cell.value }}{%if cell.wrap_list %}
    {% endif %} {% endif %} {% if forloop.first %} {% for field in row.form.hidden_fields %} {{ field }} {% for error in field.errors %} {{ field.name }}: {{ error }} {% endfor %} {% endfor %} {% if row.form.non_field_errors %}
    {{ row.form.non_field_errors }}
    {% endif %} {% endif %} {% endfor %} watcher-dashboard-3.0.0/watcher_dashboard/templates/formset_table/menu_formset.html0000664000175000017500000000302713656752043031014 0ustar zuulzuul00000000000000{% load i18n %} {{ formset.management_form }} {% for error in formset.non_form_errors %}
    {{ error }}
    {% endfor %}

    Nodes to register

    {% include 'infra_optim/nodes/_upload.html' with form=upload_form %}
    {% for form in formset %} {% include form_template with form=form active=forloop.first %} {% endfor %}
    watcher-dashboard-3.0.0/watcher_dashboard/templates/formset_table/_table.html0000664000175000017500000000257413656752043027545 0ustar zuulzuul00000000000000{% extends 'horizon/common/_data_table.html' %} {% load i18n %} {% block table_columns %} {% if not table.is_browser_table %} {% for column in columns %} {{ column }} {% endfor %} {% endif %} {% endblock table_columns %} {% block table %} {% with table.get_formset as formset %} {{ formset.management_form }} {% if formset.non_field_errors %}
    {{ formset.non_field_errors }}
    {% endif %} {% endwith %} {{ block.super }} {% endblock table %} watcher-dashboard-3.0.0/watcher_dashboard/common/0000775000175000017500000000000013656752140022063 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/common/__init__.py0000664000175000017500000000000013656752043024164 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/common/exceptions.py0000664000175000017500000000150613656752043024622 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 from watcherclient.common.apiclient import exceptions as watcherclient NOT_FOUND = exceptions.NOT_FOUND RECOVERABLE = exceptions.RECOVERABLE + ( watcherclient.ClientException, ) UNAUTHORIZED = exceptions.UNAUTHORIZED watcher-dashboard-3.0.0/watcher_dashboard/local/0000775000175000017500000000000013656752140021665 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/local/__init__.py0000664000175000017500000000000013656752043023766 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/0000775000175000017500000000000013656752140023257 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/__init__.py0000664000175000017500000000000013656752043025360 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31000_goals_panel.py0000664000175000017500000000162513656752043027005 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'goals' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.goals.panel.Goals' watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31050_action_plans_panel.py0000664000175000017500000000165113656752043030356 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'action_plans' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.action_plans.panel.ActionPlans' watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31040_audits_panel.py0000664000175000017500000000163013656752043027171 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'audits' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.audits.panel.Audits' watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31060_actions_panel.py0000664000175000017500000000163313656752043027345 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'actions' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.actions.panel.Actions' watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31030_audit_templates_panel.py0000664000175000017500000000166213656752043031070 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'audit_templates' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.audit_templates.panel.AuditTemplates' watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31020_watcher_panelgroup.py0000664000175000017500000000222713656752043030413 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.utils.translation import ugettext_lazy as _ # The slug of the panel group to be added to HORIZON_CONFIG. Required. PANEL_GROUP = 'watcher' # The display name of the PANEL_GROUP. Required. PANEL_GROUP_NAME = _('Optimization') # The slug of the dashboard the PANEL_GROUP associated with. Required. PANEL_GROUP_DASHBOARD = 'admin' ADD_INSTALLED_APPS = ['watcher_dashboard'] # ADD_ANGULAR_MODULES = [ # 'horizon.dashboard.watcher' # ] # ADD_JS_FILES = [ # 'horizon/lib/angular/angular-route.js' # ] # ADD_SCSS_FILES = [ # 'dashboard/watcher/watcher.scss' # ] AUTO_DISCOVER_STATIC_FILES = False watcher-dashboard-3.0.0/watcher_dashboard/local/enabled/_31010_strategies_panel.py0000664000175000017500000000164413656752043030054 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'strategies' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'watcher' # Python panel class of the PANEL to be added. ADD_PANEL = 'watcher_dashboard.content.strategies.panel.Strategies' watcher-dashboard-3.0.0/watcher_dashboard/static/0000775000175000017500000000000013656752140022062 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/0000775000175000017500000000000013656752140024371 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/tests/0000775000175000017500000000000013656752140025533 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/tests/formset_table.js0000664000175000017500000000452213656752043030724 0ustar zuulzuul00000000000000horizon.addInitFunction(function () { module("Formset table (watcher.formset_table.js)"); test("Reenumerate rows", function () { var html = $('#qunit-fixture'); var table = html.find('table'); var input = table.find('tbody tr#flavors__row__14 input').first(); input.attr('id', 'id_flavors-3-name'); watcher.formset_table.reenumerate_rows(table, 'flavors'); equal(input.attr('id'), 'id_flavors-0-name', "Enumerate old rows ids"); input.attr('id', 'id_flavors-__prefix__-name'); watcher.formset_table.reenumerate_rows(table, 'flavors'); equal(input.attr('id'), 'id_flavors-0-name', "Enumerate new rows ids"); }); test("Delete row", function () { var html = $('#qunit-fixture'); var table = html.find('table'); var row = table.find('tbody tr').first(); var input = row.find('input#id_flavors-0-DELETE'); equal(row.css("display"), 'table-row'); equal(input.attr('checked'), undefined); watcher.formset_table.replace_delete(row); var x = input.next('a'); watcher.formset_table.delete_row.call(x); equal(row.css("display"), 'none'); equal(input.attr('checked'), 'checked'); }); test("Add row", function() { var html = $('#qunit-fixture'); var table = html.find('table'); var empty_row_html = ''; equal(table.find('tbody tr').length, 3); equal(html.find('#id_flavors-TOTAL_FORMS').val(), 3); watcher.formset_table.add_row(table, 'flavors', empty_row_html); equal(table.find('tbody tr').length, 4); equal(table.find('tbody tr:last input').attr('id'), 'id_flavors-3-name'); equal(html.find('#id_flavors-TOTAL_FORMS').val(), 4); }); test("Init formset table", function() { var html = $('#qunit-fixture'); var table = html.find('table'); watcher.formset_table.init('flavors', '', 'Add row'); equal(table.find('tfoot tr a').html(), 'Add row'); }); test("Init formset table -- no add", function() { var html = $('#qunit-fixture'); var table = html.find('table'); watcher.formset_table.init('flavors', '', ''); equal(table.find('tfoot tr a').length, 0); }); }); watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/images/0000775000175000017500000000000013656752140025636 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/images/chevron.png0000664000175000017500000000053413656752043030014 0ustar zuulzuul00000000000000‰PNG  IHDRH-ŃbKGD˙˙˙ ˝§“ pHYs  šśtIMEŢ(v˙+MiTXtCommentCreated with GIMPd.eŔIDAT(ĎŐŇ;‚@ŕ9»Ł Ń­ŐŢ•1ţo6Ź ±Q üD"caŚ$ž ŤqÚÝŻě ’D…ÔP1?„á1Â-Ëľ.’Äţ"Ďó7LÓÁf Çťj1Ixó6ۢÓů MÓ„=čá~Ďá¸3d%Lžż„Š´šşť6@”ϡT Ď_BJ‰ÉxĂO¤b´šěaBOXƆ”¨7ęH’ëŇÂ2&©EŻÚ\TĚŐ:`QÚąřź—{Yvť' čHďIEND®B`‚watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/images/power.png0000664000175000017500000000113613656752043027503 0ustar zuulzuul00000000000000‰PNG  IHDRŕw=řbKGD˙˙˙ ˝§“ pHYs  šśtIMEŢ 30cUiTXtCommentCreated with GIMPd.eÂIDATHǵ–ŰjQ†ż˝g5™LHČ•¶`L,H/Š1Ö¤ÚńAEÖZ”RéX V×h=~B·ŰEDŇ6°„aČŹźm®ß¸É‹—ëôú}´žĽŁ2‚42™ ź>oóm÷;§Ë§Xn6‘±©+»RŠ0 Ůţň•ťť]šÍ‹T+2™™‘ôéIٶXÖ_mpçŢ}Ţřă8éüťČ÷}6^żáÖí»´ŰŚ1±Ä%šÇqř˝·ÇZë«[t:ż™$4ĄÖZ‚ Äř‡ $‰¸17—ceąÁěÜ31tÉ´{ĆułĚĎźa©^?HG”L*EőZŤ……łxž‡1濨ʸÂ"Âěɬ\ľ„çyDQ4Ö°Ť4ĂjµÂ…ó5JĄŇAŠD«B)…ÖšBˇ@c©Ną\&‚©nĆ‘ ®]˝B>źGD‚`jŇŽ4(‹˙\¦T×uš%ű=OząâŢ@Ă!›[[Çňm ‡üÜ˝˛‰}ƦIEND®B`‚watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/scss/0000775000175000017500000000000013656752140025344 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/static/infra_optim/scss/infra_optim.scss0000664000175000017500000000020313656752043030545 0ustar zuulzuul00000000000000// /* Additional CSS for infra_optim. */ // @import "/dashboard/scss/variables"; // @import "/bootstrap/scss/bootstrap/variables"; watcher-dashboard-3.0.0/watcher_dashboard/api/0000775000175000017500000000000013656752140021344 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/api/watcher.py0000664000175000017500000003767413656752043023376 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from django.conf import settings from django.utils.translation import ugettext_lazy as _ from openstack_dashboard.api import base from watcherclient import client as wc from watcher_dashboard.utils import errors as errors_utils LOG = logging.getLogger(__name__) WATCHER_SERVICE = 'infra-optim' def watcherclient(request, password=None): api_version = "1" insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_file = getattr(settings, 'OPENSTACK_SSL_CACERT', None) insert_watcher_policy_file() endpoint = base.url_for(request, WATCHER_SERVICE) LOG.debug('watcherclient connection created using token "%s" and url "%s"' % (request.user.token.id, endpoint)) client = wc.get_client( api_version, watcher_url=endpoint, insecure=insecure, ca_file=ca_file, username=request.user.username, password=password, os_auth_token=request.user.token.id ) return client def insert_watcher_policy_file(): policy_files = getattr(settings, 'POLICY_FILES', {}) policy_files['infra-optim'] = 'watcher_policy.json' setattr(settings, 'POLICY_FILES', policy_files) class Audit(base.APIDictWrapper): _attrs = ('uuid', 'name', 'created_at', 'modified_at', 'deleted_at', 'state', 'audit_type', 'audit_template_uuid', 'audit_template_name', 'interval') def __init__(self, apiresource, request=None): super(Audit, self).__init__(apiresource) self._request = request @classmethod def create(cls, request, name, audit_template_uuid, audit_type, auto_trigger=False, interval=None): """Create an audit in Watcher :param request: request object :type request: django.http.HttpRequest :param audit_template_uuid: related audit template UUID :type audit_template_uuid: string :param audit_type: audit type :type audit_type: string :param interval: Audit interval (default: None) :type interval: int :param name: Name for this audit :type name: string :return: the created Audit object :rtype: :py:class:`~.Audit` """ if interval: return watcherclient(request).audit.create( audit_template_uuid=audit_template_uuid, audit_type=audit_type, auto_trigger=auto_trigger, interval=interval, name=name) else: return watcherclient(request).audit.create( audit_template_uuid=audit_template_uuid, audit_type=audit_type, auto_trigger=auto_trigger, name=name) @classmethod def list(cls, request, **filters): """Return a list of audits in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of audits, or an empty list if there are none :rtype: list of :py:class:`~.Audit` """ return watcherclient(request).audit.list(detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve audit")) def get(cls, request, audit_id): """Return the audit that matches the ID :param request: request object :type request: django.http.HttpRequest :param audit_id: id of audit to be retrieved :type audit_id: int :return: matching audit, or None if no audit matches the ID :rtype: :py:class:`~.Audit` """ return watcherclient(request).audit.get(audit=audit_id) @classmethod def delete(cls, request, audit_id): """Delete an audit :param request: request object :type request: django.http.HttpRequest :param audit_id: audit id :type audit_id: int """ return watcherclient(request).audit.delete(audit=audit_id) @property def id(self): return self.uuid class AuditTemplate(base.APIDictWrapper): _attrs = ('uuid', 'description', 'scope', 'name', 'goal_uuid', 'goal_name', 'strategy_uuid', 'strategy_name', 'created_at', 'updated_at', 'deleted_at') def __init__(self, apiresource, request=None): super(AuditTemplate, self).__init__(apiresource) self._request = request @classmethod def create(cls, request, name, goal, strategy, description, scope): """Create an audit template in Watcher :param request: request object :type request: django.http.HttpRequest :param name: Name for this audit template :type name: string :param goal: Goal UUID or name associated to this audit template :type goal: string :param strategy: Strategy UUID or name associated to this audit template :type strategy: string :param description: Descrition of the audit template :type description: string :param scope: Audit scope :type scope: list of list of dict :param audit_template: audit template :type audit_template: string :return: the created Audit Template object :rtype: :py:class:`~.AuditTemplate` """ audit_template = watcherclient(request).audit_template.create( name=name, goal=goal, strategy=strategy, description=description, scope=scope, ) return audit_template @classmethod def patch(cls, request, audit_template_id, parameters): """Update an audit in Watcher :param request: request object :type request: django.http.HttpRequest :param audit_template_id: id of the audit template we want to update :type audit_template_id: string :param parameters: new values for the audit template's parameters :type parameters: dict :return: the updated Audit Template object :rtype: :py:class:`~.AuditTemplate` """ parameter_list = [{ 'name': str(name), 'value': str(value), } for (name, value) in parameters.items()] audit_template = watcherclient(request).audit_template.patch( audit_template_id, parameter_list) return audit_template @classmethod def list(cls, request, **filters): """Return a list of audit templates in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of audit templates, or an empty list if there are none :rtype: list of :py:class:`~.AuditTemplate` """ return watcherclient(request).audit_template.list( detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve audit template")) def get(cls, request, audit_template_id): """Return the audit template that matches the ID :param request: request object :type request: django.http.HttpRequest :param audit_template_id: id of audit template to be retrieved :type audit_template_id: int :return: matching audit template, or None if no audit template matches the ID :rtype: :py:class:`~.AuditTemplate` """ return watcherclient(request).audit_template.get( audit_template_id=audit_template_id) @classmethod def delete(cls, request, audit_template_id): """Delete an audit_template :param request: request object :type request: django.http.HttpRequest :param audit_template_id: audit id :type audit_template_id: int """ watcherclient(request).audit_template.delete( audit_template_id=audit_template_id) @property def id(self): return self.uuid class ActionPlan(base.APIDictWrapper): _attrs = ('uuid', 'created_at', 'updated_at', 'deleted_at', 'audit_uuid', 'state') def __init__(self, apiresource, request=None): super(ActionPlan, self).__init__(apiresource) self._request = request @classmethod def list(cls, request, **filters): """Return a list of action plans in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of action plans, or an empty list if there are none :rtype: list of :py:class:`~.ActionPlan` """ return watcherclient(request).action_plan.list(detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve action plan")) def get(cls, request, action_plan_id): """Return the action plan that matches the ID :param request: request object :type request: django.http.HttpRequest :param action_plan_id: id of action plan to be retrieved :type action_plan_id: int :return: matching action plan, or None if no action plan matches the ID :rtype: :py:class:`~.ActionPlan` """ return watcherclient(request).action_plan.get( action_plan_id=action_plan_id) @classmethod def delete(cls, request, action_plan_id): """Delete an action plan :param request: request object :type request: django.http.HttpRequest :param action_plan_id: audit id :type action_plan_id: int """ watcherclient(request).action_plan.delete( action_plan_id=action_plan_id) @classmethod def start(cls, request, action_plan_id): """Start an Action Plan :param request: request object :type request: django.http.HttpRequest :param action_plan_id: audit id :type action_plan_id: int """ watcherclient(request).action_plan.start(action_plan_id) @property def id(self): return self.uuid class Action(base.APIDictWrapper): _attrs = ('uuid', 'created_at', 'updated_at', 'deleted_at', 'next_uuid', 'description', 'state', 'action_plan_uuid', 'action_type', 'applies_to', 'src', 'dst', 'parameter') def __init__(self, apiresource, request=None): super(Action, self).__init__(apiresource) self._request = request @classmethod def list(cls, request, **filters): """Return a list of actions in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of actions, or an empty list if there are none :rtype: list of :py:class:`~.Action` """ return watcherclient(request).action.list(detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve action")) def get(cls, request, action_id): """Return the action that matches the ID :param request: request object :type request: django.http.HttpRequest :param action_id: id of action to be retrieved :type action_id: int :return: matching action, or None if no action matches the ID :rtype: :py:class:`~.Action` """ return watcherclient(request).action.get(action_id=action_id) @classmethod def delete(cls, request, action_id): """Delete an action :param request: request object :type request: django.http.HttpRequest :param action_id: action_plan id :type action_id: int """ watcherclient(request).action.delete( action_id=action_id) @classmethod def start(cls, request, action_id): """Start an Action Plan :param request: request object :type request: django.http.HttpRequest :param action_id: action_plan id :type action_id: int """ patch = [] patch.append({'op': 'replace', 'path': '/state', 'value': 'PENDING'}) watcherclient(request).action.update(action_id, patch) @property def id(self): return self.uuid class Goal(base.APIDictWrapper): """Goal resource.""" _attrs = ('uuid', 'name', 'display_name', 'created_at', 'updated_at', 'deleted_at', 'efficacy_specifications') def __init__(self, apiresource, request=None): super(Goal, self).__init__(apiresource) self._request = request @classmethod def list(cls, request, **filters): """Return a list of goals in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of goals, or an empty list if there are none :rtype: list of :py:class:`~.Goal` instance """ return watcherclient(request).goal.list(detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve goal")) def get(cls, request, goal): """Return the goal that matches the ID :param request: request object :type request: django.http.HttpRequest :param goal: uuid of goal to be retrieved :type goal: int :return: matching goal, or None if no goal matches the UUID :rtype: :py:class:`~.Goal` instance """ return watcherclient(request).goal.get(goal) @property def id(self): return self.uuid class Strategy(base.APIDictWrapper): """Strategy resource.""" _attrs = ('uuid', 'name', 'display_name', 'goal_uuid', 'goal_name', 'created_at', 'updated_at', 'deleted_at') def __init__(self, apiresource, request=None): super(Strategy, self).__init__(apiresource) self._request = request @classmethod def list(cls, request, **filters): """Return a list of strategies in Watcher :param request: request object :type request: django.http.HttpRequest :param filters: key/value kwargs used as filters :type filters: dict :return: list of strategies, or an empty list if there are none :rtype: list of :py:class:`~.Strategy` instances """ return watcherclient(request).strategy.list(detail=True, **filters) @classmethod @errors_utils.handle_errors(_("Unable to retrieve strategy")) def get(cls, request, strategy): """Return the strategy that matches the UUID :param request: request object :type request: django.http.HttpRequest :param strategy: uuid of strategy to be retrieved :type strategy: str :return: matching strategy, or None if no strategy matches the UUID :rtype: :py:class:`~.Strategy` instance """ return watcherclient(request).strategy.get(strategy) @property def id(self): return self.uuid class EfficacyIndicatorSpec(base.APIDictWrapper): attrs = ('name', 'description', 'unit', 'schema') class EfficacyIndicator(base.APIDictWrapper): def __init__(self, indicator): super(EfficacyIndicator, self).__init__(indicator) self.value = getattr(indicator, 'value', None) self.name = getattr(indicator, 'name', None) self.description = getattr(indicator, 'description', None) self.unit = getattr(indicator, 'unit', None) watcher-dashboard-3.0.0/watcher_dashboard/api/__init__.py0000664000175000017500000000000013656752043023445 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/0000775000175000017500000000000013656752140021552 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/__init__.py0000664000175000017500000000000013656752043023653 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/test_data/0000775000175000017500000000000013656752140023522 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/test_data/utils.py0000664000175000017500000000363613656752043025246 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 keystone_data from openstack_dashboard.test.test_data import utils def load_test_data(load_onto=None): from watcher_dashboard.test.test_data import exceptions from watcher_dashboard.test.test_data import watcher_data # The order of these loaders matters, some depend on others. loaders = (exceptions.data, keystone_data.data, watcher_data.data) if load_onto: for data_func in loaders: data_func(load_onto) return load_onto else: return utils.TestData(*loaders) class TestDataContainer(utils.TestDataContainer): def filter(self, filtered=None, **kwargs): """Returns objects in this container """ """whose attributes match the given keyword arguments. """ if filtered is None: filtered = self._objects try: key, value = kwargs.popitem() except KeyError: # We're out of filters, return return filtered def get_match(obj): return key in obj and obj.get(key) == value return self.filter(filtered=filter(get_match, filtered), **kwargs) def delete(self): """Delete the first object from this container and return a list""" self._objects.remove(self._objects[0]) return self._objects watcher-dashboard-3.0.0/watcher_dashboard/test/test_data/__init__.py0000664000175000017500000000000013656752043025623 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/test_data/watcher_data.py0000664000175000017500000001710413656752043026527 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from watcher_dashboard.api import watcher from watcher_dashboard.test.test_data import utils def data(TEST): TEST.service_catalog.append( {"type": "infra-optim", "name": "watcher", "endpoints_links": [], "endpoints": [ {"region": "RegionOne", "adminURL": "http://admin.watcher.example.com:9322", "internalURL": "http://int.watcher.example.com:9322", "publicURL": "http://public.watcher.example.com:9322"}, {"region": "RegionTwo", "adminURL": "http://admin.watcher2.example.com:9322", "internalURL": "http://int.watcher2.example.com:9322", "publicURL": "http://public.watcher2.example.com:9322"}]}, ) TEST.goals = utils.TestDataContainer() TEST.efficacy_specifications = utils.TestDataContainer() TEST.api_goals = utils.TestDataContainer() efficacy_specifications_dict1 = {'name': 'spec1'} efficacy_specifications_dict2 = {'name': 'spec2'} spec1 = watcher.EfficacyIndicatorSpec(efficacy_specifications_dict1) spec2 = watcher.EfficacyIndicatorSpec(efficacy_specifications_dict2) goal_dict1 = { 'uuid': 'gggggggg-1111-1111-1111-gggggggggggg', 'name': 'MINIMIZE_LICENSING_COST', 'display_name': 'Dummy', 'efficacy_specifications': spec1 } goal_dict2 = { 'uuid': 'gggggggg-2222-2222-2222-gggggggggggg', 'name': 'SERVER_CONSOLIDATION', 'display_name': 'Server consolidation', 'efficacy_specifications': spec2 } TEST.api_goals.add(goal_dict1) TEST.api_goals.add(goal_dict2) _goal_dict1 = copy.deepcopy(goal_dict1) _goal_dict2 = copy.deepcopy(goal_dict2) TEST.strategies = utils.TestDataContainer() TEST.api_strategies = utils.TestDataContainer() strategy_dict1 = { 'uuid': 'ssssssss-1111-1111-1111-ssssssssssss', 'name': 'minimize_licensing_cost1', 'goal_uuid': 'gggggggg-1111-1111-1111-gggggggggggg', 'display_name': 'Fake licensing cost strategy1', } strategy_dict2 = { 'uuid': 'ssssssss-2222-2222-2222-ssssssssssss', 'name': 'minimize_licensing_cost2', 'goal_uuid': 'gggggggg-1111-1111-1111-gggggggggggg', 'display_name': 'Fake licensing cost strategy2', } strategy_dict3 = { 'uuid': 'ssssssss-3333-3333-3333-ssssssssssss', 'name': 'sercon', 'goal_uuid': 'gggggggg-2222-2222-2222-gggggggggggg', 'display_name': 'Fake Sercon', } TEST.api_strategies.add(strategy_dict1) TEST.api_strategies.add(strategy_dict2) TEST.api_strategies.add(strategy_dict3) _strategy_dict1 = copy.deepcopy(strategy_dict1) _strategy_dict2 = copy.deepcopy(strategy_dict2) _strategy_dict3 = copy.deepcopy(strategy_dict3) TEST.audit_templates = utils.TestDataContainer() TEST.api_audit_templates = utils.TestDataContainer() audit_template_dict = { 'uuid': '11111111-1111-1111-1111-111111111111', 'name': 'Audit Template 1', 'description': 'Audit Template 1 description', 'scope': '', 'goal_uuid': 'gggggggg-1111-1111-1111-gggggggggggg', 'strategy_uuid': 'ssssssss-1111-1111-1111-ssssssssssss', } audit_template_dict2 = { 'uuid': '11111111-2222-2222-2222-111111111111', 'name': 'Audit Template 2', 'description': 'Audit Template 2 description', 'scope': '', 'goal_uuid': 'gggggggg-1111-1111-1111-gggggggggggg', 'strategy_uuid': 'ssssssss-2222-2222-2222-ssssssssssss', } audit_template_dict3 = { 'uuid': '11111111-3333-3333-3333-111111111111', 'name': 'Audit Template 1', 'description': 'Audit Template 3 description', 'scope': '', 'goal_uuid': 'gggggggg-2222-2222-2222-gggggggggggg', 'strategy_uuid': None, } TEST.api_audit_templates.add(audit_template_dict) TEST.api_audit_templates.add(audit_template_dict2) TEST.api_audit_templates.add(audit_template_dict3) _audit_template_dict = copy.deepcopy(audit_template_dict) _audit_template_dict2 = copy.deepcopy(audit_template_dict2) _audit_template_dict3 = copy.deepcopy(audit_template_dict3) TEST.audits = utils.TestDataContainer() TEST.api_audits = utils.TestDataContainer() audit_dict = { 'uuid': '22222222-2222-2222-2222-222222222222', 'audit_type': 'ONESHOT', 'name': 'Audit 1', 'audit_template_uuid': '11111111-1111-1111-1111-111111111111', 'interval': None, } audit_dict2 = { 'uuid': '33333333-3333-3333-3333-333333333333', 'audit_type': 'CONTINUOUS', 'name': 'Audit 2', 'audit_template_uuid': '11111111-1111-1111-1111-111111111111', 'interval': 60, } TEST.api_audits.add(audit_dict) TEST.api_audits.add(audit_dict2) _audit_dict = copy.deepcopy(audit_dict) TEST.action_plans = utils.TestDataContainer() TEST.api_action_plans = utils.TestDataContainer() action_plan_dict = { 'uuid': '33333333-3333-3333-3333-333333333333', 'state': 'RECOMMENDED', 'first_action_uuid': '44444444-4444-4444-4444-111111111111', 'audit_uuid': '33333333-3333-3333-3333-333333333333' } TEST.api_action_plans.add(action_plan_dict) _action_plan_dict = copy.deepcopy(action_plan_dict) TEST.actions = utils.TestDataContainer() TEST.api_actions = utils.TestDataContainer() action_dict1 = { 'uuid': '44444444-4444-4444-4444-111111111111', 'state': 'PENDING', 'next_uuid': '44444444-4444-4444-4444-222222222222', 'action_plan_uuid': '33333333-3333-3333-3333-333333333333' } TEST.api_actions.add(action_dict1) action_dict2 = { 'uuid': '44444444-4444-4444-4444-222222222222', 'state': 'PENDING', 'next_uuid': None, 'action_plan_uuid': '33333333-3333-3333-3333-333333333333' } TEST.api_actions.add(action_dict2) action2 = watcher.Action(action_dict2) action1 = watcher.Action(action_dict1) TEST.actions.add(action1) TEST.actions.add(action2) _action_plan_dict['actions'] = [action1, action2] action_plan = watcher.ActionPlan(_action_plan_dict) _audit_dict['action_plans'] = [action_plan] audit = watcher.Audit(_audit_dict) goal1 = watcher.Goal(_goal_dict1) goal2 = watcher.Goal(_goal_dict2) strategy1 = watcher.Strategy(_strategy_dict1) strategy2 = watcher.Strategy(_strategy_dict2) strategy3 = watcher.Strategy(_strategy_dict3) audit_template1 = watcher.AuditTemplate(_audit_template_dict) audit_template2 = watcher.AuditTemplate(_audit_template_dict2) audit_template3 = watcher.AuditTemplate(_audit_template_dict3) TEST.goals.add(goal1) TEST.goals.add(goal2) TEST.strategies.add(strategy1) TEST.strategies.add(strategy2) TEST.strategies.add(strategy3) TEST.audit_templates.add(audit_template1) TEST.audit_templates.add(audit_template2) TEST.audit_templates.add(audit_template3) TEST.audits.add(audit) TEST.action_plans.add(watcher.ActionPlan(action_plan)) watcher-dashboard-3.0.0/watcher_dashboard/test/test_data/exceptions.py0000664000175000017500000000160713656752043026263 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 exceptions from watcherclient.common.apiclient import exceptions as wexceptions def data(TEST): TEST.exceptions = exceptions.data watcher_exception = wexceptions.ClientException TEST.exceptions.watcher = exceptions.create_stubbed_exception( watcher_exception) watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/0000775000175000017500000000000013656752140025317 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/__init__.py0000664000175000017500000000000013656752043027420 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/horizon.conf0000664000175000017500000000341513656752043027663 0ustar zuulzuul00000000000000# # Configuration filed based on Tempest's tempest.conf.sample # [dashboard] # Where the dashboard can be found (string value) dashboard_url=http://localhost:8000/ # Dashboard help page url (string value) help_url=https://docs.openstack.org/ [selenium] # Timeout in seconds to wait for a page to become available # (integer value) page_timeout=30 # Output directory for screenshots. # (string value) screenshots_directory=integration_tests_screenshots # Implicit timeout to wait until element become available, # this timeout is used for every find_element, find_elements call. # (integer value) implicit_wait=10 # Explicit timeout is used for long lasting operations, # methods using explicit timeout are usually prefixed with 'wait', # those methods ignore implicit_wait when looking up web elements. # (integer value) explicit_wait=300 [image] # http accessible image (string value) http_image=http://download.cirros-cloud.net/0.3.1/cirros-0.3.1-x86_64-uec.tar.gz [identity] # Username to use for non-admin API requests. (string value) username=demo # API key to use when authenticating. (string value) password=secretadmin # Administrative Username to use for admin API requests. # (string value) admin_username=admin # API key to use when authenticating as admin. (string value) admin_password=secretadmin [scenario] # ssh username for image file (string value) ssh_user=cirros [launch_instances] #available zone to launch instances available_zone=nova #image_name to launch instances image_name=cirros-0.3.4-x86_64-uec (24.0 MB) [volume] volume_type=lvmdriver-1 volume_size=1 [plugin] is_plugin=true plugin_page_path=watcher_dashboard.test.integration_tests.pages plugin_page_structure={"Admin": {"Optimization": {"_": ["Audit Templates", "Audits", "Action Plans", "Actions"] } } } watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/tests/0000775000175000017500000000000013656752140026461 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/tests/__init__.py0000664000175000017500000000000013656752043030562 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/tests/test_audit_template_panel.py0000664000175000017500000000617213656752043034262 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import uuid from openstack_dashboard.test.integration_tests import helpers class AuditTemplateCreatePanelTests(helpers.AdminTestCase): def test_create_audit_template(self): """Test the audit template panel: * Loads the audit template panel * Creates a new audit template with random-generated name * Checks that this audit template is in list * Deletes this audit template (in tearDown) * Checks that the audit template is removed """ audit_template_name = "audit_template_%s" % uuid.uuid1() audit_template_page = \ self.home_pg.go_to_optimization_audittemplatespage() audit_template_page.create_audit_template(audit_template_name) self.assertTrue(audit_template_page.is_audit_template_present( audit_template_name)) class AuditTemplatePanelTests(helpers.AdminTestCase): def setUp(self): super(AuditTemplatePanelTests, self).setUp() self.audit_template_name = "audit_template_%s" % uuid.uuid1() audit_template_page = \ self.home_pg.go_to_optimization_audittemplatespage() audit_template_page.create_audit_template(self.audit_template_name) def tearDown(self): audit_template_page = \ self.home_pg.go_to_optimization_audittemplatespage() audit_template_page.delete_audit_template(self.audit_template_name) # Uncomment this line when button will be implemented self.assertFalse(audit_template_page.is_audit_template_present( self.audit_template_name)) super(AuditTemplatePanelTests, self).tearDown() def test_show_audit_template_info(self): """Test the audit template panel information page * Loads the audit template panel * Click on link behind the audit template name * Checks the info page (only the "Audit Template Info" title for now) """ audit_template_page = \ self.home_pg.go_to_optimization_audittemplatespage() self.assertTrue( audit_template_page.show_audit_template_info( self.audit_template_name)) def test_launch_audit(self): """Test the audit template panel "Launch Audit" row button * Loads the audit template panel * Click on the button "Launch Audit" * Checks the audits page for audit template name in page """ audit_template_page = \ self.home_pg.go_to_optimization_audittemplatespage() self.assertTrue( audit_template_page.launch_audit(self.audit_template_name)) watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/0000775000175000017500000000000013656752140026416 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/__init__.py0000664000175000017500000000000013656752043030517 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/0000775000175000017500000000000013656752140027506 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/__init__.py0000664000175000017500000000000013656752043031607 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/0000775000175000017500000000000013656752140032234 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/__init__.pywatcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/__init__.p0000664000175000017500000000000013656752043034144 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/audittemplatespage.pywatcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/audittempl0000664000175000017500000001037613656752043034340 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from selenium.webdriver.common import by from openstack_dashboard.test.integration_tests.pages import basepage from openstack_dashboard.test.integration_tests.regions import forms from openstack_dashboard.test.integration_tests.regions import tables class AuditTemplatesTable(tables.TableRegion): name = 'audit_templates' CREATE_AUDIT_TEMPLATE_FORM_FIELDS = ("name", "description", "goal_id", "strategy_id") @tables.bind_table_action('create') def create_audit_template(self, create_button): create_button.click() return forms.FormRegion( self.driver, self.conf, field_mappings=self.CREATE_AUDIT_TEMPLATE_FORM_FIELDS) @tables.bind_table_action('delete') def delete_audit_template(self, delete_button): delete_button.click() return forms.BaseFormRegion(self.driver, self.conf, None) @tables.bind_row_action('launch_audit') def launch_audit(self, launch_button, row): launch_button.click() return forms.BaseFormRegion(self.driver, self.conf) class AudittemplatesPage(basepage.BaseNavigationPage): DEFAULT_DESCRIPTION = "Fake description from integration tests" DEFAULT_GOAL = "SERVER_CONSOLIDATION" AUDITS_PAGE_TITLE = "Audits - OpenStack Dashboard" AUDIT_TEMPLATE_INFO_SUB_TITLE = "Audit Template Info" # Set fields name attribute CREATE_AUDIT_TEMPLATE_FORM_FIELDS = ( "name", "description", "goal" ) _audittemplates_info_title_locator = (by.By.CSS_SELECTOR, 'div.detail>h4') def __init__(self, driver, conf): super(AudittemplatesPage, self).__init__(driver, conf) self._page_title = "Audit Templates" @property def audittemplates_table(self): return AuditTemplatesTable(self.driver, self.conf) @property def audit_templates__action_create_form(self): return forms.FormRegion(self.driver, self.conf, None, self.CREATE_AUDIT_TEMPLATE_FORM_FIELDS) def _get_row_with_audit_template_name(self, name): self._turn_off_implicit_wait() row = self.audittemplates_table.get_row("name", name) self._turn_on_implicit_wait() return row def delete_audit_template(self, name): row = self._get_row_with_audit_template_name(name) row.mark() confirm_delete_audit_template_form = ( self.audittemplates_table.delete_audit_template()) confirm_delete_audit_template_form.submit() def create_audit_template(self, name, description=DEFAULT_DESCRIPTION, goal_id=DEFAULT_GOAL): self.audittemplates_table.create_audit_template() self.audit_templates__action_create_form.name.text = name self.audit_templates__action_create_form.description.text = description self.audit_templates__action_create_form.goal_id.value = goal_id self.audit_templates__action_create_form.submit() def is_audit_template_present(self, name): return bool( self._get_row_with_audit_template_name(name)) def launch_audit(self, name): row = self._get_row_with_audit_template_name(name) self.audittemplates_table.launch_audit(row) # Check that the name appears in Audits page return (self.driver.title == self.AUDITS_PAGE_TITLE) \ and (name in self.driver.page_source) def show_audit_template_info(self, name): self.driver.find_element_by_link_text(name).click() info_line = self._get_element(*self._audittemplates_info_title_locator) return self._is_text_visible( info_line, self.AUDIT_TEMPLATE_INFO_SUB_TITLE) ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/auditspage.pywatcher-dashboard-3.0.0/watcher_dashboard/test/integration_tests/pages/admin/optimization/auditspage0000664000175000017500000000550613656752043034315 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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.integration_tests.regions import forms from openstack_dashboard.test.integration_tests.regions import tables from openstack_dashboard.test.integration_tests.pages import basepage class AuditsTable(tables.TableRegion): name = "audits" @tables.bind_table_action('launch_audit') def launch_audit(self, launch_button): launch_button.click() return forms.BaseFormRegion(self.driver, self.conf) @tables.bind_row_action('go_to_action_plan') def go_to_action_plan(self, goto_button): goto_button.click() return forms.BaseFormRegion(self.driver, self.conf) @tables.bind_row_action('go_to_audit_template') def go_to_audit_template(self, goto_button): goto_button.click() return forms.BaseFormRegion(self.driver, self.conf) class AuditsPage(basepage.BaseNavigationPage): DEFAULT_ID = "auto" AUDIT_TABLE_NAME_COLUMN = 'name' AUDIT_TABLE_TEMPLATE_COLUMN_INDEX = 1 def __init__(self, driver, conf): super(AuditsPage, self).__init__(driver, conf) self._page_title = "Audits" @property def audits_table(self): return AuditsTable(self.driver, self.conf) def _get_audit_row(self, name): return self.audits_table.get_row(self.AUDIT_TABLE_NAME_COLUMN, name) def create_audit(self, name, id_=DEFAULT_ID, vcpus=None, ram=None, root_disk=None, ephemeral_disk=None, swap_disk=None): create_audit_form = self.audits_table.create_audit() create_audit_form.name.text = name if id_ is not None: create_audit_form.audit_id.text = id_ create_audit_form.vcpus.value = vcpus create_audit_form.memory_mb.value = ram create_audit_form.disk_gb.value = root_disk create_audit_form.eph_gb.value = ephemeral_disk create_audit_form.swap_mb.value = swap_disk create_audit_form.submit() self.wait_till_popups_disappear() def delete_audit(self, name): row = self._get_audit_row(name) row.mark() confirm_delete_audits_form = self.audits_table.delete_audit() confirm_delete_audits_form.submit() self.wait_till_popups_disappear() def is_audit_present(self, name): return bool(self._get_audit_row(name)) watcher-dashboard-3.0.0/watcher_dashboard/test/api_tests/0000775000175000017500000000000013656752140023545 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/api_tests/__init__.py0000664000175000017500000000000013656752043025646 0ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/test/api_tests/test_watcher.py0000664000175000017500000003102613656752043026617 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 watcher_dashboard import api from watcher_dashboard.test import helpers as test class WatcherAPITests(test.APITestCase): def test_goal_list(self): goals = {'goals': self.api_goals.list()} watcherclient = self.stub_watcherclient() watcherclient.goal.list = mock.Mock( return_value=goals) ret_val = api.watcher.Goal.list(self.request) self.assertIsInstance(ret_val, dict) self.assertIn('goals', ret_val) for n in ret_val['goals']: self.assertIsInstance(n, dict) watcherclient.goal.list.assert_called_with( detail=True) def test_goal_get(self): goal = self.api_goals.first() goal_id = self.api_goals.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.goal.get = mock.Mock( return_value=goal) ret_val = api.watcher.Goal.get(self.request, goal_id) self.assertIsInstance(ret_val, dict) watcherclient.goal.get.assert_called_with( goal_id) def test_strategy_list(self): strategies = {'strategies': self.api_strategies.list()} watcherclient = self.stub_watcherclient() watcherclient.strategy.list = mock.Mock( return_value=strategies) ret_val = api.watcher.Strategy.list(self.request) self.assertIn('strategies', ret_val) for n in ret_val['strategies']: self.assertIsInstance(n, dict) watcherclient.strategy.list.assert_called_with( detail=True) def test_strategy_get(self): strategy = self.api_strategies.first() strategy_id = self.api_strategies.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.strategy.get = mock.Mock( return_value=strategy) ret_val = api.watcher.Strategy.get(self.request, strategy_id) self.assertIsInstance(ret_val, dict) watcherclient.strategy.get.assert_called_with( strategy_id) def test_audit_template_list(self): audit_templates = { 'audit_templates': self.api_audit_templates.list()} watcherclient = self.stub_watcherclient() watcherclient.audit_template.list = mock.Mock( return_value=audit_templates) ret_val = api.watcher.AuditTemplate.list(self.request) self.assertIn('audit_templates', ret_val) for n in ret_val['audit_templates']: self.assertIsInstance(n, dict) watcherclient.audit_template.list.assert_called_with( detail=True) def test_audit_template_list_with_filters(self): search_opts = {'name': 'Audit Template 1'} audit_templates = { 'audit_templates': self.api_audit_templates.filter(**search_opts)} watcherclient = self.stub_watcherclient() watcherclient.audit_template.list = mock.Mock( return_value=audit_templates) ret_val = api.watcher.AuditTemplate.list( self.request, **search_opts) self.assertIn('audit_templates', ret_val) for n in ret_val['audit_templates']: self.assertIsInstance(n, dict) self.assertEqual(ret_val, audit_templates) watcherclient.audit_template.list.assert_called_with( detail=True, **search_opts) def test_audit_template_get(self): audit_template = self.api_audit_templates.first() audit_template_id = self.api_audit_templates.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.audit_template.get = mock.Mock( return_value=audit_template) ret_val = api.watcher.AuditTemplate.get(self.request, audit_template_id) self.assertIsInstance(ret_val, dict) watcherclient.audit_template.get.assert_called_with( audit_template_id=audit_template_id) def test_audit_template_create(self): audit_template = self.api_audit_templates.first() name = audit_template['name'] goal = audit_template['goal_uuid'] strategy = audit_template['strategy_uuid'] description = audit_template['description'] scope = audit_template['scope'] watcherclient = self.stub_watcherclient() watcherclient.audit_template.create = mock.Mock( return_value=audit_template) ret_val = api.watcher.AuditTemplate.create( self.request, name, goal, strategy, description, scope) self.assertIsInstance(ret_val, dict) watcherclient.audit_template.create.assert_called_with( name=name, goal=goal, strategy=strategy, description=description, scope=scope) def test_audit_template_patch(self): audit_template = self.api_audit_templates.first() audit_template_id = self.api_audit_templates.first()['uuid'] form_data = {'name': 'new Audit Template 1'} watcherclient = self.stub_watcherclient() watcherclient.audit_template.patch = mock.Mock( return_value=audit_template) ret_val = api.watcher.AuditTemplate.patch( self.request, audit_template_id, form_data) self.assertIsInstance(ret_val, dict) watcherclient.audit_template.patch.assert_called_with( audit_template_id, [{'name': 'name', 'value': 'new Audit Template 1'}] ) def test_audit_template_delete(self): audit_template_list = self.api_audit_templates.list() audit_template_id = self.api_audit_templates.first()['uuid'] deleted_at_list = self.api_audit_templates.delete() watcherclient = self.stub_watcherclient() watcherclient.audit_template.delete = mock.Mock() api.watcher.AuditTemplate.delete(self.request, audit_template_id) self.assertEqual(audit_template_list, deleted_at_list) self.assertEqual(len(audit_template_list), len(deleted_at_list)) watcherclient.audit_template.delete.assert_called_with( audit_template_id=audit_template_id) def test_audit_list(self): audits = {'audits': self.api_audits.list()} watcherclient = self.stub_watcherclient() watcherclient.audit.list = mock.Mock( return_value=audits) ret_val = api.watcher.Audit.list(self.request) self.assertIn('audits', ret_val) for n in ret_val['audits']: self.assertIsInstance(n, dict) watcherclient.audit.list.assert_called_with( detail=True) def test_audit_get(self): audit = self.api_audits.first() audit_id = self.api_audits.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.audit.get = mock.Mock( return_value=audit) ret_val = api.watcher.Audit.get(self.request, audit_id) self.assertIsInstance(ret_val, dict) watcherclient.audit.get.assert_called_with( audit=audit_id) def test_audit_create(self): audit = self.api_audits.first() audit_template_id = self.api_audit_templates.first()['uuid'] audit_type = self.api_audits.first()['audit_type'] audit_name = self.api_audits.first()['name'] audit_template_uuid = audit_template_id watcherclient = self.stub_watcherclient() watcherclient.audit.create = mock.Mock( return_value=audit) ret_val = api.watcher.Audit.create( self.request, audit_name, audit_template_uuid, audit_type) self.assertIsInstance(ret_val, dict) watcherclient.audit.create.assert_called_with( audit_template_uuid=audit_template_uuid, audit_type=audit_type, auto_trigger=False, name=audit_name) def test_audit_create_with_interval(self): audit = self.api_audits.list()[1] audit_template_id = self.api_audit_templates.first()['uuid'] audit_type = self.api_audits.first()['audit_type'] audit_name = self.api_audits.first()['name'] interval = audit['interval'] audit_template_uuid = audit_template_id watcherclient = self.stub_watcherclient() watcherclient.audit.create = mock.Mock( return_value=audit) ret_val = api.watcher.Audit.create( self.request, audit_name, audit_template_uuid, audit_type, False, interval) self.assertIsInstance(ret_val, dict) watcherclient.audit.create.assert_called_with( audit_template_uuid=audit_template_uuid, audit_type=audit_type, auto_trigger=False, interval=interval, name=audit_name) def test_audit_create_with_auto_trigger(self): audit = self.api_audits.list()[1] audit_template_id = self.api_audit_templates.first()['uuid'] audit_type = self.api_audits.first()['audit_type'] audit_name = self.api_audits.first()['name'] audit_template_uuid = audit_template_id watcherclient = self.stub_watcherclient() watcherclient.audit.create = mock.Mock( return_value=audit) ret_val = api.watcher.Audit.create( self.request, audit_name, audit_template_uuid, audit_type, True) self.assertIsInstance(ret_val, dict) watcherclient.audit.create.assert_called_with( audit_template_uuid=audit_template_uuid, audit_type=audit_type, auto_trigger=True, name=audit_name) def test_audit_delete(self): audit_id = self.api_audits.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.audit.delete = mock.Mock() api.watcher.Audit.delete(self.request, audit_id) watcherclient.audit.delete.assert_called_with( audit=audit_id) def test_action_plan_list(self): action_plans = {'action_plans': self.api_action_plans.list()} watcherclient = self.stub_watcherclient() watcherclient.action_plan.list = mock.Mock( return_value=action_plans) ret_val = api.watcher.ActionPlan.list(self.request) self.assertIn('action_plans', ret_val) for n in ret_val['action_plans']: self.assertIsInstance(n, dict) watcherclient.action_plan.list.assert_called_with( detail=True) def test_action_plan_get(self): action_plan = self.api_action_plans.first() action_plan_id = self.api_action_plans.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.action_plan.get = mock.Mock( return_value=action_plan) ret_val = api.watcher.ActionPlan.get(self.request, action_plan_id) self.assertIsInstance(ret_val, dict) watcherclient.action_plan.get.assert_called_with( action_plan_id=action_plan_id) def test_action_plan_start(self): action_plan_id = self.api_action_plans.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.action_plan.start = mock.Mock() api.watcher.ActionPlan.start(self.request, action_plan_id) watcherclient.action_plan.start.assert_called_with( action_plan_id) def test_action_plan_delete(self): action_plan_id = self.api_action_plans.first()['uuid'] watcherclient = self.stub_watcherclient() watcherclient.action_plan.delete = mock.Mock() api.watcher.ActionPlan.delete(self.request, action_plan_id) watcherclient.action_plan.delete.assert_called_with( action_plan_id=action_plan_id) def test_action_list(self): actions = {'actions': self.api_actions.list()} watcherclient = self.stub_watcherclient() watcherclient.action.list = mock.Mock( return_value=actions) ret_val = api.watcher.Action.list(self.request) self.assertIn('actions', ret_val) for n in ret_val['actions']: self.assertIsInstance(n, dict) watcherclient.action.list.assert_called_with( detail=True) watcher-dashboard-3.0.0/watcher_dashboard/test/urls.py0000664000175000017500000000131113656752043023107 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from django.conf import urls import openstack_dashboard.urls urlpatterns = [ urls.url(r'', urls.include(openstack_dashboard.urls)) ] watcher-dashboard-3.0.0/watcher_dashboard/test/selenium.py0000664000175000017500000000367313656752043023760 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import logging from horizon.test import helpers as test from selenium.common import exceptions as selenium_exceptions class BrowserTests(test.SeleniumTestCase): def test_jasmine(self): url = "%s%s" % (self.live_server_url, "/jasmine/") self.selenium.get(url) wait = self.ui.WebDriverWait(self.selenium, 10) def jasmine_done(driver): text = driver.find_element_by_id("jasmine-testresult").text return "Tests completed" in text wait.until(jasmine_done) failed_elem = self.selenium.find_element_by_class_name("failed") failed = int(failed_elem.text) if failed: self.log_failure_messages() self.assertEqual(failed, 0) def log_failure_messages(self): logger = logging.getLogger('selenium') logger.error("Errors found during jasmine test:") fail_elems = self.selenium.find_elements_by_class_name("fail") for elem in fail_elems: try: module = elem.find_element_by_class_name("module-name").text except selenium_exceptions.NoSuchElementException: continue message = elem.find_element_by_class_name("test-message").text source = elem.find_element_by_tag_name("pre").text logger.error("Module: %s, message: %s, source: %s" % ( module, message, source)) watcher-dashboard-3.0.0/watcher_dashboard/test/test_formset_table.py0000664000175000017500000000345713656752043026024 0ustar zuulzuul00000000000000# Copyright (c) 2016 b<>com # # 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 django.forms from horizon import tables from horizon.tables import formset as hformset from watcher_dashboard.test import helpers as test class FormsetTableTests(test.TestCase): def test_populate(self): """Create a FormsetDataTable and populate it with data.""" class TableObj(object): pass obj = TableObj() obj.name = 'test object' obj.value = 42 obj.id = 4 class TableForm(django.forms.Form): name = django.forms.CharField() value = django.forms.IntegerField() TableFormset = django.forms.formsets.formset_factory(TableForm, extra=0) class Table(hformset.FormsetDataTable): formset_class = TableFormset name = tables.Column('name') value = tables.Column('value') class Meta(object): name = 'table' table = Table(self.request) table.data = [obj] formset = table.get_formset() self.assertEqual(len(formset), 1) form = formset[0] form_data = form.initial self.assertEqual(form_data['name'], 'test object') self.assertEqual(form_data['value'], 42) watcher-dashboard-3.0.0/watcher_dashboard/test/helpers.py0000664000175000017500000000340113656752043023566 0ustar zuulzuul00000000000000# Copyright 2012 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Copyright 2012 Nebula, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from openstack_dashboard.test import helpers from watcher_dashboard import api from watcher_dashboard.test.test_data import utils class WatcherTestsMixin(object): def _setup_test_data(self): super(WatcherTestsMixin, self)._setup_test_data() utils.load_test_data(self) class TestCase(WatcherTestsMixin, helpers.TestCase): pass class APITestCase(WatcherTestsMixin, helpers.APITestCase): def setUp(self): super(APITestCase, self).setUp() self._original_watcherclient = api.watcher.watcherclient api.watcher.watcherclient = lambda request: self.stub_watcherclient() def tearDown(self): super(APITestCase, self).tearDown() api.watcher.watcherclient = self._original_watcherclient def stub_watcherclient(self): if not hasattr(self, "watcherclient"): self.watcherclient = mock.Mock() return self.watcherclient class BaseAdminViewTests(WatcherTestsMixin, helpers.BaseAdminViewTests): pass watcher-dashboard-3.0.0/watcher_dashboard/test/settings.py0000664000175000017500000000244313656752043023771 0ustar zuulzuul00000000000000# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from horizon.test.settings import * # noqa from openstack_dashboard.test.settings import * # noqa # Load the pluggable dashboard settings import openstack_dashboard.enabled import watcher_dashboard.local.enabled INSTALLED_APPS = ( 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sessions', 'django.contrib.staticfiles', 'django.contrib.messages', 'django.contrib.humanize', 'openstack_auth', 'compressor', 'horizon', 'openstack_dashboard', ) INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable settings_utils.update_dashboards( [ watcher_dashboard.local.enabled, openstack_dashboard.enabled, ], HORIZON_CONFIG, INSTALLED_APPS, ) watcher-dashboard-3.0.0/watcher_dashboard/templatetags/0000775000175000017500000000000013656752140023265 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/templatetags/__init__.py0000664000175000017500000000117513656752043025404 0ustar zuulzuul00000000000000# Copyright (c) 2018 Ultimum Technologies s.r.o # # 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 horizon.utils import filters # noqa watcher-dashboard-3.0.0/watcher_dashboard/conf/0000775000175000017500000000000013656752140021520 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/watcher_dashboard/conf/watcher_policy.json0000664000175000017500000000216213656752043025432 0ustar zuulzuul00000000000000{ "admin_api": "role:admin or role:administrator", "show_password": "!", "default": "rule:admin_api", "action:detail": "rule:default", "action:get": "rule:default", "action:get_all": "rule:default", "action_plan:create": "rule:default", "action_plan:detail": "rule:default", "action_plan:get": "rule:default", "action_plan:get_all": "rule:default", "action_plan:update": "rule:default", "audit:create": "rule:default", "audit:delete": "rule:default", "audit:detail": "rule:default", "audit:get": "rule:default", "audit:get_all": "rule:default", "audit:update": "rule:default", "audit_template:create": "rule:default", "audit_template:delete": "rule:default", "audit_template:detail": "rule:default", "audit_template:get": "rule:default", "audit_template:get_all": "rule:default", "audit_template:update": "rule:default", "goal:detail": "rule:default", "goal:get": "rule:default", "goal:get_all": "rule:default", "strategy:detail": "rule:default", "strategy:get": "rule:default", "strategy:get_all": "rule:default" } watcher-dashboard-3.0.0/run_tests.sh0000775000175000017500000004314113656752043017521 0ustar zuulzuul00000000000000#!/bin/bash set -o errexit function usage { echo "Usage: $0 [OPTION]..." echo "Run Horizon's test suite(s)" echo "" echo " -V, --virtual-env Always use virtualenv. Install automatically" echo " if not present" echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local" echo " environment" echo " -c, --coverage Generate reports using Coverage" echo " -f, --force Force a clean re-build of the virtual" echo " environment. Useful when dependencies have" echo " been added." echo " -m, --manage Run a Django management command." echo " --makemessages Create/Update English translation files." echo " --compilemessages Compile all translation files." echo " --check-only Do not update translation files (--makemessages only)." echo " --pseudo Pseudo translate a language." echo " -p, --pep8 Just run pep8" echo " -8, --pep8-changed []" echo " Just run PEP8 and HACKING compliance check" echo " on files changed since HEAD~1 (or )" echo " -P, --no-pep8 Don't run pep8 by default" echo " -t, --tabs Check for tab characters in files." echo " -y, --pylint Just run pylint" echo " -e, --eslint Just run eslint" echo " -k, --karma Just run karma" echo " -q, --quiet Run non-interactively. (Relatively) quiet." echo " Implies -V if -N is not set." echo " --only-selenium Run only the Selenium unit tests" echo " --with-selenium Run unit tests including Selenium tests" echo " --selenium-headless Run Selenium tests headless" echo " --selenium-phantomjs Run Selenium tests using phantomjs (headless)" echo " --integration Run the integration tests (requires a running " echo " OpenStack environment)" echo " --runserver Run the Django development server for" echo " openstack_dashboard in the virtual" echo " environment." echo " --docs Just build the documentation" echo " --backup-environment Make a backup of the environment on exit" echo " --restore-environment Restore the environment before running" echo " --destroy-environment Destroy the environment and exit" echo " -h, --help Print this usage message" echo "" echo "Note: with no options specified, the script will try to run the tests in" echo " a virtual environment, If no virtualenv is found, the script will ask" echo " if you would like to create one. If you prefer to run tests NOT in a" echo " virtual environment, simply pass the -N option." exit } # DEFAULTS FOR RUN_TESTS.SH # root=`pwd -P` venv=$root/.venv venv_env_version=$venv/environments with_venv=tools/with_venv.sh included_dirs="watcher_dashboard" always_venv=0 backup_env=0 command_wrapper="" destroy=0 force=0 just_pep8=0 just_pep8_changed=0 no_pep8=0 just_pylint=0 just_docs=0 just_tabs=0 just_eslint=0 just_karma=0 never_venv=0 quiet=0 restore_env=0 runserver=0 only_selenium=0 with_selenium=0 selenium_headless=0 selenium_phantomjs=0 integration=0 testopts="" testargs="" with_coverage=0 makemessages=0 compilemessages=0 check_only=0 pseudo=0 manage=0 # Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default" [ "$JOB_NAME" ] || JOB_NAME="default" function process_option { # If running manage command, treat the rest of options as arguments. if [ $manage -eq 1 ]; then testargs="$testargs $1" return 0 fi case "$1" in -h|--help) usage;; -V|--virtual-env) always_venv=1; never_venv=0;; -N|--no-virtual-env) always_venv=0; never_venv=1;; -p|--pep8) just_pep8=1;; -8|--pep8-changed) just_pep8_changed=1;; -P|--no-pep8) no_pep8=1;; -y|--pylint) just_pylint=1;; -e|--eslint) just_eslint=1;; -k|--karma) just_karma=1;; -f|--force) force=1;; -t|--tabs) just_tabs=1;; -q|--quiet) quiet=1;; -c|--coverage) with_coverage=1;; -m|--manage) manage=1;; --makemessages) makemessages=1;; --compilemessages) compilemessages=1;; --check-only) check_only=1;; --pseudo) pseudo=1;; --only-selenium) only_selenium=1;; --with-selenium) with_selenium=1;; --selenium-headless) selenium_headless=1;; --selenium-phantomjs) selenium_phantomjs=1;; --integration) integration=1;; --docs) just_docs=1;; --runserver) runserver=1;; --backup-environment) backup_env=1;; --restore-environment) restore_env=1;; --destroy-environment) destroy=1;; -*) testopts="$testopts $1";; *) testargs="$testargs $1" esac } function run_management_command { ${command_wrapper} python $root/manage.py $testopts $testargs } function run_server { echo "Starting Django development server..." ${command_wrapper} python $root/manage.py runserver $testopts $testargs echo "Server stopped." } function run_pylint { echo "Running pylint ..." PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true CODE=$? grep Global -A2 pylint.txt if [ $CODE -lt 32 ]; then echo "Completed successfully." exit 0 else echo "Completed with problems." exit $CODE fi } function run_eslint { echo "Running eslint ..." if [ "`which npm`" == '' ] ; then echo "npm is not present; please install, e.g. sudo apt-get install npm" else npm install npm run lint fi } function run_karma { echo "Running karma ..." npm install npm run test } function warn_on_flake8_without_venv { set +o errexit ${command_wrapper} python -c "import hacking" 2>/dev/null no_hacking=$? set -o errexit if [ $never_venv -eq 1 -a $no_hacking -eq 1 ]; then echo "**WARNING**:" >&2 echo "OpenStack hacking is not installed on your host. Its detection will be missed." >&2 echo "Please install or use virtual env if you need OpenStack hacking detection." >&2 fi } function run_pep8 { echo "Running flake8 ..." warn_on_flake8_without_venv DJANGO_SETTINGS_MODULE=watcher_dashboard.test.settings ${command_wrapper} flake8 } function run_pep8_changed { # NOTE(gilliard) We want use flake8 to check the entirety of every file that has # a change in it. Unfortunately the --filenames argument to flake8 only accepts # file *names* and there are no files named (eg) "nova/compute/manager.py". The # --diff argument behaves surprisingly as well, because although you feed it a # diff, it actually checks the file on disk anyway. local base_commit=${testargs:-HEAD~1} files=$(git diff --name-only $base_commit | tr '\n' ' ') echo "Running flake8 on ${files}" warn_on_flake8_without_venv diff -u --from-file /dev/null ${files} | DJANGO_SETTINGS_MODULE=watcher_dashboard.test.settings ${command_wrapper} flake8 --diff exit } function run_sphinx { echo "Building sphinx..." DJANGO_SETTINGS_MODULE=watcher_dashboard.test.settings ${command_wrapper} python setup.py build_sphinx echo "Build complete." } function tab_check { TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l` if [ $TAB_VIOLATIONS -gt 0 ]; then echo "TABS! $TAB_VIOLATIONS of them! Oh no!" HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"` for TABBED_FILE in $HORIZON_FILES do TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l` if [ $TAB_COUNT -gt 0 ]; then echo "$TABBED_FILE: $TAB_COUNT" fi done fi return $TAB_VIOLATIONS; } function destroy_venv { echo "Cleaning environment..." echo "Removing virtualenv..." rm -rf $venv echo "Virtualenv removed." } function environment_check { echo "Checking environment." if [ -f $venv_env_version ]; then set +o errexit cat requirements.txt test-requirements.txt | cmp $venv_env_version - > /dev/null local env_check_result=$? set -o errexit if [ $env_check_result -eq 0 ]; then # If the environment exists and is up-to-date then set our variables command_wrapper="${root}/${with_venv}" echo "Environment is up to date." return 0 fi fi if [ $always_venv -eq 1 ]; then install_venv else if [ ! -e ${venv} ]; then echo -e "Environment not found. Install? (Y/n) \c" else echo -e "Your environment appears to be out of date. Update? (Y/n) \c" fi read update_env if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then install_venv else # Set our command wrapper anyway. command_wrapper="${root}/${with_venv}" fi fi } function sanity_check { # Anything that should be determined prior to running the tests, server, etc. # Don't sanity-check anything environment-related in -N flag is set if [ $never_venv -eq 0 ]; then if [ ! -e ${venv} ]; then echo "Virtualenv not found at $venv. Did install_venv.py succeed?" exit 1 fi fi # Remove .pyc files. This is sanity checking because they can linger # after old files are deleted. find . -name "*.pyc" -exec rm -rf {} \; } function backup_environment { if [ $backup_env -eq 1 ]; then echo "Backing up environment \"$JOB_NAME\"..." if [ ! -e ${venv} ]; then echo "Environment not installed. Cannot back up." return 0 fi if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old rm -rf /tmp/.horizon_environment/$JOB_NAME fi mkdir -p /tmp/.horizon_environment/$JOB_NAME cp -r $venv /tmp/.horizon_environment/$JOB_NAME/ # Remove the backup now that we've completed successfully rm -rf /tmp/.horizon_environment/$JOB_NAME.old echo "Backup completed" fi } function restore_environment { if [ $restore_env -eq 1 ]; then echo "Restoring environment from backup..." if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then echo "No backup to restore from." return 0 fi cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true echo "Environment restored successfully." fi } function install_venv { # Install with install_venv.py export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache} export PIP_USE_MIRRORS=true if [ $quiet -eq 1 ]; then export PIP_NO_INPUT=true fi echo "Fetching new src packages..." rm -rf $venv/src python tools/install_venv.py command_wrapper="$root/${with_venv}" # Make sure it worked and record the environment version sanity_check chmod -R 754 $venv cat requirements.txt test-requirements.txt > $venv_env_version } function run_tests { sanity_check if [ $with_selenium -eq 1 ]; then export WITH_SELENIUM=1 elif [ $only_selenium -eq 1 ]; then export WITH_SELENIUM=1 export SKIP_UNITTESTS=1 fi if [ $with_selenium -eq 0 -a $integration -eq 0 ]; then testopts="$testopts --exclude=watcher_dashboard/test/integration_tests/ " fi if [ $selenium_headless -eq 1 ]; then export SELENIUM_HEADLESS=1 fi if [ $selenium_phantomjs -eq 1 ]; then export SELENIUM_PHANTOMJS=1 fi if [ -z "$testargs" ]; then run_tests_all else run_tests_subset fi } function run_tests_subset { project=`echo $testargs | awk -F. '{print $1}'` ${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs } function run_tests_all { echo "Running Horizon application tests" export NOSE_XUNIT_FILE=horizon/nosetests.xml if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then export NOSE_HTML_OUT_FILE='horizon_nose_results.html' fi if [ $with_coverage -eq 1 ]; then ${command_wrapper} python -m coverage.__main__ erase coverage_run="python -m coverage.__main__ run -p" fi ${command_wrapper} ${coverage_run} $root/manage.py test horizon --settings=horizon.test.settings $testopts # get results of the Horizon tests HORIZON_RESULT=$? echo "Running openstack_dashboard tests" export NOSE_XUNIT_FILE=openstack_dashboard/nosetests.xml if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then export NOSE_HTML_OUT_FILE='dashboard_nose_results.html' fi ${command_wrapper} ${coverage_run} $root/manage.py test openstack_dashboard --settings=watcher_dashboard.test.settings $testopts # get results of the openstack_dashboard tests DASHBOARD_RESULT=$? if [ $with_coverage -eq 1 ]; then echo "Generating coverage reports" ${command_wrapper} python -m coverage.__main__ combine ${command_wrapper} python -m coverage.__main__ xml -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' ${command_wrapper} python -m coverage.__main__ html -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' -d reports fi # Remove the leftover coverage files from the -p flag earlier. rm -f .coverage.* PEP8_RESULT=0 if [ $no_pep8 -eq 0 ] && [ $only_selenium -eq 0 ]; then run_pep8 PEP8_RESULT=$? fi TEST_RESULT=$(($HORIZON_RESULT || $DASHBOARD_RESULT || $PEP8_RESULT)) if [ $TEST_RESULT -eq 0 ]; then echo "Tests completed successfully." else echo "Tests failed." fi exit $TEST_RESULT } function run_integration_tests { export INTEGRATION_TESTS=1 if [ $selenium_headless -eq 1 ]; then export SELENIUM_HEADLESS=1 fi if [ $selenium_phantomjs -eq 1 ]; then export SELENIUM_PHANTOMJS=1 fi echo "Running Watcher Horizon integration tests..." if [ -z "$testargs" ]; then ${command_wrapper} nosetests watcher_dashboard/test/integration_tests/tests else ${command_wrapper} nosetests $testargs fi exit 0 } function babel_extract { DOMAIN=$1 KEYWORDS="-k gettext_noop -k gettext_lazy -k ngettext_lazy:1,2" KEYWORDS+=" -k ugettext_noop -k ugettext_lazy -k ungettext_lazy:1,2" KEYWORDS+=" -k npgettext:1c,2,3 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3" ${command_wrapper} pybabel extract -F ../babel-${DOMAIN}.cfg -o locale/${DOMAIN}.pot $KEYWORDS . } function run_makemessages { echo -n "horizon: " cd horizon babel_extract django HORIZON_PY_RESULT=$? echo -n "horizon javascript: " babel_extract djangojs HORIZON_JS_RESULT=$? echo -n "openstack_dashboard: " cd ../openstack_dashboard babel_extract django DASHBOARD_RESULT=$? echo -n "openstack_dashboard javascript: " babel_extract djangojs DASHBOARD_JS_RESULT=$? cd .. if [ $check_only -eq 1 ]; then git checkout -- horizon/locale/django*.pot git checkout -- openstack_dashboard/locale/django*.pot fi exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_RESULT || $DASHBOARD_JS_RESULT)) } function run_compilemessages { cd horizon ${command_wrapper} $root/manage.py compilemessages HORIZON_PY_RESULT=$? cd ../openstack_dashboard ${command_wrapper} $root/manage.py compilemessages DASHBOARD_RESULT=$? exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) } function run_pseudo { for lang in $testargs # Use English pot file as the source file/pot file just like real Horizon translations do ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/django.pot openstack_dashboard/locale/$lang/LC_MESSAGES/django.po $lang ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/djangojs.pot openstack_dashboard/locale/$lang/LC_MESSAGES/djangojs.po $lang ${command_wrapper} $root/tools/pseudo.py horizon/locale/django.pot horizon/locale/$lang/LC_MESSAGES/django.po $lang ${command_wrapper} $root/tools/pseudo.py horizon/locale/djangojs.pot horizon/locale/$lang/LC_MESSAGES/djangojs.po $lang done exit $? } # ---------PREPARE THE ENVIRONMENT------------ # # PROCESS ARGUMENTS, OVERRIDE DEFAULTS for arg in "$@"; do process_option $arg done if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ] then always_venv=1 fi # If destroy is set, just blow it away and exit. if [ $destroy -eq 1 ]; then destroy_venv exit 0 fi # Ignore all of this if the -N flag was set if [ $never_venv -eq 0 ]; then # Restore previous environment if desired if [ $restore_env -eq 1 ]; then restore_environment fi # Remove the virtual environment if --force used if [ $force -eq 1 ]; then destroy_venv fi # Then check if it's up-to-date environment_check # Create a backup of the up-to-date environment if desired if [ $backup_env -eq 1 ]; then backup_environment fi fi # ---------EXERCISE THE CODE------------ # # Run management commands if [ $manage -eq 1 ]; then run_management_command exit $? fi # Build the docs if [ $just_docs -eq 1 ]; then run_sphinx exit $? fi # Update translation files if [ $makemessages -eq 1 ]; then run_makemessages exit $? fi # Compile translation files if [ $compilemessages -eq 1 ]; then run_compilemessages exit $? fi # Generate Pseudo translation if [ $pseudo -eq 1 ]; then run_pseudo exit $? fi # PEP8 if [ $just_pep8 -eq 1 ]; then run_pep8 exit $? fi if [ $just_pep8_changed -eq 1 ]; then run_pep8_changed exit $? fi # Pylint if [ $just_pylint -eq 1 ]; then run_pylint exit $? fi # ESLint if [ $just_eslint -eq 1 ]; then run_eslint exit $? fi # Karma if [ $just_karma -eq 1 ]; then run_karma exit $? fi # Tab checker if [ $just_tabs -eq 1 ]; then tab_check exit $? fi # Integration tests if [ $integration -eq 1 ]; then run_integration_tests exit $? fi # Django development server if [ $runserver -eq 1 ]; then run_server exit $? fi # Full test suite run_tests || exit watcher-dashboard-3.0.0/devstack/0000775000175000017500000000000013656752140016733 5ustar zuulzuul00000000000000watcher-dashboard-3.0.0/devstack/settings0000664000175000017500000000013213656752043020514 0ustar zuulzuul00000000000000# DevStack settings # Enable Watcher dashboard services enable_service watcher-dashboard watcher-dashboard-3.0.0/devstack/local.conf.example0000664000175000017500000000017513656752043022333 0ustar zuulzuul00000000000000# settings file for watcher-dashboard plugin enable_plugin watcher-dashboard https://opendev.org/openstack/watcher-dashboard watcher-dashboard-3.0.0/devstack/plugin.sh0000664000175000017500000000341213656752043020567 0ustar zuulzuul00000000000000# plugin.sh - DevStack plugin.sh dispatch script watcher-dashboard WATCHER_DASHBOARD_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) function install_watcher_dashboard { setup_develop ${WATCHER_DASHBOARD_DIR} } function configure_watcher_dashboard { cp -a ${WATCHER_DASHBOARD_DIR}/watcher_dashboard/local/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ cp -a ${WATCHER_DASHBOARD_DIR}/watcher_dashboard/conf/* ${DEST}/horizon/openstack_dashboard/conf/ } function init_watcher_dashboard { # Setup alias for django-admin which could be different depending on distro python ${DEST}/horizon/manage.py collectstatic --noinput python ${DEST}/horizon//manage.py compress --force } # check for service enabled if is_service_enabled watcher-dashboard; then if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then # Set up system services # no-op : elif [[ "$1" == "stack" && "$2" == "install" ]]; then # Perform installation of service source echo_summary "Installing Watcher Dashboard" install_watcher_dashboard elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then # Configure after the other layer 1 and 2 services have been configured echo_summary "Configurng Watcher Dashboard" configure_watcher_dashboard init_watcher_dashboard elif [[ "$1" == "stack" && "$2" == "extra" ]]; then # no-op : fi if [[ "$1" == "unstack" ]]; then rm -f ${DEST}/horizon/openstack_dashboard/local/enabled/_310* rm -f ${DEST}/horizon/openstack_dashboard/conf/watcher* fi if [[ "$1" == "clean" ]]; then # Remove state and transient data # Remember clean.sh first calls unstack.sh # no-op : fi fi watcher-dashboard-3.0.0/LICENSE0000664000175000017500000002363713656752043016151 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.