watcher-dashboard-2.1.0.dev11/0000755000175000017500000000000013612123270016274 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/AUTHORS0000644000175000017500000000356513612123270017355 0ustar coreycorey00000000000000Akihiro 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-2.1.0.dev11/README.rst0000644000175000017500000000147613612123267020001 0ustar coreycorey00000000000000======================== 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-2.1.0.dev11/manage.py0000755000175000017500000000150113612123267020104 0ustar coreycorey00000000000000#!/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-2.1.0.dev11/lower-constraints.txt0000644000175000017500000000526613612123267022551 0ustar coreycorey00000000000000alabaster==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 flake8==2.5.5 futurist==1.6.0 hacking==0.12.0 horizon==17.1.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 mccabe==0.2.1 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.18.1 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 pep8==1.5.7 Pint==0.8.1 prettytable==0.7.2 pycparser==2.18 pyflakes==0.8.1 Pygments==2.2.0 pymongo==3.6.1 pyOpenSSL==17.5.0 pyparsing==2.2.0 pyperclip==1.6.0 pyScss==1.3.4 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-2.1.0.dev11/HACKING.rst0000644000175000017500000000445513612123267020110 0ustar coreycorey00000000000000Contributing ============ 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-2.1.0.dev11/watcher_dashboard/0000755000175000017500000000000013612123270021740 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templatetags/0000755000175000017500000000000013612123270024432 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templatetags/__init__.py0000644000175000017500000000117513612123267026555 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/0000755000175000017500000000000013612123270022717 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/0000755000175000017500000000000013612123270026464 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/__init__.py0000644000175000017500000000000013612123267030571 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/horizon.conf0000644000175000017500000000341513612123267031034 0ustar coreycorey00000000000000# # 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-2.1.0.dev11/watcher_dashboard/test/integration_tests/tests/0000755000175000017500000000000013612123270027626 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/tests/__init__.py0000644000175000017500000000000013612123267031733 0ustar coreycorey00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/tests/test_audit_template_panel.pywatcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/tests/test_audit_template_pan0000644000175000017500000000617213612123267034463 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/0000755000175000017500000000000013612123270027563 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/0000755000175000017500000000000013612123270030653 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/__init__.py0000644000175000017500000000000013612123267032760 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/0000755000175000017500000000000013612123270033401 5ustar coreycorey00000000000000././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/__init__.pywatcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/__in0000644000175000017500000000000013612123267034224 0ustar coreycorey00000000000000././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/audittemplatespage.pywatcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/audi0000644000175000017500000001037613612123267034263 0ustar coreycorey00000000000000# 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) ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/auditspage.pywatcher-dashboard-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/admin/optimization/audi0000644000175000017500000000550613612123267034262 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/integration_tests/pages/__init__.py0000644000175000017500000000000013612123267031670 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/test_data/0000755000175000017500000000000013612123270024667 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/test_data/__init__.py0000644000175000017500000000000013612123267026774 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/test_data/watcher_data.py0000644000175000017500000001710413612123267027700 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/test_data/utils.py0000644000175000017500000000363613612123267026417 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/test_data/exceptions.py0000644000175000017500000000160713612123267027434 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/selenium.py0000644000175000017500000000374313612123267025127 0ustar coreycorey00000000000000# 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 __future__ import absolute_import 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-2.1.0.dev11/watcher_dashboard/test/api_tests/0000755000175000017500000000000013612123270024712 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/api_tests/test_watcher.py0000644000175000017500000003105613612123267027773 0ustar coreycorey00000000000000# 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 __future__ import absolute_import 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-2.1.0.dev11/watcher_dashboard/test/api_tests/__init__.py0000644000175000017500000000000013612123267027017 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/__init__.py0000644000175000017500000000000013612123267025024 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/test/urls.py0000644000175000017500000000131113612123267024260 0ustar coreycorey00000000000000# # 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-2.1.0.dev11/watcher_dashboard/test/helpers.py0000644000175000017500000000336313612123267024746 0ustar coreycorey00000000000000# 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. 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-2.1.0.dev11/watcher_dashboard/test/test_formset_table.py0000644000175000017500000000345713612123267027175 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/test/settings.py0000644000175000017500000000244313612123267025142 0ustar coreycorey00000000000000# # 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-2.1.0.dev11/watcher_dashboard/utils/0000755000175000017500000000000013612123270023100 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/utils/__init__.py0000644000175000017500000000000013612123267025205 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/utils/utils.py0000644000175000017500000000572513612123267024631 0ustar coreycorey00000000000000# -*- 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-2.1.0.dev11/watcher_dashboard/utils/tests.py0000644000175000017500000000561313612123267024627 0ustar coreycorey00000000000000# -*- 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-2.1.0.dev11/watcher_dashboard/utils/errors.py0000644000175000017500000000563713612123267025007 0ustar coreycorey00000000000000# -*- 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-2.1.0.dev11/watcher_dashboard/static/0000755000175000017500000000000013612123270023227 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/0000755000175000017500000000000013612123270025536 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/tests/0000755000175000017500000000000013612123270026700 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/tests/formset_table.js0000644000175000017500000000452213612123267032075 0ustar coreycorey00000000000000horizon.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-2.1.0.dev11/watcher_dashboard/static/infra_optim/images/0000755000175000017500000000000013612123270027003 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/images/power.png0000644000175000017500000000113613612123267030654 0ustar coreycorey00000000000000PNG  IHDRw=bKGD pHYs  tIME 30cUiTXtCommentCreated with GIMPd.eIDATHǵjQg5LHȕ`L,H/1֤AEZRX Vh=~BED6aȏm߸ɋ}242 >om;˧Xn6+R0 򕝝]͋T+2IٶX_mp}8}6^1%qZ[t:$4Z  $17ce31tɴ{uϟa^?HGL*EZx1濨ʸ"\yDQ4ְ4Ðj…5JADB)֚B@cN\&nƑ ]B>GD`jҎ4(\Tu%=Oz@![[m }ƦIENDB`watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/images/chevron.png0000644000175000017500000000053413612123267031165 0ustar coreycorey00000000000000PNG  IHDRH-bKGD pHYs  tIME(v+MiTXtCommentCreated with GIMPd.eIDAT(;@9 1o6Q D"ca$ qݯ DP1?1-˾."7Lf ǝj1Ix6 Mӄ=~3d%L6@ϡT _BJxÐObaBOXƆ7H2&E\T:`Qڹ{Yv'HIENDB`watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/scss/0000755000175000017500000000000013612123270026511 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/static/infra_optim/scss/infra_optim.scss0000644000175000017500000000020313612123267031716 0ustar coreycorey00000000000000// /* Additional CSS for infra_optim. */ // @import "/dashboard/scss/variables"; // @import "/bootstrap/scss/bootstrap/variables"; watcher-dashboard-2.1.0.dev11/watcher_dashboard/__init__.py0000644000175000017500000000000013612123267024045 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/0000755000175000017500000000000013612123270023736 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/horizon/0000755000175000017500000000000013612123270025426 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/horizon/common/0000755000175000017500000000000013612123270026716 5ustar coreycorey00000000000000././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/horizon/common/_items_count_domain_page_header.htmlwatcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/horizon/common/_items_count_domain_page_he0000644000175000017500000000066113612123267034341 0ustar coreycorey00000000000000{% load i18n %} {% block page_header %} {% endblock %} watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/formset_table/0000755000175000017500000000000013612123270026564 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/formset_table/_table.html0000644000175000017500000000257413612123267030716 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/formset_table/_row.html0000644000175000017500000000131213612123267030423 0ustar coreycorey00000000000000 {% 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-2.1.0.dev11/watcher_dashboard/templates/formset_table/menu_formset.html0000644000175000017500000000302713612123267032165 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/client_side/0000755000175000017500000000000013612123270026220 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/client_side/_modal_chart.html0000644000175000017500000000116613612123267031534 0ustar coreycorey00000000000000{% extends "horizon/client_side/template.html" %} {% load horizon %} {% block id %}modal_chart_template{% endblock %} {% block template %} {% jstemplate %} {% endjstemplate %} {% endblock %} watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/client_side/templates.html0000644000175000017500000000005613612123267031113 0ustar coreycorey00000000000000{% include "client_side/_modal_chart.html" %} watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/0000755000175000017500000000000013612123270026245 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/0000755000175000017500000000000013612123270030717 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/details.html0000644000175000017500000000070413612123267033241 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/create.html0000644000175000017500000000051613612123267033060 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/index.html0000644000175000017500000000061213612123267032721 0ustar coreycorey00000000000000{% 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 %} ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/_details_overview.htmlwatcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/action_plans/_details_overview0000644000175000017500000000141113612123267034357 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_fullscreen_workflow.html0000644000175000017500000000317013612123267033375 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions_history/0000755000175000017500000000000013612123270031466 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions_history/details.html0000644000175000017500000000166113612123267034013 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions_history/index.html0000644000175000017500000000164713612123267033501 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_top_5_box.html0000644000175000017500000000074413612123267031203 0ustar coreycorey00000000000000
{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/strategies/0000755000175000017500000000000013612123270030417 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/strategies/details.html0000644000175000017500000000222513612123267032741 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/strategies/index.html0000644000175000017500000000054413612123267032425 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/goals/0000755000175000017500000000000013612123270027352 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/goals/details.html0000644000175000017500000000204413612123267031673 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/goals/index.html0000644000175000017500000000052113612123267031353 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_performance_chart.html0000644000175000017500000000077613612123267032774 0ustar coreycorey00000000000000

{{ label }}

watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/header_actions.html0000644000175000017500000000061213612123267032110 0ustar coreycorey00000000000000 watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_workflow_base.html0000644000175000017500000000050013612123267032137 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/base_detail.html0000644000175000017500000000075013612123267031377 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audit_templates/0000755000175000017500000000000013612123270031431 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audit_templates/details.html0000644000175000017500000000263613612123267033761 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audit_templates/_create.html0000644000175000017500000000301013612123267033721 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audit_templates/create.html0000644000175000017500000000030513612123267033566 0ustar coreycorey00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Template" %}{% endblock %} {% block main %} {% include 'infra_optim/audit_templates/_create.html' %} {% endblock %} watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audit_templates/index.html0000644000175000017500000000060413612123267033434 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/logs/0000755000175000017500000000000013612123270027211 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/logs/index.html0000644000175000017500000000060713612123267031217 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions/0000755000175000017500000000000013612123270027705 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions/details.html0000644000175000017500000000241513612123267032230 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/actions/index.html0000644000175000017500000000056113612123267031712 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/qunit.html0000644000175000017500000001572213612123267030310 0ustar coreycorey00000000000000 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audits/0000755000175000017500000000000013612123270027536 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audits/details.html0000644000175000017500000000260513612123267032062 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audits/_create.html0000644000175000017500000000053213612123267032034 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audits/create.html0000644000175000017500000000027013612123267031674 0ustar coreycorey00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Audit" %}{% endblock %} {% block main %} {% include 'infra_optim/audits/_create.html' %} {% endblock %}watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/audits/index.html0000644000175000017500000000052713612123267031545 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_fullscreen_workflow_base.html0000644000175000017500000000051013612123267034362 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/base.html0000644000175000017500000000060213612123267030051 0ustar coreycorey00000000000000{% extends 'base.html' %} {% block css %} {{block.super}} {% load compress %} {% compress css %} {% endcompress %} {% endblock %} watcher-dashboard-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_performance_chart_box.html0000644000175000017500000000514413612123267033636 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/templates/infra_optim/_top_5_chart.html0000644000175000017500000000107613612123267031513 0ustar coreycorey00000000000000{% 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-2.1.0.dev11/watcher_dashboard/content/0000755000175000017500000000000013612123270023412 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/action_plans/0000755000175000017500000000000013612123270026064 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/action_plans/views.py0000644000175000017500000001171513612123267027606 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/action_plans/panel.py0000644000175000017500000000133713612123267027547 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/action_plans/__init__.py0000644000175000017500000000000013612123267030171 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/action_plans/urls.py0000644000175000017500000000167113612123267027436 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/action_plans/tables.py0000644000175000017500000001674713612123267027735 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/action_plans/tabs.py0000644000175000017500000000201313612123267027371 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/0000755000175000017500000000000013612123270025564 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/strategies/views.py0000644000175000017500000000657513612123267027316 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/panel.py0000644000175000017500000000133213612123267027242 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/__init__.py0000644000175000017500000000000013612123267027671 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/strategies/urls.py0000644000175000017500000000153513612123267027135 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/forms.py0000644000175000017500000000000013612123267027260 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/strategies/tables.py0000644000175000017500000000441213612123267027417 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/tabs.py0000644000175000017500000000177513612123267027107 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/strategies/tests.py0000644000175000017500000000502013612123267027303 0ustar coreycorey00000000000000# 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 urls import mock 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-2.1.0.dev11/watcher_dashboard/content/goals/0000755000175000017500000000000013612123270024517 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/goals/views.py0000644000175000017500000001067213612123267026242 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/goals/panel.py0000644000175000017500000000131313612123267026174 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/goals/__init__.py0000644000175000017500000000000013612123267026624 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/goals/urls.py0000644000175000017500000000152413612123267026066 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/goals/tables.py0000644000175000017500000000354513612123267026360 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/goals/tabs.py0000644000175000017500000000175013612123267026033 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/goals/tests.py0000644000175000017500000000443413612123267026246 0ustar coreycorey00000000000000# 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 urls import mock 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-2.1.0.dev11/watcher_dashboard/content/__init__.py0000644000175000017500000000000013612123267025517 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/audit_templates/0000755000175000017500000000000013612123270026576 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/audit_templates/views.py0000644000175000017500000001143513612123267030317 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/panel.py0000644000175000017500000000135013612123267030254 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/__init__.py0000644000175000017500000000000013612123267030703 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/audit_templates/urls.py0000644000175000017500000000165713612123267030154 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/forms.py0000644000175000017500000001065213612123267030310 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/tables.py0000644000175000017500000000715013612123267030433 0ustar coreycorey00000000000000# 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 __future__ import unicode_literals 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/tabs.py0000644000175000017500000000203113612123267030103 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audit_templates/tests.py0000644000175000017500000001047513612123267030327 0ustar coreycorey00000000000000# 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 urls import mock 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-2.1.0.dev11/watcher_dashboard/content/actions/0000755000175000017500000000000013612123270025052 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/actions/views.py0000644000175000017500000000723513612123267026576 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/actions/panel.py0000644000175000017500000000132213612123267026527 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/actions/__init__.py0000644000175000017500000000000013612123267027157 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/actions/urls.py0000644000175000017500000000153013612123267026416 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/actions/tables.py0000644000175000017500000001064713612123267026714 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/actions/tabs.py0000644000175000017500000000176213612123267026371 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/0000755000175000017500000000000013612123270024703 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/audits/views.py0000644000175000017500000001115713612123267026425 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/panel.py0000644000175000017500000000131613612123267026363 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/__init__.py0000644000175000017500000000000013612123267027010 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/content/audits/urls.py0000644000175000017500000000165213612123267026254 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/forms.py0000644000175000017500000001170413612123267026414 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/tables.py0000644000175000017500000001240213612123267026534 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/content/audits/tabs.py0000644000175000017500000000175513612123267026224 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/version.py0000644000175000017500000000135513612123267024011 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/conf/0000755000175000017500000000000013612123270022665 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/conf/watcher_policy.json0000644000175000017500000000216213612123267026603 0ustar coreycorey00000000000000{ "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-2.1.0.dev11/watcher_dashboard/api/0000755000175000017500000000000013612123270022511 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/api/watcher.py0000644000175000017500000003774513612123267024546 0ustar coreycorey00000000000000# 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 __future__ import unicode_literals 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-2.1.0.dev11/watcher_dashboard/api/__init__.py0000644000175000017500000000000013612123267024616 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/common/0000755000175000017500000000000013612123270023230 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/common/__init__.py0000644000175000017500000000000013612123267025335 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/common/exceptions.py0000644000175000017500000000150613612123267025773 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/0000755000175000017500000000000013612123270023032 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/local/__init__.py0000644000175000017500000000000013612123267025137 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/local/enabled/0000755000175000017500000000000013612123270024424 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/local/enabled/_31010_strategies_panel.py0000644000175000017500000000164413612123267031225 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/_31040_audits_panel.py0000644000175000017500000000163013612123267030342 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/_31050_action_plans_panel.py0000644000175000017500000000165113612123267031527 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/__init__.py0000644000175000017500000000000013612123267026531 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard/local/enabled/_31020_watcher_panelgroup.py0000644000175000017500000000222713612123267031564 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/_31000_goals_panel.py0000644000175000017500000000162513612123267030156 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/_31060_actions_panel.py0000644000175000017500000000163313612123267030516 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard/local/enabled/_31030_audit_templates_panel.py0000644000175000017500000000166213612123267032241 0ustar coreycorey00000000000000# 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-2.1.0.dev11/watcher_dashboard.egg-info/0000755000175000017500000000000013612123270023432 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/requires.txt0000644000175000017500000000011413612123270026026 0ustar coreycorey00000000000000PyYAML>=3.12 horizon>=17.1.0 pbr!=2.1.0,>=2.0.0 python-watcherclient>=1.1.0 watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/SOURCES.txt0000644000175000017500000001743513612123270025330 0ustar coreycorey00000000000000.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-2.1.0.dev11/watcher_dashboard.egg-info/PKG-INFO0000644000175000017500000000365413612123270024537 0ustar coreycorey00000000000000Metadata-Version: 1.1 Name: watcher-dashboard Version: 2.1.0.dev11 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 :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/top_level.txt0000644000175000017500000000002213612123270026156 0ustar coreycorey00000000000000watcher_dashboard watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/pbr.json0000644000175000017500000000005713612123270025112 0ustar coreycorey00000000000000{"git_version": "0e1c0e0", "is_release": false}watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/dependency_links.txt0000644000175000017500000000000113612123270027500 0ustar coreycorey00000000000000 watcher-dashboard-2.1.0.dev11/watcher_dashboard.egg-info/not-zip-safe0000644000175000017500000000000113612123270025660 0ustar coreycorey00000000000000 watcher-dashboard-2.1.0.dev11/devstack/0000755000175000017500000000000013612123270020100 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/devstack/local.conf.example0000644000175000017500000000017513612123267023504 0ustar coreycorey00000000000000# settings file for watcher-dashboard plugin enable_plugin watcher-dashboard https://opendev.org/openstack/watcher-dashboard watcher-dashboard-2.1.0.dev11/devstack/plugin.sh0000644000175000017500000000341213612123267021740 0ustar coreycorey00000000000000# 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-2.1.0.dev11/devstack/settings0000644000175000017500000000013213612123267021665 0ustar coreycorey00000000000000# DevStack settings # Enable Watcher dashboard services enable_service watcher-dashboard watcher-dashboard-2.1.0.dev11/setup.py0000644000175000017500000000200613612123267020012 0ustar coreycorey00000000000000# 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) watcher-dashboard-2.1.0.dev11/PKG-INFO0000644000175000017500000000365413612123270017401 0ustar coreycorey00000000000000Metadata-Version: 1.1 Name: watcher-dashboard Version: 2.1.0.dev11 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 :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Topic :: Internet :: WWW/HTTP watcher-dashboard-2.1.0.dev11/setup.cfg0000644000175000017500000000171713612123270020123 0ustar coreycorey00000000000000[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 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 :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Topic :: Internet :: WWW/HTTP [files] packages = watcher_dashboard [build_sphinx] all_files = 1 build-dir = doc/build source-dir = doc/source [nosetests] verbosity = 2 detailed-errors = 1 [egg_info] tag_build = tag_date = 0 watcher-dashboard-2.1.0.dev11/test-requirements.txt0000644000175000017500000000141613612123267022545 0ustar coreycorey00000000000000# 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>=1.1.0,<1.2.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.18.1 # Apache-2.0 sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.2;python_version>='3.4' # BSD reno>=2.5.0 # Apache-2.0 watcher-dashboard-2.1.0.dev11/LICENSE0000644000175000017500000002363713612123267017322 0ustar coreycorey00000000000000 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. watcher-dashboard-2.1.0.dev11/babel-djangojs.cfg0000644000175000017500000000012513612123267021623 0ustar coreycorey00000000000000[javascript: watcher_dashboard/**.js] [angular: watcher_dashboard/**/static/**.html] watcher-dashboard-2.1.0.dev11/run_tests.sh0000755000175000017500000004314113612123267020672 0ustar coreycorey00000000000000#!/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-2.1.0.dev11/.zuul.yaml0000644000175000017500000000037213612123267020245 0ustar coreycorey00000000000000- 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-2.1.0.dev11/tools/0000755000175000017500000000000013612123270017434 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/tools/install_venv_common.py0000644000175000017500000001350613612123267024075 0ustar coreycorey00000000000000# 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 """ from __future__ import print_function 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-2.1.0.dev11/tools/with_venv.sh0000755000175000017500000000077213612123267022020 0ustar coreycorey00000000000000#!/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-2.1.0.dev11/tools/register_plugin.sh0000755000175000017500000000143313612123267023204 0ustar coreycorey00000000000000#!/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-2.1.0.dev11/tools/install_venv.py0000644000175000017500000000453413612123267022526 0ustar coreycorey00000000000000# 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-2.1.0.dev11/requirements.txt0000644000175000017500000000051613612123267021570 0ustar coreycorey00000000000000# 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>=17.1.0 # Apache-2.0 PyYAML>=3.12 # MIT python-watcherclient>=1.1.0 # Apache-2.0 watcher-dashboard-2.1.0.dev11/tox.ini0000644000175000017500000000430113612123267017613 0ustar coreycorey00000000000000[tox] minversion = 2.0 envlist = py37,py36,pep8 skipsdist = True [testenv] 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] basepython = python3 commands = flake8 [testenv:venv] basepython = python3 commands = {posargs} [testenv:releasenotes] basepython = python3 commands = sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html [testenv:cover] basepython = python3 commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:docs] basepython = python3 deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/doc/requirements.txt commands = python setup.py build_sphinx [testenv:pdf-docs] basepython = python3 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] basepython = python3 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) ignore = F405 show-source = True builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,.ropeproject,tools [testenv:lower-constraints] basepython = python3 deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt watcher-dashboard-2.1.0.dev11/doc/0000755000175000017500000000000013612123270017041 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/doc/source/0000755000175000017500000000000013612123270020341 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/doc/source/index.rst0000644000175000017500000000342213612123267022211 0ustar coreycorey00000000000000.. 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-2.1.0.dev11/doc/source/contributor/0000755000175000017500000000000013612123270022713 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/doc/source/contributor/contributing.rst0000644000175000017500000000406613612123267026170 0ustar coreycorey00000000000000.. 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-2.1.0.dev11/doc/source/contributor/index.rst0000644000175000017500000000005613612123267024563 0ustar coreycorey00000000000000.. toctree:: :maxdepth: 1 contributing watcher-dashboard-2.1.0.dev11/doc/source/conf.py0000755000175000017500000000764513612123267021665 0ustar coreycorey00000000000000# 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 = '' # Must set this variable to include year, month, day, hours, and minutes. html_last_updated_fmt = '%Y-%m-%d %H:%M' watcher-dashboard-2.1.0.dev11/doc/source/install/0000755000175000017500000000000013612123270022007 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/doc/source/install/installation.rst0000644000175000017500000001063613612123267025256 0ustar coreycorey00000000000000Installation ------------ 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-2.1.0.dev11/doc/source/install/index.rst0000644000175000017500000000005613612123267023657 0ustar coreycorey00000000000000.. toctree:: :maxdepth: 1 installation watcher-dashboard-2.1.0.dev11/doc/requirements.txt0000644000175000017500000000053613612123267022337 0ustar coreycorey00000000000000# 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.18.1 # Apache-2.0 sphinx>=1.8.0,<2.0.0;python_version=='2.7' # BSD sphinx>=1.8.0,!=2.1.0;python_version>='3.4' # BSD watcher-dashboard-2.1.0.dev11/releasenotes/0000755000175000017500000000000013612123270020765 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/0000755000175000017500000000000013612123270022265 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/_static/0000755000175000017500000000000013612123270023713 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/_static/.placeholder0000644000175000017500000000000013612123267026172 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/stein.rst0000644000175000017500000000022113612123267024142 0ustar coreycorey00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein watcher-dashboard-2.1.0.dev11/releasenotes/source/ocata.rst0000644000175000017500000000023013612123267024107 0ustar coreycorey00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/0000755000175000017500000000000013612123270023524 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/ko_KR/0000755000175000017500000000000013612123270024531 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/ko_KR/LC_MESSAGES/0000755000175000017500000000000013612123270026316 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/ko_KR/LC_MESSAGES/releasenotes.po0000644000175000017500000000126513612123267031361 0ustar coreycorey00000000000000# 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-2.1.0.dev11/releasenotes/source/locale/en_GB/0000755000175000017500000000000013612123270024476 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/en_GB/LC_MESSAGES/0000755000175000017500000000000013612123270026263 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000644000175000017500000000177313612123267031332 0ustar coreycorey00000000000000# 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-2.1.0.dev11/releasenotes/source/train.rst0000644000175000017500000000017613612123267024146 0ustar coreycorey00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train watcher-dashboard-2.1.0.dev11/releasenotes/source/rocky.rst0000644000175000017500000000022113612123267024147 0ustar coreycorey00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky watcher-dashboard-2.1.0.dev11/releasenotes/source/unreleased.rst0000644000175000017500000000016013612123267025151 0ustar coreycorey00000000000000============================== Current Series Release Notes ============================== .. release-notes:: watcher-dashboard-2.1.0.dev11/releasenotes/source/pike.rst0000644000175000017500000000021713612123267023755 0ustar coreycorey00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike watcher-dashboard-2.1.0.dev11/releasenotes/source/index.rst0000644000175000017500000000030313612123267024130 0ustar coreycorey00000000000000=============================== Watcher Dashboard Release Notes =============================== .. toctree:: :maxdepth: 1 unreleased train stein rocky queens pike ocata watcher-dashboard-2.1.0.dev11/releasenotes/source/_templates/0000755000175000017500000000000013612123270024422 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/_templates/.placeholder0000644000175000017500000000000013612123267026701 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/source/conf.py0000644000175000017500000002204713612123267023577 0ustar coreycorey00000000000000# 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 = '' # Must set this variable to include year, month, day, hours, and minutes. html_last_updated_fmt = '%Y-%m-%d %H:%M' # 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-2.1.0.dev11/releasenotes/source/queens.rst0000644000175000017500000000022313612123267024322 0ustar coreycorey00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens watcher-dashboard-2.1.0.dev11/releasenotes/notes/0000755000175000017500000000000013612123270022115 5ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/releasenotes/notes/drop-py-2-7-198cca7f72d16655.yaml0000644000175000017500000000033313612123267027052 0ustar coreycorey00000000000000--- 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-2.1.0.dev11/releasenotes/notes/.placeholder0000644000175000017500000000000013612123267024374 0ustar coreycorey00000000000000watcher-dashboard-2.1.0.dev11/ChangeLog0000644000175000017500000001512613612123270020053 0ustar coreycorey00000000000000CHANGES ======= * 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-2.1.0.dev11/babel-django.cfg0000644000175000017500000000012313612123267021264 0ustar coreycorey00000000000000[python: watcher_dashboard/**.py] [django: watcher_dashboard/**/templates/**.html]