summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDougal Matthews <dougal@redhat.com>2016-01-21 15:12:37 +0000
committerDougal Matthews <dougal@redhat.com>2016-01-22 11:26:13 +0000
commit31e0bb84f6a412777a69781d1e6438b56a28c64b (patch)
treeab01e5cb768eb3aaafde357c9ade50976c202d0a
parent0a5b41bad6424fa1bef17bdbb964867832d2bdfb (diff)
downloadtuskar-ui-31e0bb84f6a412777a69781d1e6438b56a28c64b.tar.gz
Retire the Tuskar UI codebase
Change-Id: I469fdc1339d4991586bf2e1d62d99fd5b68289eb Depends-On: I904b2f27591333e104bf9080bb8c3876fcb3596c
-rw-r--r--.mailmap12
-rw-r--r--.pylintrc42
-rw-r--r--HACKING.rst60
-rw-r--r--LICENSE176
-rw-r--r--MANIFEST.in19
-rw-r--r--Makefile24
-rw-r--r--README10
-rw-r--r--README.rst41
-rw-r--r--_10_admin.py.example2
-rw-r--r--_20_project.py.example2
-rw-r--r--_30_identity.py.example2
-rw-r--r--_50_tuskar.py.example12
-rw-r--r--dev_env.sh56
-rw-r--r--doc/Makefile153
-rw-r--r--doc/make.bat190
l---------doc/source/HACKING.rst1
l---------doc/source/README.rst1
-rw-r--r--doc/source/conf.py242
-rw-r--r--doc/source/index.rst20
-rw-r--r--doc/source/install.rst133
-rw-r--r--doc/source/user_guide.rst16
-rwxr-xr-xmanage.py11
-rwxr-xr-xnodes.sh20
-rw-r--r--requirements.txt6
-rwxr-xr-xrun_tests.sh552
-rw-r--r--setup.cfg41
-rw-r--r--setup.py29
-rw-r--r--test-requirements.txt37
-rw-r--r--tools/install_venv.py154
-rwxr-xr-xtools/with_venv.sh4
-rw-r--r--tox.ini68
-rw-r--r--tuskar_ui/__init__.py0
-rw-r--r--tuskar_ui/api/__init__.py0
-rw-r--r--tuskar_ui/api/flavor.py121
-rw-r--r--tuskar_ui/api/heat.py553
-rw-r--r--tuskar_ui/api/node.py445
-rw-r--r--tuskar_ui/api/tuskar.py558
-rw-r--r--tuskar_ui/cached_property.py63
-rw-r--r--tuskar_ui/exceptions.py22
-rw-r--r--tuskar_ui/forms.py174
-rw-r--r--tuskar_ui/handle_errors.py71
-rw-r--r--tuskar_ui/infrastructure/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/dashboard.py34
-rw-r--r--tuskar_ui/infrastructure/flavors/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/flavors/panel.py33
-rw-r--r--tuskar_ui/infrastructure/flavors/tables.py157
-rw-r--r--tuskar_ui/infrastructure/flavors/templates/flavors/create.html11
-rw-r--r--tuskar_ui/infrastructure/flavors/templates/flavors/details.html31
-rw-r--r--tuskar_ui/infrastructure/flavors/templates/flavors/index.html27
-rw-r--r--tuskar_ui/infrastructure/flavors/tests.py265
-rw-r--r--tuskar_ui/infrastructure/flavors/urls.py28
-rw-r--r--tuskar_ui/infrastructure/flavors/utils.py121
-rw-r--r--tuskar_ui/infrastructure/flavors/views.py103
-rw-r--r--tuskar_ui/infrastructure/flavors/workflows.py79
-rw-r--r--tuskar_ui/infrastructure/history/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/history/panel.py26
-rw-r--r--tuskar_ui/infrastructure/history/tables.py37
-rw-r--r--tuskar_ui/infrastructure/history/templates/history/index.html16
-rw-r--r--tuskar_ui/infrastructure/history/tests.py53
-rw-r--r--tuskar_ui/infrastructure/history/urls.py23
-rw-r--r--tuskar_ui/infrastructure/history/views.py31
-rw-r--r--tuskar_ui/infrastructure/images/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/images/forms.py17
-rw-r--r--tuskar_ui/infrastructure/images/panel.py26
-rw-r--r--tuskar_ui/infrastructure/images/tables.py74
-rw-r--r--tuskar_ui/infrastructure/images/templates/images/_create.html15
-rw-r--r--tuskar_ui/infrastructure/images/templates/images/_update.html14
-rw-r--r--tuskar_ui/infrastructure/images/templates/images/create.html12
-rw-r--r--tuskar_ui/infrastructure/images/templates/images/index.html16
-rw-r--r--tuskar_ui/infrastructure/images/templates/images/update.html12
-rw-r--r--tuskar_ui/infrastructure/images/tests.py168
-rw-r--r--tuskar_ui/infrastructure/images/urls.py25
-rw-r--r--tuskar_ui/infrastructure/images/views.py109
-rw-r--r--tuskar_ui/infrastructure/nodes/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/nodes/forms.py319
-rw-r--r--tuskar_ui/infrastructure/nodes/panel.py26
-rw-r--r--tuskar_ui/infrastructure/nodes/tables.py269
-rw-r--r--tuskar_ui/infrastructure/nodes/tabs.py378
-rw-r--r--tuskar_ui/infrastructure/nodes/tests.py596
-rw-r--r--tuskar_ui/infrastructure/nodes/urls.py31
-rw-r--r--tuskar_ui/infrastructure/nodes/views.py199
-rw-r--r--tuskar_ui/infrastructure/overview/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/overview/forms.py488
-rw-r--r--tuskar_ui/infrastructure/overview/panel.py26
-rw-r--r--tuskar_ui/infrastructure/overview/tests.py364
-rw-r--r--tuskar_ui/infrastructure/overview/urls.py38
-rw-r--r--tuskar_ui/infrastructure/overview/views.py390
-rw-r--r--tuskar_ui/infrastructure/parameters/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/parameters/forms.py281
-rw-r--r--tuskar_ui/infrastructure/parameters/panel.py26
-rw-r--r--tuskar_ui/infrastructure/parameters/templates/parameters/_simple_service_config.html26
-rw-r--r--tuskar_ui/infrastructure/parameters/templates/parameters/advanced_service_config.html88
-rw-r--r--tuskar_ui/infrastructure/parameters/templates/parameters/index.html76
-rw-r--r--tuskar_ui/infrastructure/parameters/templates/parameters/simple_service_config.html11
-rw-r--r--tuskar_ui/infrastructure/parameters/tests.py130
-rw-r--r--tuskar_ui/infrastructure/parameters/urls.py29
-rw-r--r--tuskar_ui/infrastructure/parameters/views.py107
-rw-r--r--tuskar_ui/infrastructure/roles/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/roles/panel.py26
-rw-r--r--tuskar_ui/infrastructure/roles/tables.py66
-rw-r--r--tuskar_ui/infrastructure/roles/templates/roles/config.html24
-rw-r--r--tuskar_ui/infrastructure/roles/templates/roles/detail.html39
-rw-r--r--tuskar_ui/infrastructure/roles/templates/roles/index.html16
-rw-r--r--tuskar_ui/infrastructure/roles/templates/roles/info.html11
-rw-r--r--tuskar_ui/infrastructure/roles/tests.py166
-rw-r--r--tuskar_ui/infrastructure/roles/urls.py29
-rw-r--r--tuskar_ui/infrastructure/roles/views.py191
-rw-r--r--tuskar_ui/infrastructure/roles/workflows.py180
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/images/chevron.pngbin348 -> 0 bytes
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/images/power.pngbin606 -> 0 bytes
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/horizon.capacity.js160
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3circleschart.js282
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3singlebarchart.js475
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_live.js17
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_progress.js43
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.edit_plan.js74
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.formset_table.js87
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.js6
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js87
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.number_picker.js51
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.performance.js25
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/js/tuskar.templates.js16
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_breadcrumbs.scss31
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_buttons.scss15
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_capacities.scss24
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_charts.scss15
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_detail_pages.scss12
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_flavor_usages.scss89
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_formsets.scss34
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_icons.scss5
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss271
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_numberpicker.scss49
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/_tables.scss74
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/scss/infrastructure.scss15
-rw-r--r--tuskar_ui/infrastructure/static/infrastructure/tests/formset_table.js60
-rw-r--r--tuskar_ui/infrastructure/templates/client_side/_modal_chart.html19
-rw-r--r--tuskar_ui/infrastructure/templates/client_side/templates.html1
-rw-r--r--tuskar_ui/infrastructure/templates/formset_table/_row.html24
-rw-r--r--tuskar_ui/infrastructure/templates/formset_table/_table.html43
-rw-r--r--tuskar_ui/infrastructure/templates/formset_table/menu_formset.html44
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown.html11
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown_first_item.html17
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_actions_dropdown.html27
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_data_table_table_actions.html68
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_definition_list_data_table.html5
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_enhanced_data_table.html54
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_horizontal_field.html20
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_items_count_domain_page_header.html17
-rw-r--r--tuskar_ui/infrastructure/templates/horizon/common/_items_count_tab_group.html25
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow.html37
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow_base.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_performance_chart.html16
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_performance_chart_box.html68
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_scripts.html17
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_top_5_box.html8
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_top_5_chart.html29
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/_workflow_base.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/base.html17
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/base_detail.html27
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/header_actions.html10
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_auto_discover_csv.html23
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_detail_overview.html105
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_field.html15
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_form.html49
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_overview.html146
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_register.html13
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/_upload.html7
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/detail.html13
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/index.html16
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/nodes/register.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/_deploy_confirmation.html40
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/_last_events.html18
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/_post_deploy_init.html22
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/_scale_out.html41
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/_undeploy_confirmation.html20
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deploy_confirmation.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_base.html27
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_failed.html25
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_initialize.html21
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_live.html113
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_plan.html51
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_progress.html69
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/index.html36
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/overcloudrc.sh.template9
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/post_deploy_init.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_edit.html38
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_status.html50
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/scale_out.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/overview/undeploy_confirmation.html11
-rw-r--r--tuskar_ui/infrastructure/templates/infrastructure/qunit.html189
-rw-r--r--tuskar_ui/infrastructure/templatetags/__init__.py0
-rw-r--r--tuskar_ui/infrastructure/templatetags/chart_helpers.py55
-rw-r--r--tuskar_ui/infrastructure/templatetags/icon_helpers.py58
-rw-r--r--tuskar_ui/infrastructure/templatetags/tests.py73
-rw-r--r--tuskar_ui/infrastructure/views.py46
-rw-r--r--tuskar_ui/tables.py177
-rw-r--r--tuskar_ui/test/__init__.py0
-rw-r--r--tuskar_ui/test/api_tests/__init__.py0
-rw-r--r--tuskar_ui/test/api_tests/heat_tests.py134
-rw-r--r--tuskar_ui/test/api_tests/node_tests.py161
-rw-r--r--tuskar_ui/test/api_tests/tuskar_tests.py321
-rw-r--r--tuskar_ui/test/formset_table_tests.py57
-rw-r--r--tuskar_ui/test/helpers.py53
-rw-r--r--tuskar_ui/test/selenium.py55
-rw-r--r--tuskar_ui/test/settings.py161
-rw-r--r--tuskar_ui/test/test_data/__init__.py0
-rw-r--r--tuskar_ui/test/test_data/exceptions.py23
-rw-r--r--tuskar_ui/test/test_data/flavor_data.py38
-rw-r--r--tuskar_ui/test/test_data/heat_data.py248
-rw-r--r--tuskar_ui/test/test_data/keystone_data.py30
-rw-r--r--tuskar_ui/test/test_data/node_data.py289
-rw-r--r--tuskar_ui/test/test_data/tuskar_data.py246
-rw-r--r--tuskar_ui/test/test_data/utils.py52
-rw-r--r--tuskar_ui/test/test_forms.py68
-rw-r--r--tuskar_ui/test/urls.py26
-rw-r--r--tuskar_ui/utils/__init__.py0
-rw-r--r--tuskar_ui/utils/metering.py289
-rw-r--r--tuskar_ui/utils/tests.py298
-rw-r--r--tuskar_ui/utils/utils.py152
219 files changed, 10 insertions, 17306 deletions
diff --git a/.mailmap b/.mailmap
deleted file mode 100644
index f59873d1..00000000
--- a/.mailmap
+++ /dev/null
@@ -1,12 +0,0 @@
-# Format is:
-# <preferred e-mail> <other e-mail 1>
-# <preferred e-mail> <other e-mail 2>
-<ghe@debian.org> <ghe.rivero@stackops.com>
-<jake@ansolabs.com> <admin@jakedahn.com>
-<launchpad@markgius.com> <mgius7096@gmail.com>
-<yorik.sar@gmail.com> <yorik@ytaraday>
-<jeblair@hp.com> <james.blair@rackspace.com>
-<ke.wu@ibeca.me> <ke.wu@nebula.com>
-Zhongyue Luo <zhongyue.nah@intel.com> <lzyeval@gmail.com>
-Joe Gordon <joe.gordon0@gmail.com> <jogo@cloudscaling.com>
-Kun Huang <gareth@unitedstack.com> <academicgareth@gmail.com>
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 64f5cafe..00000000
--- a/.pylintrc
+++ /dev/null
@@ -1,42 +0,0 @@
-# The format of this file isn't really documented; just use --generate-rcfile
-[MASTER]
-# Add <file or directory> to the black list. It should be a base name, not a
-# path. You may set this option multiple times.
-ignore=test
-
-[Messages Control]
-# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
-# C0111: Don't require docstrings on every method
-# W0511: TODOs in code comments are fine.
-# W0142: *args and **kwargs are fine.
-# W0622: Redefining id is fine.
-disable=C0111,W0511,W0142,W0622
-
-[Basic]
-# Variable names can be 1 to 31 characters long, with lowercase and underscores
-variable-rgx=[a-z_][a-z0-9_]{0,30}$
-
-# Argument names can be 2 to 31 characters long, with lowercase and underscores
-argument-rgx=[a-z_][a-z0-9_]{1,30}$
-
-# Method names should be at least 3 characters long
-# and be lowecased with underscores
-method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
-
-# Module names matching keystone-* are ok (files in bin/)
-module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(keystone-[a-z0-9_-]+))$
-
-# Don't require docstrings on tests.
-no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
-
-[Design]
-max-public-methods=100
-min-public-methods=0
-max-args=6
-
-[Variables]
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid to define new builtins when possible.
-# _ is used by our localization
-additional-builtins=_
diff --git a/HACKING.rst b/HACKING.rst
deleted file mode 100644
index e8df4788..00000000
--- a/HACKING.rst
+++ /dev/null
@@ -1,60 +0,0 @@
-Contributing
-============
-
-The code repository is located at `OpenStack <https://github.com/openstack>`__.
-Please go there if you want to check it out:
-
- git clone https://github.com/openstack/tuskar-ui.git
-
-The list of bugs and blueprints is on Launchpad:
-
-`<https://launchpad.net/tuskar-ui>`__
-
-We use OpenStack's Gerrit for the code contributions:
-
-`<https://review.openstack.org/#/q/status:open+project:openstack/tuskar-ui,n,z>`__
-
-and we follow the `OpenStack Gerrit Workflow <http://docs.openstack.org/infra/manual/developers.html#development-workflow>`__.
-
-If you're interested in the code, here are some key places to start:
-
-* `tuskar_ui/api.py <https://github.com/openstack/tuskar-ui/blob/master/tuskar_ui/api.py>`_
- - This file contains all the API calls made to the Tuskar API
- (through python-tuskarclient).
-* `tuskar_ui/infrastructure <https://github.com/openstack/tuskar-ui/tree/master/tuskar_ui/infrastructure>`_
- - The Tuskar UI code is contained within this directory.
-
-Running tests
-=============
-
-There are several ways to run tests for tuskar-ui.
-
-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 avaliable 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 tuskar-ui, it is possible to run a development server
- for tuskar-ui 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 --settings=tuskar_ui.test.settings``
-
-OpenStack Style Commandments
-============================
-
-- Step 1: Read http://www.python.org/dev/peps/pep-0008/
-- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
-- Step 3: Read https://github.com/openstack-dev/hacking/blob/master/HACKING.rst
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 68c771a0..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,176 +0,0 @@
-
- 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.
-
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 96b86560..00000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,19 +0,0 @@
-recursive-include bin *.js
-recursive-include doc *.py *.rst *.css *.js *.html *.conf *.jpg *.gif *.png *.css_t
-recursive-include tools *.py *.sh
-recursive-include tuskar_ui *.py *.html *.js *.scss *.mo *.po *.example *.eot *.svg *.ttf *.woff *.png *.ico *.wsgi *.gif *.csv *.template
-
-include AUTHORS
-include ChangeLog
-include LICENSE
-include Makefile
-include manage.py
-include README.rst
-include run_tests.sh
-include tox.ini
-include doc/Makefile
-include doc/source/_templates/.placeholder
-include requirements.txt
-include test-requirements.txt
-
-exclude openstack_dashboard/local/local_settings.py
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 49e4f728..00000000
--- a/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-PYTHON=`which python`
-DESTDIR=/
-PROJECT=horizon
-
-all:
- @echo "make test - Run tests"
- @echo "make source - Create source package"
- @echo "make install - Install on local system"
- @echo "make buildrpm - Generate a rpm package"
- @echo "make clean - Get rid of scratch and byte files"
-
-source:
- $(PYTHON) setup.py sdist $(COMPILE)
-
-install:
- $(PYTHON) setup.py install --root $(DESTDIR) $(COMPILE)
-
-buildrpm:
- $(PYTHON) setup.py bdist_rpm --post-install=rpm/postinstall --pre-uninstall=rpm/preuninstall
-
-clean:
- $(PYTHON) setup.py clean
- rm -rf build/ MANIFEST
- find . -name '*.pyc' -delete
diff --git a/README b/README
new file mode 100644
index 00000000..3a1e7f83
--- /dev/null
+++ b/README
@@ -0,0 +1,10 @@
+This project is no longer maintained.
+
+The contents of this repository are still available in the Git
+source code management system. To see the contents of this
+repository before it reached its end of life, please check out the
+previous commit with "git checkout HEAD^1".
+
+For any further questions, please email
+openstack-dev@lists.openstack.org or join #openstack-dev or #tripleo
+on Freenode.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index bd1d9d30..00000000
--- a/README.rst
+++ /dev/null
@@ -1,41 +0,0 @@
-=========
-Tuskar UI
-=========
-
-**Tuskar UI** is a user interface for
-`Tuskar <https://github.com/openstack/tuskar>`__, a management API for
-OpenStack deployments. It is a plugin for `OpenStack
-Horizon <https://wiki.openstack.org/wiki/Horizon>`__.
-
-High-Level Overview
--------------------
-
-Tuskar UI endeavours to be a stateless UI, relying on Tuskar API calls
-as much as possible. We use existing Horizon libraries and components
-where possible. If added libraries and components are needed, we will
-work with the OpenStack community to push those changes back into Horizon.
-
-Interested in seeing Tuskar and Tuskar UI in action?
-`Watch a demo! <https://www.youtube.com/watch?v=-6whFIqCqLU>`_
-
-
-Installation Guide
-------------------
-
-Use the `Installation Guide <http://tuskar-ui.readthedocs.org/en/latest/install.html>`_ to install Tuskar UI.
-
-License
--------
-
-This project is licensed under the Apache License, version 2. More
-information can be found in the LICENSE file.
-
-Contact Us
-----------
-
-Join us on IRC (Internet Relay Chat)::
-
- Network: Freenode (irc.freenode.net/tuskar)
- Channel: #tuskar
-
-Or send an email to openstack-dev@lists.openstack.org.
diff --git a/_10_admin.py.example b/_10_admin.py.example
deleted file mode 100644
index 868d719d..00000000
--- a/_10_admin.py.example
+++ /dev/null
@@ -1,2 +0,0 @@
-DASHBOARD = 'admin'
-DISABLED = True
diff --git a/_20_project.py.example b/_20_project.py.example
deleted file mode 100644
index b9006946..00000000
--- a/_20_project.py.example
+++ /dev/null
@@ -1,2 +0,0 @@
-DASHBOARD = 'project'
-DISABLED = True
diff --git a/_30_identity.py.example b/_30_identity.py.example
deleted file mode 100644
index 260a8505..00000000
--- a/_30_identity.py.example
+++ /dev/null
@@ -1,2 +0,0 @@
-DASHBOARD = 'identity'
-DISABLED = True
diff --git a/_50_tuskar.py.example b/_50_tuskar.py.example
deleted file mode 100644
index 7da29698..00000000
--- a/_50_tuskar.py.example
+++ /dev/null
@@ -1,12 +0,0 @@
-from tuskar_ui import exceptions
-
-DASHBOARD = 'infrastructure'
-ADD_INSTALLED_APPS = [
- 'tuskar_ui.infrastructure',
-]
-ADD_EXCEPTIONS = {
- 'recoverable': exceptions.RECOVERABLE,
- 'not_found': exceptions.NOT_FOUND,
- 'unauthorized': exceptions.UNAUTHORIZED,
-}
-DEFAULT = True
diff --git a/dev_env.sh b/dev_env.sh
deleted file mode 100644
index 020cc2a5..00000000
--- a/dev_env.sh
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-set -ex
-
-USAGE="Usage: `basename $0` <undercloud_ip> <undercloud_admin_password>"
-
-if [ "$#" -ne 2 ]; then
- echo $USAGE
- exit 1
-fi
-
-UNDERCLOUD_IP=$1
-UNDERCLOUD_ADMIN_PASSWORD=$2
-
-echo "Copying SSH key..."
-cp /home/stack/.ssh/id_rsa /root/.ssh/
-
-echo "Installing system requirements..."
-yum install -y git python-devel swig openssl-devel mysql-devel libxml2-devel libxslt-devel gcc gcc-c++
-easy_install pip nose
-
-echo "Cloning repos..."
-mkdir /opt/stack
-cd /opt/stack
-git clone git://github.com/openstack/horizon.git
-git clone git://github.com/openstack/python-tuskarclient.git
-git clone git://github.com/openstack/tuskar-ui.git
-git clone git://github.com/rdo-management/tuskar-ui-extras.git
-
-echo "Setting up repos..."
-cd horizon
-python tools/install_venv.py
-./run_tests.sh -V
-cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
-tools/with_venv.sh pip install -e ../python-tuskarclient/
-tools/with_venv.sh pip install -e ../tuskar-ui/
-tools/with_venv.sh pip install -e ../tuskar-ui-extras/
-cp ../tuskar-ui/_50_tuskar.py.example openstack_dashboard/local/enabled/_50_tuskar.py
-cp ../tuskar-ui-extras/_60_tuskar_boxes.py.example openstack_dashboard/local/enabled/_60_tuskar_boxes.py
-cp ../tuskar-ui/_10_admin.py.example openstack_dashboard/local/enabled/_10_admin.py
-cp ../tuskar-ui/_20_project.py.example openstack_dashboard/local/enabled/_20_project.py
-cp ../tuskar-ui/_30_identity.py.example openstack_dashboard/local/enabled/_30_identity.py
-sed -i s/'OPENSTACK_HOST = "127.0.0.1"'/'OPENSTACK_HOST = "192.0.2.1"'/ openstack_dashboard/local/local_settings.py
-echo 'IRONIC_DISCOVERD_URL = "http://%s:5050" % OPENSTACK_HOST' >> openstack_dashboard/local/local_settings.py
-echo 'UNDERCLOUD_ADMIN_PASSWORD = "'$UNDERCLOUD_ADMIN_PASSWORD'"' >> openstack_dashboard/local/local_settings.py
-echo 'DEPLOYMENT_MODE = "scale"' >> openstack_dashboard/local/local_settings.py
-
-echo "Setting up networking..."
-sudo ip route replace 192.0.2.0/24 dev virbr0 via $UNDERCLOUD_IP
-
-echo "Setting up iptables on the undercloud..."
-RULE_1="-A INPUT -p tcp -m tcp --dport 8585 -j ACCEPT"
-RULE_2="-A INPUT -p tcp -m tcp --dport 9696 -j ACCEPT"
-RULE_3="-A INPUT -p tcp -m tcp --dport 8777 -j ACCEPT"
-ssh $UNDERCLOUD_IP "sed -i '/$RULE_1/a $RULE_2' /etc/sysconfig/iptables"
-ssh $UNDERCLOUD_IP "sed -i '/$RULE_2/a $RULE_3' /etc/sysconfig/iptables"
-ssh $UNDERCLOUD_IP "service iptables restart"
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 74543341..00000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,153 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/TuskarUI.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/TuskarUI.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/TuskarUI"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/TuskarUI"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/doc/make.bat b/doc/make.bat
deleted file mode 100644
index 5004e35e..00000000
--- a/doc/make.bat
+++ /dev/null
@@ -1,190 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
-set I18NSPHINXOPTS=%SPHINXOPTS% source
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\TuskarUI.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\TuskarUI.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-:end
diff --git a/doc/source/HACKING.rst b/doc/source/HACKING.rst
deleted file mode 120000
index a2f06b72..00000000
--- a/doc/source/HACKING.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../HACKING.rst \ No newline at end of file
diff --git a/doc/source/README.rst b/doc/source/README.rst
deleted file mode 120000
index c768ff7d..00000000
--- a/doc/source/README.rst
+++ /dev/null
@@ -1 +0,0 @@
-../../README.rst \ No newline at end of file
diff --git a/doc/source/conf.py b/doc/source/conf.py
deleted file mode 100644
index 756f56f9..00000000
--- a/doc/source/conf.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Tuskar UI documentation build configuration file, created by
-# sphinx-quickstart on Thu Apr 24 09:19:32 2014.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'oslosphinx']
-
-# 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'Tuskar UI'
-copyright = u'2014, Tuskar Team'
-
-# 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.
-version = 'Juno'
-# The full version, including alpha/beta/rc tags.
-release = 'Juno'
-
-# 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 = []
-
-
-# -- 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 = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If 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 <link> 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 = 'TuskarUIdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'TuskarUI.tex', u'Tuskar UI Documentation',
- u'Tuskar Team', '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', 'tuskarui', u'Tuskar UI Documentation',
- [u'Tuskar Team'], 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', 'TuskarUI', u'Tuskar UI Documentation',
- u'Tuskar Team', 'TuskarUI', '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'
diff --git a/doc/source/index.rst b/doc/source/index.rst
deleted file mode 100644
index d6176cb0..00000000
--- a/doc/source/index.rst
+++ /dev/null
@@ -1,20 +0,0 @@
-Tuskar UI
-=========
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- README
- install
- user_guide
- HACKING
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/doc/source/install.rst b/doc/source/install.rst
deleted file mode 100644
index 7f2590ab..00000000
--- a/doc/source/install.rst
+++ /dev/null
@@ -1,133 +0,0 @@
-Installation instructions
-=========================
-
-Note
-----
-
-If you want to install and configure the entire TripleO + Tuskar + Tuskar UI
-stack, you can use
-`the devtest installation guide <https://wiki.openstack.org/wiki/Tuskar/Devtest>`_.
-
-Otherwise, you can use the installation instructions for Tuskar UI below.
-
-Prerequisites
--------------
-
-Installation prerequisites are:
-
-1. A functional OpenStack installation. Horizon and Tuskar UI will
- connect to the Keystone service here. Keystone does *not* need to be
- on the same machine as your Tuskar UI interface, but its HTTP API
- must be accessible.
-2. A functional Tuskar installation. Tuskar UI talks to Tuskar via an
- HTTP interface. It may, but does not have to, reside on the same
- machine as Tuskar UI, but it must be network accessible.
-
-You may find
-`the Tuskar install guide <https://github.com/openstack/tuskar/blob/master/doc/source/install.rst>`_
-helpful.
-
-
-Installing the packages
------------------------
-
-Tuskar UI is a Django app written in Python and has a few installation
-dependencies:
-
-On a RHEL 6 system, you should install the following:
-
-::
-
- yum install git python-devel swig openssl-devel mysql-devel libxml2-devel libxslt-devel gcc gcc-c++
-
-The above should work well for similar RPM-based distributions. For
-other distros or platforms, you will obviously need to convert as
-appropriate.
-
-Then, you'll want to use the ``easy_install`` utility to set up a few
-other tools:
-
-::
-
- easy_install pip
- easy_install nose
-
-Install the management UI
--------------------------
-
-Begin by cloning the Horizon and Tuskar UI repositories:
-
-::
-
- git clone git://github.com/openstack/horizon.git
- git clone git://github.com/openstack/python-tuskarclient.git
- git clone git://github.com/openstack/tuskar-ui.git
-
-Go into ``horizon`` and install a virtual environment for your setup::
-
- cd horizon
- python tools/install_venv.py
-
-
-Next, run ``run_tests.sh`` to have pip install Horizon dependencies:
-
-::
-
- ./run_tests.sh
-
-Set up your ``local_settings.py`` file:
-
-::
-
- cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
-
-Open up the copied ``local_settings.py`` file in your preferred text
-editor. You will want to customize several settings:
-
-- ``OPENSTACK_HOST`` should be configured with the hostname of your
- OpenStack server. Verify that the ``OPENSTACK_KEYSTONE_URL`` and
- ``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` settings are correct for your
- environment. (They should be correct unless you modified your
- OpenStack server to change them.)
-
-Install Tuskar UI with all dependencies in your virtual environment::
-
- tools/with_venv.sh pip install -e ../python-tuskarclient/
- tools/with_venv.sh pip install -e ../tuskar-ui/
-
-And enable it in Horizon::
-
- cp ../tuskar-ui/_50_tuskar.py.example openstack_dashboard/local/enabled/_50_tuskar.py
-
-Then disable the other dashboards::
-
- cp ../tuskar-ui/_10_admin.py.example openstack_dashboard/local/enabled/_10_admin.py
- cp ../tuskar-ui/_20_project.py.example openstack_dashboard/local/enabled/_20_project.py
- cp ../tuskar-ui/_30_identity.py.example openstack_dashboard/local/enabled/_30_identity.py
-
-
-Starting the app
-----------------
-
-If everything has gone according to plan, you should be able to run:
-
-::
-
- tools/with_venv.sh ./manage.py runserver
-
-and have the application start on port 8080. The Tuskar UI dashboard will
-be located at http://localhost:8080/infrastructure
-
-If you wish to access it remotely (i.e., not just from localhost), you
-need to open port 8080 in iptables:
-
-::
-
- iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
-
-and launch the server with ``0.0.0.0:8080`` on the end:
-
-::
-
- tools/with_venv.sh ./manage.py runserver 0.0.0.0:8080
-
diff --git a/doc/source/user_guide.rst b/doc/source/user_guide.rst
deleted file mode 100644
index 782c9e05..00000000
--- a/doc/source/user_guide.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-==========
-User Guide
-==========
-
-Nodes List File
----------------
-
-To allow users to load a bunch of nodes at once, there is possibility to
-upload CSV file with given list of nodes. This file should be formatted as
-
-::
-
- driver,address,username,password/ssh key,mac addresses,cpu architecture,number of CPUs,available memory,available storage
-
-Even if there is no all data available, we assume empty values for missing
-keys and try to parse everything, what is possible.
diff --git a/manage.py b/manage.py
deleted file mode 100755
index 256981c5..00000000
--- a/manage.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import sys
-
-from django.core.management import execute_from_command_line
-
-if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE",
- "openstack_dashboard.settings")
- execute_from_command_line(sys.argv)
diff --git a/nodes.sh b/nodes.sh
deleted file mode 100755
index bef9f51e..00000000
--- a/nodes.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-set -eux
-
-OUTPUT_FILE=${OUTPUT_FILE:-"nodes.csv"}
-NODES_JSON_FILE=${NODES_JSON_FILE:-"/home/stack/instackenv.json"}
-
-NUM_NODES=$(jq '.nodes | length' $NODES_JSON_FILE)
-
-if [ -e $OUTPUT_FILE ]; then
- rm $OUTPUT_FILE
-fi
-
-for i in $(seq 0 $(expr $NUM_NODES - 1)); do
- DRIVER=$(jq -r ".nodes[${i}] | .[\"pm_type\"]" $NODES_JSON_FILE)
- SSH_ADDRESS=$(jq -r ".nodes[${i}] | .[\"pm_addr\"]" $NODES_JSON_FILE)
- SSH_USERNAME=$(jq -r ".nodes[${i}] | .[\"pm_user\"]" $NODES_JSON_FILE)
- SSH_KEY_CONTENTS=$(jq -r ".nodes[${i}] | .[\"pm_password\"]" $NODES_JSON_FILE)
- MAC=$(jq -r ".nodes[${i}] | .mac[0]" $NODES_JSON_FILE)
- echo "${DRIVER},${SSH_ADDRESS},${SSH_USERNAME},\"${SSH_KEY_CONTENTS}\",${MAC}" >> $OUTPUT_FILE
-done
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 4fe5429e..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-# 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.
-os-cloud-config
-python-ironic-inspector-client>=1.0.1
-python-ironicclient>=0.8.0
diff --git a/run_tests.sh b/run_tests.sh
deleted file mode 100755
index 37b44a2c..00000000
--- a/run_tests.sh
+++ /dev/null
@@ -1,552 +0,0 @@
-#!/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 [<basecommit>]"
- echo " Just run PEP8 and HACKING compliance check"
- echo " on files changed since HEAD~1 (or <basecommit>)"
- 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 " -j, --jshint Just run jshint"
- echo " -s, --jscs Just run jscs"
- 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 " --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="tuskar_ui"
-
-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_jscs=0
-just_jshint=0
-never_venv=0
-quiet=0
-restore_env=0
-runserver=0
-only_selenium=0
-with_selenium=0
-selenium_headless=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;;
- -j|--jshint) just_jshint=1;;
- -s|--jscs) just_jscs=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;;
- --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_jshint {
- echo "Running jshint ..."
- jshint tuskar_ui/infrastructure/static/infrastructure
-}
-
-function run_jscs {
- echo "Running jscs ..."
- if [ "`which jscs`" == '' ] ; then
- echo "jscs is not present; please install, e.g. sudo npm install jscs -g"
- else
- jscs tuskar_ui/infrastructure/static/infrastructure/js \
- tuskar_ui/infrastructure/static/infrastructure/tests
- fi
-}
-
-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=tuskar_ui.test.settings ${command_wrapper} flake8 $included_dirs
-}
-
-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=openstack_dashboard.test.settings ${command_wrapper} flake8 --diff
- exit
-}
-
-function run_sphinx {
- echo "Building sphinx..."
- export DJANGO_SETTINGS_MODULE=openstack_dashboard.settings
- ${command_wrapper} sphinx-build -b html doc/source doc/build/html
- 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/
- cp .environment_version /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-dir=tuskar_ui/test/integration_tests"
- fi
-
- if [ $selenium_headless -eq 1 ]; then
- export SELENIUM_HEADLESS=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 Tuskar-UI application tests"
- export NOSE_XUNIT_FILE=tuskar_ui/nosetests.xml
- if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then
- export NOSE_HTML_OUT_FILE='tuskar_ui_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 tuskar_ui --settings=tuskar_ui.test.settings $testopts
- # get results of the Horizon tests
- TUSKAR_UI_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="tuskar_ui/*" --omit='/usr*,setup.py,*egg*,.venv/*'
- ${command_wrapper} python -m coverage.__main__ html -i --include="tuskar_ui/*" --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 [ $only_selenium -eq 0 ]; then
- run_pep8
- PEP8_RESULT=$?
- fi
-
- TEST_RESULT=$(($TUSKAR_UI_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
-
- echo "Running Tuskar-UI integration tests..."
- if [ -z "$testargs" ]; then
- ${command_wrapper} nosetests tuskar_ui/test/integration_tests/tests
- else
- ${command_wrapper} nosetests $testargs
- fi
- exit 0
-}
-
-function run_makemessages {
- cd horizon
- ${command_wrapper} $root/manage.py makemessages --all --no-obsolete
- HORIZON_PY_RESULT=$?
- ${command_wrapper} $root/manage.py makemessages -d djangojs --all --no-obsolete
- HORIZON_JS_RESULT=$?
- cd ../openstack_dashboard
- ${command_wrapper} $root/manage.py makemessages --all --no-obsolete
- DASHBOARD_RESULT=$?
- cd ..
- exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_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=$?
- cd ..
- exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT))
-}
-
-
-# ---------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
-
-# 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
-
-# Jshint
-if [ $just_jshint -eq 1 ]; then
- run_jshint
- exit $?
-fi
-
-# Jscs
-if [ $just_jscs -eq 1 ]; then
- run_jscs
- exit $?
-fi
-
-# Tab checker
-if [ $just_tabs -eq 1 ]; then
- tab_check
- exit $?
-fi
-
-
-# Django development server
-if [ $runserver -eq 1 ]; then
- run_server
- exit $?
-fi
-
-# Full test suite
-run_tests || exit
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 8e4c5c6f..00000000
--- a/setup.cfg
+++ /dev/null
@@ -1,41 +0,0 @@
-[metadata]
-name = tuskar-ui
-version = 2013.2
-summary = Tuskar Management Dashboard
-description-file =
- README.rst
-author = OpenStack
-author-email = openstack-dev@lists.openstack.org
-home-page = http://www.openstack.org/
-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 :: 2
- Programming Language :: Python :: 2.7
- Programming Language :: Python :: 2.6
- Topic :: Internet :: WWW/HTTP
-
-[global]
-setup-hooks =
- pbr.hooks.setup_hook
-
-[files]
-packages =
- tuskar_ui
-
-[build_sphinx]
-all_files = 1
-build-dir = doc/build
-source-dir = doc/source
-
-[nosetests]
-verbosity=2
-detailed-errors=1
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 782bb21f..00000000
--- a/setup.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# 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>=1.8'],
- pbr=True)
diff --git a/test-requirements.txt b/test-requirements.txt
deleted file mode 100644
index bc873bfc..00000000
--- a/test-requirements.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-# 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<0.11,>=0.10.0
-# Testing Requirements
-http://tarballs.openstack.org/horizon/horizon-master.tar.gz#egg=horizon
-
-
-
-
-
-
-http://tarballs.openstack.org/python-tuskarclient/python-tuskarclient-master.tar.gz#egg=python-tuskarclient
-
-
-
-
-
-
-coverage>=3.6
-django-nose>=1.2
-mock>=1.2
-mox>=0.5.3
-mox3>=0.7.0
-nodeenv>=0.9.4 # BSD License
-nose
-nose-exclude
-nosexcover
-openstack.nose-plugin>=0.7
-nosehtmloutput>=0.0.3
-selenium
-xvfbwrapper>=0.1.3 #license: MIT
-# Docs Requirements
-sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
-oslosphinx>=2.5.0 # Apache-2.0
-
diff --git a/tools/install_venv.py b/tools/install_venv.py
deleted file mode 100644
index 8550e2c6..00000000
--- a/tools/install_venv.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# Copyright 2012 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
-#
-# Copyright 2012 OpenStack, LLC
-#
-# 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.
-
-"""
-Installation script for the OpenStack Dashboard development virtualenv.
-"""
-
-import os
-import subprocess
-import sys
-
-
-ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-VENV = os.path.join(ROOT, '.venv')
-WITH_VENV = os.path.join(ROOT, 'tools', 'with_venv.sh')
-PIP_REQUIRES = os.path.join(ROOT, 'requirements.txt')
-TEST_REQUIRES = os.path.join(ROOT, 'test-requirements.txt')
-
-
-def die(message, *args):
- print >> sys.stderr, message % args
- sys.exit(1)
-
-
-def run_command(cmd, redirect_output=True, check_exit_code=True, cwd=ROOT,
- die_message=None):
- """
- Runs a command in an out-of-process shell, returning the
- output of that command. Working directory is ROOT.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
-
- proc = subprocess.Popen(cmd, cwd=cwd, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- if die_message is None:
- die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- else:
- die(die_message)
- return output
-
-
-HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
- check_exit_code=False).strip())
-HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
- check_exit_code=False).strip())
-
-
-def check_dependencies():
- """Make sure virtualenv is in the path."""
-
- print 'Checking dependencies...'
- if not HAS_VIRTUALENV:
- print 'Virtual environment not found.'
- # Try installing it via easy_install...
- if HAS_EASY_INSTALL:
- print 'Installing virtualenv via easy_install...',
- run_command(['easy_install', 'virtualenv'],
- die_message='easy_install failed to install virtualenv'
- '\ndevelopment requires virtualenv, please'
- ' install it using your favorite tool')
- if not run_command(['which', 'virtualenv']):
- die('ERROR: virtualenv not found in path.\n\ndevelopment '
- ' requires virtualenv, please install it using your'
- ' favorite package management tool and ensure'
- ' virtualenv is in your path')
- print 'virtualenv installation done.'
- else:
- die('easy_install not found.\n\nInstall easy_install'
- ' (python-setuptools in ubuntu) or virtualenv by hand,'
- ' then rerun.')
- print 'dependency check done.'
-
-
-def create_virtualenv(venv=VENV):
- """Creates the virtual environment and installs PIP only into the
- virtual environment
- """
- print 'Creating venv...',
- run_command(['virtualenv', '-q', '--no-site-packages', VENV])
- print 'done.'
- print 'Installing pip in virtualenv...',
- if not run_command([WITH_VENV, 'easy_install', 'pip']).strip():
- die("Failed to install pip.")
- print 'done.'
- print 'Installing distribute in virtualenv...'
- pip_install('distribute>=0.6.24')
- print 'done.'
-
-
-def pip_install(*args):
- args = [WITH_VENV, 'pip', 'install', '--upgrade'] + list(args)
- run_command(args, redirect_output=False)
-
-
-def install_dependencies(venv=VENV):
- print "Installing dependencies..."
- print "(This may take several minutes, don't panic)"
- pip_install('-r', TEST_REQUIRES)
- pip_install('-r', PIP_REQUIRES)
-
- # Tell the virtual env how to "import dashboard"
- py = 'python%d.%d' % (sys.version_info[0], sys.version_info[1])
- pthfile = os.path.join(venv, "lib", py, "site-packages", "dashboard.pth")
- f = open(pthfile, 'w')
- f.write("%s\n" % ROOT)
-
-
-def install_horizon():
- print 'Installing horizon module in development mode...'
- run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=ROOT)
-
-
-def print_summary():
- summary = """
-Horizon development environment setup is complete.
-
-To activate the virtualenv for the extent of your current shell session you
-can run:
-
-$ source .venv/bin/activate
-"""
- print summary
-
-
-def main():
- check_dependencies()
- create_virtualenv()
- install_dependencies()
- install_horizon()
- print_summary()
-
-if __name__ == '__main__':
- main()
diff --git a/tools/with_venv.sh b/tools/with_venv.sh
deleted file mode 100755
index c8d2940f..00000000
--- a/tools/with_venv.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-TOOLS=`dirname $0`
-VENV=$TOOLS/../.venv
-source $VENV/bin/activate && $@
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 4af6d7a7..00000000
--- a/tox.ini
+++ /dev/null
@@ -1,68 +0,0 @@
-[tox]
-envlist = py27,py27dj14,py27dj15,py27dj16,pep8,selenium,jshint
-
-[testenv]
-setenv = VIRTUAL_ENV={envdir}
- NOSE_WITH_OPENSTACK=1
- NOSE_OPENSTACK_COLOR=1
- NOSE_OPENSTACK_RED=0.05
- NOSE_OPENSTACK_YELLOW=0.025
- NOSE_OPENSTACK_SHOW_ELAPSED=1
-deps = -r{toxinidir}/requirements.txt
- -r{toxinidir}/test-requirements.txt
-commands = /bin/bash run_tests.sh -N
-
-[testenv:pep8]
-commands = /bin/bash run_tests.sh -N --pep8
-
-[testenv:venv]
-commands = {posargs}
-
-[testenv:cover]
-commands = /bin/bash run_tests.sh -N --coverage
-
-[testenv:py27dj14]
-basepython = python2.7
-commands = pip install django>=1.4,<1.5
- /bin/bash run_tests.sh -N
-
-[testenv:py27dj15]
-basepython = python2.7
-commands = pip install django>=1.5,<1.6
- /bin/bash run_tests.sh -N
-
-[testenv:py27dj16]
-basepython = python2.7
-commands = pip install django>=1.6,<1.7
- /bin/bash run_tests.sh -N
-
-[testenv:selenium]
-commands = /bin/bash run_tests.sh -N --only-selenium
-
-[testenv:jshint]
-commands = nodeenv -p
- npm install jshint -g
- /bin/bash run_tests.sh -N --jshint
-
-[flake8]
-builtins = _
-exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,
- build,panel_template,dash_template,local_settings.py
-
-[hacking]
-import_exceptions = collections.defaultdict,
- django.conf.settings,
- django.core.urlresolvers.reverse,
- django.core.urlresolvers.reverse_lazy,
- django.template.loader.render_to_string,
- django.utils.datastructures.SortedDict,
- django.utils.encoding.force_unicode,
- django.utils.html.conditional_escape,
- django.utils.html.escape,
- django.utils.http.urlencode,
- django.utils.safestring.mark_safe,
- django.utils.translation.pgettext_lazy,
- django.utils.translation.ugettext_lazy,
- django.utils.translation.ungettext_lazy,
- operator.attrgetter,
- StringIO.StringIO
diff --git a/tuskar_ui/__init__.py b/tuskar_ui/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/api/__init__.py b/tuskar_ui/api/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/api/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/api/flavor.py b/tuskar_ui/api/flavor.py
deleted file mode 100644
index 518400cc..00000000
--- a/tuskar_ui/api/flavor.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# 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 _
-from horizon.utils import memoized
-from openstack_dashboard.api import nova
-
-import tuskar_ui
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.handle_errors import handle_errors # noqa
-
-
-LOG = logging.getLogger(__name__)
-
-
-class Flavor(object):
-
- def __init__(self, flavor):
- """Construct by wrapping Nova flavor
-
- :param flavor: Nova flavor
- :type flavor: novaclient.v2.flavors.Flavor
- """
- self._flavor = flavor
-
- def __getattr__(self, name):
- return getattr(self._flavor, name)
-
- @property
- def ram_bytes(self):
- """Get RAM size in bytes
-
- Default RAM size is in MB.
- """
- return self.ram * 1024 * 1024
-
- @property
- def disk_bytes(self):
- """Get disk size in bytes
-
- Default disk size is in GB.
- """
- return self.disk * 1024 * 1024 * 1024
-
- @cached_property
- def extras_dict(self):
- """Return extra flavor parameters
-
- :return: Nova flavor keys
- :rtype: dict
- """
- return self._flavor.get_keys()
-
- @property
- def cpu_arch(self):
- return self.extras_dict.get('cpu_arch', '')
-
- @property
- def kernel_image_id(self):
- return self.extras_dict.get('baremetal:deploy_kernel_id', '')
-
- @property
- def ramdisk_image_id(self):
- return self.extras_dict.get('baremetal:deploy_ramdisk_id', '')
-
- @classmethod
- def create(cls, request, name, memory, vcpus, disk, cpu_arch,
- kernel_image_id=None, ramdisk_image_id=None):
- extras_dict = {
- 'cpu_arch': cpu_arch,
- 'capabilities:boot_option': 'local',
- }
- if kernel_image_id is not None:
- extras_dict['baremetal:deploy_kernel_id'] = kernel_image_id
- if ramdisk_image_id is not None:
- extras_dict['baremetal:deploy_ramdisk_id'] = ramdisk_image_id
- return cls(nova.flavor_create(request, name, memory, vcpus, disk,
- metadata=extras_dict))
-
- @classmethod
- @handle_errors(_("Unable to load flavor."))
- def get(cls, request, flavor_id):
- return cls(nova.flavor_get(request, flavor_id))
-
- @classmethod
- @handle_errors(_("Unable to load flavor."))
- def get_by_name(cls, request, name):
- for flavor in cls.list(request):
- if flavor.name == name:
- return flavor
-
- @classmethod
- @handle_errors(_("Unable to retrieve flavor list."), [])
- def list(cls, request):
- return [cls(item) for item in nova.flavor_list(request)]
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve existing servers list."), [])
- def list_deployed_ids(cls, request):
- """Get and memoize ID's of deployed flavors."""
- servers = nova.server_list(request)[0]
- deployed_ids = set(server.flavor['id'] for server in servers)
- deployed_names = []
- for plan in tuskar_ui.api.tuskar.Plan.list(request):
- deployed_names.extend(
- [plan.parameter_value(role.flavor_parameter_name)
- for role in plan.role_list])
- return [flavor.id for flavor in cls.list(request)
- if flavor.id in deployed_ids or flavor.name in deployed_names]
diff --git a/tuskar_ui/api/heat.py b/tuskar_ui/api/heat.py
deleted file mode 100644
index 18056de5..00000000
--- a/tuskar_ui/api/heat.py
+++ /dev/null
@@ -1,553 +0,0 @@
-# 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
-import os
-import tempfile
-import urlparse
-
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from heatclient.common import template_utils
-from heatclient.exc import HTTPNotFound
-from horizon.utils import memoized
-from openstack_dashboard.api import base
-from openstack_dashboard.api import heat
-from openstack_dashboard.api import keystone
-
-from tuskar_ui.api import node
-from tuskar_ui.api import tuskar
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.handle_errors import handle_errors # noqa
-from tuskar_ui.utils import utils
-
-
-LOG = logging.getLogger(__name__)
-
-
-@memoized.memoized
-def overcloud_keystoneclient(request, endpoint, password):
- """Returns a client connected to the Keystone backend.
-
- Several forms of authentication are supported:
-
- * Username + password -> Unscoped authentication
- * Username + password + tenant id -> Scoped authentication
- * Unscoped token -> Unscoped authentication
- * Unscoped token + tenant id -> Scoped authentication
- * Scoped token -> Scoped authentication
-
- Available services and data from the backend will vary depending on
- whether the authentication was scoped or unscoped.
-
- Lazy authentication if an ``endpoint`` parameter is provided.
-
- Calls requiring the admin endpoint should have ``admin=True`` passed in
- as a keyword argument.
-
- The client is cached so that subsequent API calls during the same
- request/response cycle don't have to be re-authenticated.
- """
- api_version = keystone.VERSIONS.get_active_version()
-
- # TODO(lsmola) add support of certificates and secured http and rest of
- # parameters according to horizon and add configuration to local settings
- # (somehow plugin based, we should not maintain a copy of settings)
- LOG.debug("Creating a new keystoneclient connection to %s." % endpoint)
-
- # TODO(lsmola) we should create tripleo-admin user for this purpose
- # this needs to be done first on tripleo side
- conn = api_version['client'].Client(username="admin",
- password=password,
- tenant_name="admin",
- auth_url=endpoint)
-
- return conn
-
-
-def _save_templates(templates):
- """Saves templates into tmpdir on server
-
- This should go away and get replaced by libutils.save_templates from
- tripleo-common https://github.com/openstack/tripleo-common/
- """
- output_dir = tempfile.mkdtemp()
-
- for template_name, template_content in templates.items():
-
- # It's possible to organize the role templates and their dependent
- # files into directories, in which case the template_name will carry
- # the directory information. If that's the case, first create the
- # directory structure (if it hasn't already been created by another
- # file in the templates list).
- template_dir = os.path.dirname(template_name)
- output_template_dir = os.path.join(output_dir, template_dir)
- if template_dir and not os.path.exists(output_template_dir):
- os.makedirs(output_template_dir)
-
- filename = os.path.join(output_dir, template_name)
- with open(filename, 'w+') as template_file:
- template_file.write(template_content)
- return output_dir
-
-
-def _process_templates(templates):
- """Process templates
-
- Due to bug in heat api
- https://bugzilla.redhat.com/show_bug.cgi?id=1212740, we need to
- save the templates in tmpdir, reprocess them with template_utils
- from heatclient and then we can use them in creating/updating stack.
-
- This should be replaced by the same code that is in tripleo-common and
- eventually it will not be needed at all.
- """
-
- tpl_dir = _save_templates(templates)
-
- tpl_files, template = template_utils.get_template_contents(
- template_file=os.path.join(tpl_dir, tuskar.MASTER_TEMPLATE_NAME))
- env_files, env = (
- template_utils.process_multiple_environments_and_files(
- env_paths=[os.path.join(tpl_dir, tuskar.ENVIRONMENT_NAME)]))
-
- files = dict(list(tpl_files.items()) + list(env_files.items()))
-
- return template, env, files
-
-
-class Stack(base.APIResourceWrapper):
- _attrs = ('id', 'stack_name', 'outputs', 'stack_status', 'parameters')
-
- def __init__(self, apiresource, request=None):
- super(Stack, self).__init__(apiresource)
- self._request = request
-
- @classmethod
- def create(cls, request, stack_name, templates):
- template, environment, files = _process_templates(templates)
-
- fields = {
- 'stack_name': stack_name,
- 'template': template,
- 'environment': environment,
- 'files': files,
- 'timeout_mins': 240,
- }
- password = getattr(settings, 'UNDERCLOUD_ADMIN_PASSWORD', None)
- stack = heat.stack_create(request, password, **fields)
- return cls(stack, request=request)
-
- def update(self, request, stack_name, templates):
- template, environment, files = _process_templates(templates)
-
- fields = {
- 'stack_name': stack_name,
- 'template': template,
- 'environment': environment,
- 'files': files,
- }
- password = getattr(settings, 'UNDERCLOUD_ADMIN_PASSWORD', None)
- heat.stack_update(request, self.id, password, **fields)
-
- @classmethod
- @handle_errors(_("Unable to retrieve heat stacks"), [])
- def list(cls, request):
- """Return a list of stacks in Heat
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :return: list of Heat stacks, or an empty list if there
- are none
- :rtype: list of tuskar_ui.api.heat.Stack
- """
- stacks, has_more_data, has_prev_data = heat.stacks_list(request)
- return [cls(stack, request=request) for stack in stacks]
-
- @classmethod
- @handle_errors(_("Unable to retrieve stack"))
- def get(cls, request, stack_id):
- """Return the Heat Stack associated with this Overcloud
-
- :return: Heat Stack associated with the stack_id; or None
- if no Stack is associated, or no Stack can be
- found
- :rtype: tuskar_ui.api.heat.Stack or None
- """
- return cls(heat.stack_get(request, stack_id), request=request)
-
- @classmethod
- @handle_errors(_("Unable to retrieve stack"))
- def get_by_plan(cls, request, plan):
- """Return the Heat Stack associated with a Plan
-
- :return: Heat Stack associated with the plan; or None
- if no Stack is associated, or no Stack can be
- found
- :rtype: tuskar_ui.api.heat.Stack or None
- """
- # TODO(lsmola) until we have working deployment through Tuskar-API,
- # this will not work
- # for stack in Stack.list(request):
- # if stack.plan and (stack.plan.id == plan.id):
- # return stack
- try:
- stack = Stack.list(request)[0]
- except IndexError:
- return None
- # TODO(lsmola) stack list actually does not contain all the detail
- # info, there should be call for that, investigate
- return Stack.get(request, stack.id)
-
- @classmethod
- @handle_errors(_("Unable to delete Heat stack"), [])
- def delete(cls, request, stack_id):
- heat.stack_delete(request, stack_id)
-
- @memoized.memoized
- def resources(self, with_joins=True, role=None):
- """Return list of OS::Nova::Server Resources
-
- Return list of OS::Nova::Server Resources associated with the Stack
- and which are associated with a Role
-
- :param with_joins: should we also retrieve objects associated with each
- retrieved Resource?
- :type with_joins: bool
-
- :return: list of all Resources or an empty list if there are none
- :rtype: list of tuskar_ui.api.heat.Resource
- """
-
- if role:
- roles = [role]
- else:
- roles = self.plan.role_list
- resource_dicts = []
-
- # A provider resource is deployed as a nested stack, so we have to
- # drill down and retrieve those that match a tuskar role
- for role in roles:
- resource_group_name = role.name
- try:
- resource_group = heat.resource_get(self._request,
- self.id,
- resource_group_name)
-
- group_resources = heat.resources_list(
- self._request, resource_group.physical_resource_id)
- for group_resource in group_resources:
- if not group_resource.physical_resource_id:
- # Skip groups who has no physical resource.
- continue
- nova_resources = heat.resources_list(
- self._request,
- group_resource.physical_resource_id)
- resource_dicts.extend([{"resource": resource,
- "role": role}
- for resource in nova_resources])
-
- except HTTPNotFound:
- pass
-
- if not with_joins:
- return [Resource(rd['resource'], request=self._request,
- stack=self, role=rd['role'])
- for rd in resource_dicts]
-
- nodes_dict = utils.list_to_dict(node.Node.list(self._request,
- associated=True),
- key_attribute='instance_uuid')
- joined_resources = []
- for rd in resource_dicts:
- resource = rd['resource']
- joined_resources.append(
- Resource(resource,
- node=nodes_dict.get(resource.physical_resource_id,
- None),
- request=self._request, stack=self, role=rd['role']))
- # TODO(lsmola) I want just resources with nova instance
- # this could be probably filtered a better way, investigate
- return [r for r in joined_resources if r.node is not None]
-
- @memoized.memoized
- def resources_count(self, overcloud_role=None):
- """Return count of associated Resources
-
- :param overcloud_role: role of resources to be counted; None means all
- :type overcloud_role: tuskar_ui.api.tuskar.Role
-
- :return: Number of matching resources
- :rtype: int
- """
- # TODO(dtantsur): there should be better way to do it, rather than
- # fetching and calling len()
- # FIXME(dtantsur): should also be able to use with_joins=False
- # but unable due to bug #1289505
- if overcloud_role is None:
- resources = self.resources()
- else:
- resources = self.resources(role=overcloud_role)
- return len(resources)
-
- @cached_property
- def plan(self):
- """return associated Plan if a plan_id exists within stack parameters.
-
- :return: associated Plan if plan_id exists and a matching plan
- exists as well; None otherwise
- :rtype: tuskar_ui.api.tuskar.Plan
- """
- # TODO(lsmola) replace this by actual reference, I am pretty sure
- # the relation won't be stored in parameters, that would mean putting
- # that into template, which doesn't make sense
- # if 'plan_id' in self.parameters:
- # return tuskar.Plan.get(self._request,
- # self.parameters['plan_id'])
- try:
- plan = tuskar.Plan.list(self._request)[0]
- except IndexError:
- return None
- return plan
-
- @cached_property
- def is_initialized(self):
- """Check if this Stack is successfully initialized.
-
- :return: True if this Stack is successfully initialized, False
- otherwise
- :rtype: bool
- """
- return len(self.dashboard_urls) > 0
-
- @cached_property
- def is_deployed(self):
- """Check if this Stack is successfully deployed.
-
- :return: True if this Stack is successfully deployed, False otherwise
- :rtype: bool
- """
- return self.stack_status in ('CREATE_COMPLETE',
- 'UPDATE_COMPLETE')
-
- @cached_property
- def is_deploying(self):
- """Check if this Stack is currently deploying.
-
- :return: True if deployment is in progress, False otherwise.
- :rtype: bool
- """
- return self.stack_status in ('CREATE_IN_PROGRESS',)
-
- @cached_property
- def is_updating(self):
- """Check if this Stack is currently updating.
-
- :return: True if updating is in progress, False otherwise.
- :rtype: bool
- """
- return self.stack_status in ('UPDATE_IN_PROGRESS',)
-
- @cached_property
- def is_failed(self):
- """Check if this Stack failed to update or deploy.
-
- :return: True if deployment there was an error, False otherwise.
- :rtype: bool
- """
- return self.stack_status in ('CREATE_FAILED',
- 'UPDATE_FAILED',)
-
- @cached_property
- def is_deleting(self):
- """Check if this Stack is deleting.
-
- :return: True if Stack is deleting, False otherwise.
- :rtype: bool
- """
- return self.stack_status in ('DELETE_IN_PROGRESS', )
-
- @cached_property
- def is_delete_failed(self):
- """Check if Stack deleting has failed.
-
- :return: True if Stack deleting has failed, False otherwise.
- :rtype: bool
- """
- return self.stack_status in ('DELETE_FAILED', )
-
- @cached_property
- def events(self):
- """Return the Heat Events associated with this Stack
-
- :return: list of Heat Events associated with this Stack;
- or an empty list if there is no Stack associated with
- this Stack, or there are no Events
- :rtype: list of heatclient.v1.events.Event
- """
- return heat.events_list(self._request,
- self.stack_name)
-
- @property
- def stack_outputs(self):
- return getattr(self, 'outputs', [])
-
- @cached_property
- def keystone_auth_url(self):
- for output in self.stack_outputs:
- if output['output_key'] == 'KeystoneURL':
- return output['output_value']
-
- @cached_property
- def keystone_ip(self):
- if self.keystone_auth_url:
- return urlparse.urlparse(self.keystone_auth_url).hostname
-
- @cached_property
- def overcloud_keystone(self):
- try:
- return overcloud_keystoneclient(
- self._request,
- self.keystone_auth_url,
- self.plan.parameter_value('Controller-1::AdminPassword'))
- except Exception:
- LOG.debug('Unable to connect to overcloud keystone.')
- return None
-
- @cached_property
- def dashboard_urls(self):
- client = self.overcloud_keystone
- if not client:
- return []
-
- try:
- services = client.services.list()
- for service in services:
- if service.name == 'horizon':
- break
- else:
- return []
- except Exception:
- return []
-
- admin_urls = [endpoint.adminurl for endpoint
- in client.endpoints.list()
- if endpoint.service_id == service.id]
-
- return admin_urls
-
-
-class Resource(base.APIResourceWrapper):
- _attrs = ('resource_name', 'resource_type', 'resource_status',
- 'physical_resource_id')
-
- def __init__(self, apiresource, request=None, **kwargs):
- """Initialize a resource
-
- :param apiresource: apiresource we want to wrap
- :type apiresource: heatclient.v1.resources.Resource
-
- :param request: request
- :type request: django.core.handlers.wsgi.WSGIRequest
-
- :param node: node relation we want to cache
- :type node: tuskar_ui.api.node.Node
-
- :return: Resource object
- :rtype: Resource
- """
- super(Resource, self).__init__(apiresource)
- self._request = request
- if 'node' in kwargs:
- self._node = kwargs['node']
- if 'stack' in kwargs:
- self._stack = kwargs['stack']
- if 'role' in kwargs:
- self._role = kwargs['role']
-
- @classmethod
- @memoized.memoized
- def _resources_by_nodes(cls, request):
- return {resource.physical_resource_id: resource
- for resource in cls.list_all_resources(request)}
-
- @classmethod
- def get_by_node(cls, request, node):
- """Return the specified Heat Resource given a Node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param node: node to match
- :type node: tuskar_ui.api.node.Node
-
- :return: matching Resource, or raises LookupError if no
- resource matches the node
- :rtype: tuskar_ui.api.heat.Resource
- """
- return cls._resources_by_nodes(request)[node.instance_uuid]
-
- @classmethod
- def list_all_resources(cls, request):
- """Iterate through all the stacks and return all relevant resources
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :return: list of resources
- :rtype: list of tuskar_ui.api.heat.Resource
- """
- all_resources = []
- for stack in Stack.list(request):
- all_resources.extend(stack.resources(with_joins=False))
- return all_resources
-
- @cached_property
- def role(self):
- """Return the Role associated with this Resource
-
- :return: Role associated with this Resource, or None if no
- Role is associated
- :rtype: tuskar_ui.api.tuskar.Role
- """
- if hasattr(self, '_role'):
- return self._role
-
- @cached_property
- def node(self):
- """Return the Ironic Node associated with this Resource
-
- :return: Ironic Node associated with this Resource, or None if no
- Node is associated
- :rtype: tuskar_ui.api.node.Node
-
- :raises: ironicclient.exc.HTTPNotFound if there is no Node with the
- matching instance UUID
- """
- if hasattr(self, '_node'):
- return self._node
- if self.physical_resource_id:
- return node.Node.get_by_instance_uuid(self._request,
- self.physical_resource_id)
- return None
-
- @cached_property
- def stack(self):
- """Return the Stack associated with this Resource
-
- :return: Stack associated with this Resource, or None if no
- Stack is associated
- :rtype: tuskar_ui.api.heat.Stack
- """
- if hasattr(self, '_stack'):
- return self._stack
diff --git a/tuskar_ui/api/node.py b/tuskar_ui/api/node.py
deleted file mode 100644
index e9fef71b..00000000
--- a/tuskar_ui/api/node.py
+++ /dev/null
@@ -1,445 +0,0 @@
-# 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
-import time
-
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from horizon.utils import memoized
-from ironic_inspector_client import client as inspector_client
-from ironicclient import client as ironic_client
-from openstack_dashboard.api import base
-from openstack_dashboard.api import glance
-from openstack_dashboard.api import nova
-
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.handle_errors import handle_errors # noqa
-from tuskar_ui.utils import utils
-
-
-# power states
-ERROR_STATES = set(['deploy failed', 'error'])
-POWER_ON_STATES = set(['on', 'power on'])
-
-# provision_states of ironic aggregated to reasonable groups
-PROVISION_STATE_FREE = ['available', 'deleted', None]
-PROVISION_STATE_PROVISIONED = ['active']
-PROVISION_STATE_PROVISIONING = [
- 'deploying', 'wait call-back', 'rebuild', 'deploy complete']
-PROVISION_STATE_DELETING = ['deleting']
-PROVISION_STATE_ERROR = ['error', 'deploy failed']
-
-# names for states of ironic used in UI,
-# provison_states + discovery states
-DISCOVERING_STATE = 'discovering'
-DISCOVERED_STATE = 'discovered'
-DISCOVERY_FAILED_STATE = 'discovery failed'
-MAINTENANCE_STATE = 'manageable'
-PROVISIONED_STATE = 'provisioned'
-PROVISIONING_FAILED_STATE = 'provisioning failed'
-PROVISIONING_STATE = 'provisioning'
-DELETING_STATE = 'deleting'
-FREE_STATE = 'free'
-
-
-IRONIC_DISCOVERD_URL = getattr(settings, 'IRONIC_DISCOVERD_URL', None)
-LOG = logging.getLogger(__name__)
-
-
-@memoized.memoized
-def ironicclient(request):
- api_version = 1
- kwargs = {'os_auth_token': request.user.token.id,
- 'ironic_url': base.url_for(request, 'baremetal')}
- return ironic_client.get_client(api_version, **kwargs)
-
-
-# FIXME(lsmola) This should be done in Horizon, they don't have caching
-@memoized.memoized
-@handle_errors(_("Unable to retrieve image."))
-def image_get(request, image_id):
- """Returns an Image object with metadata
-
- Returns an Image object populated with metadata for image
- with supplied identifier.
-
- :param image_id: list of objects to be put into a dict
- :type image_id: list
-
- :return: object
- :rtype: glanceclient.v1.images.Image
- """
- image = glance.image_get(request, image_id)
- return image
-
-
-class Node(base.APIResourceWrapper):
- _attrs = ('id', 'uuid', 'instance_uuid', 'driver', 'driver_info',
- 'properties', 'power_state', 'target_power_state',
- 'provision_state', 'maintenance', 'extra')
-
- def __init__(self, apiresource, request=None, instance=None):
- """Initialize a Node
-
- :param apiresource: apiresource we want to wrap
- :type apiresource: IronicNode
-
- :param request: request
- :type request: django.core.handlers.wsgi.WSGIRequest
-
- :param instance: instance relation we want to cache
- :type instance: openstack_dashboard.api.nova.Server
-
- :return: Node object
- :rtype: tusar_ui.api.node.Node
- """
- super(Node, self).__init__(apiresource)
- self._request = request
- self._instance = instance
-
- @classmethod
- def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None,
- memory_mb=None, local_gb=None, mac_addresses=[],
- ipmi_username=None, ipmi_password=None, ssh_address=None,
- ssh_username=None, ssh_key_contents=None,
- deployment_kernel=None, deployment_ramdisk=None,
- driver=None):
- """Create a Node in Ironic."""
- if driver == 'pxe_ssh':
- driver_info = {
- 'ssh_address': ssh_address,
- 'ssh_username': ssh_username,
- 'ssh_key_contents': ssh_key_contents,
- 'ssh_virt_type': 'virsh',
- }
- else:
- driver_info = {
- 'ipmi_address': ipmi_address,
- 'ipmi_username': ipmi_username,
- 'ipmi_password': ipmi_password
- }
- driver_info.update(
- deploy_kernel=deployment_kernel,
- deploy_ramdisk=deployment_ramdisk
- )
-
- properties = {'capabilities': 'boot_option:local', }
- if cpus:
- properties.update(cpus=cpus)
- if memory_mb:
- properties.update(memory_mb=memory_mb)
- if local_gb:
- properties.update(local_gb=local_gb)
- if cpu_arch:
- properties.update(cpu_arch=cpu_arch)
-
- node = ironicclient(request).node.create(
- driver=driver,
- driver_info=driver_info,
- properties=properties,
- )
- for mac_address in mac_addresses:
- ironicclient(request).port.create(
- node_uuid=node.uuid,
- address=mac_address
- )
-
- return cls(node, request)
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve node"))
- def get(cls, request, uuid):
- """Return the Node that matches the ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node to be retrieved
- :type uuid: str
-
- :return: matching Node, or None if no IronicNode matches the ID
- :rtype: tuskar_ui.api.node.Node
- """
- node = ironicclient(request).node.get(uuid)
- if node.instance_uuid is not None:
- server = nova.server_get(request, node.instance_uuid)
- else:
- server = None
- return cls(node, request, server)
-
- @classmethod
- @handle_errors(_("Unable to retrieve node"))
- def get_by_instance_uuid(cls, request, instance_uuid):
- """Return the Node associated with the instance ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param instance_uuid: ID of Instance that is deployed on the Node
- to be retrieved
- :type instance_uuid: str
-
- :return: matching Node
- :rtype: tuskar_ui.api.node.Node
-
- :raises: ironicclient.exc.HTTPNotFound if there is no Node with
- the matching instance UUID
- """
- node = ironicclient(request).node.get_by_instance_uuid(instance_uuid)
- server = nova.server_get(request, instance_uuid)
- return cls(node, request, server)
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve nodes"), [])
- def list(cls, request, associated=None, maintenance=None):
- """Return a list of Nodes
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param associated: should we also retrieve all Nodes, only those
- associated with an Instance, or only those not
- associated with an Instance?
- :type associated: bool
-
- :param maintenance: should we also retrieve all Nodes, only those
- in maintenance mode, or those which are not in
- maintenance mode?
- :type maintenance: bool
-
- :return: list of Nodes, or an empty list if there are none
- :rtype: list of tuskar_ui.api.node.Node
- """
- nodes = ironicclient(request).node.list(associated=associated,
- maintenance=maintenance)
- if associated is None or associated:
- servers = nova.server_list(request)[0]
- servers_dict = utils.list_to_dict(servers)
- nodes_with_instance = []
- for n in nodes:
- server = servers_dict.get(n.instance_uuid, None)
- nodes_with_instance.append(cls(n, instance=server,
- request=request))
- return [cls.get(request, node.uuid)
- for node in nodes_with_instance]
- return [cls.get(request, node.uuid) for node in nodes]
-
- @classmethod
- def delete(cls, request, uuid):
- """Delete an Node
-
- Remove the IronicNode matching the ID if it
- exists; otherwise, does nothing.
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of IronicNode to be removed
- :type uuid: str
- """
- return ironicclient(request).node.delete(uuid)
-
- @classmethod
- def discover(cls, request, uuids):
- """Set the maintenance status of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuids: IDs of IronicNodes
- :type uuids: list of str
- """
- if not IRONIC_DISCOVERD_URL:
- return
- for uuid in uuids:
-
- inspector_client.introspect(
- uuid,
- base_url=IRONIC_DISCOVERD_URL,
- auth_token=request.user.token.id)
-
- # NOTE(dtantsur): PXE firmware on virtual machines misbehaves when
- # a lot of nodes start DHCPing simultaneously: it ignores NACK from
- # DHCP server, tries to get the same address, then times out. Work
- # around it by using sleep, anyway introspection takes much longer.
- time.sleep(5)
-
- @classmethod
- def set_maintenance(cls, request, uuid, maintenance):
- """Set the maintenance status of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node to be removed
- :type uuid: str
-
- :param maintenance: desired maintenance state
- :type maintenance: bool
- """
- patch = {
- 'op': 'replace',
- 'value': 'True' if maintenance else 'False',
- 'path': '/maintenance'
- }
- node = ironicclient(request).node.update(uuid, [patch])
- return cls(node, request)
-
- @classmethod
- def set_power_state(cls, request, uuid, power_state):
- """Set the power_state of node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of Node
- :type uuid: str
-
- :param power_state: desired power_state
- :type power_state: str
- """
- node = ironicclient(request).node.set_power_state(uuid, power_state)
- return cls(node, request)
-
- @classmethod
- @memoized.memoized
- def list_ports(cls, request, uuid):
- """Return a list of ports associated with this Node
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param uuid: ID of IronicNode
- :type uuid: str
- """
- return ironicclient(request).node.list_ports(uuid)
-
- @cached_property
- def addresses(self):
- """Return a list of port addresses associated with this IronicNode
-
- :return: list of port addresses associated with this IronicNode, or
- an empty list if no addresses are associated with
- this IronicNode
- :rtype: list of str
- """
- ports = self.list_ports(self._request, self.uuid)
- return [port.address for port in ports]
-
- @cached_property
- def cpus(self):
- return self.properties.get('cpus', None)
-
- @cached_property
- def memory_mb(self):
- return self.properties.get('memory_mb', None)
-
- @cached_property
- def local_gb(self):
- return self.properties.get('local_gb', None)
-
- @cached_property
- def cpu_arch(self):
- return self.properties.get('cpu_arch', None)
-
- @cached_property
- def state(self):
- if self.maintenance:
- if not IRONIC_DISCOVERD_URL:
- return MAINTENANCE_STATE
- try:
- status = inspector_client.get_status(
- uuid=self.uuid,
- base_url=IRONIC_DISCOVERD_URL,
- auth_token=self._request.user.token.id,
- )
- except inspector_client.ClientError as e:
- if getattr(e.response, 'status_code', None) == 404:
- return MAINTENANCE_STATE
- raise
- if status['error']:
- return DISCOVERY_FAILED_STATE
- elif status['finished']:
- return DISCOVERED_STATE
- else:
- return DISCOVERING_STATE
- else:
- if self.provision_state in PROVISION_STATE_FREE:
- return FREE_STATE
- if self.provision_state in PROVISION_STATE_PROVISIONING:
- return PROVISIONING_STATE
- if self.provision_state in PROVISION_STATE_PROVISIONED:
- return PROVISIONED_STATE
- if self.provision_state in PROVISION_STATE_DELETING:
- return DELETING_STATE
- if self.provision_state in PROVISION_STATE_ERROR:
- return PROVISIONING_FAILED_STATE
- # Unknown state
- return None
-
- @cached_property
- def instance(self):
- """Return the Nova Instance associated with this Node
-
- :return: Nova Instance associated with this Node; or
- None if there is no Instance associated with this
- Node, or no matching Instance is found
- :rtype: Instance
- """
- if self._instance is not None:
- return self._instance
- if self.instance_uuid:
- servers, _has_more_data = nova.server_list(self._request)
- for server in servers:
- if server.id == self.instance_uuid:
- return server
-
- @cached_property
- def ip_address(self):
- try:
- apiresource = self.instace._apiresource
- except AttributeError:
- LOG.error("Couldn't obtain IP address")
- return None
- return apiresource.addresses['ctlplane'][0]['addr']
-
- @cached_property
- def image_name(self):
- """Return image name of associated instance
-
- Returns image name of instance associated with node
-
- :return: Image name of instance
- :rtype: string
- """
- if self.instance is None:
- return
- image = image_get(self._request, self.instance.image['id'])
- return image.name
-
- @cached_property
- def instance_status(self):
- return getattr(getattr(self, 'instance', None), 'status', None)
-
- @cached_property
- def provisioning_status(self):
- if self.instance_uuid:
- return _("Provisioned")
- return _("Free")
-
- @classmethod
- def get_all_mac_addresses(cls, request):
- macs = [node.addresses for node in cls.list(request)]
- return set([mac.upper() for sublist in macs for mac in sublist])
diff --git a/tuskar_ui/api/tuskar.py b/tuskar_ui/api/tuskar.py
deleted file mode 100644
index 89244591..00000000
--- a/tuskar_ui/api/tuskar.py
+++ /dev/null
@@ -1,558 +0,0 @@
-# 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
-import random
-import string
-
-from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
-from glanceclient import exc as glance_exceptions
-from horizon.utils import memoized
-from openstack_dashboard.api import base
-from openstack_dashboard.api import glance
-from openstack_dashboard.api import neutron
-from os_cloud_config import keystone_pki
-from tuskarclient import client as tuskar_client
-
-from tuskar_ui.api import flavor
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.handle_errors import handle_errors # noqa
-
-LOG = logging.getLogger(__name__)
-MASTER_TEMPLATE_NAME = 'plan.yaml'
-ENVIRONMENT_NAME = 'environment.yaml'
-TUSKAR_SERVICE = 'management'
-
-SSL_HIDDEN_PARAMS = ('SSLCertificate', 'SSLKey')
-KEYSTONE_CERTIFICATE_PARAMS = (
- 'KeystoneSigningCertificate', 'KeystoneCACertificate',
- 'KeystoneSigningKey')
-
-
-@memoized.memoized
-def tuskarclient(request, password=None):
- api_version = "2"
- insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
- ca_file = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
- endpoint = base.url_for(request, TUSKAR_SERVICE)
-
- LOG.debug('tuskarclient connection created using token "%s" and url "%s"' %
- (request.user.token.id, endpoint))
-
- client = tuskar_client.get_client(api_version,
- tuskar_url=endpoint,
- insecure=insecure,
- ca_file=ca_file,
- username=request.user.username,
- password=password,
- os_auth_token=request.user.token.id)
- return client
-
-
-def password_generator(size=40, chars=(string.ascii_uppercase +
- string.ascii_lowercase +
- string.digits)):
- return ''.join(random.choice(chars) for _ in range(size))
-
-
-def strip_prefix(parameter_name):
- return parameter_name.split('::', 1)[-1]
-
-
-def _is_blank(parameter):
- return not parameter['value'] or parameter['value'] == 'unset'
-
-
-def _should_generate_password(parameter):
- # TODO(lsmola) Filter out SSL params for now. Once it will be generated
- # in TripleO add it here too. Note: this will also affect how endpoints are
- # created
- key = parameter['name']
- return all([
- parameter['hidden'],
- _is_blank(parameter),
- strip_prefix(key) not in SSL_HIDDEN_PARAMS,
- strip_prefix(key) not in KEYSTONE_CERTIFICATE_PARAMS,
- key != 'SnmpdReadonlyUserPassword',
- ])
-
-
-def _should_generate_keystone_cert(parameter):
- return all([
- strip_prefix(parameter['name']) in KEYSTONE_CERTIFICATE_PARAMS,
- _is_blank(parameter),
- ])
-
-
-def _should_generate_neutron_control_plane(parameter):
- return all([
- strip_prefix(parameter['name']) == 'NeutronControlPlaneID',
- _is_blank(parameter),
- ])
-
-
-class Plan(base.APIResourceWrapper):
- _attrs = ('uuid', 'name', 'description', 'created_at', 'modified_at',
- 'roles', 'parameters')
-
- def __init__(self, apiresource, request=None):
- super(Plan, self).__init__(apiresource)
- self._request = request
-
- @classmethod
- def create(cls, request, name, description):
- """Create a Plan in Tuskar
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param name: plan name
- :type name: string
-
- :param description: plan description
- :type description: string
-
- :return: the created Plan object
- :rtype: tuskar_ui.api.tuskar.Plan
- """
- plan = tuskarclient(request).plans.create(name=name,
- description=description)
- return cls(plan, request=request)
-
- @classmethod
- def patch(cls, request, plan_id, parameters):
- """Update a Plan in Tuskar
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param plan_id: id of the plan we want to update
- :type plan_id: string
-
- :param parameters: new values for the plan's parameters
- :type parameters: dict
-
- :return: the updated Plan object
- :rtype: tuskar_ui.api.tuskar.Plan
- """
- parameter_list = [{
- 'name': unicode(name),
- 'value': unicode(value),
- } for (name, value) in parameters.items()]
- plan = tuskarclient(request).plans.patch(plan_id, parameter_list)
- return cls(plan, request=request)
-
- @classmethod
- @memoized.memoized
- def list(cls, request):
- """Return a list of Plans in Tuskar
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :return: list of Plans, or an empty list if there are none
- :rtype: list of tuskar_ui.api.tuskar.Plan
- """
- plans = tuskarclient(request).plans.list()
- return [cls(plan, request=request) for plan in plans]
-
- @classmethod
- @handle_errors(_("Unable to retrieve plan"))
- def get(cls, request, plan_id):
- """Return the Plan that matches the ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param plan_id: id of Plan to be retrieved
- :type plan_id: int
-
- :return: matching Plan, or None if no Plan matches
- the ID
- :rtype: tuskar_ui.api.tuskar.Plan
- """
- plan = tuskarclient(request).plans.get(plan_uuid=plan_id)
- return cls(plan, request=request)
-
- # TODO(lsmola) before will will support multiple overclouds, we
- # can work only with overcloud that is named overcloud. Delete
- # this once we have more overclouds. Till then, this is the overcloud
- # that rules them all.
- # This is how API supports it now, so we have to have it this way.
- # Also till Overcloud workflow is done properly, we have to work
- # with situations that overcloud is deleted, but stack is still
- # there. So overcloud will pretend to exist when stack exist.
- @classmethod
- def get_the_plan(cls, request):
- plan_list = cls.list(request)
- for plan in plan_list:
- return plan
- # if plan doesn't exist, create it
- plan = cls.create(request, 'overcloud', 'overcloud')
- return plan
-
- @classmethod
- def delete(cls, request, plan_id):
- """Delete a Plan
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param plan_id: plan id
- :type plan_id: int
- """
- tuskarclient(request).plans.delete(plan_uuid=plan_id)
-
- @cached_property
- def role_list(self):
- return [Role.get(self._request, role.uuid)
- for role in self.roles]
-
- @cached_property
- def _roles_by_name(self):
- return dict((role.name, role) for role in self.role_list)
-
- def get_role_by_name(self, role_name):
- """Get the role with the given name."""
- return self._roles_by_name[role_name]
-
- def get_role_node_count(self, role):
- """Get the node count for the given role."""
- return int(self.parameter_value(role.node_count_parameter_name,
- 0) or 0)
-
- @cached_property
- def templates(self):
- return tuskarclient(self._request).plans.templates(self.uuid)
-
- @cached_property
- def master_template(self):
- return self.templates.get(MASTER_TEMPLATE_NAME, '')
-
- @cached_property
- def environment(self):
- return self.templates.get(ENVIRONMENT_NAME, '')
-
- @cached_property
- def provider_resource_templates(self):
- template_dict = dict(self.templates)
- del template_dict[MASTER_TEMPLATE_NAME]
- del template_dict[ENVIRONMENT_NAME]
- return template_dict
-
- def parameter_list(self, include_key_parameters=True):
- params = self.parameters
- if not include_key_parameters:
- key_params = []
- for role in self.role_list:
- key_params.extend([role.node_count_parameter_name,
- role.image_parameter_name,
- role.flavor_parameter_name])
- params = [p for p in params if p['name'] not in key_params]
- return [Parameter(p, plan=self) for p in params]
-
- def parameter(self, param_name):
- for parameter in self.parameters:
- if parameter['name'] == param_name:
- return Parameter(parameter, plan=self)
-
- def parameter_value(self, param_name, default=None):
- parameter = self.parameter(param_name)
- if parameter is not None:
- return parameter.value
- return default
-
- def list_generated_parameters(self, with_prefix=True):
- if with_prefix:
- key_format = lambda key: key
- else:
- key_format = strip_prefix
-
- # Get all password like parameters
- return dict(
- (key_format(parameter['name']), parameter)
- for parameter in self.parameter_list()
- if any([
- _should_generate_password(parameter),
- _should_generate_keystone_cert(parameter),
- _should_generate_neutron_control_plane(parameter),
- ])
- )
-
- def _make_keystone_certificates(self, wanted_generated_params):
- generated_params = {}
- for cert_param in KEYSTONE_CERTIFICATE_PARAMS:
- if cert_param in wanted_generated_params.keys():
- # If one of the keystone certificates is not set, we have
- # to generate all of them.
- generate_certificates = True
- break
- else:
- generate_certificates = False
-
- # Generate keystone certificates
- if generate_certificates:
- ca_key_pem, ca_cert_pem = keystone_pki.create_ca_pair()
- signing_key_pem, signing_cert_pem = (
- keystone_pki.create_signing_pair(ca_key_pem, ca_cert_pem))
- generated_params['KeystoneSigningCertificate'] = (
- signing_cert_pem)
- generated_params['KeystoneCACertificate'] = ca_cert_pem
- generated_params['KeystoneSigningKey'] = signing_key_pem
- return generated_params
-
- def make_generated_parameters(self):
- wanted_generated_params = self.list_generated_parameters(
- with_prefix=False)
-
- # Generate keystone certificates
- generated_params = self._make_keystone_certificates(
- wanted_generated_params)
-
- # Generate passwords and control plane id
- for (key, param) in wanted_generated_params.items():
- if _should_generate_password(param):
- generated_params[key] = password_generator()
- elif _should_generate_neutron_control_plane(param):
- generated_params[key] = neutron.network_list(
- self._request, name='ctlplane')[0].id
-
- # Fill all the Tuskar parameters with generated content. There are
- # parameters that has just different prefix, such parameters should
- # have the same values.
- wanted_prefixed_params = self.list_generated_parameters(
- with_prefix=True)
- tuskar_params = {}
-
- for (key, param) in wanted_prefixed_params.items():
- tuskar_params[key] = generated_params[strip_prefix(key)]
-
- return tuskar_params
-
- @property
- def id(self):
- return self.uuid
-
-
-class Role(base.APIResourceWrapper):
- _attrs = ('uuid', 'name', 'version', 'description', 'created')
-
- def __init__(self, apiresource, request=None):
- super(Role, self).__init__(apiresource)
- self._request = request
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve overcloud roles"), [])
- def list(cls, request):
- """Return a list of Overcloud Roles in Tuskar
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :return: list of Overcloud Roles, or an empty list if there
- are none
- :rtype: list of tuskar_ui.api.tuskar.Role
- """
- roles = tuskarclient(request).roles.list()
- return [cls(role, request=request) for role in roles]
-
- @classmethod
- @memoized.memoized
- @handle_errors(_("Unable to retrieve overcloud role"))
- def get(cls, request, role_id):
- """Return the Tuskar Role that matches the ID
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param role_id: ID of Role to be retrieved
- :type role_id: int
-
- :return: matching Role, or None if no matching
- Role can be found
- :rtype: tuskar_ui.api.tuskar.Role
- """
- for role in Role.list(request):
- if role.uuid == role_id:
- return role
-
- @classmethod
- @memoized.memoized
- def _roles_by_image(cls, request, plan):
- roles_by_image = {}
-
- for role in Role.list(request):
- image = plan.parameter_value(role.image_parameter_name)
- if image in roles_by_image:
- roles_by_image[image].append(role)
- else:
- roles_by_image[image] = [role]
-
- return roles_by_image
-
- @classmethod
- @handle_errors(_("Unable to retrieve overcloud role"))
- def get_by_image(cls, request, plan, image):
- """Return the Role whose ImageID parameter matches the image.
-
- :param request: request object
- :type request: django.http.HttpRequest
-
- :param plan: associated plan to check against
- :type plan: Plan
-
- :param image: image to be matched
- :type image: Image
-
- :return: matching Role, or None if no matching
- Role can be found
- :rtype: tuskar_ui.api.tuskar.Role
- """
- roles = cls._roles_by_image(request, plan)
- try:
- return roles[image.name]
- except KeyError:
- return []
-
- @classmethod
- @memoized.memoized
- def _roles_by_resource_type(cls, request):
- return {role.provider_resource_type: role
- for role in Role.list(request)}
-
- @classmethod
- @handle_errors(_("Unable to retrieve overcloud role"))
- def get_by_resource_type(cls, request, resource_type):
- roles = cls._roles_by_resource_type(request)
- try:
- return roles[resource_type]
- except KeyError:
- return None
-
- @property
- def provider_resource_type(self):
- return "Tuskar::{0}-{1}".format(self.name, self.version)
-
- @property
- def parameter_prefix(self):
- return "{0}-{1}::".format(self.name, self.version)
-
- @property
- def node_count_parameter_name(self):
- return self.parameter_prefix + 'count'
-
- @property
- def image_parameter_name(self):
- return self.parameter_prefix + 'Image'
-
- @property
- def flavor_parameter_name(self):
- return self.parameter_prefix + 'Flavor'
-
- def image(self, plan):
- image_name = plan.parameter_value(self.image_parameter_name)
- if image_name:
- try:
- return glance.image_list_detailed(
- self._request, filters={'name': image_name})[0][0]
- except (glance_exceptions.HTTPNotFound, IndexError):
- LOG.error("Couldn't obtain image with name %s" % image_name)
- return None
-
- def flavor(self, plan):
- flavor_name = plan.parameter_value(
- self.flavor_parameter_name)
- if flavor_name:
- return flavor.Flavor.get_by_name(self._request, flavor_name)
-
- def parameter_list(self, plan):
- return [p for p in plan.parameter_list() if self == p.role]
-
- def is_valid_for_deployment(self, plan):
- node_count = plan.get_role_node_count(self)
- pending_required_params = list(Parameter.pending_parameters(
- Parameter.required_parameters(self.parameter_list(plan))))
- return not (
- self.image(plan) is None or
- (node_count and self.flavor(plan) is None) or
- pending_required_params
- )
-
- @property
- def id(self):
- return self.uuid
-
-
-class Parameter(base.APIDictWrapper):
-
- _attrs = ['name', 'value', 'default', 'description', 'hidden', 'label',
- 'parameter_type', 'constraints']
-
- def __init__(self, apidict, plan=None):
- super(Parameter, self).__init__(apidict)
- self._plan = plan
-
- @property
- def stripped_name(self):
- return strip_prefix(self.name)
-
- @property
- def plan(self):
- return self._plan
-
- @property
- def role(self):
- if self.plan:
- for role in self.plan.role_list:
- if self.name.startswith(role.parameter_prefix):
- return role
-
- def is_required(self):
- """Boolean: True if parameter is required, False otherwise."""
- return self.default is None
-
- def get_constraint_by_type(self, constraint_type):
- """Returns parameter constraint by it's type.
-
- For available constraint types see HOT Spec:
- http://docs.openstack.org/developer/heat/template_guide/hot_spec.html
- """
-
- constraints_of_type = [c for c in self.constraints
- if c['constraint_type'] == constraint_type]
- if constraints_of_type:
- return constraints_of_type[0]
- else:
- return None
-
- @staticmethod
- def required_parameters(parameters):
- """Yields parameters which are required."""
- for parameter in parameters:
- if parameter.is_required():
- yield parameter
-
- @staticmethod
- def pending_parameters(parameters):
- """Yields parameters which don't have value set."""
- for parameter in parameters:
- if not parameter.value:
- yield parameter
-
- @staticmethod
- def global_parameters(parameters):
- """Yields parameters with name without role prefix."""
- for parameter in parameters:
- if '::' not in parameter.name:
- yield parameter
diff --git a/tuskar_ui/cached_property.py b/tuskar_ui/cached_property.py
deleted file mode 100644
index 7e370441..00000000
--- a/tuskar_ui/cached_property.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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.
-
-# Copyright (c) Django Software Foundation and individual contributors.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice,
-# this list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# 3. Neither the name of Django nor the names of its contributors may be
-# used to endorse or promote products derived from this software without
-# specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-
-
-# We would be using django.utils.functional.cached_property, except it
-# breaks when used with mox in our tests, because of
-# https://code.djangoproject.com/ticket/19872
-#
-# So we have a copy of it here, with the bug fixed.
-# FIXME: Use django's version when the bug is fixed there.
-class cached_property(object):
- """Cached property decorator.
-
- Decorator that creates converts a method with a single self argument
- into a property cached on the instance.
- """
-
- def __init__(self, func):
- self.func = func
-
- def __get__(self, instance, type):
- if instance is None:
- return self
- res = instance.__dict__[self.func.__name__] = self.func(instance)
- return res
diff --git a/tuskar_ui/exceptions.py b/tuskar_ui/exceptions.py
deleted file mode 100644
index 0fd36788..00000000
--- a/tuskar_ui/exceptions.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# 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 ironicclient import exceptions as ironic_exceptions
-from openstack_dashboard import exceptions
-from tuskarclient.openstack.common.apiclient import exceptions as tuskarclient
-
-NOT_FOUND = exceptions.NOT_FOUND
-RECOVERABLE = exceptions.RECOVERABLE + (
- ironic_exceptions.Conflict, tuskarclient.ClientException,
-)
-UNAUTHORIZED = exceptions.UNAUTHORIZED
diff --git a/tuskar_ui/forms.py b/tuskar_ui/forms.py
deleted file mode 100644
index e5aab204..00000000
--- a/tuskar_ui/forms.py
+++ /dev/null
@@ -1,174 +0,0 @@
-#
-# 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
-
-from django import forms
-from django.utils import html
-from django.utils.translation import ugettext_lazy as _
-import netaddr
-
-
-SEPARATOR_RE = re.compile('[\s,;|]+', re.UNICODE)
-
-
-def label_with_tooltip(label, tooltip=None, title=None):
- if not tooltip:
- return label
- return html.format_html(
- u'{0}&nbsp;<a class="help-icon fa fa-question-circle" '
- u'data-content="{1}" tabindex="0" href="#" '
- u'data-title="{2}"></a>',
- html.escape(label),
- html.escape(tooltip),
- html.escape(title or label)
- )
-
-
-def fieldset(form, *args, **kwargs):
- """A helper function for grouping fields based on their names."""
-
- prefix = kwargs.pop('prefix', '.*')
- names = args or form.fields.keys()
-
- for name in names:
- if prefix is not None and re.match(prefix, name):
- yield forms.forms.BoundField(form, form.fields[name], name)
-
-
-class MACDialect(netaddr.mac_eui48):
- """For validating MAC addresses. Same validation as Nova uses."""
- word_fmt = '%.02x'
- word_sep = ':'
-
-
-def normalize_MAC(value):
- try:
- return str(netaddr.EUI(
- value.strip(), version=48, dialect=MACDialect)).upper()
- except (netaddr.AddrFormatError, TypeError):
- raise ValueError('Invalid MAC address')
-
-
-class NumberInput(forms.widgets.TextInput):
- """A form input for numbers."""
- input_type = 'number'
-
-
-class NumberPickerInput(forms.widgets.TextInput):
- """A form input that is rendered as a big number picker."""
-
- def __init__(self, attrs=None):
- default_attrs = {'class': 'number-picker'}
- if attrs:
- default_attrs.update(attrs)
- super(NumberPickerInput, self).__init__(default_attrs)
-
-
-class MACField(forms.fields.Field):
- """A form field for entering a single MAC address."""
-
- def clean(self, value):
- value = super(MACField, self).clean(value)
- try:
- return normalize_MAC(value)
- except ValueError:
- raise forms.ValidationError(_(u'Enter a valid MAC address.'))
-
-
-class MultiMACField(forms.fields.Field):
- """A form field for entering multiple MAC addresses.
-
- The individual MAC addresses can be separated by any whitespace,
- commas, semicolons or pipe characters.
-
- Gives a string of normalized MAC addresses separated by spaces.
- """
-
- def clean(self, value):
- value = super(MultiMACField, self).clean(value)
-
- macs = []
- for mac in SEPARATOR_RE.split(value):
- if mac:
- try:
- normalized_mac = normalize_MAC(mac)
- except ValueError:
- raise forms.ValidationError(
- _(u'%r is not a valid MAC address.') % mac)
- else:
- macs.append(normalized_mac)
-
- return ' '.join(sorted(set(macs)))
-
-
-class NetworkField(forms.fields.Field):
- """A form field for entering a network specification with a mask."""
-
- def clean(self, value):
- value = super(NetworkField, self).clean(value)
- try:
- return str(netaddr.IPNetwork(value, version=4))
- except netaddr.AddrFormatError:
- raise forms.ValidationError(_("Enter valid IPv4 network address."))
-
-
-class SelfHandlingFormset(forms.formsets.BaseFormSet):
- def __init__(self, *args, **kwargs):
- self.request = kwargs.pop('request', None)
- super(SelfHandlingFormset, self).__init__(*args, **kwargs)
-
- def handle(self, request, data):
- success = True
- for form in self:
- form_success = form.handle(request, form.cleaned_data)
- if not form_success:
- success = False
- else:
- pass
- return success
-
-
-class LabelWidget(forms.Widget):
- """A widget for displaying information.
-
- This is a custom widget to show context information just as text,
- as readonly inputs are confusing.
- Note that the field also must be required=False, as no input
- is rendered, and it must be ignored in the handle() method.
- """
- def render(self, name, value, attrs=None):
- if value:
- return html.escape(value)
- return ''
-
-
-class StaticTextWidget(forms.Widget):
- def render(self, name, value, attrs=None):
- if value is None:
- value = ''
- return html.format_html('<p class="form-control-static">{0}</p>',
- value)
-
-
-class StaticTextPasswordWidget(forms.Widget):
- def render(self, name, value, attrs=None):
- if value is None or value == '':
- return html.format_html(u'<p class="form-control-static"></p>')
- else:
- return html.format_html(
- u'<p class="form-control-static">'
- u'<a href="" class="btn btn-default btn-xs password-button"'
- u' data-content="{0}"><i class="fa fa-eye"></i>&nbsp;{1}</a>'
- u'</p>', value, _(u"Reveal")
- )
diff --git a/tuskar_ui/handle_errors.py b/tuskar_ui/handle_errors.py
deleted file mode 100644
index a505f4eb..00000000
--- a/tuskar_ui/handle_errors.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- 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
diff --git a/tuskar_ui/infrastructure/__init__.py b/tuskar_ui/infrastructure/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/dashboard.py b/tuskar_ui/infrastructure/dashboard.py
deleted file mode 100644
index 39c28145..00000000
--- a/tuskar_ui/infrastructure/dashboard.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# 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 Infrastructure(horizon.Dashboard):
- name = _("Infrastructure")
- slug = "infrastructure"
- panels = (
- 'overview',
- 'parameters',
- 'roles',
- 'nodes',
- 'flavors',
- 'images',
- 'history',
- )
- default_panel = 'overview'
- permissions = ('openstack.roles.admin',)
-
-
-horizon.register(Infrastructure)
diff --git a/tuskar_ui/infrastructure/flavors/__init__.py b/tuskar_ui/infrastructure/flavors/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/flavors/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/flavors/panel.py b/tuskar_ui/infrastructure/flavors/panel.py
deleted file mode 100644
index 2129b292..00000000
--- a/tuskar_ui/infrastructure/flavors/panel.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-from tuskar_ui.infrastructure.flavors import utils
-
-
-class Flavors(horizon.Panel):
- name = _("Flavors")
- slug = "flavors"
-
- def can_access(self, context):
- if not utils.matching_deployment_mode():
- return False
-
- return super(Flavors, self).can_access(context)
-
-
-dashboard.Infrastructure.register(Flavors)
diff --git a/tuskar_ui/infrastructure/flavors/tables.py b/tuskar_ui/infrastructure/flavors/tables.py
deleted file mode 100644
index 68431a5b..00000000
--- a/tuskar_ui/infrastructure/flavors/tables.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# -*- 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 django.shortcuts
-from django.utils.translation import ugettext_lazy as _
-import horizon.exceptions
-import horizon.messages
-import horizon.tables
-from openstack_dashboard.dashboards.admin.flavors import (
- tables as flavor_tables)
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.flavors import utils
-
-
-class CreateFlavor(flavor_tables.CreateFlavor):
- verbose_name = _(u"New Flavor")
- url = "horizon:infrastructure:flavors:create"
-
-
-class CreateSuggestedFlavor(horizon.tables.Action):
- name = 'create'
- verbose_name = _(u"Create")
- verbose_name_plural = _(u"Create Suggested Flavors")
- method = 'POST'
- icon = 'plus'
-
- def create_flavor(self, request, node_id):
- node = api.node.Node.get(request, node_id)
- suggestion = utils.FlavorSuggestion.from_node(node)
- return suggestion.create_flavor(request)
-
- def handle(self, data_table, request, node_ids):
- for node_id in node_ids:
- try:
- self.create_flavor(request, node_id)
- except Exception:
- horizon.exceptions.handle(
- request,
- _(u"Unable to create flavor for node %r") % node_id,
- )
- return django.shortcuts.redirect(request.get_full_path())
-
-
-class EditAndCreateSuggestedFlavor(CreateFlavor):
- name = 'edit_and_create'
- verbose_name = _(u"Edit before creating")
- icon = 'pencil'
-
-
-class DeleteFlavor(flavor_tables.DeleteFlavor):
-
- def __init__(self, **kwargs):
- super(DeleteFlavor, self).__init__(**kwargs)
- # NOTE(dtantsur): setting class attributes doesn't work
- # probably due to metaclass magic in actions
- self.data_type_singular = _("Flavor")
- self.data_type_plural = _("Flavors")
-
- def allowed(self, request, datum=None):
- """Check that action is allowed on flavor
-
- This is overridden method from horizon.tables.BaseAction.
-
- :param datum: flavor we're operating on
- :type datum: tuskar_ui.api.Flavor
- """
- if datum is not None:
- deployed_flavors = api.flavor.Flavor.list_deployed_ids(
- request, _error_default=None)
- if deployed_flavors is None or datum.id in deployed_flavors:
- return False
- return super(DeleteFlavor, self).allowed(request, datum)
-
-
-class FlavorsTable(horizon.tables.DataTable):
- name = horizon.tables.Column('name',
- link="horizon:infrastructure:flavors:details")
- arch = horizon.tables.Column('cpu_arch', verbose_name=_('Architecture'))
- vcpus = horizon.tables.Column('vcpus', verbose_name=_('CPUs'))
- ram = horizon.tables.Column(flavor_tables.get_size,
- verbose_name=_('Memory'),
- attrs={'data-type': 'size'})
- disk = horizon.tables.Column(flavor_tables.get_disk_size,
- verbose_name=_('Disk'),
- attrs={'data-type': 'size'})
-
- class Meta(object):
- name = "flavors"
- verbose_name = _("Available")
- table_actions = (
- DeleteFlavor,
- flavor_tables.FlavorFilterAction,
- )
- row_actions = (
- DeleteFlavor,
- )
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class FlavorRolesTable(horizon.tables.DataTable):
- name = horizon.tables.Column('name', verbose_name=_('Role Name'))
-
- def __init__(self, request, *args, **kwargs):
- # TODO(dtantsur): support multiple overclouds
- plan = api.tuskar.Plan.get_the_plan(request)
- stack = api.heat.Stack.get_by_plan(request, plan)
-
- if stack is None:
- count = lambda role: _('Not Deployed')
- else:
- count = stack.resources_count
-
- self._columns['count'] = horizon.tables.Column(
- count,
- verbose_name=_("Instances Count")
- )
- super(FlavorRolesTable, self).__init__(request, *args, **kwargs)
-
- class Meta(object):
- name = "flavor_roles"
- verbose_name = _("Overcloud Roles")
- table_actions = ()
- row_actions = ()
- hidden_title = False
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class FlavorSuggestionsTable(horizon.tables.DataTable):
- name = horizon.tables.Column('name',)
- arch = horizon.tables.Column('cpu_arch', verbose_name=_('Architecture'))
- vcpus = horizon.tables.Column('vcpus', verbose_name=_('CPUs'))
- ram = horizon.tables.Column(flavor_tables.get_size,
- verbose_name=_('Memory'),
- attrs={'data-type': 'size'})
- disk = horizon.tables.Column(flavor_tables.get_disk_size,
- verbose_name=_('Disk'),
- attrs={'data-type': 'size'})
-
- class Meta(object):
- name = "suggested_flavors"
- verbose_name = _("Suggested")
- row_actions = (
- CreateSuggestedFlavor,
- EditAndCreateSuggestedFlavor,
- )
- template = "horizon/common/_enhanced_data_table.html"
diff --git a/tuskar_ui/infrastructure/flavors/templates/flavors/create.html b/tuskar_ui/infrastructure/flavors/templates/flavors/create.html
deleted file mode 100644
index 4fedb05a..00000000
--- a/tuskar_ui/infrastructure/flavors/templates/flavors/create.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Create Flavor" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Create Flavor") %}
-{% endblock page_header %}
-
-{% block main %}
- {% include 'horizon/common/_workflow.html' %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/flavors/templates/flavors/details.html b/tuskar_ui/infrastructure/flavors/templates/flavors/details.html
deleted file mode 100644
index 41d59603..00000000
--- a/tuskar_ui/infrastructure/flavors/templates/flavors/details.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans 'Flavor: ' %}{{ flavor.name }}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_page_header.html' with title=_('Flavor: ')|add:flavor.name %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-md-4">
- <h4>{% trans "Hardware Info" %}</h4>
- <dl class="dl-horizontal dl-horizontal-left">
- <dt>{% trans "Architecture" %}</dt>
- <dd>{{ flavor.cpu_arch|default:"&mdash;" }}</dd>
- <dt>{% trans "CPUs" %}</dt>
- <dd>{{ flavor.vcpus|default:"&mdash;" }}</dd>
- <dt>{% trans "Memory" %}</dt>
- <dd>{{ flavor.ram_bytes|filesizeformat|default:"&mdash;" }}</dd>
- <dt>{% trans "Disk" %}</dt>
- <dd>{{ flavor.disk_bytes|filesizeformat|default:"&mdash;" }}</dd>
- </dl>
- </div>
-</div>
-<div class="row">
- <div class="col-xs-12">
- {{ table.render }}
- </div>
-</div>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/flavors/templates/flavors/index.html b/tuskar_ui/infrastructure/flavors/templates/flavors/index.html
deleted file mode 100644
index 830efc7b..00000000
--- a/tuskar_ui/infrastructure/flavors/templates/flavors/index.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% load url from future %}
-{% block title %}{% trans 'Flavors' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Flavors') items_count=flavors_count %}
-{% endblock page_header %}
-
-{% block main %}
-{% if suggested_flavors_count %}
-<div class="panel panel-default">
- <div class="panel-heading" data-toggle="collapse" href="#suggestedFlavors">
- <a href="#suggestedFlavors">{{ suggested_flavors_count }}&times; Suggested Flavor</a>
- </div>
- <div class="panel-collapse collapse" id="suggestedFlavors">
- <div class="panel-body">
- {{ suggested_flavors_table.render }}
- </div>
- </div>
-</div>
-{% endif %}
-
-<div id="flavors">
- {{ flavors_table.render }}
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/flavors/tests.py b/tuskar_ui/infrastructure/flavors/tests.py
deleted file mode 100644
index 470cb4f9..00000000
--- a/tuskar_ui/infrastructure/flavors/tests.py
+++ /dev/null
@@ -1,265 +0,0 @@
-# -*- 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 contextlib
-
-from django.core import urlresolvers
-from mock import patch, call # noqa
-from novaclient import exceptions as nova_exceptions
-from novaclient.v2 import servers
-from openstack_dashboard.test.test_data import utils
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.flavors import utils as flavors_utils
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import flavor_data
-from tuskar_ui.test.test_data import heat_data
-from tuskar_ui.test.test_data import tuskar_data
-
-
-TEST_DATA = utils.TestDataContainer()
-flavor_data.data(TEST_DATA)
-heat_data.data(TEST_DATA)
-tuskar_data.data(TEST_DATA)
-INDEX_URL = urlresolvers.reverse(
- 'horizon:infrastructure:flavors:index')
-CREATE_URL = urlresolvers.reverse(
- 'horizon:infrastructure:flavors:create')
-DETAILS_VIEW = 'horizon:infrastructure:flavors:details'
-
-
-@contextlib.contextmanager
-def _prepare_create():
- flavor = TEST_DATA.novaclient_flavors.first()
- all_flavors = TEST_DATA.novaclient_flavors.list()
- data = {'name': 'foobar',
- 'vcpus': 3,
- 'memory_mb': 1024,
- 'disk_gb': 40,
- 'arch': 'amd64'}
- with contextlib.nested(
- patch('tuskar_ui.api.flavor.Flavor.create',
- return_value=flavor),
- # Inherited code calls this directly
- patch('openstack_dashboard.api.nova.flavor_list',
- return_value=all_flavors),
- ) as mocks:
- yield mocks[0], data
-
-
-def _raise_nova_client_exception(*args, **kwargs):
- raise nova_exceptions.ClientException("Boom!")
-
-
-class FlavorsTest(test.BaseAdminViewTests):
-
- def test_index(self):
- plans = [api.tuskar.Plan(plan, self.request)
- for plan in TEST_DATA.tuskarclient_plans.list()]
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
-
- with contextlib.nested(
- patch('tuskar_ui.api.node.ironicclient'),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('openstack_dashboard.api.nova.flavor_list',
- return_value=TEST_DATA.novaclient_flavors.list()),
- patch('openstack_dashboard.api.nova.server_list',
- return_value=([], False)),
- ) as (ironic_mock, plans_mock, roles_mock, flavors_mock, servers_mock):
- res = self.client.get(INDEX_URL)
- self.assertEqual(plans_mock.call_count, 1)
- self.assertEqual(roles_mock.call_count, 4)
- self.assertEqual(flavors_mock.call_count, 3)
- self.assertEqual(servers_mock.call_count, 2)
-
- self.assertTemplateUsed(res, 'infrastructure/flavors/index.html')
-
- def test_index_recoverable_failure(self):
- with patch(
- 'openstack_dashboard.api.nova.flavor_list',
- side_effect=_raise_nova_client_exception
- ) as flavor_list, patch('tuskar_ui.api.node.ironicclient'):
- res = self.client.get(INDEX_URL)
- self.assertEqual(flavor_list.call_count, 2)
- self.assertEqual(
- [(m.message, m.tags) for m in res.context['messages']],
- [
- (u'Unable to retrieve flavor list.', u'error'),
- (u'Unable to retrieve nodes', u'error'),
- ],
- )
- self.assertMessageCount(response=res, error=2, warning=0)
-
- def test_create_get(self):
- res = self.client.get(CREATE_URL)
- self.assertTemplateUsed(res, 'infrastructure/flavors/create.html')
-
- def test_create_post_ok(self):
- with _prepare_create() as (create_mock, data):
- res = self.client.post(CREATE_URL, data)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
- request = create_mock.call_args_list[0][0][0]
- self.assertListEqual(create_mock.call_args_list, [
- call(request, name=u'foobar', memory=1024, vcpus=3, disk=40,
- cpu_arch='amd64')
- ])
-
- def test_create_post_name_exists(self):
- flavor = TEST_DATA.novaclient_flavors.first()
- with _prepare_create() as (create_mock, data):
- data['name'] = flavor.name
- res = self.client.post(CREATE_URL, data)
- self.assertFormErrors(res)
-
- def test_delete_ok(self):
- flavors = TEST_DATA.novaclient_flavors.list()
- data = {'action': 'flavors__delete',
- 'object_ids': [flavors[0].id, flavors[1].id]}
- with contextlib.nested(
- patch('openstack_dashboard.api.nova.flavor_delete'),
- patch('openstack_dashboard.api.nova.server_list',
- return_value=([], False)),
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=[]),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=[]),
- patch('openstack_dashboard.api.nova.flavor_list',
- return_value=TEST_DATA.novaclient_flavors.list())
- ):
- res = self.client.post(INDEX_URL, data)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_delete_deployed_on_servers(self):
- flavors = TEST_DATA.novaclient_flavors.list()
- server = servers.Server(
- servers.ServerManager(None),
- {'id': 'aa',
- 'name': 'Compute',
- 'image': {'id': 1},
- 'status': 'ACTIVE',
- 'flavor': {'id': flavors[0].id}}
- )
- data = {'action': 'flavors__delete',
- 'object_ids': [flavors[0].id, flavors[1].id]}
- with contextlib.nested(
- patch('openstack_dashboard.api.nova.flavor_delete'),
- patch('openstack_dashboard.api.nova.server_list',
- return_value=([server], False)),
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=[]),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=[]),
- patch('openstack_dashboard.api.nova.flavor_list',
- return_value=TEST_DATA.novaclient_flavors.list()),
- patch('tuskar_ui.api.node.Node.list',
- return_value=[])
- ):
- res = self.client.post(INDEX_URL, data)
- self.assertMessageCount(error=1, warning=0)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_details_no_overcloud(self):
- flavor = api.flavor.Flavor(TEST_DATA.novaclient_flavors.first())
- plan = api.tuskar.Plan(TEST_DATA.tuskarclient_plans.first())
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
-
- with contextlib.nested(
- patch('tuskar_ui.api.flavor.Flavor.get',
- return_value=flavor),
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Role.list', return_value=roles),
- patch('tuskar_ui.api.tuskar.Role.flavor', return_value=flavor),
- ) as (get_mock, plan_mock, roles_mock, role_flavor_mock):
- res = self.client.get(urlresolvers.reverse(DETAILS_VIEW,
- args=(flavor.id,)))
- self.assertEqual(get_mock.call_count, 1)
- self.assertEqual(plan_mock.call_count, 2)
- self.assertEqual(roles_mock.call_count, 1)
- self.assertEqual(role_flavor_mock.call_count, 8)
- self.assertTemplateUsed(res, 'infrastructure/flavors/details.html')
-
- def test_details(self):
- flavor = api.flavor.Flavor(TEST_DATA.novaclient_flavors.first())
- plan = api.tuskar.Plan(TEST_DATA.tuskarclient_plans.first())
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- patch('tuskar_ui.api.flavor.Flavor.get',
- return_value=flavor),
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Role.list', return_value=roles),
- patch('tuskar_ui.api.tuskar.Role.flavor', return_value=flavor),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- # __name__ is required for horizon.tables
- patch('tuskar_ui.api.heat.Stack.resources_count',
- return_value=42, __name__='')
- ) as (flavor_mock, plan_mock, roles_mock, role_flavor_mock,
- stack_mock, count_mock):
- res = self.client.get(urlresolvers.reverse(DETAILS_VIEW,
- args=(flavor.id,)))
- self.assertEqual(flavor_mock.call_count, 1)
- self.assertEqual(plan_mock.call_count, 2)
- self.assertEqual(roles_mock.call_count, 1)
- self.assertEqual(role_flavor_mock.call_count, 8)
- self.assertEqual(stack_mock.call_count, 1)
- self.assertEqual(count_mock.call_count, 4)
- self.assertTemplateUsed(res, 'infrastructure/flavors/details.html')
-
-
-class FlavorsUtilsTest(test.TestCase):
- def test_get_unmached_suggestions(self):
- flavors = [api.flavor.Flavor(flavor)
- for flavor in TEST_DATA.novaclient_flavors.list()]
- nodes = [api.node.Node(api.node.Node(node))
- for node in self.ironicclient_nodes.list()]
- with (
- patch('tuskar_ui.api.flavor.Flavor.list', return_value=flavors)
- ), (
- patch('tuskar_ui.api.node.Node.list', return_value=nodes)
- ):
- ret = flavors_utils.get_flavor_suggestions(None)
- FS = flavors_utils.FlavorSuggestion
- self.assertEqual(ret, set([
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='aa-11'),
- FS(vcpus=16, ram_bytes=4294967296, disk_bytes=107374182400,
- cpu_arch='x86_64', node_id='bb-22'),
- FS(vcpus=32, ram_bytes=8589934592, disk_bytes=1073741824,
- cpu_arch='x86_64', node_id='cc-33'),
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='cc-44'),
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='dd-55'),
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='ff-66'),
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='gg-77'),
- FS(vcpus=8, ram_bytes=4294967296, disk_bytes=10737418240,
- cpu_arch='x86_64', node_id='hh-88'),
- FS(vcpus=16, ram_bytes=8589934592, disk_bytes=1073741824000,
- cpu_arch='x86_64', node_id='ii-99'),
- ]))
diff --git a/tuskar_ui/infrastructure/flavors/urls.py b/tuskar_ui/infrastructure/flavors/urls.py
deleted file mode 100644
index 7f78dc45..00000000
--- a/tuskar_ui/infrastructure/flavors/urls.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.flavors import views
-
-
-urlpatterns = urls.patterns(
- 'tuskar_ui.infrastructure.flavors.views',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^create/(?P<suggestion_id>[^/]+)$', views.CreateView.as_view(),
- name='create'),
- urls.url(r'^create/$', views.CreateView.as_view(), name='create'),
- urls.url(r'^(?P<flavor_id>[^/]+)/$', views.DetailView.as_view(),
- name='details'),
-)
diff --git a/tuskar_ui/infrastructure/flavors/utils.py b/tuskar_ui/infrastructure/flavors/utils.py
deleted file mode 100644
index cf2d63dd..00000000
--- a/tuskar_ui/infrastructure/flavors/utils.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- 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.
-
-from django.conf import settings
-
-from tuskar_ui import api
-from tuskar_ui.utils import utils
-
-
-def matching_deployment_mode():
- deployment_mode = getattr(settings, 'DEPLOYMENT_MODE', 'scale')
- return deployment_mode.lower() == 'scale'
-
-
-def _get_unmatched_suggestions(request):
- unmatched_suggestions = []
- flavor_suggestions = [FlavorSuggestion.from_flavor(flavor)
- for flavor in api.flavor.Flavor.list(request)]
- for node in api.node.Node.list(request):
- node_suggestion = FlavorSuggestion.from_node(node)
- for flavor_suggestion in flavor_suggestions:
- if flavor_suggestion == node_suggestion:
- break
- else:
- unmatched_suggestions.append(node_suggestion)
- return unmatched_suggestions
-
-
-def get_flavor_suggestions(request):
- return set(_get_unmatched_suggestions(request))
-
-
-class FlavorSuggestion(object):
- """Describe node parameters in a way that is easy to compare."""
-
- def __init__(self, vcpus=None, ram=None, disk=None, cpu_arch=None,
- ram_bytes=None, disk_bytes=None, node_id=None):
- self.vcpus = vcpus
- self.ram_bytes = ram_bytes or ram * 1024 * 1024 or 0
- self.disk_bytes = disk_bytes or (disk or 0) * 1024 * 1024 * 1024
- self.cpu_arch = cpu_arch
- self.id = node_id
-
- @classmethod
- def from_node(cls, node):
- return cls(
- node_id=node.uuid,
- vcpus=utils.safe_int_cast(node.cpus),
- ram=utils.safe_int_cast(node.memory_mb),
- disk=utils.safe_int_cast(node.local_gb),
- cpu_arch=node.cpu_arch
- )
-
- @classmethod
- def from_flavor(cls, flavor):
- return cls(
- vcpus=flavor.vcpus,
- ram_bytes=flavor.ram_bytes,
- disk_bytes=flavor.disk_bytes,
- cpu_arch=flavor.cpu_arch
- )
-
- @property
- def name(self):
- return 'Flavor-%scpu-%s-%sMB-%sGB' % (
- self.vcpus or '0',
- self.cpu_arch or '',
- self.ram or '0',
- self.disk or '0',
- )
-
- @property
- def ram(self):
- return self.ram_bytes / 1024 / 1024
-
- @property
- def disk(self):
- return self.disk_bytes / 1024 / 1024 / 1024
-
- def __hash__(self):
- return self.name.__hash__()
-
- def __eq__(self, other):
- return self.name == other.name
-
- def __ne__(self, other):
- return not self == other
-
- def __repr__(self):
- return (
- '%s(vcpus=%r, ram_bytes=%r, disk_bytes=%r, '
- 'cpu_arch=%r, node_id=%r)' % (
- self.__class__.__name__,
- self.vcpus,
- self.ram_bytes,
- self.disk_bytes,
- self.cpu_arch,
- self.id,
- )
- )
-
- def create_flavor(self, request):
- return api.flavor.Flavor.create(
- request,
- name=self.name,
- memory=self.ram,
- vcpus=self.vcpus,
- disk=self.disk,
- cpu_arch=self.cpu_arch,
- )
diff --git a/tuskar_ui/infrastructure/flavors/views.py b/tuskar_ui/infrastructure/flavors/views.py
deleted file mode 100644
index 09c36ac7..00000000
--- a/tuskar_ui/infrastructure/flavors/views.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- 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.
-
-from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
-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 tuskar_ui import api
-from tuskar_ui.infrastructure.flavors import tables
-from tuskar_ui.infrastructure.flavors import utils
-from tuskar_ui.infrastructure.flavors import workflows
-
-
-class IndexView(horizon.tables.MultiTableView):
- table_classes = (tables.FlavorsTable, tables.FlavorSuggestionsTable)
- template_name = 'infrastructure/flavors/index.html'
-
- def get_context_data(self, **kwargs):
- context = super(IndexView, self).get_context_data(**kwargs)
- create_action = {
- 'name': _("New Flavor"),
- 'url': reverse('horizon:infrastructure:flavors:create'),
- 'icon': 'fa-plus',
- 'ajax_modal': True,
- }
- context['header_actions'] = [create_action]
- context['flavors_count'] = self.get_flavors_count()
- context['suggested_flavors_count'] = self.get_suggested_flavors_count()
- return context
-
- @memoized.memoized_method
- def get_flavors_data(self):
- flavors = api.flavor.Flavor.list(self.request)
- flavors.sort(key=lambda np: (np.vcpus, np.ram, np.disk))
- return flavors
-
- @memoized.memoized_method
- def get_suggested_flavors_data(self):
- return list(utils.get_flavor_suggestions(self.request))
-
- def get_flavors_count(self):
- return len(self.get_flavors_data())
-
- def get_suggested_flavors_count(self):
- return len(self.get_suggested_flavors_data())
-
-
-class CreateView(horizon.workflows.WorkflowView):
- workflow_class = workflows.CreateFlavor
- template_name = 'infrastructure/flavors/create.html'
-
- def get_initial(self):
- suggestion_id = self.kwargs.get('suggestion_id')
- if not suggestion_id:
- return super(CreateView, self).get_initial()
- node = api.node.Node.get(self.request, suggestion_id)
- suggestion = utils.FlavorSuggestion.from_node(node)
- return {
- 'name': suggestion.name,
- 'vcpus': suggestion.vcpus,
- 'memory_mb': suggestion.ram,
- 'disk_gb': suggestion.disk,
- 'arch': suggestion.cpu_arch,
- }
-
-
-class DetailView(horizon.tables.DataTableView):
- table_class = tables.FlavorRolesTable
- template_name = 'infrastructure/flavors/details.html'
- error_redirect = reverse_lazy('horizon:infrastructure:flavors:index')
-
- def get_context_data(self, **kwargs):
- context = super(DetailView, self).get_context_data(**kwargs)
- context['flavor'] = api.flavor.Flavor.get(
- self.request,
- kwargs.get('flavor_id'),
- _error_redirect=self.error_redirect
- )
- return context
-
- def get_data(self):
- flavor_id = self.kwargs.get('flavor_id')
- plan = api.tuskar.Plan.get_the_plan(self.request)
-
- return [role for role in api.tuskar.Role.list(self.request)
- if role.flavor(plan)
- and role.flavor(plan).id == flavor_id]
diff --git a/tuskar_ui/infrastructure/flavors/workflows.py b/tuskar_ui/infrastructure/flavors/workflows.py
deleted file mode 100644
index 6f1e1dee..00000000
--- a/tuskar_ui/infrastructure/flavors/workflows.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- 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.
-
-from django.forms import fields
-from django.utils.translation import ugettext_lazy as _
-from horizon import exceptions
-from horizon import workflows
-from openstack_dashboard.dashboards.admin.flavors import (
- workflows as flavor_workflows)
-
-from tuskar_ui import api
-
-
-class CreateFlavorAction(flavor_workflows.CreateFlavorInfoAction):
- arch = fields.ChoiceField(choices=(('i386', 'i386'), ('amd64', 'amd64'),
- ('x86_64', 'x86_64')),
- label=_("Architecture"))
-
- def __init__(self, *args, **kwrds):
- super(CreateFlavorAction, self).__init__(*args, **kwrds)
- # Delete what is not applicable to hardware
- del self.fields['eph_gb']
- del self.fields['swap_mb']
- # Alter user-visible strings
- self.fields['vcpus'].label = _("CPUs")
- self.fields['disk_gb'].label = _("Disk GB")
- # No idea why Horizon exposes this database detail
- del self.fields['flavor_id']
-
- class Meta(object):
- name = _("Flavor")
- help_text = _("Flavors define the sizes for RAM, disk, number of "
- "cores, and other resources. Flavors should be "
- "associated with roles when planning a deployment.")
-
-
-class CreateFlavorStep(workflows.Step):
- action_class = CreateFlavorAction
- contributes = ("name",
- "vcpus",
- "memory_mb",
- "disk_gb",
- "arch")
-
-
-class CreateFlavor(flavor_workflows.CreateFlavor):
- slug = "create_flavor"
- name = _("Create Flavor")
- finalize_button_name = _("Create Flavor")
- success_message = _('Created new flavor "%s".')
- failure_message = _('Unable to create flavor "%s".')
- success_url = "horizon:infrastructure:flavors:index"
- default_steps = (CreateFlavorStep,)
-
- def handle(self, request, data):
- try:
- self.object = api.flavor.Flavor.create(
- request,
- name=data['name'],
- memory=data['memory_mb'],
- vcpus=data['vcpus'],
- disk=data['disk_gb'],
- cpu_arch=data['arch']
- )
- except Exception:
- exceptions.handle(request, _("Unable to create flavor"))
- return False
- return True
diff --git a/tuskar_ui/infrastructure/history/__init__.py b/tuskar_ui/infrastructure/history/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/history/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/history/panel.py b/tuskar_ui/infrastructure/history/panel.py
deleted file mode 100644
index 0f29c372..00000000
--- a/tuskar_ui/infrastructure/history/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class History(horizon.Panel):
- name = _("Deployment Log")
- slug = "history"
-
-
-dashboard.Infrastructure.register(History)
diff --git a/tuskar_ui/infrastructure/history/tables.py b/tuskar_ui/infrastructure/history/tables.py
deleted file mode 100644
index 4e0b789e..00000000
--- a/tuskar_ui/infrastructure/history/tables.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-from horizon import tables
-
-
-class HistoryTable(tables.DataTable):
-
- timestamp = tables.Column('event_time',
- verbose_name=_("Timestamp"),
- attrs={'data-type': 'timestamp'})
- resource_name = tables.Column('resource_name',
- verbose_name=_("Resource Name"))
- resource_status = tables.Column('resource_status',
- verbose_name=_("Status"))
- resource_status_reason = tables.Column('resource_status_reason',
- verbose_name=_("Reason"))
-
- class Meta(object):
- name = "log"
- verbose_name = _("Deployment Log")
- multi_select = False
- table_actions = ()
- row_actions = ()
- template = "horizon/common/_enhanced_data_table.html"
diff --git a/tuskar_ui/infrastructure/history/templates/history/index.html b/tuskar_ui/infrastructure/history/templates/history/index.html
deleted file mode 100644
index a2ae7db6..00000000
--- a/tuskar_ui/infrastructure/history/templates/history/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans 'Deployment Log' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_page_header.html' with title=_('Deployment Log') %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-xs-12">
- {{ table.render }}
- </div>
-</div>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/history/tests.py b/tuskar_ui/infrastructure/history/tests.py
deleted file mode 100644
index 7a687ef2..00000000
--- a/tuskar_ui/infrastructure/history/tests.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- 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 contextlib
-
-from django.core import urlresolvers
-from mock import patch, call # noqa
-from openstack_dashboard.test.test_data import utils
-
-from tuskar_ui import api
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import heat_data
-from tuskar_ui.test.test_data import tuskar_data
-
-
-TEST_DATA = utils.TestDataContainer()
-heat_data.data(TEST_DATA)
-tuskar_data.data(TEST_DATA)
-INDEX_URL = urlresolvers.reverse(
- 'horizon:infrastructure:history:index')
-
-
-class HistoryTest(test.BaseAdminViewTests):
-
- def test_index(self):
- plan = api.tuskar.Plan(
- TEST_DATA.tuskarclient_plans.first())
- stack = api.heat.Stack(
- TEST_DATA.heatclient_stacks.first())
- events = TEST_DATA.heatclient_events.list()
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.events',
- return_value=events)
- ):
- res = self.client.get(INDEX_URL)
-
- self.assertTemplateUsed(res, 'infrastructure/history/index.html')
diff --git a/tuskar_ui/infrastructure/history/urls.py b/tuskar_ui/infrastructure/history/urls.py
deleted file mode 100644
index 15e57e1c..00000000
--- a/tuskar_ui/infrastructure/history/urls.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.history import views
-
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
-)
diff --git a/tuskar_ui/infrastructure/history/views.py b/tuskar_ui/infrastructure/history/views.py
deleted file mode 100644
index 31449bf2..00000000
--- a/tuskar_ui/infrastructure/history/views.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- 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.
-
-from horizon import tables as horizon_tables
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.history import tables
-
-
-class IndexView(horizon_tables.DataTableView):
- table_class = tables.HistoryTable
- template_name = "infrastructure/history/index.html"
-
- def get_data(self):
- plan = api.tuskar.Plan.get_the_plan(self.request)
- if plan:
- stack = api.heat.Stack.get_by_plan(self.request, plan)
- if stack:
- return stack.events
- return []
diff --git a/tuskar_ui/infrastructure/images/__init__.py b/tuskar_ui/infrastructure/images/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/images/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/images/forms.py b/tuskar_ui/infrastructure/images/forms.py
deleted file mode 100644
index 0d14befb..00000000
--- a/tuskar_ui/infrastructure/images/forms.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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.dashboards.project.images.images import forms
-
-
-class UpdateImageForm(forms.UpdateImageForm):
- pass
diff --git a/tuskar_ui/infrastructure/images/panel.py b/tuskar_ui/infrastructure/images/panel.py
deleted file mode 100644
index 3bb728c4..00000000
--- a/tuskar_ui/infrastructure/images/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class Images(horizon.Panel):
- name = _("Provisioning Images")
- slug = "images"
-
-
-dashboard.Infrastructure.register(Images)
diff --git a/tuskar_ui/infrastructure/images/tables.py b/tuskar_ui/infrastructure/images/tables.py
deleted file mode 100644
index bbc8c0a9..00000000
--- a/tuskar_ui/infrastructure/images/tables.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-from horizon import tables
-from openstack_dashboard import api
-from openstack_dashboard.dashboards.project.images.images import (
- tables as project_tables)
-
-
-class DeleteImage(project_tables.DeleteImage):
- def allowed(self, request, image=None):
- if image and image.protected:
- return False
- else:
- return True
-
-
-class CreateImage(project_tables.CreateImage):
- url = "horizon:infrastructure:images:create"
-
-
-class UpdateRow(tables.Row):
- ajax = True
-
- def get_data(self, request, image_id):
- image = api.glance.image_get(request, image_id)
- return image
-
-
-class ImageFilterAction(tables.FilterAction):
- filter_type = "server"
- filter_choices = (('name', _("Image Name ="), True),
- ('status', _('Status ='), True),
- ('disk_format', _('Format ='), True),
- ('size_min', _('Min. Size (MB)'), True),
- ('size_max', _('Max. Size (MB)'), True))
-
-
-class EditImage(project_tables.EditImage):
- url = "horizon:infrastructure:images:update"
-
- def allowed(self, request, image=None):
- return True
-
-
-class ImagesTable(tables.DataTable):
-
- name = tables.Column('name',
- verbose_name=_("Image Name"))
- disk_format = tables.Column('disk_format',
- verbose_name=_("Format"))
- roles = tables.Column(lambda image:
- ', '.join([r.name for r in image.roles]),
- verbose_name=_("Deployment Roles"))
-
- class Meta(object):
- name = "images"
- row_class = UpdateRow
- verbose_name = _("Provisioning Images")
- table_actions = (CreateImage, DeleteImage, ImageFilterAction)
- row_actions = (EditImage, DeleteImage)
- template = "horizon/common/_enhanced_data_table.html"
diff --git a/tuskar_ui/infrastructure/images/templates/images/_create.html b/tuskar_ui/infrastructure/images/templates/images/_create.html
deleted file mode 100644
index 65c9f2b6..00000000
--- a/tuskar_ui/infrastructure/images/templates/images/_create.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}create_image_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:images:create' %}{% endblock %}
-{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
-
-{% block modal_id %}create_image_modal{% endblock %}
-{% block modal-header %}{% trans "Create Image" %}{% endblock %}
-
-{% block modal-body-right %}
-<h3>{% trans "Description" %}:</h3>
-<p>{% trans "Modify different properties of an image." %}</p>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/images/templates/images/_update.html b/tuskar_ui/infrastructure/images/templates/images/_update.html
deleted file mode 100644
index 4a3e98f9..00000000
--- a/tuskar_ui/infrastructure/images/templates/images/_update.html
+++ /dev/null
@@ -1,14 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}update_image_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:images:update' image.id %}{% endblock %}
-
-{% block modal_id %}update_image_modal{% endblock %}
-{% block modal-header %}{% trans "Update Image" %}{% endblock %}
-
-{% block modal-body-right %}
-<h3>{% trans "Description" %}:</h3>
-<p>{% trans "Modify different properties of an image." %}</p>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/images/templates/images/create.html b/tuskar_ui/infrastructure/images/templates/images/create.html
deleted file mode 100644
index d9d23a02..00000000
--- a/tuskar_ui/infrastructure/images/templates/images/create.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends 'base.html' %}
-{% load i18n %}
-
-{% block title %}{% trans "Create Image" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Create Image") %}
-{% endblock page_header %}
-
-{% block main %}
- {% include 'infrastructure/images/_create.html' %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/images/templates/images/index.html b/tuskar_ui/infrastructure/images/templates/images/index.html
deleted file mode 100644
index 88e69c95..00000000
--- a/tuskar_ui/infrastructure/images/templates/images/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans 'Provisioning Images' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Provisioning Images') %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-xs-12">
- {{ table.render }}
- </div>
-</div>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/images/templates/images/update.html b/tuskar_ui/infrastructure/images/templates/images/update.html
deleted file mode 100644
index d6d6d0b9..00000000
--- a/tuskar_ui/infrastructure/images/templates/images/update.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends 'base.html' %}
-{% load i18n %}
-
-{% block title %}{% trans "Update Image" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Update Image") %}
-{% endblock page_header %}
-
-{% block main %}
- {% include 'infrastructure/images/_update.html' %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/images/tests.py b/tuskar_ui/infrastructure/images/tests.py
deleted file mode 100644
index b7424430..00000000
--- a/tuskar_ui/infrastructure/images/tests.py
+++ /dev/null
@@ -1,168 +0,0 @@
-# -*- 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 contextlib
-
-import mock
-from mock import patch, call # noqa
-from django.core import urlresolvers
-from openstack_dashboard.dashboards.project.images.images import forms
-
-from tuskar_ui import api
-from tuskar_ui.test import helpers as test
-
-INDEX_URL = urlresolvers.reverse('horizon:infrastructure:images:index')
-CREATE_URL = 'horizon:infrastructure:images:create'
-UPDATE_URL = 'horizon:infrastructure:images:update'
-
-
-class ImagesTest(test.BaseAdminViewTests):
-
- def test_index(self):
- roles = [api.tuskar.Role(role) for role in
- self.tuskarclient_roles.list()]
- plans = [api.tuskar.Plan(plan) for plan in
- self.tuskarclient_plans.list()]
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[self.glanceclient_images.list(),
- False, False]),):
-
- res = self.client.get(INDEX_URL)
-
- self.assertTemplateUsed(res, 'infrastructure/images/index.html')
-
- def test_create_get(self):
- res = self.client.get(urlresolvers.reverse(CREATE_URL))
- self.assertTemplateUsed(res, 'infrastructure/images/create.html')
-
- def test_create_post(self):
- image = self.images.list()[0]
- data = {
- 'name': 'Fedora',
- 'description': 'Login with admin/admin',
- 'source_type': 'url',
- 'image_url': 'http://www.test.com/test.iso',
- 'disk_format': 'qcow2',
- 'architecture': 'x86-64',
- 'minimum_disk': 15,
- 'minimum_ram': 512,
- 'is_public': True,
- 'protected': False}
-
- forms.IMAGE_FORMAT_CHOICES = [('qcow2', 'qcow2')]
-
- with contextlib.nested(
- patch('openstack_dashboard.api.glance.image_create',
- return_value=image),) as (mocked_create,):
-
- res = self.client.post(
- urlresolvers.reverse(CREATE_URL), data)
-
- self.assertNoFormErrors(res)
- self.assertEqual(res.status_code, 302)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- mocked_create.assert_called_once_with(
- mock.ANY, name='Fedora', container_format='bare',
- min_ram=512, disk_format='qcow2', protected=False,
- is_public=True, min_disk=15,
- location='http://www.test.com/test.iso',
- properties={'description': 'Login with admin/admin',
- 'architecture': 'x86-64'})
-
- def test_update_get(self):
- image = self.images.list()[0]
-
- with contextlib.nested(
- patch('openstack_dashboard.api.glance.image_get',
- return_value=image),) as (mocked_get,):
-
- res = self.client.get(
- urlresolvers.reverse(UPDATE_URL, args=(image.id,)))
-
- mocked_get.assert_called_once_with(mock.ANY, image.id)
- self.assertTemplateUsed(res, 'infrastructure/images/update.html')
-
- def test_update_post(self):
- image = self.images.list()[0]
- data = {
- 'image_id': image.id,
- 'name': 'Fedora',
- 'description': 'Login with admin/admin',
- 'source_type': 'url',
- 'copy_from': 'http://test_url.com',
- 'disk_format': 'qcow2',
- 'architecture': 'x86-64',
- 'minimum_disk': 15,
- 'minimum_ram': 512,
- 'is_public': True,
- 'protected': False}
-
- forms.IMAGE_FORMAT_CHOICES = [('qcow2', 'qcow2')]
-
- with contextlib.nested(
- patch('openstack_dashboard.api.glance.image_get',
- return_value=image),
- patch('openstack_dashboard.api.glance.image_update',
- return_value=image),) as (mocked_get, mocked_update,):
-
- res = self.client.post(
- urlresolvers.reverse(UPDATE_URL, args=(image.id,)), data)
-
- self.assertNoFormErrors(res)
- self.assertEqual(res.status_code, 302)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- mocked_get.assert_called_once_with(mock.ANY, image.id)
- mocked_update.assert_called_once_with(
- mock.ANY, image.id, name='Fedora', container_format='bare',
- min_ram=512, disk_format='qcow2', protected=False,
- is_public=False, min_disk=15, purge_props=False,
- properties={'description': 'Login with admin/admin',
- 'architecture': 'x86-64'})
-
- def test_delete_ok(self):
- roles = [api.tuskar.Role(role) for role in
- self.tuskarclient_roles.list()]
- plans = [api.tuskar.Plan(plan) for plan in
- self.tuskarclient_plans.list()]
- images = self.glanceclient_images.list()
-
- data = {'action': 'images__delete',
- 'object_ids': [images[0].id, images[1].id]}
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images, False, False]),
- patch('openstack_dashboard.api.glance.image_delete',
- return_value=None),) as (
- mock_role_list, plan_list, mock_image_lict, mock_image_delete):
-
- res = self.client.post(INDEX_URL, data)
-
- mock_image_delete.has_calls(
- call(mock.ANY, images[0].id),
- call(mock.ANY, images[1].id))
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
diff --git a/tuskar_ui/infrastructure/images/urls.py b/tuskar_ui/infrastructure/images/urls.py
deleted file mode 100644
index f67d5576..00000000
--- a/tuskar_ui/infrastructure/images/urls.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.images import views
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^create/$', views.CreateView.as_view(), name='create'),
- urls.url(r'^(?P<image_id>[^/]+)/update/$',
- views.UpdateView.as_view(), name='update'),
-)
diff --git a/tuskar_ui/infrastructure/images/views.py b/tuskar_ui/infrastructure/images/views.py
deleted file mode 100644
index 198fdf43..00000000
--- a/tuskar_ui/infrastructure/images/views.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- 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 logging
-
-from django.core.urlresolvers import reverse_lazy
-from django.utils.translation import ugettext_lazy as _
-from horizon import exceptions
-from horizon import tables as horizon_tables
-from horizon.utils import memoized
-from openstack_dashboard import api
-from openstack_dashboard.dashboards.project.images.images import views
-
-from tuskar_ui import api as tuskar_api
-from tuskar_ui.infrastructure.images import forms
-from tuskar_ui.infrastructure.images import tables
-import tuskar_ui.infrastructure.views as infrastructure_views
-from tuskar_ui.utils import utils
-
-LOG = logging.getLogger(__name__)
-
-
-class IndexView(infrastructure_views.ItemCountMixin,
- horizon_tables.DataTableView):
- table_class = tables.ImagesTable
- template_name = "infrastructure/images/index.html"
-
- @memoized.memoized_method
- def get_data(self):
- images = []
- filters = self.get_filters()
-
- sort_dir = 'desc'
- try:
- images, self._more, self._prev = api.glance.image_list_detailed(
- self.request,
- paginate=False,
- filters=filters,
- sort_dir=sort_dir)
- images = [image for image in images
- if utils.check_image_type(image,
- 'overcloud provisioning')]
- except Exception:
- msg = _('Unable to retrieve image list.')
- exceptions.handle(self.request, msg)
-
- plan = tuskar_api.tuskar.Plan.get_the_plan(self.request)
- for image in images:
- image.roles = tuskar_api.tuskar.Role.get_by_image(
- self.request, plan, image)
-
- return images
-
- def get_filters(self):
- filters = {'is_public': None}
- filter_field = self.table.get_filter_field()
- filter_string = self.table.get_filter_string()
- filter_action = self.table._meta._filter_action
- if filter_field and filter_string and (
- filter_action.is_api_filter(filter_field)):
- if filter_field in ['size_min', 'size_max']:
- invalid_msg = ('API query is not valid and is ignored: %s=%s'
- % (filter_field, filter_string))
- try:
- filter_string = long(float(filter_string) * (1024 ** 2))
- if filter_string >= 0:
- filters[filter_field] = filter_string
- else:
- LOG.warning(invalid_msg)
- except ValueError:
- LOG.warning(invalid_msg)
- else:
- filters[filter_field] = filter_string
- return filters
-
-
-class CreateView(views.CreateView):
- submit_url = "horizon:infrastructure:images:create"
- template_name = 'infrastructure/images/create.html'
- success_url = reverse_lazy("horizon:infrastructure:images:index")
- page_title = _("Create Image")
-
-
-class UpdateView(views.UpdateView):
- template_name = 'infrastructure/images/update.html'
- form_class = forms.UpdateImageForm
- success_url = reverse_lazy('horizon:infrastructure:images:index')
- submit_url = "horizon:infrastructure:images:update"
- submit_label = _("Update Image")
-
- @memoized.memoized_method
- def get_object(self):
- try:
- return api.glance.image_get(self.request, self.kwargs['image_id'])
- except Exception:
- msg = _('Unable to retrieve image.')
- url = reverse_lazy('horizon:infrastructure:images:index')
- exceptions.handle(self.request, msg, redirect=url)
diff --git a/tuskar_ui/infrastructure/nodes/__init__.py b/tuskar_ui/infrastructure/nodes/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/nodes/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/nodes/forms.py b/tuskar_ui/infrastructure/nodes/forms.py
deleted file mode 100644
index 6ec6b6e8..00000000
--- a/tuskar_ui/infrastructure/nodes/forms.py
+++ /dev/null
@@ -1,319 +0,0 @@
-# -*- 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 django.forms
-from django.utils.translation import ugettext_lazy as _
-from horizon import exceptions
-from horizon import forms
-from horizon import messages
-
-from tuskar_ui import api
-import tuskar_ui.forms
-from tuskar_ui.utils import utils
-
-
-DEFAULT_KERNEL_IMAGE_NAME = 'bm-deploy-kernel'
-DEFAULT_RAMDISK_IMAGE_NAME = 'bm-deploy-ramdisk'
-
-CPU_ARCH_CHOICES = [
- ('', _("unspecified")),
- ('amd64', _("amd64")),
- ('x86', _("x86")),
- ('x86_64', _("x86_64")),
-]
-DRIVER_CHOICES = [
- ('pxe_ipmitool', _("IPMI Driver")),
- ('pxe_ssh', _("PXE + SSH")),
-]
-
-
-def get_driver_info_dict(data):
- driver = data['driver']
- driver_dict = {'driver': driver,
- 'deployment_kernel': data['deployment_kernel'],
- 'deployment_ramdisk': data['deployment_ramdisk'],
- }
- if driver == 'pxe_ipmitool':
- driver_dict.update(
- ipmi_address=data['ipmi_address'],
- ipmi_username=data.get('ipmi_username'),
- ipmi_password=data.get('ipmi_password'),
- )
- elif driver == 'pxe_ssh':
- driver_dict.update(
- ssh_address=data['ssh_address'],
- ssh_username=data['ssh_username'],
- ssh_key_contents=data['ssh_key_contents'],
- )
- return driver_dict
-
-
-def create_node(request, data):
- cpu_arch = data.get('cpu_arch')
- cpus = data.get('cpus')
- memory_mb = data.get('memory_mb')
- local_gb = data.get('local_gb')
-
- kwargs = get_driver_info_dict(data)
- kwargs.update(
- cpu_arch=cpu_arch,
- cpus=cpus,
- memory_mb=memory_mb,
- local_gb=local_gb,
- mac_addresses=data['mac_addresses'].split(),
- )
- success = True
- try:
- node = api.node.Node.create(request, **kwargs)
- except Exception:
- success = False
- exceptions.handle(request, _(u"Unable to register node."))
- else:
- # If not all the parameters have been filled in,
- # run the auto-discovery. Note, that the node has been created,
- # so even if we fail here, we report success.
- if not all([cpu_arch, cpus, memory_mb, local_gb]):
- node_uuid = node.uuid
- try:
- api.node.Node.set_maintenance(request, node_uuid, True)
- except Exception:
- exceptions.handle(request, _(
- u"Can't set maintenance mode on node {0}."
- ).format(node_uuid))
- else:
- try:
- api.node.Node.discover(request, [node_uuid])
- except Exception:
- exceptions.handle(request, _(
- u"Can't start discovery on node {0}."
- ).format(node_uuid))
- return success
-
-
-class NodeForm(django.forms.Form):
- id = django.forms.IntegerField(
- label="",
- required=False,
- widget=django.forms.HiddenInput(),
- )
-
- driver = django.forms.ChoiceField(
- label=_("Driver"),
- choices=DRIVER_CHOICES,
- required=True,
- widget=django.forms.Select(attrs={
- 'class': 'form-control switchable',
- 'data-slug': 'driver',
- }),
- )
-
- ipmi_address = django.forms.IPAddressField(
- label=_("IPMI Address"),
- required=False,
- widget=django.forms.TextInput(attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ipmitool': _("IPMI Driver"),
- }),
- )
- ipmi_username = django.forms.CharField(
- label=_("IPMI User"),
- required=False,
- widget=django.forms.TextInput(attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ipmitool': _("IPMI Driver"),
- }),
- )
- ipmi_password = django.forms.CharField(
- label=_("IPMI Password"),
- required=False,
- widget=django.forms.PasswordInput(render_value=True, attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ipmitool': _("IPMI Driver"),
- }),
- )
- ssh_address = django.forms.IPAddressField(
- label=_("SSH Address"),
- required=False,
- widget=django.forms.TextInput(attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ssh': _("PXE + SSH"),
- }),
- )
- ssh_username = django.forms.CharField(
- label=_("SSH User"),
- required=False,
- widget=django.forms.TextInput(attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ssh': _("PXE + SSH"),
- }),
- )
- ssh_key_contents = django.forms.CharField(
- label=_("SSH Key Contents"),
- required=False,
- widget=django.forms.Textarea(attrs={
- 'class': 'form-control switched',
- 'data-switch-on': 'driver',
- 'data-driver-pxe_ssh': _("PXE + SSH"),
- 'rows': 2,
- }),
- )
- mac_addresses = tuskar_ui.forms.MultiMACField(
- label=_("NIC MAC Addresses"),
- required=True,
- widget=django.forms.Textarea(attrs={
- 'placeholder': _('unspecified'),
- 'rows': '2',
- }),
- )
- cpu_arch = django.forms.ChoiceField(
- label=_("Architecture"),
- required=False,
- choices=CPU_ARCH_CHOICES,
- widget=django.forms.Select(
- attrs={'placeholder': _('unspecified')}),
- )
- cpus = django.forms.IntegerField(
- label=_("CPUs"),
- required=False,
- min_value=0,
- widget=tuskar_ui.forms.NumberInput(
- attrs={'placeholder': _('unspecified')}),
- )
- memory_mb = django.forms.IntegerField(
- label=_("Memory"),
- required=False,
- min_value=0,
- widget=tuskar_ui.forms.NumberInput(
- attrs={'placeholder': _('unspecified')}),
- )
- local_gb = django.forms.IntegerField(
- label=_("Local Disk"),
- required=False,
- min_value=0,
- widget=tuskar_ui.forms.NumberInput(
- attrs={'placeholder': _('unspecified')}),
- )
- deployment_kernel = django.forms.ChoiceField(
- label=_("Kernel"),
- required=False,
- choices=[],
- widget=django.forms.Select(),
- )
- deployment_ramdisk = django.forms.ChoiceField(
- label=_("Ramdisk"),
- required=False,
- choices=[],
- widget=django.forms.Select(),
- )
-
- def get_name(self):
- try:
- name = (self.fields['ipmi_address'].value() or
- self.fields['ssh_address'].value())
- except AttributeError:
- # when the field is not bound
- name = _("Undefined node")
- return name
-
- def handle(self, request, data):
- return create_node(request, data)
-
- def clean_ipmi_username(self):
- return self.cleaned_data.get('ipmi_username') or None
-
- def clean_ipmi_password(self):
- return self.cleaned_data.get('ipmi_password') or None
-
- def _require_field(self, field_name, cleaned_data):
- if cleaned_data.get(field_name):
- return
- self._errors[field_name] = self.error_class([_(
- u"This field is required"
- )])
-
- def clean(self):
- cleaned_data = super(NodeForm, self).clean()
- driver = cleaned_data['driver']
-
- if driver == 'pxe_ipmitool':
- self._require_field('ipmi_address', cleaned_data)
- elif driver == 'pxe_ssh':
- self._require_field('ssh_address', cleaned_data)
- self._require_field('ssh_username', cleaned_data)
- self._require_field('ssh_key_contents', cleaned_data)
-
- return cleaned_data
-
-
-class BaseNodeFormset(tuskar_ui.forms.SelfHandlingFormset):
- def __init__(self, *args, **kwargs):
- self.kernel_images = kwargs.pop('kernel_images')
- self.ramdisk_images = kwargs.pop('ramdisk_images')
- super(BaseNodeFormset, self).__init__(*args, **kwargs)
-
- def add_fields(self, form, index):
- deployment_kernel_choices = [(kernel.id, kernel.name)
- for kernel in self.kernel_images]
- deployment_ramdisk_choices = [(ramdisk.id, ramdisk.name)
- for ramdisk in self.ramdisk_images]
- form.fields['deployment_kernel'].choices = deployment_kernel_choices
- form.fields['deployment_ramdisk'].choices = deployment_ramdisk_choices
-
- def clean(self):
- all_macs = api.node.Node.get_all_mac_addresses(self.request)
- bad_macs = set()
- bad_macs_error = _("Duplicate MAC addresses submitted: %s.")
-
- for form in self:
- if not form.cleaned_data:
- raise django.forms.ValidationError(
- _("Please provide node data for all nodes."))
-
- new_macs = form.cleaned_data.get('mac_addresses')
- if not new_macs:
- continue
- new_macs = set(new_macs.split())
-
- # Prevent submitting duplicated MAC addresses
- # or MAC addresses of existing nodes
- bad_macs |= all_macs & new_macs
- all_macs |= new_macs
-
- if bad_macs:
- raise django.forms.ValidationError(
- bad_macs_error % ", ".join(bad_macs))
-
-
-class UploadNodeForm(forms.SelfHandlingForm):
- csv_file = forms.FileField(label='', required=False)
-
- def handle(self, request, data):
- return True
-
- def get_data(self):
- try:
- output = utils.parse_csv_file(self.cleaned_data['csv_file'])
- except ValueError as e:
- messages.error(self.request, e.message)
- output = []
-
- return output
-
-
-RegisterNodeFormset = django.forms.formsets.formset_factory(
- NodeForm, extra=1, formset=BaseNodeFormset)
diff --git a/tuskar_ui/infrastructure/nodes/panel.py b/tuskar_ui/infrastructure/nodes/panel.py
deleted file mode 100644
index 124ec3d3..00000000
--- a/tuskar_ui/infrastructure/nodes/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class Nodes(horizon.Panel):
- name = _("Nodes")
- slug = "nodes"
-
-
-dashboard.Infrastructure.register(Nodes)
diff --git a/tuskar_ui/infrastructure/nodes/tables.py b/tuskar_ui/infrastructure/nodes/tables.py
deleted file mode 100644
index 898bd76e..00000000
--- a/tuskar_ui/infrastructure/nodes/tables.py
+++ /dev/null
@@ -1,269 +0,0 @@
-# -*- 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.
-
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext_lazy as _
-from horizon import messages
-from horizon import tables
-from horizon.utils import memoized
-
-from tuskar_ui import api
-
-
-class DeleteNode(tables.BatchAction):
- name = "delete"
- action_present = _("Delete")
- action_past = _("Deleting")
- data_type_singular = _("Node")
- data_type_plural = _("Nodes")
- classes = ('btn-danger',)
-
- def allowed(self, request, obj=None):
- if not obj:
- # this is necessary because table actions use this function
- # with obj=None
- return True
- return (getattr(obj, 'instance_uuid', None) is None and
- obj.power_state not in api.node.POWER_ON_STATES)
-
- def action(self, request, obj_id):
- if obj_id is None:
- messages.error(request, _("Select some nodes to delete."))
- return
- api.node.Node.delete(request, obj_id)
-
-
-class ActivateNode(tables.BatchAction):
- name = "activate"
- action_present = _("Activate")
- action_past = _("Activated")
- data_type_singular = _("Node")
- data_type_plural = _("Nodes")
-
- def allowed(self, request, obj=None):
- if not obj:
- # this is necessary because table actions use this function
- # with obj=None
- return True
- return (obj.cpus and obj.memory_mb and obj.local_gb and
- obj.cpu_arch)
-
- def action(self, request, obj_id):
- if obj_id is None:
- messages.error(request, _("Select some nodes to activate."))
- return
- api.node.Node.set_maintenance(request, obj_id, False)
- api.node.Node.set_power_state(request, obj_id, 'off')
-
-
-class SetPowerStateOn(tables.BatchAction):
- name = "set_power_state_on"
- action_present = _("Power On")
- action_past = _("Powering On")
- data_type_singular = _("Node")
- data_type_plural = _("Nodes")
-
- def allowed(self, request, obj=None):
- if not obj:
- # this is necessary because table actions use this function
- # with obj=None
- return True
- return obj.power_state not in api.node.POWER_ON_STATES
-
- def action(self, request, obj_id):
- if obj_id is None:
- messages.error(request, _("Select some nodes to power on."))
- return
- api.node.Node.set_power_state(request, obj_id, 'on')
-
-
-class SetPowerStateOff(tables.BatchAction):
- name = "set_power_state_off"
- action_present = _("Power Off")
- action_past = _("Powering Off")
- data_type_singular = _("Node")
- data_type_plural = _("Nodes")
-
- def allowed(self, request, obj=None):
- if not obj:
- # this is necessary because table actions use this function
- # with obj=None
- return True
- return (
- obj.power_state in api.node.POWER_ON_STATES and
- getattr(obj, 'instance_uuid', None) is None
- )
-
- def action(self, request, obj_id):
- if obj_id is None:
- messages.error(request, _("Select some nodes to power off."))
- return
- api.node.Node.set_power_state(request, obj_id, 'off')
-
-
-class NodeFilterAction(tables.FilterAction):
- def filter(self, table, nodes, filter_string):
- """Really naive case-insensitive search."""
- q = filter_string.lower()
-
- def comp(node):
- return any(q in unicode(value).lower() for value in (
- node.ip_address,
- node.cpus,
- node.memory_mb,
- node.local_gb,
- ))
-
- return filter(comp, nodes)
-
-
-class DiscoverNode(tables.BatchAction):
- name = "discover_nodes"
- action_present = _("Discover")
- action_past = _("Discovered")
- data_type_singular = _("Node")
- data_type_plural = _("Nodes")
-
- def allowed(self, request, obj=None):
- if not obj:
- # this is necessary because table actions use this function
- # with obj=None
- return True
- return obj.state == api.node.MAINTENANCE_STATE
-
- def action(self, request, obj_id):
- if obj_id is None:
- messages.error(request, _("Select some nodes to discover."))
- return
- api.node.Node.discover(request, [obj_id])
-
-
-@memoized.memoized
-def _get_role_link(role_id):
- if role_id:
- return reverse('horizon:infrastructure:roles:detail',
- kwargs={'role_id': role_id})
-
-
-def get_role_link(datum):
- return _get_role_link(getattr(datum, 'role_id', None))
-
-
-def get_power_state_with_transition(node):
- if node.target_power_state and (
- node.power_state != node.target_power_state):
- return "{0} -> {1}".format(
- node.power_state, node.target_power_state)
- return node.power_state
-
-
-def get_state_string(node):
- state_dict = {
- api.node.DISCOVERING_STATE: _('Discovering'),
- api.node.DISCOVERED_STATE: _('Discovered'),
- api.node.PROVISIONED_STATE: _('Provisioned'),
- api.node.PROVISIONING_FAILED_STATE: _('Provisioning Failed'),
- api.node.PROVISIONING_STATE: _('Provisioning'),
- api.node.FREE_STATE: _('Free'),
- }
-
- node_state = node.state
- return state_dict.get(node_state, node_state)
-
-
-class BaseNodesTable(tables.DataTable):
- node = tables.Column('uuid',
- link="horizon:infrastructure:nodes:node_detail",
- verbose_name=_("Node Name"))
- role_name = tables.Column('role_name',
- link=get_role_link,
- verbose_name=_("Deployment Role"))
- cpus = tables.Column('cpus',
- verbose_name=_("CPU (cores)"))
- memory_mb = tables.Column('memory_mb',
- verbose_name=_("Memory (MB)"))
- local_gb = tables.Column('local_gb',
- verbose_name=_("Disk (GB)"))
- power_status = tables.Column(get_power_state_with_transition,
- verbose_name=_("Power Status"))
- state = tables.Column(get_state_string,
- verbose_name=_("Status"))
-
- class Meta(object):
- name = "nodes_table"
- verbose_name = _("Nodes")
- table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
- DeleteNode)
- row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
- template = "horizon/common/_enhanced_data_table.html"
-
- def get_object_id(self, datum):
- return datum.uuid
-
- def get_object_display(self, datum):
- return datum.uuid
-
-
-class AllNodesTable(BaseNodesTable):
-
- class Meta(object):
- name = "all_nodes_table"
- verbose_name = _("All")
- hidden_title = False
- columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status',
- 'state')
- table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
- DeleteNode)
- row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class ProvisionedNodesTable(BaseNodesTable):
-
- class Meta(object):
- name = "provisioned_nodes_table"
- verbose_name = _("Provisioned")
- hidden_title = False
- table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
- DeleteNode)
- row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode)
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class FreeNodesTable(BaseNodesTable):
-
- class Meta(object):
- name = "free_nodes_table"
- verbose_name = _("Free")
- hidden_title = False
- columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status')
- table_actions = (NodeFilterAction, SetPowerStateOn, SetPowerStateOff,
- DeleteNode)
- row_actions = (SetPowerStateOn, SetPowerStateOff, DeleteNode,)
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class MaintenanceNodesTable(BaseNodesTable):
-
- class Meta(object):
- name = "maintenance_nodes_table"
- verbose_name = _("Maintenance")
- hidden_title = False
- columns = ('node', 'cpus', 'memory_mb', 'local_gb', 'power_status',
- 'state')
- table_actions = (NodeFilterAction, ActivateNode, SetPowerStateOn,
- SetPowerStateOff, DiscoverNode, DeleteNode)
- row_actions = (ActivateNode, SetPowerStateOn, SetPowerStateOff,
- DeleteNode)
- template = "horizon/common/_enhanced_data_table.html"
diff --git a/tuskar_ui/infrastructure/nodes/tabs.py b/tuskar_ui/infrastructure/nodes/tabs.py
deleted file mode 100644
index 1b8f81b6..00000000
--- a/tuskar_ui/infrastructure/nodes/tabs.py
+++ /dev/null
@@ -1,378 +0,0 @@
-# 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 itertools
-
-from django.core import urlresolvers
-from django.utils.translation import ugettext_lazy as _
-from horizon import tabs
-from horizon.utils import functions
-from openstack_dashboard.api import base as api_base
-
-from tuskar_ui import api
-from tuskar_ui.cached_property import cached_property # noqa
-from tuskar_ui.infrastructure.nodes import tables
-from tuskar_ui.utils import metering as metering_utils
-from tuskar_ui.utils import utils
-
-
-def filter_extra(nodes, index, value):
- return (node for node in nodes
- if node.extra.get(index, None) == value)
-
-
-class OverviewTab(tabs.Tab):
- name = _("Overview")
- slug = "overview"
- template_name = "infrastructure/nodes/_overview.html"
-
- def get_context_data(self, request):
- nodes = self.tab_group.kwargs['nodes']
- cpus = sum(int(node.cpus) for node in nodes if node.cpus)
- memory_mb = sum(int(node.memory_mb) for node in nodes if
- node.memory_mb)
- local_gb = sum(int(node.local_gb) for node in nodes if node.local_gb)
-
- nodes_provisioned = set(utils.filter_items(
- nodes, provision_state__in=api.node.PROVISION_STATE_PROVISIONED))
- nodes_free = set(utils.filter_items(
- nodes, provision_state__in=api.node.PROVISION_STATE_FREE))
- nodes_deleting = set(utils.filter_items(
- nodes, provision_state__in=api.node.PROVISION_STATE_DELETING))
- nodes_error = set(utils.filter_items(
- nodes, provision_state__in=api.node.PROVISION_STATE_ERROR))
-
- nodes_provisioned_maintenance = set(utils.filter_items(
- nodes_provisioned, maintenance=True))
- nodes_provisioned_not_maintenance = (
- nodes_provisioned - nodes_provisioned_maintenance)
-
- nodes_provisioning = set(utils.filter_items(
- nodes,
- provision_state__in=api.node.PROVISION_STATE_PROVISIONING))
-
- nodes_free_maintenance = set(utils.filter_items(
- nodes_free, maintenance=True))
- nodes_free_not_maintenance = (
- nodes_free - nodes_free_maintenance)
-
- nodes_maintenance = (
- nodes_provisioned_maintenance | nodes_free_maintenance)
-
- nodes_provisioned_down = utils.filter_items(
- nodes_provisioned, power_state__not_in=api.node.POWER_ON_STATES)
- nodes_free_down = utils.filter_items(
- nodes_free, power_state__not_in=api.node.POWER_ON_STATES)
-
- nodes_on_discovery = filter_extra(
- nodes_maintenance, 'on_discovery', 'true')
- nodes_discovered = filter_extra(
- nodes_maintenance, 'newly_discovered', 'true')
- nodes_discovery_failed = filter_extra(
- nodes_maintenance, 'discovery_failed', 'true')
-
- nodes_down = itertools.chain(nodes_provisioned_down, nodes_free_down)
- nodes_up = utils.filter_items(
- nodes, power_state__in=api.node.POWER_ON_STATES)
-
- nodes_free_count = len(nodes_free_not_maintenance)
- nodes_provisioned_count = len(
- nodes_provisioned_not_maintenance)
- nodes_provisioning_count = len(nodes_provisioning)
- nodes_maintenance_count = len(nodes_maintenance)
- nodes_deleting_count = len(nodes_deleting)
- nodes_error_count = len(nodes_error)
-
- context = {
- 'cpus': cpus,
- 'memory_gb': memory_mb / 1024.0,
- 'local_gb': local_gb,
- 'nodes_up_count': utils.length(nodes_up),
- 'nodes_down_count': utils.length(nodes_down),
- 'nodes_provisioned_count': nodes_provisioned_count,
- 'nodes_provisioning_count': nodes_provisioning_count,
- 'nodes_free_count': nodes_free_count,
- 'nodes_deleting_count': nodes_deleting_count,
- 'nodes_error_count': nodes_error_count,
- 'nodes_maintenance_count': nodes_maintenance_count,
- 'nodes_all_count': len(nodes),
- 'nodes_on_discovery_count': utils.length(nodes_on_discovery),
- 'nodes_discovered_count': utils.length(nodes_discovered),
- 'nodes_discovery_failed_count': utils.length(
- nodes_discovery_failed),
- 'nodes_status_data':
- 'Provisioned={0}|Free={1}|Maintenance={2}'.format(
- nodes_provisioned_count, nodes_free_count,
- nodes_maintenance_count)
- }
- # additional node status pie chart data, showing only if it appears
- if nodes_provisioning_count:
- context['nodes_status_data'] += '|Provisioning={0}'.format(
- nodes_provisioning_count)
- if nodes_deleting_count:
- context['nodes_status_data'] += '|Deleting={0}'.format(
- nodes_deleting_count)
- if nodes_error_count:
- context['nodes_status_data'] += '|Error={0}'.format(
- nodes_error_count)
-
- if api_base.is_service_enabled(self.request, 'metering'):
- context['meter_conf'] = (
- (_('System Load'),
- metering_utils.url_part('hardware.cpu.load.1min', False),
- None),
- (_('CPU Utilization'),
- metering_utils.url_part('hardware.system_stats.cpu.util',
- True),
- '100'),
- (_('Swap Utilization'),
- metering_utils.url_part('hardware.memory.swap.util',
- True),
- '100'),
- )
-
- # TODO(akrivoka): Ajaxize these calls so that they don't hold up the
- # whole page load
- context['top_5'] = {
- 'fan': metering_utils.get_top_5(request, 'hardware.ipmi.fan'),
- 'voltage': metering_utils.get_top_5(
- request, 'hardware.ipmi.voltage'),
- 'temperature': metering_utils.get_top_5(
- request, 'hardware.ipmi.temperature'),
- 'current': metering_utils.get_top_5(
- request, 'hardware.ipmi.current'),
- }
-
- return context
-
-
-class BaseTab(tabs.TableTab):
- table_classes = (tables.BaseNodesTable,)
- name = _("Nodes")
- slug = "nodes"
- template_name = "horizon/common/_detail_table.html"
-
- def __init__(self, tab_group, request):
- super(BaseTab, self).__init__(tab_group, request)
-
- @cached_property
- def _nodes(self):
- return []
-
- def get_items_count(self):
- return len(self._nodes)
-
- @cached_property
- def _nodes_info(self):
- page_size = functions.get_page_size(self.request)
-
- prev_marker = self.request.GET.get(
- self.table_classes[0]._meta.prev_pagination_param, None)
-
- if prev_marker is not None:
- sort_dir = 'asc'
- marker = prev_marker
- else:
- sort_dir = 'desc'
- marker = self.request.GET.get(
- self.table_classes[0]._meta.pagination_param, None)
-
- nodes = self._nodes
-
- if marker:
- node_ids = [node.uuid for node in self._nodes]
- position = node_ids.index(marker)
- if sort_dir == 'asc':
- start = max(0, position - page_size)
- end = position
- else:
- start = position + 1
- end = start + page_size
- else:
- start = 0
- end = page_size
-
- prev = start != 0
- more = len(nodes) > end
- return nodes[start:end], prev, more
-
- def get_base_nodes_table_data(self):
- nodes, prev, more = self._nodes_info
- return nodes
-
- def has_prev_data(self, table):
- return self._nodes_info[1]
-
- def has_more_data(self, table):
- return self._nodes_info[2]
-
-
-class AllTab(BaseTab):
- table_classes = (tables.AllNodesTable,)
- name = _("All")
- slug = "all"
-
- def __init__(self, tab_group, request):
- super(AllTab, self).__init__(tab_group, request)
-
- @cached_property
- def _nodes(self):
- return self.tab_group.kwargs['nodes']
-
- def get_all_nodes_table_data(self):
- nodes, prev, more = self._nodes_info
- return nodes
-
-
-class ProvisionedTab(BaseTab):
- table_classes = (tables.ProvisionedNodesTable,)
- name = _("Provisioned")
- slug = "provisioned"
-
- def __init__(self, tab_group, request):
- super(ProvisionedTab, self).__init__(tab_group, request)
-
- @cached_property
- def _nodes(self):
- redirect = urlresolvers.reverse('horizon:infrastructure:nodes:index')
- return api.node.Node.list(self.request, associated=True,
- maintenance=False, _error_redirect=redirect)
-
- def get_provisioned_nodes_table_data(self):
- nodes, prev, more = self._nodes_info
-
- if nodes:
- for node in nodes:
- try:
- resource = api.heat.Resource.get_by_node(
- self.request, node)
- except LookupError:
- node.role_name = '-'
- else:
- node.role_name = resource.role.name
- node.role_id = resource.role.id
- node.stack_id = resource.stack.id
-
- return nodes
-
-
-class FreeTab(BaseTab):
- table_classes = (tables.FreeNodesTable,)
- name = _("Free")
- slug = "free"
-
- def __init__(self, tab_group, request):
- super(FreeTab, self).__init__(tab_group, request)
-
- @cached_property
- def _nodes(self):
- redirect = urlresolvers.reverse('horizon:infrastructure:nodes:index')
- return api.node.Node.list(self.request, associated=False,
- maintenance=False, _error_redirect=redirect)
-
- def get_free_nodes_table_data(self):
- nodes, prev, more = self._nodes_info
- return nodes
-
-
-class MaintenanceTab(BaseTab):
- table_classes = (tables.MaintenanceNodesTable,)
- name = _("Maintenance")
- slug = "maintenance"
-
- def __init__(self, tab_group, request):
- super(MaintenanceTab, self).__init__(tab_group, request)
-
- @cached_property
- def _nodes(self):
- nodes = self.tab_group.kwargs['nodes']
- return list(utils.filter_items(nodes, maintenance=True))
-
- def get_maintenance_nodes_table_data(self):
- return self._nodes
-
-
-class DetailOverviewTab(tabs.Tab):
- name = _("Overview")
- slug = "detail_overview"
- template_name = 'infrastructure/nodes/_detail_overview.html'
-
- def get_context_data(self, request):
- node = self.tab_group.kwargs['node']
- context = {'node': node}
- try:
- resource = api.heat.Resource.get_by_node(self.request, node)
- except LookupError:
- pass
- else:
- context['role'] = resource.role
- context['stack'] = resource.stack
-
- kernel_id = node.driver_info.get('deploy_kernel')
- if kernel_id:
- context['kernel_image'] = api.node.image_get(request, kernel_id)
-
- ramdisk_id = node.driver_info.get('deploy_ramdisk')
- if ramdisk_id:
- context['ramdisk_image'] = api.node.image_get(request, ramdisk_id)
-
- if node.instance_uuid:
- if api_base.is_service_enabled(self.request, 'metering'):
- # Meter configuration in the following format:
- # (meter label, url part, y_max)
- context['meter_conf'] = (
- (_('System Load'),
- metering_utils.url_part('hardware.cpu.load.1min', False),
- None),
- (_('CPU Utilization'),
- metering_utils.url_part('hardware.system_stats.cpu.util',
- True),
- '100'),
- (_('Swap Utilization'),
- metering_utils.url_part('hardware.memory.swap.util',
- True),
- '100'),
- (_('Current'),
- metering_utils.url_part('hardware.ipmi.current', False),
- None),
- (_('Network IO'),
- metering_utils.url_part('network-io', False),
- None),
- (_('Disk IO'),
- metering_utils.url_part('disk-io', False),
- None),
- (_('Temperature'),
- metering_utils.url_part('hardware.ipmi.temperature',
- False),
- None),
- (_('Fan Speed'),
- metering_utils.url_part('hardware.ipmi.fan', False),
- None),
- (_('Voltage'),
- metering_utils.url_part('hardware.ipmi.voltage', False),
- None),
- )
- return context
-
-
-class NodeTabs(tabs.TabGroup):
- slug = "nodes"
- tabs = (OverviewTab, AllTab, ProvisionedTab, FreeTab, MaintenanceTab,)
- sticky = True
- template_name = "horizon/common/_items_count_tab_group.html"
-
-
-class NodeDetailTabs(tabs.TabGroup):
- slug = "node_details"
- tabs = (DetailOverviewTab,)
diff --git a/tuskar_ui/infrastructure/nodes/tests.py b/tuskar_ui/infrastructure/nodes/tests.py
deleted file mode 100644
index 485a31b4..00000000
--- a/tuskar_ui/infrastructure/nodes/tests.py
+++ /dev/null
@@ -1,596 +0,0 @@
-# -*- 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 contextlib
-import json
-
-from ceilometerclient.v2 import client as ceilometer_client
-from django.core import urlresolvers
-from horizon import exceptions as horizon_exceptions
-from ironicclient import exceptions as ironic_exceptions
-import mock
-from novaclient import exceptions as nova_exceptions
-from openstack_dashboard.test.test_data import utils
-
-from tuskar_ui import api
-from tuskar_ui.handle_errors import handle_errors # noqa
-from tuskar_ui.infrastructure.nodes import forms
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import heat_data
-from tuskar_ui.test.test_data import node_data
-from tuskar_ui.test.test_data import tuskar_data
-
-
-INDEX_URL = urlresolvers.reverse('horizon:infrastructure:nodes:index')
-REGISTER_URL = urlresolvers.reverse('horizon:infrastructure:nodes:register')
-DETAIL_VIEW = 'horizon:infrastructure:nodes:node_detail'
-PERFORMANCE_VIEW = 'horizon:infrastructure:nodes:performance'
-TEST_DATA = utils.TestDataContainer()
-node_data.data(TEST_DATA)
-heat_data.data(TEST_DATA)
-tuskar_data.data(TEST_DATA)
-
-
-def _raise_nova_client_exception(*args, **kwargs):
- raise nova_exceptions.ClientException("Boom!")
-
-
-class NodesTests(test.BaseAdminViewTests):
- @handle_errors("Error!", [])
- def _raise_tuskar_exception(self, request, *args, **kwargs):
- raise self.exceptions.tuskar
-
- @handle_errors("Error!", [])
- def _raise_horizon_exception_not_found(self, request, *args, **kwargs):
- raise horizon_exceptions.NotFound
-
- def _raise_ironic_exception(self, request, *args, **kwargs):
- raise ironic_exceptions.Conflict
-
- def stub_ceilometerclient(self):
- if not hasattr(self, "ceilometerclient"):
- self.mox.StubOutWithMock(ceilometer_client, 'Client')
- self.ceilometerclient = self.mox.CreateMock(
- ceilometer_client.Client,
- )
- return self.ceilometerclient
-
- def test_index_get(self):
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list'],
- 'list.return_value': [],
- }) as mocked:
- res = self.client.get(INDEX_URL)
- self.assertEqual(mocked.list.call_count, 3)
-
- self.assertTemplateUsed(
- res, 'infrastructure/nodes/index.html')
- self.assertTemplateUsed(res, 'infrastructure/nodes/_overview.html')
-
- def _all_mocked_nodes(self):
- request = mock.MagicMock()
- return [api.node.Node(api.node.Node(node, request))
- for node in self.ironicclient_nodes.list()]
-
- def _test_index_tab(self, tab_name, nodes):
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list'],
- 'list.return_value': nodes,
- }) as Node:
- res = self.client.get(INDEX_URL + '?tab=nodes__' + tab_name)
- self.assertEqual(Node.list.call_count, 3)
-
- self.assertTemplateUsed(
- res, 'infrastructure/nodes/index.html')
- self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
- self.assertItemsEqual(
- res.context[tab_name + '_nodes_table_table'].data,
- nodes)
-
- def test_all_nodes(self):
- nodes = self._all_mocked_nodes()
- self._test_index_tab('all', nodes)
-
- def test_provisioned_nodes(self):
- nodes = self._all_mocked_nodes()
- self._test_index_tab('provisioned', nodes)
-
- def test_free_nodes(self):
- nodes = self._all_mocked_nodes()
- self._test_index_tab('free', nodes)
-
- def test_maintenance_nodes(self):
- nodes = self._all_mocked_nodes()[6:]
- self._test_index_tab('maintenance', nodes)
-
- def _test_index_tab_list_exception(self, tab_name):
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list'],
- 'list.side_effect': self._raise_tuskar_exception,
- }) as mocked:
- res = self.client.get(INDEX_URL + '?tab=nodes__' + tab_name)
- self.assertEqual(mocked.list.call_count, 2)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_all_nodes_list_exception(self):
- self._test_index_tab_list_exception('all')
-
- def test_provisioned_nodes_list_exception(self):
- self._test_index_tab_list_exception('provisioned')
-
- def test_free_nodes_list_exception(self):
- self._test_index_tab_list_exception('free')
-
- def test_maintenance_nodes_list_exception(self):
- self._test_index_tab_list_exception('maintenance')
-
- def test_register_get(self):
- with mock.patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=([], False)) as mocked:
- res = self.client.get(REGISTER_URL)
- self.assertEqual(mocked.call_count, 2)
- self.assertTemplateUsed(
- res, 'infrastructure/nodes/register.html')
-
- def test_register_post(self):
- node = TEST_DATA.ironicclient_nodes.first
- nodes = self._all_mocked_nodes()
- images = self.glanceclient_images.list()
- data = {
- 'register_nodes-TOTAL_FORMS': 2,
- 'register_nodes-INITIAL_FORMS': 1,
- 'register_nodes-MAX_NUM_FORMS': 1000,
-
- 'register_nodes-0-driver': 'pxe_ipmitool',
- 'register_nodes-0-ipmi_address': '127.0.0.1',
- 'register_nodes-0-ipmi_username': 'username',
- 'register_nodes-0-ipmi_password': 'password',
- 'register_nodes-0-mac_addresses': 'de:ad:be:ef:ca:fe',
- 'register_nodes-0-cpu_arch': 'x86',
- 'register_nodes-0-cpus': '1',
- 'register_nodes-0-memory_mb': '2',
- 'register_nodes-0-local_gb': '3',
- 'register_nodes-0-deployment_kernel': images[3].id,
- 'register_nodes-0-deployment_ramdisk': images[4].id,
-
- 'register_nodes-1-driver': 'pxe_ipmitool',
- 'register_nodes-1-ipmi_address': '127.0.0.2',
- 'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff',
- 'register_nodes-1-cpu_arch': 'x86',
- 'register_nodes-1-cpus': '4',
- 'register_nodes-1-memory_mb': '5',
- 'register_nodes-1-local_gb': '6',
- 'register_nodes-1-deployment_kernel': images[3].id,
- 'register_nodes-1-deployment_ramdisk': images[4].id,
- }
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['create', 'get_all_mac_addresses'],
- 'create.return_value': node,
- 'get_all_mac_addresses.return_value': set(nodes),
- }) as Node, mock.patch(
- 'openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images, False, False]
- ):
- res = self.client.post(REGISTER_URL, data)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
- self.assertListEqual(Node.create.call_args_list, [
- mock.call(
- mock.ANY,
- ipmi_address=u'127.0.0.1',
- cpu_arch='x86',
- cpus=1,
- memory_mb=2,
- local_gb=3,
- mac_addresses=['DE:AD:BE:EF:CA:FE'],
- ipmi_username=u'username',
- ipmi_password=u'password',
- driver='pxe_ipmitool',
- deployment_kernel=images[3].id,
- deployment_ramdisk=images[4].id,
- ),
- mock.call(
- mock.ANY,
- ipmi_address=u'127.0.0.2',
- cpu_arch='x86',
- cpus=4,
- memory_mb=5,
- local_gb=6,
- mac_addresses=['DE:AD:BE:EF:CA:FF'],
- ipmi_username=None,
- ipmi_password=None,
- driver='pxe_ipmitool',
- deployment_kernel=images[3].id,
- deployment_ramdisk=images[4].id,
- ),
- ])
-
- def test_register_post_exception(self):
- nodes = self._all_mocked_nodes()
- images = self.glanceclient_images.list()
- data = {
- 'register_nodes-TOTAL_FORMS': 2,
- 'register_nodes-INITIAL_FORMS': 1,
- 'register_nodes-MAX_NUM_FORMS': 1000,
-
- 'register_nodes-0-driver': 'pxe_ipmitool',
- 'register_nodes-0-ipmi_address': '127.0.0.1',
- 'register_nodes-0-ipmi_username': 'username',
- 'register_nodes-0-ipmi_password': 'password',
- 'register_nodes-0-mac_addresses': 'de:ad:be:ef:ca:fe',
- 'register_nodes-0-cpu_arch': 'x86',
- 'register_nodes-0-cpus': '1',
- 'register_nodes-0-memory_mb': '2',
- 'register_nodes-0-local_gb': '3',
- 'register_nodes-0-deployment_kernel': images[3].id,
- 'register_nodes-0-deployment_ramdisk': images[4].id,
-
- 'register_nodes-1-driver': 'pxe_ipmitool',
- 'register_nodes-1-ipmi_address': '127.0.0.2',
- 'register_nodes-1-mac_addresses': 'de:ad:be:ef:ca:ff',
- 'register_nodes-1-cpu_arch': 'x86',
- 'register_nodes-1-cpus': '4',
- 'register_nodes-1-memory_mb': '5',
- 'register_nodes-1-local_gb': '6',
- 'register_nodes-1-deployment_kernel': images[3].id,
- 'register_nodes-1-deployment_ramdisk': images[4].id,
- }
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['create', 'get_all_mac_addresses'],
- 'create.side_effect': self.exceptions.tuskar,
- 'get_all_mac_addresses.return_value': set(nodes),
- }) as Node, mock.patch(
- 'openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images, False, False]
- ):
- res = self.client.post(REGISTER_URL, data)
- self.assertEqual(res.status_code, 200)
- self.assertListEqual(Node.create.call_args_list, [
- mock.call(
- mock.ANY,
- ipmi_address=u'127.0.0.1',
- cpu_arch='x86',
- cpus=1,
- memory_mb=2,
- local_gb=3,
- mac_addresses=['DE:AD:BE:EF:CA:FE'],
- ipmi_username=u'username',
- ipmi_password=u'password',
- driver='pxe_ipmitool',
- deployment_kernel=images[3].id,
- deployment_ramdisk=images[4].id,
- ),
- mock.call(
- mock.ANY,
- ipmi_address=u'127.0.0.2',
- cpu_arch='x86',
- cpus=4,
- memory_mb=5,
- local_gb=6,
- mac_addresses=['DE:AD:BE:EF:CA:FF'],
- ipmi_username=None,
- ipmi_password=None,
- driver='pxe_ipmitool',
- deployment_kernel=images[3].id,
- deployment_ramdisk=images[4].id,
- ),
- ])
- self.assertTemplateUsed(
- res, 'infrastructure/nodes/register.html')
-
- def test_node_detail(self):
- node = api.node.Node(self.ironicclient_nodes.list()[0])
-
- def get_node(request, uuid, **kwargs):
- node._request = request
- node.addresses = []
- return node
-
- image = self.glanceclient_images.first()
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['get'],
- 'get.side_effect': get_node,
- }),
- mock.patch('tuskar_ui.api.heat.Resource', **{
- 'spec_set': ['get_by_node'],
- 'get_by_node.side_effect': lambda *args, **kwargs: {}[None],
- # Raises LookupError
- }),
- mock.patch(
- 'openstack_dashboard.api.glance.image_get',
- return_value=image,
- ),
- mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=([], False),
- ),
- ) as (mock_node, mock_heat, mock_glance, mock_nova):
- res = self.client.get(
- urlresolvers.reverse(DETAIL_VIEW, args=(node.uuid,))
- )
- self.assertEqual(mock_node.get.call_count, 1)
-
- self.assertTemplateUsed(res, 'infrastructure/nodes/detail.html')
- self.assertEqual(res.context['node'], node)
-
- def test_node_detail_exception(self):
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['get'],
- 'get.side_effect': self._raise_tuskar_exception,
- }) as mocked:
- res = self.client.get(
- urlresolvers.reverse(DETAIL_VIEW, args=('no-such-node',))
- )
- self.assertEqual(mocked.get.call_count, 1)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_node_set_power_on(self):
- all_nodes = [api.node.Node(api.node.Node(node))
- for node in self.ironicclient_nodes.list()]
- node = all_nodes[6]
- roles = [api.tuskar.Role(r)
- for r in TEST_DATA.tuskarclient_roles.list()]
- instance = TEST_DATA.novaclient_servers.first()
- image = TEST_DATA.glanceclient_images.first()
- data = {'action': "all_nodes_table__set_power_state_on__{0}".format(
- node.uuid)}
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list', 'set_power_state'],
- 'list.return_value': all_nodes,
- 'set_power_state.return_value': node,
- }),
- mock.patch('tuskar_ui.api.tuskar.Role', **{
- 'spec_set': ['list', 'name'],
- 'list.return_value': roles,
- }),
- mock.patch('tuskar_ui.api.node.nova', **{
- 'spec_set': ['server_get', 'server_list'],
- 'server_get.return_value': instance,
- 'server_list.return_value': ([instance], False),
- }),
- mock.patch('tuskar_ui.api.node.glance', **{
- 'spec_set': ['image_get'],
- 'image_get.return_value': image,
- }),
- mock.patch('tuskar_ui.api.heat.Resource', **{
- 'spec_set': ['get_by_node', 'list_all_resources'],
- 'get_by_node.side_effect': (
- self._raise_horizon_exception_not_found),
- 'list_all_resources.return_value': [],
- }),
- ) as (mock_node, mock_role, mock_nova, mock_glance, mock_resource):
- res = self.client.post(INDEX_URL + '?tab=nodes__all', data)
- self.assertNoFormErrors(res)
- self.assertEqual(mock_node.set_power_state.call_count, 1)
- self.assertRedirectsNoFollow(res, INDEX_URL + '?tab=nodes__all')
-
- def test_node_set_power_on_empty(self):
- all_nodes = [api.node.Node(api.node.Node(node))
- for node in self.ironicclient_nodes.list()]
- node = all_nodes[6]
- roles = [api.tuskar.Role(r)
- for r in TEST_DATA.tuskarclient_roles.list()]
- instance = TEST_DATA.novaclient_servers.first()
- image = TEST_DATA.glanceclient_images.first()
- data = {
- 'action': 'all_nodes_table__set_power_state_on',
- 'object_ids': '',
- }
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list', 'set_power_state'],
- 'list.return_value': all_nodes,
- 'set_power_state.return_value': node,
- }),
- mock.patch('tuskar_ui.api.tuskar.Role', **{
- 'spec_set': ['list', 'name'],
- 'list.return_value': roles,
- }),
- mock.patch('tuskar_ui.api.node.nova', **{
- 'spec_set': ['server_get', 'server_list'],
- 'server_get.return_value': instance,
- 'server_list.return_value': ([instance], False),
- }),
- mock.patch('tuskar_ui.api.node.glance', **{
- 'spec_set': ['image_get'],
- 'image_get.return_value': image,
- }),
- mock.patch('tuskar_ui.api.heat.Resource', **{
- 'spec_set': ['get_by_node', 'list_all_resources'],
- 'get_by_node.side_effect': (
- self._raise_horizon_exception_not_found),
- 'list_all_resources.return_value': [],
- }),
- ) as (mock_node, mock_role, mock_nova, mock_glance, mock_resource):
- res = self.client.post(INDEX_URL + '?tab=nodes__all', data)
- self.assertEqual(mock_node.set_power_state.call_count, 0)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_node_set_power_off(self):
- all_nodes = [api.node.Node(api.node.Node(node))
- for node in self.ironicclient_nodes.list()]
- node = all_nodes[8]
- roles = [api.tuskar.Role(r)
- for r in TEST_DATA.tuskarclient_roles.list()]
- instance = TEST_DATA.novaclient_servers.first()
- image = TEST_DATA.glanceclient_images.first()
- data = {'action': "all_nodes_table__set_power_state_off__{0}".format(
- node.uuid)}
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['list', 'set_power_state'],
- 'list.return_value': all_nodes,
- 'set_power_state.return_value': node,
- }),
- mock.patch('tuskar_ui.api.tuskar.Role', **{
- 'spec_set': ['list', 'name'],
- 'list.return_value': roles,
- }),
- mock.patch('tuskar_ui.api.node.nova', **{
- 'spec_set': ['server_get', 'server_list'],
- 'server_get.return_value': instance,
- 'server_list.return_value': ([instance], False),
- }),
- mock.patch('tuskar_ui.api.node.glance', **{
- 'spec_set': ['image_get'],
- 'image_get.return_value': image,
- }),
- mock.patch('tuskar_ui.api.heat.Resource', **{
- 'spec_set': ['get_by_node', 'list_all_resources'],
- 'get_by_node.side_effect': (
- self._raise_horizon_exception_not_found),
- 'list_all_resources.return_value': [],
- }),
- ) as (mock_node, mock_role, mock_nova, mock_glance, mock_resource):
- res = self.client.post(INDEX_URL + '?tab=nodes__all', data)
- self.assertNoFormErrors(res)
- self.assertEqual(mock_node.set_power_state.call_count, 1)
- self.assertRedirectsNoFollow(res,
- INDEX_URL + '?tab=nodes__all')
-
- def test_performance(self):
- node = api.node.Node(self.ironicclient_nodes.list()[0])
- instance = TEST_DATA.novaclient_servers.first()
-
- ceilometerclient = self.stub_ceilometerclient()
- ceilometerclient.resources = self.mox.CreateMockAnything()
- ceilometerclient.meters = self.mox.CreateMockAnything()
-
- self.mox.ReplayAll()
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['get'],
- 'get.return_value': node,
- }),
- mock.patch('tuskar_ui.api.node.nova', **{
- 'spec_set': ['servers', 'server_get', 'server_list'],
- 'servers.return_value': [instance],
- 'server_list.return_value': ([instance], None),
- }),
- mock.patch('tuskar_ui.utils.metering.query_data',
- return_value=[]),
- ):
- url = urlresolvers.reverse(PERFORMANCE_VIEW, args=(node.uuid,))
- url += '?meter=cpu&date_options=7'
- res = self.client.get(url)
-
- json_content = json.loads(res.content)
- self.assertEqual(res.status_code, 200)
- self.assertIn('series', json_content)
- self.assertIn('settings', json_content)
-
- def test_get_driver_info_dict(self):
- data = {
- 'driver': 'pxe_ipmitool',
- 'ipmi_address': '127.0.0.1',
- 'ipmi_username': 'root',
- 'ipmi_password': 'P@55W0rd',
- 'deployment_kernel': '7',
- 'deployment_ramdisk': '8',
- }
- ret = forms.get_driver_info_dict(data)
- self.assertEqual(ret, {
- 'driver': 'pxe_ipmitool',
- 'ipmi_address': '127.0.0.1',
- 'ipmi_username': 'root',
- 'ipmi_password': 'P@55W0rd',
- 'deployment_kernel': '7',
- 'deployment_ramdisk': '8',
- })
- data = {
- 'driver': 'pxe_ssh',
- 'ssh_address': '127.0.0.1',
- 'ssh_username': 'root',
- 'ssh_key_contents': 'P@55W0rd',
- 'deployment_kernel': '7',
- 'deployment_ramdisk': '8',
- }
- ret = forms.get_driver_info_dict(data)
- self.assertEqual(ret, {
- 'driver': 'pxe_ssh',
- 'ssh_address': '127.0.0.1',
- 'ssh_username': 'root',
- 'ssh_key_contents': 'P@55W0rd',
- 'deployment_kernel': '7',
- 'deployment_ramdisk': '8',
- })
-
- def test_create_node(self):
- data = {
- 'ipmi_address': '127.0.0.1',
- 'cpu_arch': 'x86',
- 'cpus': 1,
- 'memory_mb': 2,
- 'local_gb': 3,
- 'mac_addresses': 'DE:AD:BE:EF:CA:FE',
- 'ipmi_username': 'username',
- 'ipmi_password': 'password',
- 'driver': 'pxe_ipmitool',
- 'deployment_kernel': '7',
- 'deployment_ramdisk': '8',
- }
- with mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': ['create', 'set_maintenance', 'discover'],
- 'create.return_value': None,
- }) as Node:
- forms.create_node(None, data)
- self.assertListEqual(Node.create.call_args_list, [
- mock.call(
- mock.ANY,
- ipmi_address=u'127.0.0.1',
- cpu_arch='x86',
- cpus=1,
- memory_mb=2,
- local_gb=3,
- mac_addresses=['DE:AD:BE:EF:CA:FE'],
- ipmi_username=u'username',
- ipmi_password=u'password',
- driver='pxe_ipmitool',
- deployment_kernel='7',
- deployment_ramdisk='8',
- ),
- ])
-
- def test_delete_deployed_on_servers(self):
- all_nodes = [api.node.Node(node)
- for node in self.ironicclient_nodes.list()]
- node = all_nodes[6]
- data = {'action': 'all_nodes_table__delete',
- 'object_ids': [node.uuid]}
-
- with contextlib.nested(
- mock.patch('tuskar_ui.api.node.Node', **{
- 'spec_set': [
- 'list',
- 'delete',
- ],
- 'list.return_value': [node],
- 'delete.side_effect': self._raise_ironic_exception,
- }),
- mock.patch('openstack_dashboard.api.nova.server_list',
- return_value=([], False)),
- ):
- res = self.client.post(INDEX_URL, data)
- self.assertMessageCount(error=1, warning=0)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
diff --git a/tuskar_ui/infrastructure/nodes/urls.py b/tuskar_ui/infrastructure/nodes/urls.py
deleted file mode 100644
index c75129fe..00000000
--- a/tuskar_ui/infrastructure/nodes/urls.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.nodes import views
-
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^register/$', views.RegisterView.as_view(),
- name='register'),
- urls.url(r'^nodes_performance/$',
- views.PerformanceView.as_view(), name='nodes_performance'),
- urls.url(r'^(?P<node_uuid>[^/]+)/$', views.DetailView.as_view(),
- name='node_detail'),
- urls.url(r'^(?P<node_uuid>[^/]+)/performance/$',
- views.PerformanceView.as_view(), name='performance'),
-)
diff --git a/tuskar_ui/infrastructure/nodes/views.py b/tuskar_ui/infrastructure/nodes/views.py
deleted file mode 100644
index eece7bfb..00000000
--- a/tuskar_ui/infrastructure/nodes/views.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# -*- 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 json
-
-from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
-import django.forms
-import django.http
-from django.utils.translation import ugettext_lazy as _
-from django.views.generic import base
-from horizon import exceptions
-from horizon import forms as horizon_forms
-from horizon import tabs as horizon_tabs
-from horizon.utils import memoized
-from openstack_dashboard.api import glance
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.nodes import forms
-from tuskar_ui.infrastructure.nodes import tables
-from tuskar_ui.infrastructure.nodes import tabs
-import tuskar_ui.infrastructure.views as infrastructure_views
-from tuskar_ui.utils import metering as metering_utils
-
-
-def get_kernel_images(request):
- try:
- kernel_images = glance.image_list_detailed(
- request, filters={'disk_format': 'aki'})[0]
- except Exception:
- exceptions.handle(request, _('Unable to retrieve kernel image list.'))
- kernel_images = []
- return kernel_images
-
-
-def get_ramdisk_images(request):
- try:
- ramdisk_images = glance.image_list_detailed(
- request, filters={'disk_format': 'ari'})[0]
- except Exception:
- exceptions.handle(request, _('Unable to retrieve ramdisk image list.'))
- ramdisk_images = []
- return ramdisk_images
-
-
-class IndexView(infrastructure_views.ItemCountMixin,
- horizon_tabs.TabbedTableView):
- tab_group_class = tabs.NodeTabs
- template_name = 'infrastructure/nodes/index.html'
-
- def get_context_data(self, **kwargs):
- context = super(IndexView, self).get_context_data(**kwargs)
- register_action = {
- 'name': _('Register Nodes'),
- 'url': reverse('horizon:infrastructure:nodes:register'),
- 'icon': 'fa-plus',
- 'ajax_modal': True,
- }
- context['header_actions'] = [register_action]
- return context
-
- @memoized.memoized_method
- def get_data(self):
- return api.node.Node.list(self.request)
-
- def get_tabs(self, request, **kwargs):
- nodes = self.get_data()
- return self.tab_group_class(request, nodes=nodes, **kwargs)
-
-
-class RegisterView(horizon_forms.ModalFormView):
- form_class = forms.RegisterNodeFormset
- form_prefix = 'register_nodes'
- template_name = 'infrastructure/nodes/register.html'
- success_url = reverse_lazy('horizon:infrastructure:nodes:index')
- submit_label = _("Register Nodes")
-
- def get_data(self):
- return []
-
- def get_form(self, form_class):
- initial = []
-
- if self.request.FILES:
- csv_form = forms.UploadNodeForm(self.request,
- data=self.request.POST,
- files=self.request.FILES)
- if csv_form.is_valid():
- initial = csv_form.get_data()
- formset = forms.RegisterNodeFormset(
- self.request.POST,
- prefix=self.form_prefix,
- request=self.request,
- kernel_images=get_kernel_images(self.request),
- ramdisk_images=get_ramdisk_images(self.request)
- )
- if formset.is_valid():
- initial += formset.cleaned_data
- formset = forms.RegisterNodeFormset(
- None,
- initial=initial,
- prefix=self.form_prefix,
- request=self.request,
- kernel_images=get_kernel_images(self.request),
- ramdisk_images=get_ramdisk_images(self.request)
- )
- formset.extra = 0
- return formset
- return forms.RegisterNodeFormset(
- self.request.POST or None,
- initial=initial,
- prefix=self.form_prefix,
- request=self.request,
- kernel_images=get_kernel_images(self.request),
- ramdisk_images=get_ramdisk_images(self.request)
- )
-
- def get_context_data(self, **kwargs):
- context = super(RegisterView, self).get_context_data(**kwargs)
- context['upload_form'] = forms.UploadNodeForm(self.request)
- return context
-
-
-class DetailView(horizon_tabs.TabView):
- tab_group_class = tabs.NodeDetailTabs
- template_name = 'infrastructure/nodes/detail.html'
-
- def get_context_data(self, **kwargs):
- context = super(DetailView, self).get_context_data(**kwargs)
-
- node = self.get_data()
-
- if node.maintenance:
- table = tables.MaintenanceNodesTable(self.request)
- else:
- table = tables.ProvisionedNodesTable(self.request)
-
- context['node'] = node
- context['title'] = _("Node: %(uuid)s") % {'uuid': node.uuid}
- context['url'] = self.get_redirect_url()
- context['actions'] = table.render_row_actions(node)
- return context
-
- @memoized.memoized_method
- def get_data(self):
- node_uuid = self.kwargs.get('node_uuid')
- node = api.node.Node.get(self.request, node_uuid,
- _error_redirect=self.get_redirect_url())
- return node
-
- def get_tabs(self, request, **kwargs):
- node = self.get_data()
- return self.tab_group_class(self.request, node=node, **kwargs)
-
- @staticmethod
- def get_redirect_url():
- return reverse_lazy('horizon:infrastructure:nodes:index')
-
-
-class PerformanceView(base.TemplateView):
- def get(self, request, *args, **kwargs):
- meter = request.GET.get('meter')
- date_options = request.GET.get('date_options')
- date_from = request.GET.get('date_from')
- date_to = request.GET.get('date_to')
- stats_attr = request.GET.get('stats_attr', 'avg')
- barchart = bool(request.GET.get('barchart'))
-
- node_uuid = kwargs.get('node_uuid', None)
- if node_uuid:
- node = api.node.Node.get(request, node_uuid)
- instance_uuids = [node.instance_uuid]
- else:
- # Aggregated stats for all nodes
- instance_uuids = []
-
- json_output = metering_utils.get_nodes_stats(
- request=request,
- node_uuid=node_uuid,
- instance_uuids=instance_uuids,
- meter=meter,
- date_options=date_options,
- date_from=date_from,
- date_to=date_to,
- stats_attr=stats_attr,
- barchart=barchart)
-
- return django.http.HttpResponse(
- json.dumps(json_output), content_type='application/json')
diff --git a/tuskar_ui/infrastructure/overview/__init__.py b/tuskar_ui/infrastructure/overview/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/overview/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/overview/forms.py b/tuskar_ui/infrastructure/overview/forms.py
deleted file mode 100644
index 77ff8c37..00000000
--- a/tuskar_ui/infrastructure/overview/forms.py
+++ /dev/null
@@ -1,488 +0,0 @@
-# -*- 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 logging
-import six
-import uuid
-
-from django.conf import settings
-import django.forms
-from django.utils.translation import ugettext_lazy as _
-import horizon.exceptions
-import horizon.forms
-import horizon.messages
-from os_cloud_config import keystone as keystone_config
-from os_cloud_config.utils import clients
-
-from tuskar_ui import api
-import tuskar_ui.api.heat
-import tuskar_ui.api.tuskar
-import tuskar_ui.forms
-import tuskar_ui.infrastructure.flavors.utils as flavors_utils
-import tuskar_ui.utils.utils as tuskar_utils
-
-MATCHING_DEPLOYMENT_MODE = flavors_utils.matching_deployment_mode()
-LOG = logging.getLogger(__name__)
-MESSAGE_ICONS = {
- 'ok': 'fa-check-square-o text-success',
- 'pending': 'fa-square-o text-info',
- 'error': 'fa-exclamation-circle text-danger',
- 'warning': 'fa-exclamation-triangle text-warning',
- None: 'fa-exclamation-triangle text-warning',
-}
-WEBROOT = getattr(settings, 'WEBROOT', '/')
-
-
-def validate_roles(request, plan):
- """Validates the roles in plan and returns dict describing the issues"""
- for role in plan.role_list:
- if (
- plan.get_role_node_count(role) and
- not role.is_valid_for_deployment(plan)
- ):
- message = {
- 'text': _(u"Configure Roles."),
- 'is_critical': True,
- 'status': 'pending',
- }
- break
- else:
- message = {
- 'text': _(u"Configure Roles."),
- 'status': 'ok',
- }
- return message
-
-
-def validate_global_parameters(request, plan):
- pending_required_global_params = list(
- api.tuskar.Parameter.pending_parameters(
- api.tuskar.Parameter.required_parameters(
- api.tuskar.Parameter.global_parameters(
- plan.parameter_list()))))
- if pending_required_global_params:
- message = {
- 'text': _(u"Global Service Configuration."),
- 'is_critical': True,
- 'status': 'pending',
- }
- else:
- message = {
- 'text': _(u"Global Service Configuration."),
- 'status': 'ok',
- }
- return message
-
-
-def validate_plan(request, plan):
- """Validates the plan and returns a list of dicts describing the issues."""
- messages = []
- requested_nodes = 0
- for role in plan.role_list:
- node_count = plan.get_role_node_count(role)
- requested_nodes += node_count
- available_flavors = len(api.flavor.Flavor.list(request))
- if available_flavors == 0:
- messages.append({
- 'text': _(u"Define Flavors."),
- 'is_critical': True,
- 'status': 'pending',
- })
- else:
- messages.append({
- 'text': _(u"Define Flavors."),
- 'status': 'ok',
- })
- available_nodes = len(api.node.Node.list(request, associated=False,
- maintenance=False))
- if available_nodes == 0:
- messages.append({
- 'text': _(u"Register Nodes."),
- 'is_critical': True,
- 'status': 'pending',
- })
- elif requested_nodes > available_nodes:
- messages.append({
- 'text': _(u"Not enough registered nodes for this plan. "
- u"You need {0} more.").format(
- requested_nodes - available_nodes),
- 'is_critical': True,
- 'status': 'error',
- })
- else:
- messages.append({
- 'text': _(u"Register Nodes."),
- 'status': 'ok',
- })
- messages.append(validate_roles(request, plan))
- messages.append(validate_global_parameters(request, plan))
- if not MATCHING_DEPLOYMENT_MODE:
- # All roles have to have the same flavor.
- default_flavor_name = api.flavor.Flavor.list(request)[0].name
- for role in plan.role_list:
- if role.flavor(plan).name != default_flavor_name:
- messages.append({
- 'text': _(u"Role {0} doesn't use default flavor.").format(
- role.name,
- ),
- 'is_critical': False,
- 'statis': 'error',
- })
- roles_assigned = True
- messages.append({
- 'text': _(u"Assign roles."),
- 'status': lambda: 'ok' if roles_assigned else 'pending',
- })
- try:
- controller_role = plan.get_role_by_name("Controller")
- except KeyError:
- messages.append({
- 'text': _(u"Controller Role Needed."),
- 'is_critical': True,
- 'status': 'error',
- 'indent': 1,
- })
- roles_assigned = False
- else:
- if plan.get_role_node_count(controller_role) not in (1, 3):
- messages.append({
- 'text': _(u"1 or 3 Controllers Needed."),
- 'is_critical': True,
- 'status': 'pending',
- 'indent': 1,
- })
- roles_assigned = False
- else:
- messages.append({
- 'text': _(u"1 or 3 Controllers Needed."),
- 'status': 'ok',
- 'indent': 1,
- })
-
- try:
- compute_role = plan.get_role_by_name("Compute")
- except KeyError:
- messages.append({
- 'text': _(u"Compute Role Needed."),
- 'is_critical': True,
- 'status': 'error',
- 'indent': 1,
- })
- roles_assigned = False
- else:
- if plan.get_role_node_count(compute_role) < 1:
- messages.append({
- 'text': _(u"1 Compute Needed."),
- 'is_critical': True,
- 'status': 'pending',
- 'indent': 1,
- })
- roles_assigned = False
- else:
- messages.append({
- 'text': _(u"1 Compute Needed."),
- 'status': 'ok',
- 'indent': 1,
- })
- for message in messages:
- status = message.get('status')
- if callable(status):
- message['status'] = status = status()
- message['classes'] = MESSAGE_ICONS.get(status, MESSAGE_ICONS[None])
- return messages
-
-
-class EditPlan(horizon.forms.SelfHandlingForm):
- def __init__(self, *args, **kwargs):
- super(EditPlan, self).__init__(*args, **kwargs)
- self.plan = api.tuskar.Plan.get_the_plan(self.request)
- self.fields.update(self._role_count_fields(self.plan))
-
- def _role_count_fields(self, plan):
- fields = {}
- for role in plan.role_list:
- field = django.forms.IntegerField(
- label=role.name,
- widget=tuskar_ui.forms.NumberPickerInput(attrs={
- 'min': 1 if role.name in ('Controller', 'Compute') else 0,
- 'step': 2 if role.name == 'Controller' else 1,
- }),
- initial=plan.get_role_node_count(role),
- required=False
- )
- field.role = role
- fields['%s-count' % role.id] = field
- return fields
-
- def handle(self, request, data):
- parameters = dict(
- (field.role.node_count_parameter_name, data[name])
- for (name, field) in self.fields.items() if name.endswith('-count')
- )
- # NOTE(gfidente): this is a bad hack meant to magically add the
- # parameter which enables Neutron L3 HA when the number of
- # Controllers is > 1
- try:
- controller_role = self.plan.get_role_by_name('Controller')
- compute_role = self.plan.get_role_by_name('Compute')
- except Exception as e:
- LOG.warning('Unable to find a required role: %s', e.message)
- else:
- number_controllers = parameters[
- controller_role.node_count_parameter_name]
- if number_controllers > 1:
- for role in [controller_role, compute_role]:
- l3ha_param = role.parameter_prefix + 'NeutronL3HA'
- parameters[l3ha_param] = 'True'
- l3agent_param = (role.parameter_prefix +
- 'NeutronAllowL3AgentFailover')
- parameters[l3agent_param] = 'True'
- dhcp_agents_per_net = (number_controllers if number_controllers and
- number_controllers > 3 else 3)
- dhcp_agents_param = (controller_role.parameter_prefix +
- 'NeutronDhcpAgentsPerNetwork')
- parameters[dhcp_agents_param] = dhcp_agents_per_net
-
- try:
- ceph_storage_role = self.plan.get_role_by_name('Ceph-Storage')
- except Exception as e:
- LOG.warning('Unable to find role: %s', 'Ceph-Storage')
- else:
- if parameters[ceph_storage_role.node_count_parameter_name] > 0:
- parameters.update({
- 'CephClusterFSID': six.text_type(uuid.uuid4()),
- 'CephMonKey': tuskar_utils.create_cephx_key(),
- 'CephAdminKey': tuskar_utils.create_cephx_key()
- })
-
- cinder_enable_rbd_param = (controller_role.parameter_prefix
- + 'CinderEnableRbdBackend')
- glance_backend_param = (controller_role.parameter_prefix +
- 'GlanceBackend')
- nova_enable_rbd_param = (compute_role.parameter_prefix +
- 'NovaEnableRbdBackend')
- cinder_enable_iscsi_param = (
- controller_role.parameter_prefix +
- 'CinderEnableIscsiBackend')
-
- parameters.update({
- cinder_enable_rbd_param: True,
- glance_backend_param: 'rbd',
- nova_enable_rbd_param: True,
- cinder_enable_iscsi_param: False
- })
-
- try:
- self.plan = self.plan.patch(request, self.plan.uuid, parameters)
- except Exception as e:
- horizon.exceptions.handle(request, _("Unable to update the plan."))
- LOG.exception(e)
- return False
- return True
-
-
-class ScaleOut(EditPlan):
- def __init__(self, *args, **kwargs):
- super(ScaleOut, self).__init__(*args, **kwargs)
- for name, field in self.fields.items():
- if name.endswith('-count'):
- field.widget.attrs['min'] = field.initial
-
- def handle(self, request, data):
- if not super(ScaleOut, self).handle(request, data):
- return False
- plan = self.plan
- try:
- stack = api.heat.Stack.get_by_plan(self.request, plan)
- stack.update(request, plan.name, plan.templates)
- except Exception as e:
- LOG.exception(e)
- if hasattr(e, 'error'):
- horizon.exceptions.handle(
- request,
- _(
- "Unable to deploy overcloud. Reason: {0}"
- ).format(e.error['error']['message']),
- )
- return False
- else:
- raise
- else:
- msg = _('Deployment in progress.')
- horizon.messages.success(request, msg)
- return True
-
-
-class DeployOvercloud(horizon.forms.SelfHandlingForm):
- network_isolation = horizon.forms.BooleanField(
- label=_("Enable Network Isolation"),
- required=False)
-
- def handle(self, request, data):
- try:
- plan = api.tuskar.Plan.get_the_plan(request)
- except Exception as e:
- LOG.exception(e)
- horizon.exceptions.handle(request,
- _("Unable to deploy overcloud."))
- return False
-
- # If network isolation selected, read environment file data
- # and add to plan
- env_temp = '/usr/share/openstack-tripleo-heat-templates/environments'
- try:
- if self.cleaned_data['network_isolation']:
- with open(env_temp, 'r') as env_file:
- env_contents = ''.join(
- [line for line in
- env_file.readlines() if '#' not in line]
- )
- plan.environment += env_contents
- except Exception as e:
- LOG.exception(e)
- pass
-
- # Auto-generate missing passwords and certificates
- if plan.list_generated_parameters():
- generated_params = plan.make_generated_parameters()
- plan = plan.patch(request, plan.uuid, generated_params)
-
- # Validate plan and create stack
- for message in validate_plan(request, plan):
- if message.get('is_critical'):
- horizon.messages.success(request, message.text)
- return False
- try:
- stack = api.heat.Stack.get_by_plan(self.request, plan)
- if not stack:
- api.heat.Stack.create(request, plan.name, plan.templates)
- except Exception as e:
- LOG.exception(e)
- horizon.exceptions.handle(
- request, _("Unable to deploy overcloud. Reason: {0}").format(
- e.error['error']['message']))
- return False
- else:
- msg = _('Deployment in progress.')
- horizon.messages.success(request, msg)
- return True
-
-
-class UndeployOvercloud(horizon.forms.SelfHandlingForm):
- def handle(self, request, data):
- try:
- plan = api.tuskar.Plan.get_the_plan(request)
- stack = api.heat.Stack.get_by_plan(self.request, plan)
- if stack:
- api.heat.Stack.delete(request, stack.id)
- except Exception as e:
- LOG.exception(e)
- horizon.exceptions.handle(request,
- _("Unable to undeploy overcloud."))
- return False
- else:
- msg = _('Undeployment in progress.')
- horizon.messages.success(request, msg)
- return True
-
-
-class PostDeployInit(horizon.forms.SelfHandlingForm):
- admin_email = horizon.forms.CharField(label=_("Admin Email"))
- public_host = horizon.forms.CharField(
- label=_("Public Host"), initial="", required=False)
- region = horizon.forms.CharField(
- label=_("Region"), initial="regionOne")
-
- def build_endpoints(self, plan, controller_role):
- return {
- "ceilometer": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'CeilometerPassword')},
- "cinder": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'CinderPassword')},
- "cinderv2": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'CinderPassword')},
- "ec2": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'GlancePassword')},
- "glance": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'GlancePassword')},
- "heat": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'HeatPassword')},
- "neutron": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'NeutronPassword')},
- "nova": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'NovaPassword')},
- "novav3": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'NovaPassword')},
- "swift": {
- "password": plan.parameter_value(
- controller_role.parameter_prefix + 'SwiftPassword'),
- 'path': '/v1/AUTH_%(tenant_id)s',
- 'admin_path': '/v1'},
- "horizon": {
- 'port': '80',
- 'path': WEBROOT,
- 'admin_path': '%sadmin' % WEBROOT}}
-
- def handle(self, request, data):
- try:
- plan = api.tuskar.Plan.get_the_plan(request)
- controller_role = plan.get_role_by_name("Controller")
- stack = api.heat.Stack.get_by_plan(self.request, plan)
-
- admin_token = plan.parameter_value(
- controller_role.parameter_prefix + 'AdminToken')
- admin_password = plan.parameter_value(
- controller_role.parameter_prefix + 'AdminPassword')
- admin_email = data['admin_email']
- auth_ip = stack.keystone_ip
- auth_url = stack.keystone_auth_url
- auth_tenant = 'admin'
- auth_user = 'admin'
-
- # do the keystone init
- keystone_config.initialize(
- auth_ip, admin_token, admin_email, admin_password,
- region='regionOne', ssl=None, public=None, user='heat-admin',
- pki_setup=False)
-
- # retrieve needed Overcloud clients
- keystone_client = clients.get_keystone_client(
- auth_user, admin_password, auth_tenant, auth_url)
-
- # do the setup endpoints
- keystone_config.setup_endpoints(
- self.build_endpoints(plan, controller_role),
- public_host=data['public_host'],
- region=data['region'],
- os_auth_url=auth_url,
- client=keystone_client)
-
- except Exception as e:
- LOG.exception(e)
- horizon.exceptions.handle(request,
- _("Unable to initialize Overcloud."))
- return False
- else:
- msg = _('Overcloud has been initialized.')
- horizon.messages.success(request, msg)
- return True
diff --git a/tuskar_ui/infrastructure/overview/panel.py b/tuskar_ui/infrastructure/overview/panel.py
deleted file mode 100644
index 535cd13b..00000000
--- a/tuskar_ui/infrastructure/overview/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class Overview(horizon.Panel):
- name = _("Overview")
- slug = "overview"
-
-
-dashboard.Infrastructure.register(Overview)
diff --git a/tuskar_ui/infrastructure/overview/tests.py b/tuskar_ui/infrastructure/overview/tests.py
deleted file mode 100644
index 20155c86..00000000
--- a/tuskar_ui/infrastructure/overview/tests.py
+++ /dev/null
@@ -1,364 +0,0 @@
-# -*- 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 contextlib
-
-from django.core import urlresolvers
-from mock import patch, call # noqa
-from openstack_dashboard.test.test_data import utils
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.overview import forms
-from tuskar_ui.infrastructure.overview import views
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import heat_data
-from tuskar_ui.test.test_data import tuskar_data
-
-
-INDEX_URL = urlresolvers.reverse(
- 'horizon:infrastructure:overview:index')
-DEPLOY_URL = urlresolvers.reverse(
- 'horizon:infrastructure:overview:deploy_confirmation')
-DELETE_URL = urlresolvers.reverse(
- 'horizon:infrastructure:overview:undeploy_confirmation')
-POST_DEPLOY_INIT_URL = urlresolvers.reverse(
- 'horizon:infrastructure:overview:post_deploy_init')
-TEST_DATA = utils.TestDataContainer()
-heat_data.data(TEST_DATA)
-tuskar_data.data(TEST_DATA)
-
-
-@contextlib.contextmanager
-def _mock_plan(**kwargs):
- plan = None
-
- params = {
- 'spec_set': [
- 'create',
- 'delete',
- 'get',
- 'get_the_plan',
- 'id',
- 'uuid',
- 'patch',
- 'parameters',
- 'role_list',
- 'parameter_value',
- 'get_role_by_name',
- 'get_role_node_count',
- 'list_generated_parameters',
- 'make_generated_parameters',
- 'parameter_list',
- ],
- 'create.side_effect': lambda *args, **kwargs: plan,
- 'delete.return_value': None,
- 'get.side_effect': lambda *args, **kwargs: plan,
- 'get_the_plan.side_effect': lambda *args, **kwargs: plan,
- 'id': 'plan-1',
- 'uuid': 'plan-1',
- 'patch.side_effect': lambda *args, **kwargs: plan,
- 'role_list': [],
- 'parameter_list.return_value': [],
- 'parameter_value.return_value': None,
- 'get_role_by_name.side_effect': KeyError,
- 'get_role_node_count.return_value': 0,
- 'list_generated_parameters.return_value': {},
- 'make_generated_parameters.return_value': {},
- }
- params.update(kwargs)
- with patch(
- 'tuskar_ui.api.tuskar.Plan', **params) as Plan:
- plan = Plan
- yield Plan
-
-
-class OverviewTests(test.BaseAdminViewTests):
- def test_index_stack_not_created(self):
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.list', return_value=[]),
- patch('tuskar_ui.api.node.Node.list', return_value=[]),
- patch('tuskar_ui.api.flavor.Flavor.list', return_value=[]),
- ):
- res = self.client.get(INDEX_URL)
- get_the_plan = api.tuskar.Plan.get_the_plan
- request = get_the_plan.call_args_list[0][0][0]
- self.assertListEqual(get_the_plan.call_args_list, [
- call(request),
- call(request),
- call(request),
- ])
- self.assertListEqual(api.heat.Stack.list.call_args_list, [
- call(request),
- ])
- self.assertListEqual(api.node.Node.list.call_args_list, [
- call(request, associated=False, maintenance=False),
- ])
- self.assertListEqual(api.flavor.Flavor.list.call_args_list, [
- call(request),
- ])
- self.assertTemplateUsed(
- res, 'infrastructure/overview/index.html')
- self.assertTemplateUsed(
- res, 'infrastructure/overview/role_nodes_edit.html')
-
- def test_index_stack_not_created_post(self):
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.list', return_value=[]),
- patch('tuskar_ui.api.node.Node.list', return_value=[]),
- patch('tuskar_ui.api.flavor.Flavor.list', return_value=[]),
- ) as (plan, _stack_list, _node_list, _flavor_list):
- data = {
- 'role-1-count': 1,
- 'role-2-count': 0,
- 'role-3-count': 0,
- 'role-4-count': 0,
- }
- res = self.client.post(INDEX_URL, data)
- self.assertNoFormErrors(res)
- self.assertRedirectsNoFollow(res, INDEX_URL)
- get_the_plan = api.tuskar.Plan.get_the_plan
- request = get_the_plan.call_args_list[0][0][0]
- self.assertListEqual(get_the_plan.call_args_list, [
- call(request),
- ])
- self.assertListEqual(
- api.tuskar.Plan.patch.call_args_list,
- [call(request, plan.id, {})],
- )
-
- def test_index_stack_deployed(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
-
- with contextlib.nested(
- _mock_plan(**{'get_role_by_name.side_effect': None,
- 'get_role_by_name.return_value': roles[0]}),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.events',
- return_value=[]),
- ) as (Plan, stack_get_mock, stack_events_mock):
- res = self.client.get(INDEX_URL)
- request = Plan.get_the_plan.call_args_list[0][0][0]
- self.assertListEqual(
- Plan.get_the_plan.call_args_list,
- [
- call(request),
- call(request),
- call(request),
- ])
-
- self.assertTemplateUsed(
- res, 'infrastructure/overview/index.html')
- self.assertTemplateUsed(
- res, 'infrastructure/overview/deployment_live.html')
-
- def test_index_stack_undeploy_in_progress(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.is_deleting',
- return_value=True),
- patch('tuskar_ui.api.heat.Stack.is_deployed',
- return_value=False),
- patch('tuskar_ui.api.heat.Stack.resources',
- return_value=[]),
- patch('tuskar_ui.api.heat.Stack.events',
- return_value=[]),
- ):
- res = self.client.get(INDEX_URL)
-
- self.assertTemplateUsed(
- res, 'infrastructure/overview/index.html')
- self.assertTemplateUsed(
- res, 'infrastructure/overview/deployment_progress.html')
-
- def test_deploy_get(self):
- with _mock_plan():
- res = self.client.get(DEPLOY_URL)
- self.assertTemplateUsed(
- res, 'infrastructure/overview/deploy_confirmation.html')
-
- def test_delete_get(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- ):
- res = self.client.get(DELETE_URL)
- self.assertTemplateUsed(
- res, 'infrastructure/overview/undeploy_confirmation.html')
-
- def test_delete_post(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.delete',
- return_value=None),
- ):
- res = self.client.post(DELETE_URL)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- def test_post_deploy_init_get(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- _mock_plan(),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- ):
- res = self.client.get(POST_DEPLOY_INIT_URL)
- self.assertEqual(res.context['form']['admin_email'].value(), '')
- self.assertTemplateUsed(
- res, 'infrastructure/overview/post_deploy_init.html')
-
- def test_post_deploy_init_post(self):
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
-
- data = {
- 'admin_email': "example@example.org",
- 'public_host': '',
- 'region': 'regionOne',
- }
-
- with contextlib.nested(
- _mock_plan(**{'get_role_by_name.side_effect': None,
- 'get_role_by_name.return_value': roles[0]}),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('os_cloud_config.keystone.initialize',
- return_value=None),
- patch('os_cloud_config.keystone.setup_endpoints',
- return_value=None),
- patch('os_cloud_config.utils.clients.get_keystone_client',
- return_value='keystone_client'),
- ) as (mock_plan, mock_get_by_plan, mock_initialize,
- mock_setup_endpoints, mock_get_keystone_client):
- res = self.client.post(POST_DEPLOY_INIT_URL, data)
-
- self.assertNoFormErrors(res)
- self.assertEqual(res.status_code, 302)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- mock_initialize.assert_called_once_with(
- '192.0.2.23', None, 'example@example.org', None, ssl=None,
- region='regionOne', user='heat-admin', public=None,
- pki_setup=False)
- mock_setup_endpoints.assert_called_once_with(
- {'nova': {'password': None},
- 'heat': {'password': None},
- 'ceilometer': {'password': None},
- 'ec2': {'password': None},
- "horizon": {
- 'port': '80',
- 'path': '/',
- 'admin_path': '/admin'},
- 'cinder': {'password': None},
- 'cinderv2': {'password': None},
- 'glance': {'password': None},
- 'swift': {'password': None,
- 'path': '/v1/AUTH_%(tenant_id)s',
- 'admin_path': '/v1'},
- 'novav3': {'password': None},
- 'neutron': {'password': None}},
- os_auth_url=stack.keystone_auth_url,
- client='keystone_client',
- region='regionOne',
- public_host='')
- mock_get_keystone_client.assert_called_once_with(
- 'admin', None, 'admin', stack.keystone_auth_url)
-
- def test_get_role_data(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- stack = api.heat.Stack(self.heatclient_stacks.first())
- role = api.tuskar.Role(self.tuskarclient_roles.first())
- stack.resources = lambda *args, **kwargs: []
- ret = views._get_role_data(plan, stack, None, role)
- self.assertEqual(ret, {
- 'deployed_node_count': 0,
- 'deploying_node_count': 0,
- 'error_node_count': 0,
- 'field': '',
- 'finished': False,
- 'icon': 'fa-exclamation',
- 'id': 'role-1',
- 'name': 'Controller',
- 'planned_node_count': 1,
- 'role': role,
- 'status': 'warning',
- 'total_node_count': 0,
- 'waiting_node_count': 0,
- })
-
- def test_validate_plan_empty(self):
- with (
- _mock_plan()
- ) as plan, (
- patch('tuskar_ui.api.node.Node.list', return_value=[])
- ), (
- patch('tuskar_ui.api.flavor.Flavor.list', return_value=[])
- ):
- ret = forms.validate_plan(None, plan)
- for m in ret:
- m['text'] = unicode(m['text'])
- self.assertEqual(ret, [
- {
- 'is_critical': True,
- 'text': u'Define Flavors.',
- 'status': 'pending',
- 'classes': 'fa-square-o text-info',
- }, {
- 'is_critical': True,
- 'text': u'Register Nodes.',
- 'status': 'pending',
- 'classes': 'fa-square-o text-info',
- }, {
- 'status': 'ok',
- 'text': u'Configure Roles.',
- 'classes': 'fa-check-square-o text-success',
- }, {
- 'status': 'ok',
- 'text': u'Global Service Configuration.',
- 'classes': 'fa-check-square-o text-success',
- }, {
- 'status': 'pending',
- 'text': u'Assign roles.',
- 'classes': 'fa-square-o text-info',
- }, {
- 'is_critical': True,
- 'text': u'Controller Role Needed.',
- 'status': 'error',
- 'indent': 1,
- 'classes': 'fa-exclamation-circle text-danger',
- }, {
- 'is_critical': True,
- 'text': u'Compute Role Needed.',
- 'status': 'error',
- 'indent': 1,
- 'classes': 'fa-exclamation-circle text-danger',
- },
- ])
diff --git a/tuskar_ui/infrastructure/overview/urls.py b/tuskar_ui/infrastructure/overview/urls.py
deleted file mode 100644
index 35410d5d..00000000
--- a/tuskar_ui/infrastructure/overview/urls.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.overview import views
-
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^deploy-confirmation$',
- views.DeployConfirmationView.as_view(),
- name='deploy_confirmation'),
- urls.url(r'^undeploy-confirmation$',
- views.UndeployConfirmationView.as_view(),
- name='undeploy_confirmation'),
- urls.url(r'^post-deploy-init$',
- views.PostDeployInitView.as_view(),
- name='post_deploy_init'),
- urls.url(r'^scale-out$',
- views.ScaleOutView.as_view(),
- name='scale_out'),
- urls.url(r'^download-overcloudrc$',
- views.download_overcloudrc_file,
- name='download_overcloudrc'),
-)
diff --git a/tuskar_ui/infrastructure/overview/views.py b/tuskar_ui/infrastructure/overview/views.py
deleted file mode 100644
index 90f9fa37..00000000
--- a/tuskar_ui/infrastructure/overview/views.py
+++ /dev/null
@@ -1,390 +0,0 @@
-# -*- 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 json
-import logging
-import urlparse
-
-from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
-from django import http
-from django import shortcuts
-import django.utils.text
-from django.utils.translation import ugettext_lazy as _
-import heatclient
-import horizon.forms
-from horizon import messages
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.overview import forms
-from tuskar_ui.infrastructure import views
-
-
-INDEX_URL = 'horizon:infrastructure:overview:index'
-
-LOG = logging.getLogger(__name__)
-
-
-def _steps_message(messages):
- total_steps = len(messages)
- completed_steps = len([m for m in messages if not m.get('is_critical')])
- return _("{0} of {1} Steps Completed").format(completed_steps, total_steps)
-
-
-def _get_role_data(plan, stack, form, role):
- """Gathers data about a single deployment role.
-
- Gathers data about a single deployment role from the related Overcloud
- and Role objects, and presents it in the form convenient for use
- from the template.
-
- """
- data = {
- 'id': role.id,
- 'role': role,
- 'name': role.name,
- 'planned_node_count': plan.get_role_node_count(role),
- 'field': form['%s-count' % role.id] if form else '',
- }
-
- if stack:
- resources = stack.resources(role=role, with_joins=True)
- nodes = [r.node for r in resources]
- node_count = len(nodes)
-
- deployed_node_count = 0
- deploying_node_count = 0
- error_node_count = 0
- waiting_node_count = node_count
-
- status = 'warning'
- if nodes:
- deployed_node_count = sum(1 for node in nodes
- if node.instance.status == 'ACTIVE')
- deploying_node_count = sum(1 for node in nodes
- if node.instance.status == 'BUILD')
- error_node_count = sum(1 for node in nodes
- if node.instance.status == 'ERROR')
- waiting_node_count = (node_count - deployed_node_count -
- deploying_node_count - error_node_count)
-
- if error_node_count or 'FAILED' in stack.stack_status:
- status = 'danger'
- elif deployed_node_count == data['planned_node_count']:
- status = 'success'
- else:
- status = 'info'
-
- finished = deployed_node_count == data['planned_node_count']
- if finished:
- icon = 'fa-check'
- elif status in ('danger', 'warning'):
- icon = 'fa-exclamation'
- else:
- icon = 'fa-spinner fa-spin'
-
- data.update({
- 'status': status,
- 'finished': finished,
- 'total_node_count': node_count,
- 'deployed_node_count': deployed_node_count,
- 'deploying_node_count': deploying_node_count,
- 'waiting_node_count': waiting_node_count,
- 'error_node_count': error_node_count,
- 'icon': icon,
- })
-
- # TODO(rdopieralski) get this from ceilometer
- # data['capacity'] = 20
- return data
-
-
-class IndexView(horizon.forms.ModalFormView, views.StackMixin):
- template_name = 'infrastructure/overview/index.html'
- form_class = forms.EditPlan
- success_url = reverse_lazy(INDEX_URL)
-
- def get_progress_update(self, request, data):
- return {
- 'progress': data.get('progress'),
- 'show_last_events': data.get('show_last_events'),
- 'last_events_title': unicode(data.get('last_events_title')),
- 'last_events': [{
- 'event_time': event.event_time,
- 'resource_name': event.resource_name,
- 'resource_status': event.resource_status,
- 'resource_status_reason': event.resource_status_reason,
- } for event in data.get('last_events', [])],
- 'roles': [{
- 'status': role.get('status', 'warning'),
- 'finished': role.get('finished', False),
- 'name': role.get('name', ''),
- 'slug': django.utils.text.slugify(role.get('name', '')),
- 'id': role.get('id', ''),
- 'total_node_count': role.get('node_count', 0),
- 'deployed_node_count': role.get('deployed_node_count', 0),
- 'deploying_node_count': role.get('deploying_node_count', 0),
- 'waiting_node_count': role.get('waiting_node_count', 0),
- 'error_node_count': role.get('error_node_count', 0),
- 'planned_node_count': role.get('planned_node_count', 0),
- 'icon': role.get('icon', ''),
- } for role in data.get('roles', [])],
- }
-
- def get(self, request, *args, **kwargs):
- if request.META.get('HTTP_X_HORIZON_PROGRESS', ''):
- # If it's an AJAX call for progress update, send it.
- data = self.get_data(request, {})
- return http.HttpResponse(
- json.dumps(self.get_progress_update(request, data)),
- content_type='application/json',
- )
- return super(IndexView, self).get(request, *args, **kwargs)
-
- def get_form(self, form_class):
- return form_class(self.request, **self.get_form_kwargs())
-
- def get_context_data(self, *args, **kwargs):
- context = super(IndexView, self).get_context_data(*args, **kwargs)
- context.update(self.get_data(self.request, context))
- return context
-
- def get_data(self, request, context, *args, **kwargs):
- plan = api.tuskar.Plan.get_the_plan(request)
- stack = self.get_stack()
- form = context.get('form')
-
- context['plan'] = plan
- context['stack'] = stack
-
- roles = [_get_role_data(plan, stack, form, role)
- for role in plan.role_list]
- context['roles'] = roles
-
- if stack:
- context['show_last_events'] = True
- failed_events = [e for e in stack.events
- if 'FAILED' in e.resource_status and
- 'aborted' not in e.resource_status_reason][-3:]
-
- if failed_events:
- context['last_events_title'] = _('Last failed events')
- context['last_events'] = failed_events
- else:
- context['last_events_title'] = _('Last event')
- context['last_events'] = [stack.events[0]]
-
- if stack.is_deleting or stack.is_delete_failed:
- # TODO(lsmola) since at this point we don't have total number
- # of nodes we will hack this around, till API can show this
- # information. So it will actually show progress like the total
- # number is 10, or it will show progress of 5%. Ugly, but
- # workable.
- total_num_nodes_count = 10
-
- try:
- resources_count = len(
- stack.resources(with_joins=False))
- except heatclient.exc.HTTPNotFound:
- # Immediately after undeploying has started, heat returns
- # this exception so we can take it as kind of init of
- # undeploying.
- resources_count = total_num_nodes_count
-
- # TODO(lsmola) same as hack above
- total_num_nodes_count = max(
- resources_count, total_num_nodes_count)
-
- context['progress'] = min(95, max(
- 5, 100 * float(resources_count) / total_num_nodes_count))
- elif stack.is_deploying or stack.is_updating:
- total = sum(d['total_node_count'] for d in roles)
- context['progress'] = min(95, max(
- 5, 100 * sum(float(d.get('deployed_node_count', 0))
- for d in roles) / (total or 1)
- ))
- else:
- # stack is active
- if not stack.is_failed:
- context['show_last_events'] = False
- context['progress'] = 100
- controller_role = plan.get_role_by_name("Controller")
- context['admin_password'] = plan.parameter_value(
- controller_role.parameter_prefix + 'AdminPassword')
-
- context['dashboard_urls'] = stack.dashboard_urls
- no_proxy = [urlparse.urlparse(url).hostname
- for url in stack.dashboard_urls]
- context['no_proxy'] = ",".join(no_proxy)
- context['auth_url'] = stack.keystone_auth_url
- else:
- messages = forms.validate_plan(request, plan)
- context['plan_messages'] = messages
- context['plan_invalid'] = any(message.get('is_critical')
- for message in messages)
- context['steps_message'] = _steps_message(messages)
- return context
-
- def post(self, request, *args, **kwargs):
- """If the post comes from ajax, return validation results as json."""
-
- if not request.META.get('HTTP_X_HORIZON_VALIDATE', ''):
- return super(IndexView, self).post(request, *args, **kwargs)
- form_class = self.get_form_class()
- form = self.get_form(form_class)
- if form.is_valid():
- handled = form.handle(self.request, form.cleaned_data)
- else:
- handled = False
- if handled:
- messages = forms.validate_plan(request, form.plan)
- else:
- messages = [{
- 'text': _(u"Error saving the plan."),
- 'is_critical': True,
- }]
- messages.extend({
- 'text': repr(error),
- } for error in form.non_field_errors)
- messages.extend({
- 'text': repr(error),
- } for field in form.fields for error in field.errors)
- # We need to unlazify all the lazy urls and translations.
- return http.HttpResponse(json.dumps({
- 'plan_invalid': any(m.get('is_critical') for m in messages),
- 'steps_message': _steps_message(messages),
- 'messages': [{
- 'text': unicode(m.get('text', '')),
- 'is_critical': m.get('is_critical', False),
- 'indent': m.get('indent', 0),
- 'classes': m['classes'],
- } for m in messages],
- }), content_type='application/json')
-
-
-class DeployConfirmationView(horizon.forms.ModalFormView, views.StackMixin):
- form_class = forms.DeployOvercloud
- template_name = 'infrastructure/overview/deploy_confirmation.html'
- submit_label = _("Deploy")
-
- def get_context_data(self, **kwargs):
- context = super(DeployConfirmationView,
- self).get_context_data(**kwargs)
- plan = api.tuskar.Plan.get_the_plan(self.request)
-
- context['autogenerated_parameters'] = (
- plan.list_generated_parameters(with_prefix=False).keys())
- return context
-
- def get_success_url(self):
- return reverse(INDEX_URL)
-
-
-class UndeployConfirmationView(horizon.forms.ModalFormView, views.StackMixin):
- form_class = forms.UndeployOvercloud
- template_name = 'infrastructure/overview/undeploy_confirmation.html'
- submit_label = _("Undeploy")
-
- def get_success_url(self):
- return reverse(INDEX_URL)
-
- def get_context_data(self, **kwargs):
- context = super(UndeployConfirmationView,
- self).get_context_data(**kwargs)
- context['stack_id'] = self.get_stack().id
- return context
-
- def get_initial(self, **kwargs):
- initial = super(UndeployConfirmationView, self).get_initial(**kwargs)
- initial['stack_id'] = self.get_stack().id
- return initial
-
-
-class PostDeployInitView(horizon.forms.ModalFormView, views.StackMixin):
- form_class = forms.PostDeployInit
- template_name = 'infrastructure/overview/post_deploy_init.html'
- submit_label = _("Initialize")
-
- def get_success_url(self):
- return reverse(INDEX_URL)
-
- def get_context_data(self, **kwargs):
- context = super(PostDeployInitView,
- self).get_context_data(**kwargs)
- context['stack_id'] = self.get_stack().id
- return context
-
- def get_initial(self, **kwargs):
- initial = super(PostDeployInitView, self).get_initial(**kwargs)
- initial['stack_id'] = self.get_stack().id
- initial['admin_email'] = getattr(self.request.user, 'email', '')
- return initial
-
-
-class ScaleOutView(horizon.forms.ModalFormView, views.StackMixin):
- form_class = forms.ScaleOut
- template_name = "infrastructure/overview/scale_out.html"
- submit_label = _("Deploy Changes")
-
- def get_success_url(self):
- return reverse(INDEX_URL)
-
- def get_form(self, form_class):
- return form_class(self.request, **self.get_form_kwargs())
-
- def get_context_data(self, *args, **kwargs):
- context = super(ScaleOutView, self).get_context_data(*args, **kwargs)
- plan = api.tuskar.Plan.get_the_plan(self.request)
- form = context.get('form')
- roles = [_get_role_data(plan, None, form, role)
- for role in plan.role_list]
- context.update({
- 'roles': roles,
- 'plan': plan,
- })
- return context
-
-
-def _get_openrc_credentials(request):
- plan = api.tuskar.Plan.get_the_plan(request)
- stack = api.heat.Stack.get_by_plan(request, plan)
- no_proxy = [urlparse.urlparse(url).hostname
- for url in stack.dashboard_urls]
- controller_role = plan.get_role_by_name("Controller")
- credentials = dict(tenant_name='admin',
- auth_url=stack.keystone_auth_url,
- admin_password=plan.parameter_value(
- controller_role.parameter_prefix + 'AdminPassword'),
- no_proxy=",".join(no_proxy))
- return credentials
-
-
-def download_overcloudrc_file(request):
- template = 'infrastructure/overview/overcloudrc.sh.template'
- try:
- context = _get_openrc_credentials(request)
-
- response = shortcuts.render(request,
- template,
- context,
- content_type="text/plain")
- response['Content-Disposition'] = ('attachment; '
- 'filename="overcloudrc"')
- response['Content-Length'] = str(len(response.content))
- return response
-
- except Exception as e:
- LOG.exception("Exception in DownloadOvercloudrcForm.")
- messages.error(request, _('Error Downloading RC File: %s') % e)
- return shortcuts.redirect(request.build_absolute_uri())
diff --git a/tuskar_ui/infrastructure/parameters/__init__.py b/tuskar_ui/infrastructure/parameters/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/parameters/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/parameters/forms.py b/tuskar_ui/infrastructure/parameters/forms.py
deleted file mode 100644
index a0e91e63..00000000
--- a/tuskar_ui/infrastructure/parameters/forms.py
+++ /dev/null
@@ -1,281 +0,0 @@
-# -*- 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 json
-import logging
-
-import django.forms
-from django.utils.datastructures import SortedDict
-from django.utils.translation import ugettext_lazy as _
-import horizon.exceptions
-import horizon.forms
-import horizon.messages
-
-from tuskar_ui import api
-import tuskar_ui.forms
-from tuskar_ui.utils import utils
-
-
-LOG = logging.getLogger(__name__)
-
-
-VIRT_TYPE_CHOICES = [
- ('qemu', _("Virtualized (qemu)")),
- ('kvm', _("Baremetal (kvm)")),
-]
-
-CINDER_ISCSI_HELPER_CHOICES = [
- ('tgtadm', _('tgtadm')),
- ('lioadm', _('lioadm')),
-]
-
-
-class ParameterAwareMixin(object):
- parameter = None
-
-
-def parameter_fields(request, prefix=None, read_only=False):
- fields = SortedDict()
- plan = api.tuskar.Plan.get_the_plan(request)
- parameters = plan.parameter_list(include_key_parameters=False)
-
- for p in parameters:
- if prefix and not p.name.startswith(prefix):
- continue
- Field = django.forms.CharField
- field_kwargs = {}
- widget = None
- if read_only:
- if p.hidden:
- widget = tuskar_ui.forms.StaticTextPasswordWidget
- else:
- widget = tuskar_ui.forms.StaticTextWidget
- else:
- if p.hidden:
- widget = django.forms.PasswordInput(render_value=True)
- elif p.parameter_type == 'number':
- Field = django.forms.IntegerField
- elif p.parameter_type == 'boolean':
- Field = django.forms.BooleanField
- elif (p.parameter_type == 'string' and
- p.get_constraint_by_type('allowed_values')):
- Field = django.forms.ChoiceField
- field_kwargs['choices'] = [
- (choice, choice) for choice in
- p.get_constraint_by_type('allowed_values')['definition']]
- elif (p.parameter_type in ['json', 'comma_delimited_list'] or
- 'Certificate' in p.name):
- widget = django.forms.Textarea
-
- fields[p.name] = Field(
- required=False,
- label=_parameter_label(p),
- initial=p.value,
- widget=widget,
- **field_kwargs
- )
- fields[p.name].__class__ = type('ParameterAwareField',
- (ParameterAwareMixin, Field), {})
- fields[p.name].parameter = p
- return fields
-
-
-def _parameter_label(parameter):
- return tuskar_ui.forms.label_with_tooltip(
- parameter.label or utils.de_camel_case(parameter.stripped_name),
- parameter.description)
-
-
-class ServiceConfig(horizon.forms.SelfHandlingForm):
- def __init__(self, *args, **kwargs):
- super(ServiceConfig, self).__init__(*args, **kwargs)
- self.fields.update(parameter_fields(self.request, read_only=True))
-
- def global_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='^(?!.*::)')
-
- def controller_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='Controller-1')
-
- def compute_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='Compute-1')
-
- def block_storage_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='Cinder-Storage-1')
-
- def object_storage_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='Swift-Storage-1')
-
- def ceph_storage_fieldset(self):
- return tuskar_ui.forms.fieldset(self, prefix='Ceph-Storage-1')
-
- def handle():
- pass
-
-
-class AdvancedEditServiceConfig(ServiceConfig):
- def __init__(self, *args, **kwargs):
- super(AdvancedEditServiceConfig, self).__init__(*args, **kwargs)
- self.fields.update(parameter_fields(self.request))
-
- def handle(self, request, data):
- plan = api.tuskar.Plan.get_the_plan(self.request)
-
- # TODO(bcrochet): Commenting this out.
- # For advanced config, we should have a whitelist of which params
- # must be synced across roles.
- # data = self._sync_common_params_across_roles(plan, data)
-
- try:
- plan.patch(request, plan.uuid, data)
- except Exception as e:
- horizon.exceptions.handle(
- request,
- _("Unable to update the service configuration."))
- LOG.exception(e)
- return False
- else:
- horizon.messages.success(
- request,
- _("Service configuration updated."))
- return True
-
- @staticmethod
- def _sync_common_params_across_roles(plan, parameters_dict):
- for (p_key, p_value) in parameters_dict.iteritems():
- for role in plan.role_list:
- role_parameter_key = (role.parameter_prefix +
- api.tuskar.strip_prefix(p_key))
- if role_parameter_key in parameters_dict:
- parameters_dict[role_parameter_key] = p_value
- return parameters_dict
-
-
-class SimpleEditServiceConfig(horizon.forms.SelfHandlingForm):
- virt_type = django.forms.ChoiceField(
- label=_("Deployment Type"),
- choices=VIRT_TYPE_CHOICES,
- required=True,
- help_text=_('If you are testing OpenStack in a virtual machine, '
- 'you must configure Compute to use qemu without KVM '
- 'and hardware virtualization.'))
- neutron_public_interface = django.forms.CharField(
- label=_("Public Interface"),
- required=True,
- initial='eth0',
- help_text=_('What interface to bridge onto br-ex for network nodes. '
- 'If you are testing OpenStack in a virtual machine'
- 'you must configure interface to eth0.'))
- snmp_password = django.forms.CharField(
- label=_("SNMP Password"),
- required=True,
- help_text=_('The user password for SNMPd with readonly '
- 'rights running on all Overcloud nodes'),
- widget=django.forms.PasswordInput(render_value=True))
- cloud_name = django.forms.CharField(
- label=_("Cloud name"),
- required=True,
- initial="overcloud",
- help_text=_('The DNS name of this cloud. '
- 'E.g. ci-overcloud.tripleo.org'))
- cinder_iscsi_helper = django.forms.ChoiceField(
- label=_("Cinder ISCSI helper"),
- choices=CINDER_ISCSI_HELPER_CHOICES,
- required=True,
- help_text=_('The iSCSI helper to use with cinder.'))
- ntp_server = django.forms.CharField(
- label=_("NTP server"),
- required=False,
- initial="",
- help_text=_('Address of the NTP server. If blank, public NTP servers '
- 'will be used.'))
- extra_config = django.forms.CharField(
- label=_("Extra Config"),
- required=False,
- widget=django.forms.Textarea(attrs={'rows': 2}),
- help_text=("Additional configuration to inject into the cluster."
- "The data format of this field is JSON."
- "See http://git.io/PuwLXQ for more information."))
-
- def clean_extra_config(self):
- data = self.cleaned_data['extra_config']
- try:
- json.loads(data)
- except Exception as json_error:
- raise django.forms.ValidationError(
- _("%(err_msg)s"), params={'err_msg': json_error.message})
- return data
-
- @staticmethod
- def _load_additional_parameters(plan, data, form_key, param_name):
- params = {}
- param_value = data.get(form_key)
- # Set the same parameter and value in all roles.
- for role in plan.role_list:
- key = role.parameter_prefix + param_name
- if key in [parameter.name
- for parameter in role.parameter_list(plan)]:
- params[key] = param_value
-
- return params
-
- def handle(self, request, data):
- plan = api.tuskar.Plan.get_the_plan(self.request)
- compute_prefix = plan.get_role_by_name('Compute').parameter_prefix
- controller_prefix = plan.get_role_by_name(
- 'Controller').parameter_prefix
- cinder_prefix = plan.get_role_by_name(
- 'Cinder-Storage').parameter_prefix
-
- virt_type = data.get('virt_type')
- neutron_public_interface = data.get('neutron_public_interface')
- cloud_name = data.get('cloud_name')
- cinder_iscsi_helper = data.get('cinder_iscsi_helper')
- ntp_server = data.get('ntp_server')
-
- parameters = {
- compute_prefix + 'NovaComputeLibvirtType': virt_type,
- controller_prefix + 'CinderISCSIHelper': cinder_iscsi_helper,
- cinder_prefix + 'CinderISCSIHelper': cinder_iscsi_helper,
- controller_prefix + 'CloudName': cloud_name,
- controller_prefix + 'NeutronPublicInterface':
- neutron_public_interface,
- compute_prefix + 'NeutronPublicInterface':
- neutron_public_interface,
- controller_prefix + 'NtpServer':
- ntp_server,
- compute_prefix + 'NtpServer':
- ntp_server,
- }
-
- parameters.update(self._load_additional_parameters(
- plan, data,
- 'snmp_password', 'SnmpdReadonlyUserPassword'))
- parameters.update(self._load_additional_parameters(
- plan, data,
- 'extra_config', 'ExtraConfig'))
-
- try:
- plan.patch(request, plan.uuid, parameters)
- except Exception as e:
- horizon.exceptions.handle(
- request,
- _("Unable to update the service configuration."))
- LOG.exception(e)
- return False
- else:
- horizon.messages.success(
- request,
- _("Service configuration updated."))
- return True
diff --git a/tuskar_ui/infrastructure/parameters/panel.py b/tuskar_ui/infrastructure/parameters/panel.py
deleted file mode 100644
index 3c085ae0..00000000
--- a/tuskar_ui/infrastructure/parameters/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class Parameters(horizon.Panel):
- name = _("Service Configuration")
- slug = "parameters"
-
-
-dashboard.Infrastructure.register(Parameters)
diff --git a/tuskar_ui/infrastructure/parameters/templates/parameters/_simple_service_config.html b/tuskar_ui/infrastructure/parameters/templates/parameters/_simple_service_config.html
deleted file mode 100644
index f8788d2f..00000000
--- a/tuskar_ui/infrastructure/parameters/templates/parameters/_simple_service_config.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}configuration_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:parameters:simple_service_configuration' %}{% endblock %}
-
-{% block modal_id %}provision_modal{% endblock %}
-{% block modal-header %}{% trans "Service Configuration" %}{% endblock %}
-
-{% block modal-body %}
-<div class="left">
- <fieldset>
- {% include "horizon/common/_form_fields.html" %}
- </fieldset>
-</div>
-<div class="right">
- <h3>{% trans "Description:" %}</h3>
- <p>
- {% trans "Configure values that cannot be defaulted" %}
- </p>
- <p>
- {% trans "These values cannot be defaulted. Please choose values for them and save them before you deploy your overcloud." %}
- </p>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/parameters/templates/parameters/advanced_service_config.html b/tuskar_ui/infrastructure/parameters/templates/parameters/advanced_service_config.html
deleted file mode 100644
index 9f9955be..00000000
--- a/tuskar_ui/infrastructure/parameters/templates/parameters/advanced_service_config.html
+++ /dev/null
@@ -1,88 +0,0 @@
-{% extends "infrastructure/base.html" %}
-{% load i18n %}
-{% load url from future %}
-{% block title %}{% trans "Advanced Service Configuration" %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Advanced Service Configuration') %}
-{% endblock %}
-
-{% block main %}
- <div class="row">
- <form id="{% block form_id %}{{ form_id }}{% endblock %}"
- name="{% block form_name %}{% endblock %}"
- autocomplete="{% block autocomplete %}{% if form.no_autocomplete %}off{% endif %}{% endblock %}"
- class="{% block form_class %}{% endblock %} form-horizontal"
- action="{% block form_action %}{{ submit_url }}{% endblock %}"
- method="{% block form-method %}POST{% endblock %}"
- {% block form_validation %}{% endblock %}
- {% if add_to_field %}data-add-to-field="{{ add_to_field }}"{% endif %} {% block form_attrs %}{% endblock %}>{% csrf_token %}
- <div class="col-sm-12 page_form_actions">
- <div class="pull-right">
- <a href="{% block cancel_url %}{{ cancel_url }}{% endblock %}"
- class="btn btn-default cancel">
- {{ cancel_label }}
- </a>
- <input class="btn btn-primary" type="submit" value="{{ submit_label }}">
- </div>
- </div>
- {% include 'horizon/common/_form_errors.html' with form=form %}
- <div class="col-md-2">
- <ul class="nav nav-pills nav-stacked nav-arrow" role="tablist">
- <li class="active"><a href="#global" role="tab" data-toggle="tab">{% trans "Global" %}</a></li>
- <li><a href="#controller" role="tab" data-toggle="tab">{% trans "Controller" %}</a></li>
- <li><a href="#compute" role="tab" data-toggle="tab">{% trans "Compute" %}</a></li>
- <li><a href="#block-storage" role="tab" data-toggle="tab">{% trans "Block Storage" %}</a></li>
- <li><a href="#object-storage" role="tab" data-toggle="tab">{% trans "Object Storage" %}</a></li>
- <li><a href="#ceph-storage" role="tab" data-toggle="tab">{% trans "Ceph Storage" %}</a></li>
- </ul>
- </div>
- <div class="col-md-10">
- <div class="tab-content panel panel-default configuration-panel">
- <div class="tab-pane active" id="global">
- {% for field in form.global_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="controller">
- {% for field in form.controller_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="compute">
- {% for field in form.compute_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="block-storage">
- {% for field in form.block_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="object-storage">
- {% for field in form.object_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="ceph-storage">
- {% for field in form.ceph_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- </div>
- </div>
- </form>
- </div>
-
- <script type="text/javascript">
- (window.$ || window.addHorizonLoadEvent)(function () {
- $(document).tooltip('hide'); // prevent horizon from adding tooltip
- $('a.help-icon').click(function () {
- return false;
- }).popover({
- trigger: 'focus',
- placement: 'right'
- });
- });
- </script>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/parameters/templates/parameters/index.html b/tuskar_ui/infrastructure/parameters/templates/parameters/index.html
deleted file mode 100644
index 1671e707..00000000
--- a/tuskar_ui/infrastructure/parameters/templates/parameters/index.html
+++ /dev/null
@@ -1,76 +0,0 @@
-{% extends "infrastructure/base.html" %}
-{% load i18n %}
-{% load url from future %}
-{% block title %}{% trans "Service Configuration" %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Service Configuration') %}
-{% endblock %}
-
-{% block main %}
- <div class="row">
- <form class="form-horizontal">
- {% include 'horizon/common/_form_errors.html' with form=form %}
- <div class="col-md-2">
- <ul class="nav nav-pills nav-stacked nav-arrow" role="tablist">
- <li class="active"><a href="#global" role="tab" data-toggle="tab">{% trans "Global" %}</a></li>
- <li><a href="#controller" role="tab" data-toggle="tab">{% trans "Controller" %}</a></li>
- <li><a href="#compute" role="tab" data-toggle="tab">{% trans "Compute" %}</a></li>
- <li><a href="#block-storage" role="tab" data-toggle="tab">{% trans "Block Storage" %}</a></li>
- <li><a href="#object-storage" role="tab" data-toggle="tab">{% trans "Object Storage" %}</a></li>
- <li><a href="#ceph-storage" role="tab" data-toggle="tab">{% trans "Ceph Storage" %}</a></li>
- </ul>
- </div>
- <div class="col-md-10">
- <div class="tab-content panel panel-default configuration-panel">
- <div class="tab-pane active" id="global">
- {% for field in form.global_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="controller">
- {% for field in form.controller_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="compute">
- {% for field in form.compute_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="block-storage">
- {% for field in form.block_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="object-storage">
- {% for field in form.object_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- <div class="tab-pane" id="ceph-storage">
- {% for field in form.ceph_storage_fieldset %}
- {% include 'horizon/common/_horizontal_field.html' with field=field %}
- {% endfor %}
- </div>
- </div>
- </div>
- </form>
- </div>
-
- <script type="text/javascript">
- (window.$ || window.addHorizonLoadEvent)(function () {
- $(document).tooltip('hide'); // prevent horizon from adding tooltip
- $('a.help-icon').click(function () {
- return false;
- }).popover({
- trigger: 'focus',
- placement: 'right'
- });
- $('a.password-button').popover({
- trigger: 'click',
- placement: 'right'
- });
- });
- </script>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/parameters/templates/parameters/simple_service_config.html b/tuskar_ui/infrastructure/parameters/templates/parameters/simple_service_config.html
deleted file mode 100644
index 803fc3bb..00000000
--- a/tuskar_ui/infrastructure/parameters/templates/parameters/simple_service_config.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends "base.html" %}
-{% load i18n %}
-{% block title %}{% trans "Simple Service Configuration" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Simple Service Configuration") %}
-{% endblock %}
-
-{% block main %}
- {% include "infrastructure/parameters/_simple_service_config.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/parameters/tests.py b/tuskar_ui/infrastructure/parameters/tests.py
deleted file mode 100644
index 0d008884..00000000
--- a/tuskar_ui/infrastructure/parameters/tests.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# -*- 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 contextlib
-
-from django.core import urlresolvers
-from mock import patch, call, ANY # noqa
-from openstack_dashboard.test.test_data import utils
-
-from tuskar_ui import api
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import tuskar_data
-
-
-INDEX_URL = urlresolvers.reverse(
- 'horizon:infrastructure:parameters:index')
-SIMPLE_SERVICE_CONFIG_URL = urlresolvers.reverse(
- 'horizon:infrastructure:parameters:simple_service_configuration')
-ADVANCED_SERVICE_CONFIG_URL = urlresolvers.reverse(
- 'horizon:infrastructure:parameters:advanced_service_configuration')
-
-TEST_DATA = utils.TestDataContainer()
-tuskar_data.data(TEST_DATA)
-
-
-class ParametersTest(test.BaseAdminViewTests):
-
- def test_index(self):
- plans = [api.tuskar.Plan(plan)
- for plan in self.tuskarclient_plans.list()]
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- ):
- res = self.client.get(INDEX_URL)
-
- self.assertTemplateUsed(res, 'infrastructure/parameters/index.html')
-
- def test_simple_service_config_get(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- role = api.tuskar.Role(self.tuskarclient_roles.first())
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Plan.get_role_by_name',
- return_value=role),
- ):
- res = self.client.get(SIMPLE_SERVICE_CONFIG_URL)
- self.assertTemplateUsed(
- res, 'infrastructure/parameters/simple_service_config.html')
-
- def test_advanced_service_config_post(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
- parameters = [api.tuskar.Parameter(p, plan=self)
- for p in plan.parameters]
-
- data = {p.name: unicode(p.value) for p in parameters}
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Plan.role_list',
- return_value=roles),
- patch('tuskar_ui.api.tuskar.Plan.parameter_list',
- return_value=parameters),
- patch('tuskar_ui.api.tuskar.Plan.patch',
- return_value=plan),
- ) as (get_the_plan, role_list, parameter_list, plan_patch):
- res = self.client.post(ADVANCED_SERVICE_CONFIG_URL, data)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- plan_patch.assert_called_once_with(ANY, plan.uuid, data)
-
- def test_simple_service_config_post(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- roles = [api.tuskar.Role(role) for role in
- self.tuskarclient_roles.list()]
- plan.role_list = roles
-
- data = {
- 'virt_type': 'qemu',
- 'snmp_password': 'password',
- 'cinder_iscsi_helper': 'lioadm',
- 'cloud_name': 'cloud_name',
- 'neutron_public_interface': 'eth0',
- 'extra_config': '{}'
- }
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Plan.patch',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Plan.get_role_by_name',
- return_value=roles[0]),
- ) as (get_the_plan, plan_patch, get_role_by_name):
- res = self.client.post(SIMPLE_SERVICE_CONFIG_URL, data)
-
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- plan_patch.assert_called_once_with(ANY, plan.uuid, {
- 'Controller-1::CloudName': u'cloud_name',
- 'Controller-1::SnmpdReadonlyUserPassword': u'password',
- 'Controller-1::NeutronPublicInterface': u'eth0',
- 'Controller-1::CinderISCSIHelper': u'lioadm',
- 'Controller-1::NovaComputeLibvirtType': u'qemu',
- 'Compute-1::SnmpdReadonlyUserPassword': u'password',
- 'Controller-1::NtpServer': u'',
- 'Controller-1::ExtraConfig': u'{}',
- 'Compute-1::ExtraConfig': u'{}',
- 'Block Storage-1::ExtraConfig': u'{}',
- 'Object Storage-1::ExtraConfig': u'{}'})
diff --git a/tuskar_ui/infrastructure/parameters/urls.py b/tuskar_ui/infrastructure/parameters/urls.py
deleted file mode 100644
index efadf7ea..00000000
--- a/tuskar_ui/infrastructure/parameters/urls.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.parameters import views
-
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^simple-service-config$',
- views.SimpleServiceConfigView.as_view(),
- name='simple_service_configuration'),
- urls.url(r'^advanced-service-config$',
- views.AdvancedServiceConfigView.as_view(),
- name='advanced_service_configuration'),
-)
diff --git a/tuskar_ui/infrastructure/parameters/views.py b/tuskar_ui/infrastructure/parameters/views.py
deleted file mode 100644
index b7748299..00000000
--- a/tuskar_ui/infrastructure/parameters/views.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# -*- 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.
-
-from django.core.urlresolvers import reverse
-from django.core.urlresolvers import reverse_lazy
-from django.utils.translation import ugettext_lazy as _
-import horizon.forms
-import horizon.tables
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.parameters import forms
-
-
-class SimpleServiceConfigView(horizon.forms.ModalFormView):
- form_class = forms.SimpleEditServiceConfig
- success_url = reverse_lazy('horizon:infrastructure:parameters:index')
- submit_label = _("Save Configuration")
- template_name = "infrastructure/parameters/simple_service_config.html"
-
- def get_initial(self):
- plan = api.tuskar.Plan.get_the_plan(self.request)
- compute_prefix = plan.get_role_by_name('Compute').parameter_prefix
- controller_prefix = plan.get_role_by_name(
- 'Controller').parameter_prefix
-
- cinder_iscsi_helper = plan.parameter_value(
- controller_prefix + 'CinderISCSIHelper')
- cloud_name = plan.parameter_value(
- controller_prefix + 'CloudName')
- extra_config = plan.parameter_value(
- controller_prefix + 'ExtraConfig')
- neutron_public_interface = plan.parameter_value(
- controller_prefix + 'NeutronPublicInterface')
- ntp_server = plan.parameter_value(
- controller_prefix + 'NtpServer')
- snmp_password = plan.parameter_value(
- controller_prefix + 'SnmpdReadonlyUserPassword')
- virt_type = plan.parameter_value(
- compute_prefix + 'NovaComputeLibvirtType')
- return {
- 'cinder_iscsi_helper': cinder_iscsi_helper,
- 'cloud_name': cloud_name,
- 'neutron_public_interface': neutron_public_interface,
- 'ntp_server': ntp_server,
- 'extra_config': extra_config,
- 'neutron_public_interface': neutron_public_interface,
- 'snmp_password': snmp_password,
- 'virt_type': virt_type}
-
-
-class IndexView(horizon.forms.ModalFormView):
- form_class = forms.ServiceConfig
- form_id = "service_config"
- template_name = "infrastructure/parameters/index.html"
-
- def get_initial(self):
- self.plan = api.tuskar.Plan.get_the_plan(self.request)
- self.parameters = self.plan.parameter_list(
- include_key_parameters=False)
- return {p.name: p.value for p in self.parameters}
-
- def get_context_data(self, **kwargs):
- context = super(IndexView, self).get_context_data(**kwargs)
- advanced_edit_action = {
- 'name': _('Advanced Configuration'),
- 'url': reverse('horizon:infrastructure:parameters:'
- 'advanced_service_configuration'),
- 'icon': 'fa-pencil',
- 'ajax_modal': False,
- }
- simplified_edit_action = {
- 'name': _('Simplified Configuration'),
- 'url': reverse('horizon:infrastructure:parameters:'
- 'simple_service_configuration'),
- 'icon': 'fa-pencil-square-o',
- 'ajax_modal': True,
- }
- context['header_actions'] = [advanced_edit_action,
- simplified_edit_action]
- return context
-
-
-class AdvancedServiceConfigView(IndexView):
- form_class = forms.AdvancedEditServiceConfig
- form_id = "advanced_service_config"
- success_url = reverse_lazy('horizon:infrastructure:parameters:index')
- submit_label = _("Save Configuration")
- submit_url = reverse_lazy('horizon:infrastructure:parameters:'
- 'advanced_service_configuration')
- template_name = "infrastructure/parameters/advanced_service_config.html"
-
- def get_context_data(self, **kwargs):
- context = super(AdvancedServiceConfigView,
- self) .get_context_data(**kwargs)
- context['header_actions'] = []
- return context
diff --git a/tuskar_ui/infrastructure/roles/__init__.py b/tuskar_ui/infrastructure/roles/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/roles/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/roles/panel.py b/tuskar_ui/infrastructure/roles/panel.py
deleted file mode 100644
index 3a2041f6..00000000
--- a/tuskar_ui/infrastructure/roles/panel.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-import horizon
-
-from tuskar_ui.infrastructure import dashboard
-
-
-class Roles(horizon.Panel):
- name = _("Deployment Roles")
- slug = "roles"
-
-
-dashboard.Infrastructure.register(Roles)
diff --git a/tuskar_ui/infrastructure/roles/tables.py b/tuskar_ui/infrastructure/roles/tables.py
deleted file mode 100644
index 8da446ca..00000000
--- a/tuskar_ui/infrastructure/roles/tables.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- 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.
-
-from django.utils.translation import ugettext_lazy as _
-from horizon import tables
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.nodes import tables as nodes_tables
-
-
-class UpdateRole(tables.LinkAction):
- name = "update"
- verbose_name = _("Edit Role")
- url = "horizon:infrastructure:roles:update"
- classes = ("ajax-modal",)
- icon = "pencil"
-
- def allowed(self, request, datum):
- plan = api.tuskar.Plan.get_the_plan(request)
-
- if datum.id in [role.id for role in plan.role_list]:
- return True
- return False
-
-
-class RolesTable(tables.DataTable):
-
- name = tables.Column('name',
- link="horizon:infrastructure:roles:detail",
- verbose_name=_("Role"))
- flavor = tables.Column('flavor',
- verbose_name=_("Flavor"))
- image = tables.Column('image',
- verbose_name=_("Image"))
-
- def get_object_id(self, datum):
- return datum.uuid
-
- class Meta(object):
- name = "roles"
- verbose_name = _("Deployment Roles")
- table_actions = ()
- row_actions = (UpdateRole,)
- template = "horizon/common/_enhanced_data_table.html"
-
-
-class NodeTable(nodes_tables.ProvisionedNodesTable):
-
- class Meta(object):
- name = "nodetable"
- verbose_name = _("Nodes")
- hidden_title = False
- table_actions = ()
- row_actions = ()
- template = "horizon/common/_enhanced_data_table.html"
diff --git a/tuskar_ui/infrastructure/roles/templates/roles/config.html b/tuskar_ui/infrastructure/roles/templates/roles/config.html
deleted file mode 100644
index 854871b4..00000000
--- a/tuskar_ui/infrastructure/roles/templates/roles/config.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<noscript><h3>{{ step }}</h3></noscript>
-<div class="row">
- <div class="col-sm-12">
- <div class="form form-horizontal" id="role-config-form">
- {% include "horizon/common/_horizontal_fields.html" %}
- </div>
- </div>
-</div>
-<script type="text/javascript">
- (window.$ || window.addHorizonLoadEvent)(function () {
- $(document).tooltip('hide'); // Prevent Horizon from adding tooltip.
- $('#role-config-form a.help-icon').click(function () {
- return false;
- }).popover({
- trigger: 'focus',
- placement: 'right',
- container: '#role-config-form'
- });
- $('#role-config-form a.password-button').popover({
- trigger: 'click',
- placement: 'right'
- });
- });
-</script>
diff --git a/tuskar_ui/infrastructure/roles/templates/roles/detail.html b/tuskar_ui/infrastructure/roles/templates/roles/detail.html
deleted file mode 100644
index 0839ca0d..00000000
--- a/tuskar_ui/infrastructure/roles/templates/roles/detail.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% load url from future %}
-{% block title %}{% trans 'Role' %}: {{ role.name }}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_domain_page_header.html' with title=_('Deployment Role: ')|add:role.name %}
-{% endblock page_header %}
-
-{% block main %}
-
-<div class="row">
- <div class="col-md-4">
- <h3>{% trans "Properties" %}</h3>
- <h4 class="info">{% blocktrans count counter=nodes|length %}{{ counter }} instance{% plural %}{{ counter }} instances{% endblocktrans %}</h4>
- <dl class="dl-horizontal dl-horizontal-left">
- <dt>{% trans 'Flavor' %}</dt>
- {% if flavor %}
- <dd><em>{{ flavor.name }}</em> {{ flavor.get_keys.cpu_arch }} | {{ flavor.vcpus }} {% trans "CPU" %} | {{ flavor.ram }} {% trans "MB RAM" %} | {{ flavor.disk }} {% trans "GB HDD" %}</dd>
- {% else %}
- <dd>{% trans 'No flavor associated' %}</dd>
- {% endif %}
- <dt>{% trans 'Image' %}</dt>
- {% if image %}
- <dd>{{ image.name }}</dd>
- {% else %}
- <dd>{% trans 'No image associated' %}</dd>
- {% endif %}
- </dl>
- </div>
- <div class="col-md-8">
- <h3>{% trans "Performance & Metrics" %}</h3>
- {% url 'horizon:infrastructure:roles:performance' role.uuid as node_perf_url %}
- {% include "infrastructure/_performance_chart_box.html" with meter_conf=meter_conf node_perf_url=node_perf_url col_size=4 %}
- </div>
-</div>
-
-{{ table.render }}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/roles/templates/roles/index.html b/tuskar_ui/infrastructure/roles/templates/roles/index.html
deleted file mode 100644
index b17ae230..00000000
--- a/tuskar_ui/infrastructure/roles/templates/roles/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans 'Deployment Roles' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Deployment Roles') %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-xs-12">
- {{ table.render }}
- </div>
-</div>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/roles/templates/roles/info.html b/tuskar_ui/infrastructure/roles/templates/roles/info.html
deleted file mode 100644
index 2909bd1c..00000000
--- a/tuskar_ui/infrastructure/roles/templates/roles/info.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<noscript><h3>{{ step }}</h3></noscript>
-<div class="row">
- <div class="col-sm-8">
- <div class="form form-horizontal">
- {% include "horizon/common/_horizontal_fields.html" %}
- </div>
- </div>
- <div class="col-sm-4">
- {{ step.get_help_text }}
- </div>
-</div>
diff --git a/tuskar_ui/infrastructure/roles/tests.py b/tuskar_ui/infrastructure/roles/tests.py
deleted file mode 100644
index d9ee2057..00000000
--- a/tuskar_ui/infrastructure/roles/tests.py
+++ /dev/null
@@ -1,166 +0,0 @@
-# -*- 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 contextlib
-
-from django.core import urlresolvers
-from openstack_dashboard.test.test_data import utils
-from mock import patch, call # noqa
-
-from tuskar_ui import api
-from tuskar_ui.test import helpers as test
-from tuskar_ui.test.test_data import flavor_data
-from tuskar_ui.test.test_data import heat_data
-from tuskar_ui.test.test_data import node_data
-from tuskar_ui.test.test_data import tuskar_data
-
-
-INDEX_URL = urlresolvers.reverse(
- 'horizon:infrastructure:roles:index')
-DETAIL_URL = urlresolvers.reverse(
- 'horizon:infrastructure:roles:detail', args=('role-1',))
-UPDATE_URL = urlresolvers.reverse(
- 'horizon:infrastructure:roles:update', args=('role-1',))
-
-TEST_DATA = utils.TestDataContainer()
-flavor_data.data(TEST_DATA)
-node_data.data(TEST_DATA)
-heat_data.data(TEST_DATA)
-tuskar_data.data(TEST_DATA)
-
-
-class RolesTest(test.BaseAdminViewTests):
-
- def test_index_get(self):
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
- plans = [api.tuskar.Plan(plan)
- for plan in self.tuskarclient_plans.list()]
- flavor = self.novaclient_flavors.first()
- images = self.glanceclient_images.list()
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images]),
- patch('tuskar_ui.api.flavor.Flavor.get_by_name',
- return_value=flavor)):
- res = self.client.get(INDEX_URL)
-
- self.assertTemplateUsed(res, 'infrastructure/roles/index.html')
-
- def test_detail_get(self):
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
- plans = [api.tuskar.Plan(plan)
- for plan in self.tuskarclient_plans.list()]
- flavor = self.novaclient_flavors.first()
- images = self.glanceclient_images.list()
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.events',
- return_value=[]),
- patch('tuskar_ui.api.heat.Stack.resources',
- return_value=[]),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images]),
- patch('tuskar_ui.api.flavor.Flavor.get_by_name',
- return_value=flavor)):
- res = self.client.get(DETAIL_URL)
-
- self.assertTemplateUsed(
- res, 'infrastructure/roles/detail.html')
-
- def test_update_get(self):
- roles = [api.tuskar.Role(role)
- for role in self.tuskarclient_roles.list()]
- plans = [api.tuskar.Plan(plan)
- for plan in self.tuskarclient_plans.list()]
- flavors = self.novaclient_flavors.list()
- images = self.glanceclient_images.list()
- stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Role.list',
- return_value=roles),
- patch('tuskar_ui.api.heat.Stack.get_by_plan',
- return_value=stack),
- patch('tuskar_ui.api.heat.Stack.events',
- return_value=[]),
- patch('tuskar_ui.api.heat.Stack.resources',
- return_value=[]),
- patch('tuskar_ui.api.tuskar.Plan.list',
- return_value=plans),
- patch('openstack_dashboard.api.glance.image_get',
- return_value=images[0]),
- patch('tuskar_ui.api.flavor.Flavor.list',
- return_value=flavors),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images])):
-
- res = self.client.get(UPDATE_URL)
-
- # Check that the expected fields are in the form:
- self.assertIn('id="id_flavor" name="flavor"', res.content)
- self.assertIn('id="id_image" name="image"', res.content)
- self.assertIn('flavor-1', res.content)
- self.assertIn('flavor-2', res.content)
-
- def test_update_post(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- role = api.tuskar.Role(self.tuskarclient_roles.first())
- flavors = self.novaclient_flavors.list()
- images = self.glanceclient_images.list()
-
- data = {
- 'name': 'controller',
- 'description': 'The controller node role.',
- 'flavor': self.novaclient_flavors.first().name,
- 'image': self.glanceclient_images.first().name,
- 'nodes': '0',
- }
-
- with contextlib.nested(
- patch('tuskar_ui.api.flavor.Flavor.list',
- return_value=flavors),
- patch('openstack_dashboard.api.glance.image_list_detailed',
- return_value=[images]),
- patch('openstack_dashboard.api.glance.image_get',
- return_value=images[0]),
- patch('tuskar_ui.api.tuskar.Role.get',
- return_value=role),
- patch('tuskar_ui.api.tuskar.Plan.patch',
- return_value=plan),
- patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
- return_value=plan)) as mocks:
-
- mock_patch = mocks[4]
- res = self.client.post(UPDATE_URL, data)
- self.assertRedirectsNoFollow(res, INDEX_URL)
-
- self.assertEqual(len(mock_patch.call_args_list), 1)
- args = mock_patch.call_args_list[0][0]
- self.assertEqual(args[1], plan.id)
- self.assertEqual(args[2]['Controller-1::Flavor'], u'flavor-1')
- self.assertEqual(args[2]['Controller-1::Image'], u'overcloud-full')
diff --git a/tuskar_ui/infrastructure/roles/urls.py b/tuskar_ui/infrastructure/roles/urls.py
deleted file mode 100644
index 9dfdb778..00000000
--- a/tuskar_ui/infrastructure/roles/urls.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- 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.
-
-from django.conf import urls
-
-from tuskar_ui.infrastructure.roles import views
-
-
-urlpatterns = urls.patterns(
- '',
- urls.url(r'^$', views.IndexView.as_view(), name='index'),
- urls.url(r'^(?P<role_id>[^/]+)/$', views.DetailView.as_view(),
- name='detail'),
- urls.url(r'^(?P<role_id>[^/]+)/edit$', views.UpdateView.as_view(),
- name='update'),
- urls.url(r'^(?P<role_id>[^/]+)/performance/$',
- views.PerformanceView.as_view(), name='performance'),
-)
diff --git a/tuskar_ui/infrastructure/roles/views.py b/tuskar_ui/infrastructure/roles/views.py
deleted file mode 100644
index b5cb5801..00000000
--- a/tuskar_ui/infrastructure/roles/views.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# -*- 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 json
-
-from django.core.urlresolvers import reverse
-from django import http
-from django.utils.translation import ugettext_lazy as _
-from django.views.generic import base
-from glanceclient import exc as glance_exc
-from horizon import tables as horizon_tables
-from horizon import utils
-from horizon import workflows
-from openstack_dashboard.api import base as api_base
-
-from tuskar_ui import api
-from tuskar_ui.infrastructure.roles import tables
-from tuskar_ui.infrastructure.roles import workflows as role_workflows
-from tuskar_ui.infrastructure import views
-from tuskar_ui.utils import metering as metering_utils
-
-
-INDEX_URL = 'horizon:infrastructure:roles:index'
-
-
-class IndexView(views.ItemCountMixin, horizon_tables.DataTableView):
- table_class = tables.RolesTable
- template_name = "infrastructure/roles/index.html"
-
- @utils.memoized.memoized
- def get_data(self):
- roles = api.tuskar.Role.list(self.request)
- plan = api.tuskar.Plan.get_the_plan(self.request)
- for role in roles:
- role_flavor = role.flavor(plan)
- try:
- role_image = role.image(plan)
- except glance_exc.HTTPNotFound:
- # Glance returns a 404 if the image doesn't exist
- role_image = None
- if role_flavor:
- role.flavor = role_flavor.name
- else:
- role.flavor = _('Unknown')
- if role_image:
- role.image = role_image.name
- else:
- role.image = _('Unknown')
-
- return roles
-
-
-class DetailView(horizon_tables.DataTableView, views.RoleMixin,
- views.StackMixin):
- table_class = tables.NodeTable
- template_name = 'infrastructure/roles/detail.html'
-
- @utils.memoized.memoized
- def _get_nodes(self, stack, role):
- resources = stack.resources(role=role, with_joins=True)
- nodes = [r.node for r in resources]
- for node in nodes:
- try:
- resource = api.heat.Resource.get_by_node(self.request, node)
- except LookupError:
- node.role_name = '-'
- else:
- node.role_name = resource.role.name
- node.role_id = resource.role.id
- node.stack_id = resource.stack.id
-
- return nodes
-
- def get_data(self):
- redirect = reverse(INDEX_URL)
- stack = self.get_stack()
- if stack:
- role = self.get_role(redirect)
- return self._get_nodes(stack, role)
- return []
-
- def get_context_data(self, **kwargs):
- context = super(DetailView, self).get_context_data(**kwargs)
- redirect = reverse(INDEX_URL)
-
- plan = api.tuskar.Plan.get_the_plan(self.request)
- stack = self.get_stack()
- role = self.get_role(redirect)
-
- context['role'] = role
- if stack:
- context['nodes'] = self._get_nodes(stack, role)
- else:
- context['nodes'] = []
- context['flavor'] = role.flavor(plan)
- context['image'] = role.image(plan)
-
- if stack:
- if api_base.is_service_enabled(self.request, 'metering'):
- # Meter configuration in the following format:
- # (meter label, url part, barchart (True/False))
- context['meter_conf'] = (
- (_('System Load'),
- metering_utils.url_part('hardware.cpu.load.1min', False),
- None),
- (_('CPU Utilization'),
- metering_utils.url_part('hardware.system_stats.cpu.util',
- True),
- '100'),
- (_('Swap Utilization'),
- metering_utils.url_part('hardware.memory.swap.util',
- True),
- '100'),
- )
- return context
-
-
-class UpdateView(workflows.WorkflowView, views.StackMixin, views.RoleMixin):
- workflow_class = role_workflows.UpdateRole
-
- def get_initial(self):
- plan = self.get_plan()
- role = self.get_role()
-
- stack = self.get_stack()
- if stack:
- resources = stack.resources(role=role, with_joins=True)
- role_nodes = len(resources)
- else:
- role_nodes = 0
-
- role_flavor = role.flavor(plan)
- role_flavor = '' if role_flavor is None else role_flavor.name
-
- role_image = role.image(plan)
- role_image = '' if role_image is None else role_image.name
-
- free_nodes = len(api.node.Node.list(self.request, associated=False,
- maintenance=False))
- available_nodes = role_nodes + free_nodes
-
- return {
- 'role_id': role.id,
- 'name': role.name,
- 'flavor': role_flavor,
- 'image': role_image,
- 'nodes': role_nodes,
- 'available_nodes': available_nodes,
- }
-
-
-class PerformanceView(base.TemplateView, views.RoleMixin, views.StackMixin):
- def get(self, request, *args, **kwargs):
- meter = request.GET.get('meter')
- date_options = request.GET.get('date_options')
- date_from = request.GET.get('date_from')
- date_to = request.GET.get('date_to')
- stats_attr = request.GET.get('stats_attr', 'avg')
- barchart = bool(request.GET.get('barchart'))
-
- role = self.get_role()
- stack = self.get_stack()
- instances = stack.resources(role=role, with_joins=True)
-
- if instances:
- instance_uuids = [i.physical_resource_id for i in instances]
- json_output = metering_utils.get_nodes_stats(
- request=request,
- node_uuid=None,
- instance_uuids=instance_uuids,
- meter=meter,
- date_options=date_options,
- date_from=date_from,
- date_to=date_to,
- stats_attr=stats_attr,
- barchart=barchart)
- else:
- json_output = None
-
- return http.HttpResponse(json.dumps(json_output),
- content_type='application/json')
diff --git a/tuskar_ui/infrastructure/roles/workflows.py b/tuskar_ui/infrastructure/roles/workflows.py
deleted file mode 100644
index b9786112..00000000
--- a/tuskar_ui/infrastructure/roles/workflows.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# -*- 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.
-from django.core.urlresolvers import reverse_lazy
-import django.forms
-from django.utils.translation import ugettext_lazy as _
-from horizon import exceptions
-from horizon import forms
-from horizon import workflows
-from openstack_dashboard.api import glance
-
-from tuskar_ui import api
-import tuskar_ui.forms
-from tuskar_ui.infrastructure.flavors import utils
-from tuskar_ui.infrastructure.parameters import forms as parameters_forms
-from tuskar_ui.utils import utils as tuskar_utils
-
-
-class UpdateRoleInfoAction(workflows.Action):
- # TODO(rdopiera) Make the name and description editable.
- name = forms.CharField(
- label=_("Name"),
- required=False,
- widget=tuskar_ui.forms.StaticTextWidget
- )
- description = forms.CharField(
- label=_("Description"),
- required=False,
- widget=tuskar_ui.forms.StaticTextWidget
- )
- flavor = forms.ChoiceField(
- label=_("Flavor"),
- )
- image = forms.ChoiceField(
- label=_("Image"),
- )
- nodes = forms.IntegerField(
- label=_("Number of Nodes"),
- required=False,
- initial=0,
- )
-
- class Meta(object):
- name = _("Overall Settings")
- slug = 'update_role_info'
- help_text = _("Edit the role details.")
-
- def __init__(self, request, context, *args, **kwargs):
- super(UpdateRoleInfoAction, self).__init__(request, context, *args,
- **kwargs)
- self.available_nodes = context['available_nodes']
- self.fields['nodes'].widget = tuskar_ui.forms.NumberInput(attrs={
- 'min': 0,
- 'max': self.available_nodes,
- })
- self.fields['nodes'].help_text = _(
- "{0} nodes available").format(self.available_nodes)
- if not utils.matching_deployment_mode():
- del self.fields['flavor']
-
- def populate_flavor_choices(self, request, context):
- flavors = api.flavor.Flavor.list(self.request)
- choices = [(f.name, f.name) for f in flavors]
- return [('', _('Unknown'))] + choices
-
- def populate_image_choices(self, request, context):
- images = glance.image_list_detailed(self.request)[0]
- images = [image for image in images
- if tuskar_utils.check_image_type(image,
- 'overcloud provisioning')]
- choices = [(i.name, i.name) for i in images]
- return [('', _('Unknown'))] + choices
-
- def clean_nodes(self):
- new_count = int(self.cleaned_data['nodes'] or 0)
- if new_count > self.available_nodes:
- raise django.forms.ValidationError(_(
- "There are only {0} nodes available "
- "for the selected flavor."
- ).format(self.available_nodes))
- return str(new_count)
-
- def handle(self, request, context):
- return {
- 'name': self.cleaned_data['name'],
- 'description': self.cleaned_data['description'],
- 'flavor': self.cleaned_data.get('flavor'),
- 'image': self.cleaned_data['image'],
- 'nodes': self.cleaned_data['nodes'],
- }
-
-
-class UpdateRoleConfigAction(workflows.Action):
- class Meta(object):
- name = _("Service Configuration")
- slug = 'update_role_config'
- help_text = _("Edit the role's services configuration.")
-
- def __init__(self, request, context, *args, **kwargs):
- super(UpdateRoleConfigAction, self).__init__(request, context,
- *args, **kwargs)
- self.fields.update(
- parameters_forms.parameter_fields(
- request,
- prefix='%s-1::' % context['name']),
- )
-
- def handle(self, request, context):
- return {'parameters': self.cleaned_data}
-
-
-class UpdateRoleInfo(workflows.Step):
- action_class = UpdateRoleInfoAction
- depends_on = ("role_id", "available_nodes")
- contributes = ("name", "description", "flavor", "image", "nodes")
- template_name = 'infrastructure/roles/info.html'
-
-
-class UpdateRoleConfig(workflows.Step):
- action_class = UpdateRoleConfigAction
- depends_on = ("role_id", "name")
- contributes = ("parameters",)
- template_name = 'infrastructure/roles/config.html'
-
-
-class UpdateRole(workflows.Workflow):
- slug = "update_role"
- finalize_button_name = _("Save")
- success_message = _('Modified role "%s".')
- failure_message = _('Unable to modify role "%s".')
- index_url = "horizon:infrastructure:roles:index"
- default_steps = (
- UpdateRoleInfo,
- UpdateRoleConfig,
- )
- success_url = reverse_lazy(
- 'horizon:infrastructure:roles:index')
-
- def name(self):
- # Use context_seed here, as context['name'] returns empty
- # as it's one of the fields.
- return _('Edit Role "%s"') % self.context_seed['name']
-
- def format_status_message(self, message):
- # Use context_seed here, as context['name'] returns empty
- # as it's one of the fields.
- return message % self.context_seed['name']
-
- def handle(self, request, data):
- # save it!
- role_id = data['role_id']
- try:
- # Get initial role information
- plan = api.tuskar.Plan.get_the_plan(self.request)
- role = api.tuskar.Role.get(self.request, role_id)
- except Exception:
- exceptions.handle(
- self.request,
- _('Unable to retrieve role details.'),
- redirect=reverse_lazy(self.index_url))
-
- parameters = data['parameters']
- parameters[role.image_parameter_name] = data['image']
- parameters[role.node_count_parameter_name] = data['nodes']
- if utils.matching_deployment_mode():
- parameters[role.flavor_parameter_name] = data['flavor']
-
- plan.patch(request, plan.uuid, parameters)
- # TODO(rdopiera) Find out how to update role's name and description.
- return True
diff --git a/tuskar_ui/infrastructure/static/infrastructure/images/chevron.png b/tuskar_ui/infrastructure/static/infrastructure/images/chevron.png
deleted file mode 100644
index 4e4cef9e..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/images/chevron.png
+++ /dev/null
Binary files differ
diff --git a/tuskar_ui/infrastructure/static/infrastructure/images/power.png b/tuskar_ui/infrastructure/static/infrastructure/images/power.png
deleted file mode 100644
index 8d3590dd..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/images/power.png
+++ /dev/null
Binary files differ
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.capacity.js b/tuskar_ui/infrastructure/static/infrastructure/js/horizon.capacity.js
deleted file mode 100644
index d82b7309..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.capacity.js
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- Used for animating and displaying capacity information using
- D3js progress bars.
-
- Usage:
- In order to have capacity bars that work with this, you need to have a
- DOM structure like this in your Django template:
-
- <div id="your_capacity_bar_id" class="capacity_bar">
- </div>
-
- With this capacity bar, you then need to add some data- HTML attributes
- to the div #your_capacity_bar_id. The available data- attributes are:
-
- data-chart-type="capacity_bar_chart" REQUIRED
- Must be "capacity_bar_chart".
-
- data-capacity-used="integer" OPTIONAL
- Integer representing the total number used by the user.
-
- data-capacity-limit="integer" OPTIONAL
- Integer representing the total quota limit the user has. Note this IS
- NOT the amount remaining they can use, but the total original capacity.
-
- data-average-capacity-used="integer" OPTIONAL
- Integer representing the average usage of given capacity.
-*/
-
-horizon.Capacity = {
- capacity_bars: [],
-
- // Determines the capacity bars to be used for capacity display.
- init: function() {
- this.capacity_bars = $('div[data-chart-type="capacity_bar_chart"]');
-
- // Draw the capacity bars
- this._initialCreation(this.capacity_bars);
- },
-
-
- /*
- Create a new d3 bar and populate it with the current amount used,
- average used, and percentage label
- */
- drawUsed: function(element, used_perc, used_px, average_perc) {
- var w= "100%";
- var h= 15;
- var lvl_curve= 3;
- var bkgrnd= "#F2F2F2";
- var frgrnd= "grey";
- var usage_color = d3.scale.linear()
- .domain([0, 50, 75, 90, 100])
- .range(["#669900", "#669900", "#FF9900", "#FF3300", "#CC0000"]);
-
- // Horizontal Bars
- var bar = d3.select("#"+element).append("svg:svg")
- .attr("class", "chart")
- .attr("width", w)
- .attr("height", h)
- .style("background-color", "white")
- .append("g");
-
- // background - unused resources
- bar.append("rect")
- .attr("y", 0)
- .attr("width", w)
- .attr("height", h)
- .attr("rx", lvl_curve)
- .attr("ry", lvl_curve)
- .style("fill", bkgrnd)
- .style("stroke", "#bebebe")
- .style("stroke-width", 1);
-
- // used resources
- if (used_perc) {
- bar.append("rect")
- .attr("class", "usedbar")
- .attr("y", 0)
- .attr("id", "test")
- .attr("width", 0)
- .attr("height", h)
- .attr("rx", lvl_curve)
- .attr("ry", lvl_curve)
- .style("fill", usage_color(used_perc))
- .style("stroke", "#a0a0a0")
- .style("stroke-width", 1)
- .attr("d", used_perc)
- .transition()
- .duration(500)
- .attr("width", used_perc + "%");
- }
-
- // average
- if (average_perc) {
- bar.append("rect")
- .attr("y",1)
- .attr("x", 0)
- .attr("class", "average")
- .attr("height", h-2)
- .attr("width", 1)
- .style("fill", "black")
- .transition()
- .duration(500)
- .attr("x", average_perc + "%");
- }
-
- // used text
- if (used_perc) {
- bar.append("text")
- .text(used_perc + "%")
- .attr("y", 8)
- .attr("x", 3)
- .attr("dominant-baseline", "middle")
- .attr("font-size", 10)
- .transition()
- .duration(500)
- .attr("x", function() {
- // position the percentage label
- if (used_perc > 99 && used_px > 25){
- return used_px - 30;
- } else if (used_px > 25) {
- return used_px - 25;
- } else {
- return used_px + 3;
- }
- });
- }
- },
-
-
- // Draw the initial d3 bars
- _initialCreation: function(bars) {
- var scope = this;
-
- $(bars).each(function(index, element) {
- var progress_element = $(element);
-
- var capacity_limit = parseInt(progress_element.attr('data-capacity-limit'), 10);
- var capacity_used = parseInt(progress_element.attr('data-capacity-used'), 10);
- var average_used = parseInt(progress_element.attr('data-average-capacity-used'), 10);
- var percentage_used = 0;
- var average_percentage = 0;
- var _used_px = 0;
-
- if (!isNaN(capacity_limit) && !isNaN(average_used)) {
- average_percentage = ((average_used / capacity_limit) * 100);
- }
- if (!isNaN(capacity_limit) && !isNaN(capacity_used)) {
- percentage_used = Math.round((capacity_used / capacity_limit) * 100);
- _used_px = progress_element.width() / 100 * percentage_used;
- }
-
- scope.drawUsed($(element).attr('id'), percentage_used, _used_px, average_percentage);
- });
- }
-};
-
-horizon.addInitFunction(function () {
- horizon.Capacity.init();
-});
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3circleschart.js b/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3circleschart.js
deleted file mode 100644
index 67547e18..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3circleschart.js
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- Draw circles chart in d3.
-
- To use, a div is required with the data attributes
- data-chart-type="circles_chart", data-url and data-size in the
- div.
-
- data-chart-type - must be "circles_chart" so chart gets initialized
- data-url - (string) url for the json data for the chart
- data-time - (string) time parameter, gets appended to url as time=...
- data-size - (integer) size of the circles in pixels
-
- If used in popup, initialization must be made manually e.g.:
- addHorizonLoadEvent(function() {
- horizon.d3_circles_chart.init('.html_element');
- horizon.d3_circles_chart.init('.health_chart');
- });
-
-
- Example:
- <div class="html_element"
- data-chart-type="circles_chart"
- data-url="/infrastructure/racks/1/top_communicating.json?cond=to"
- data-time="now"
- data-size="22">
- </div>
-
- There are control elements for the cirles chart, implementing some commands
- that will be executed over the chart.
-
- 1. The selectbox for time change, implements ChangeTime command. It has to
- have data attribute data-circles-chart-command="change_time", with defined
- receiver as jquery selector (selector can point to more elements and it will
- execute the command on all of them) e.g. data-receiver="#rack_health_chart"
- Option value is then appended to url and chart is refreshed.
-
- Example
- <div class="span3 circles_chart_time_picker">
- <select data-circles-chart-command="change_time"
- data-receiver="#rack_health_chart">
- <option value="now">Now</option>
- <option value="yesterday">Yesterday</option>
- <option value="last_week">Last Week</option>
- <option value="last_month">Last Month</option>
- </select>
- </div>
- <div class="clear"></div>
-
- 2. Bootstrap tabs for switching different circle_charts implement command
- ChangeUrl. It has to have data attribute data-circles-chart-command="change_url",
- with defined receiver as jquery selector (selector can point to more elements and
- it will execute the command on all of them) e.g. data-receiver="#rack_health_chart"
-
- Inner li a element has to have attribute data-url, e.g.
- data-url="/infrastructure/racks/1/top_communicating.json?type=alerts"
-
- The url of the chart is then switched and chart is refreshed.
-
- <ul class="nav nav-tabs"
- data-circles-chart-command="change_url"
- data-receiver="#rack_health_chart">
- <li class="active">
- <a data-url="/infrastructure/racks/1/top_communicating.json?type=overall_health" href="#">
- Overall Health</a>
- </li>
- <li>
- <a data-url="/infrastructure/racks/1/top_communicating.json?type=alerts" href="#">
- Alerts</a>
- </li>
- <li>
- <a data-url="/infrastructure/racks/1/top_communicating.json?type=capacities" href="#">
- Capacities</a>
- </li>
- <li>
- <a data-url="/infrastructure/racks/1/top_communicating.json?type=status" href="#">
- Status</a>
- </li>
- </ul>
-*/
-
-
-horizon.d3_circles_chart = {
- CirclesChart: function(chart_class, html_element){
- this.chart_class = chart_class;
- this.html_element = html_element;
-
- var jquery_element = $(html_element);
- this.size = jquery_element.data('size');
- this.time = jquery_element.data('time');
- this.url = jquery_element.data('url');
-
- this.final_url = this.url;
- if (this.final_url.indexOf('?') > -1){
- this.final_url += '&time=' + this.time;
- }else{
- this.final_url += '?time=' + this.time;
- }
-
- this.time = jquery_element.data('time');
- this.data = [];
-
- this.refresh = refresh;
- function refresh(){
- var self = this;
- this.jqxhr = $.getJSON( this.final_url, function() {
- //FIXME add loader in the target element
- })
- .done(function(data) {
- // FIXME find a way how to only update graph with new data
- // not delete and create
- $(self.html_element).html("");
-
- self.data = data.data;
- self.settings = data.settings;
- self.chart_class.render(self.html_element, self.size, self.data, self.settings);
- })
- .fail(function() {
- // FIXME add proper fail message
- console.log( "error" ); })
- .always(function() {
- // FIXME add behaviour that should be always done
- });
- }
- },
- init: function(selector, settings) {
- var self = this;
- $(selector).each(function() {
- self.refresh(this);
- });
- self.bind_commands();
- },
- refresh: function(html_element){
- var chart = new this.CirclesChart(this, html_element);
- // FIXME save chart objects somewhere so I can use them again when
- // e.g. I am swithing tabs, or if I want to update them
- // via web sockets
- // this.charts.add_or_update(chart)
- chart.refresh();
- },
- render: function(html_element, size, data, settings){
- var self = this;
- // FIXME rewrite to scatter plot once we have some cool D3 chart
- // library
- var width = size + 4,
- height = size + 4,
- round = size / 2,
- center_x = width / 2,
- center_y = height / 2;
-
- var svg = d3.select(html_element).selectAll("svg")
- .data(data)
- .enter().append("svg")
- .attr("width", width)
- .attr("height", height);
-
- // FIXME use some pretty tooltip from some library we will use
- // this one is just temporary
- var tooltip = d3.select(html_element).append("div")
- .style("position", "absolute")
- .style("z-index", "10")
- .style("visibility", "hidden")
- .style("min-width", "100px")
- .style("max-width", "110px")
- .style("min-height", "30px")
- .style("border", "1px ridge grey")
- .style("background-color", "white")
- .text(function(d) { "a simple tooltip"; });
-
- var circle = svg.append("circle")
- .attr("r", round)//function(d) { return d.r; })// can be sent form server
- .attr("cx", center_x)
- .attr("cy", center_y)
- .attr("stroke", "#cecece")
- .attr("stroke-width", function (d) {
- return 1;
- })
- .style("fill", function (d) {
- if (d.color) {
- return d.color;
- } else if (settings.scale == "linear_color_scale") {
- return self.linear_color_scale(d.percentage, settings.domain, settings.range);
- }
- })
- .on("mouseover", function (d) {
- if (d.tooltip) {
- tooltip.html(d.tooltip);
- } else {
- tooltip.html(d.name + "<br/>" + d.status);
- }
- tooltip.style("visibility", "visible");
- })
- .on("mousemove", function (d) {
- tooltip.style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");
- })
- .on("mouseout", function (d) {
- tooltip.style("visibility", "hidden");
- });
-
- /*
- // or just d3 title element
- circle.append("svg:title")
- .text(function(d) { return d.x; });
-
- */
- },
- linear_color_scale: function(percentage, domain, range){
- usage_color = d3.scale.linear()
- .domain(domain)
- .range(range);
- return usage_color(percentage);
- },
- bind_commands: function (){
- var change_time_command_selector = 'select[data-circles-chart-command="change_time"]';
- var change_url_command_selector = '[data-circles-chart-command="change_url"]';
- var self = this;
- bind_change_time = function () {
- $(change_time_command_selector).each(function() {
- $(this).change(function () {
- var invoker = $(this);
- var command = new self.Command.ChangeTime(self, invoker);
- command.execute();
- });
- });
- };
- bind_change_url = function(){
- $(change_url_command_selector + ' a').click(function (e) {
- // Bootstrap tabs functionality
- e.preventDefault();
- $(this).tab('show');
-
- // Command for url change and refresh
- var invoker = $(this);
- var command = new self.Command.ChangeUrl(self, invoker);
- command.execute();
- });
- };
- bind_change_time();
- bind_change_url();
- },
- Command: {
- ChangeTime: function (chart_class, invoker){
- // Invoker of the command should know about it's receiver.
- // Also invoker brings all parameters of the command.
- this.receiver_selector = invoker.data('receiver');
- this.new_time = invoker.find("option:selected").val();
-
- this.execute = execute;
- function execute(){
- var self = this;
- $(this.receiver_selector).each(function(){
- // change time of the chart
- $(this).data('time', self.new_time);
- // refresh the chart
- chart_class.refresh(this);
- });
- }
- },
- ChangeUrl: function (chart_class, invoker, new_url){
- // Invoker of the command should know about it's receiver.
- // Also invoker brings all parameters of the command.
- this.receiver_selector = invoker.parents('ul').first().data('receiver');
- this.new_url = invoker.data('url');
-
- this.execute = execute;
- function execute(){
- var self = this;
- $(this.receiver_selector).each(function(){
- // change time of the chart
- $(this).data('url', self.new_url);
- // refresh the chart
- chart_class.refresh(this);
- });
- }
- }
- }
-};
-
-/* init the graphs */
-horizon.addInitFunction(function () {
- horizon.d3_circles_chart.init('div[data-chart-type="circles_chart"]');
-});
-
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3singlebarchart.js b/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3singlebarchart.js
deleted file mode 100644
index 2c681f28..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3singlebarchart.js
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- Used for animating and displaying single-bar information using
- D3js rect.
-
- Usage:
- In order to have single bars that work with this, you need to have a
- DOM structure like this in your Django template:
-
- Example:
- <div class="flavor_usage_bar"
- data-popup-free='<p>Capacity remaining by flavors: </p>
- {{resource_class.all_instances_flavors_info}}'
- data-single-bar-orientation="horizontal"
- data-single-bar-height="50"
- data-single-bar-width="100%"
- data-single-bar-used="{{ resource_class.all_used_instances_info }}"
- data-single-bar-auto-scale-selector=".flavors_scale_selector"
- data-single-bar-color-scale-range='["#000060", "#99FFFF"]'>
- </div>
-
- The available data- attributes are:
- data-popup-free, data-popup-used, data-popup-average OPTIONAL
- Html content of popups that will be displayed over this areas.
-
- data-single-bar-orientation REQUIRED
- String representing orientation of the bar.Can be "horizontal"
- or "vertical"
-
- data-single-bar-height REQUIRED
- Integer or string with percent mark format e.g. "50%". Determines
- the total height of the bar.
-
- data-single-bar-width="100%" REQUIRED
- Integer or string with percent mark format e.g. "50%". Determines
- the total width of the bar.
-
- data-single-bar-used="integer" REQUIRED
- 1. Integer
- Integer representing the percent used.
- 2. Array
- Array of following structure:
- [{"popup_used": "Popup html 1", "used_instances": "5"},
- {"popup_used": "Popup html 2", "used_instances": "15"},....]
-
- used_instances: Integer representing the percent used.
- popup_used: Html that will be displayed in popup window over
- this area.
-
- data-single-bar-used-average="integer" OPTIONAL
- Integer representing the average usage in percent of given
- single-bar.
-
- data-single-bar-auto-scale-selector OPTIONAL
- Jquery selector of bar elements that have Integer
- data-single-bar-used attribute. It then takes maximum of these
- values as 100% of the liner scale of the colors.
- So the array representing linear scale interval is set
- automatically.This then maps to data-single-bar-color-scale-range.
- (arrays must have the same structure)
-
- single-bar-color-scale-range OPTIONAL
- Array representing linear scale interval that is set manually.
- E.g "[0,10]". This then maps to data-single-bar-color-scale-range.
- (arrays must have the same structure)
-
- data-single-bar-color-scale-range OPTIONAL
- Array representing linear scale of colors.
- E.g '["#000060", "#99FFFF"]'
-
-*/
-
-horizon.d3_single_bar_chart = {
- SingleBarChart: function(chart_class, html_element){
- var self = this;
- self.chart_class = chart_class;
-
- self.html_element = html_element;
- self.jquery_element = $(self.html_element);
- // Using only percent, so limit is 100%
- self.single_bar_limit = 100;
-
- self.single_bar_used = $.parseJSON(self.jquery_element.attr('data-single-bar-used'));
- self.average_used = parseInt(self.jquery_element.attr('data-single-bar-average-used'), 10);
-
- self.data = {};
-
- // Percentage and used_px count
- if ($.isArray(self.single_bar_used)){
- self.data.used_px = 0;
- self.data.percentage_used = Array();
- self.data.tooltip_used_contents = Array();
- for (var i = 0; i < self.single_bar_used.length; ++i) {
- if (!isNaN(self.single_bar_limit) && !isNaN(self.single_bar_used[i].used_instances)) {
- used = Math.round((self.single_bar_used[i].used_instances / self.single_bar_limit) * 100);
-
- self.data.percentage_used.push(used);
- // for multi-bar chart, tooltip is in the data
- self.data.tooltip_used_contents.push(self.single_bar_used[i].popup_used);
-
- self.data.used_px += self.jquery_element.width() / 100 * used;
- } else { // If NaN self.data.percentage_used is 0
-
- }
- }
-
- }
- else {
- if (!isNaN(self.single_bar_limit) && !isNaN(self.single_bar_used)) {
- self.data.percentage_used = Math.round((self.single_bar_used / self.single_bar_limit) * 100);
- self.data.used_px = self.jquery_element.width() / 100 * self.data.percentage_used;
-
- } else { // If NaN self.data.percentage_used is 0
- self.data.percentage_used = 0;
- self.data.used_px = 0;
- }
-
- if (!isNaN(self.single_bar_limit) && !isNaN(self.average_used)) {
- self.data.percentage_average = ((self.average_used / self.single_bar_limit) * 100);
- } else {
- self.data.percentage_average = 0;
- }
- }
-
- // Width and height of bar
- self.data.width = self.jquery_element.data('single-bar-width');
- self.data.height = self.jquery_element.data('single-bar-height');
-
- // Color scales
- self.auto_scale_selector = function () {
- return self.jquery_element.data('single-bar-auto-scale-selector');
- };
- self.is_auto_scaling = function () {
- return self.auto_scale_selector();
- };
- self.auto_scale = function () {
- var max_scale = 0;
- $(self.auto_scale_selector()).each(function() {
- var scale = parseInt($(this).data('single-bar-used'), 10);
- if (scale > max_scale)
- max_scale = scale;
- });
- return [0, max_scale];
- };
-
- if (self.jquery_element.data('single-bar-color-scale-domain'))
- self.data.color_scale_domain =
- self.jquery_element.data('single-bar-color-scale-domain');
- else if (self.is_auto_scaling())
- // Dynamically set scale based on biggest value
- self.data.color_scale_domain = self.auto_scale();
- else
- self.data.color_scale_domain = [0,100];
-
- if (self.jquery_element.data('single-bar-color-scale-range'))
- self.data.color_scale_range =
- self.jquery_element.data('single-bar-color-scale-range');
- else
- self.data.color_scale_range = ["#000000", "#0000FF"];
-
- // Tooltips data
- self.data.popup_average = self.jquery_element.data('popup-average');
- self.data.popup_free = self.jquery_element.data('popup-free');
- self.data.popup_used = self.jquery_element.data('popup-used');
-
- // Orientation of the Bar chart
- self.data.orientation = self.jquery_element.data('single-bar-orientation');
-
- // Refresh method
- self.refresh = function (){
- self.chart_class.render(self.html_element, self.data);
- };
- },
- BaseComponent: function(data){
- var self = this;
-
- self.data = data;
-
- self.w = data.width;
- self.h = data.height;
- self.lvl_curve = 3;
- self.bkgrnd = "#F2F2F2";
- self.frgrnd = "grey";
- self.color_scale_max = 25;
-
- self.percentage_used = data.percentage_used;
- self.total_used_perc = 0;
- self.used_px = data.used_px;
- self.percentage_average = data.percentage_average;
- self.tooltip_used_contents = data.tooltip_used_contents;
-
- // set scales
- self.usage_color = d3.scale.linear()
- .domain(data.color_scale_domain)
- .range(data.color_scale_range);
-
- // return true if it renders used percentage multiple in one chart
- self.used_multi = function (){
- return ($.isArray(self.percentage_used));
- };
-
- // deals with percentage if there should be multiple in one chart
- self.used_multi_iterator = 0;
- self.percentage_used_value = function(){
- if (self.used_multi()){
- return self.percentage_used[self.used_multi_iterator];
- } else {
- return self.percentage_used;
- }
- };
- // deals with html tooltips if there should be multiple in one chart
- self.tooltip_used_value = function (){
- if (self.used_multi()){
- return self.tooltip_used_contents[self.used_multi_iterator];
- } else {
- return "";
- }
- };
-
- // return true if it chart is oriented horizontally
- self.horizontal_orientation = function (){
- return (self.data.orientation == "horizontal");
- };
-
- },
- UsedComponent: function(base_component){
- var self = this;
- self.base_component = base_component;
-
- // FIXME would be good to abstract all attributes and resolve orientation inside
- if (base_component.horizontal_orientation()){
- // Horizontal Bars
- self.y = 0;
- self.x = base_component.total_used_perc + "%";
- self.width = 0;
- self.height = base_component.h;
- self.trasition_attr = "width";
- self.trasition_value = base_component.percentage_used_value() + "%";
- } else { // Vertical Bars
- self.y = base_component.h;
- self.x = 0;
- self.width = base_component.w;
- self.height = base_component.percentage_used_value() + "%";
- self.trasition_attr = "y";
- self.trasition_value = 100 - base_component.percentage_used_value() + "%";
- }
-
- self.append = function(bar, tooltip){
- var used_component = self;
- var base_component = self.base_component;
-
- bar.append("rect")
- .attr("class", "usedbar")
- .attr("y", used_component.y)
- .attr("x", used_component.x)
- .attr("width", used_component.width)
- .attr("height", used_component.height)
- //.attr("rx", base_component.lvl_curve)
- //.attr("ry", base_component.lvl_curve)
- .style("fill", base_component.usage_color(base_component.percentage_used_value()))
- .style("stroke", "#bebebe")
- .style("stroke-width", 0)
- .attr("d", base_component.percentage_used_value())
- .attr("popup-used", base_component.tooltip_used_value())
- .on("mouseover", function(d){
- if ($(this).attr('popup-used')){
- tooltip.html($(this).attr('popup-used'));
- }
- tooltip.style("visibility", "visible");})
- .on("mousemove", function(d){tooltip.style("top",
- (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
- .on("mouseout", function(d){tooltip.style("visibility", "hidden");})
- .transition()
- .duration(500)
- .attr(used_component.trasition_attr, used_component.trasition_value);
- };
- },
- AverageComponent: function(base_component){
- var self = this;
- self.base_component = base_component;
-
- // FIXME would be good to abstract all attributes and resolve orientation inside
- if (base_component.horizontal_orientation()){
- // Horizontal Bars
- self.y = 1;
- self.x = 0;
- self.width = 1;
- self.height = base_component.h;
- self.trasition_attr = "x";
- self.trasition_value = base_component.percentage_average + "%";
- } else { // Vertical Bars
- self.y = 0;
- self.x = 0;
- self.width = base_component.w;
- self.height = 1;
- self.trasition_attr = "y";
- self.trasition_value = 100 - base_component.percentage_average + "%";
- }
-
- self.append = function(bar, tooltip){
- var average_component = self;
- var base_component = self.base_component;
-
- bar.append("rect")
- .attr("y", average_component.y)
- .attr("x", average_component.x)
- .attr("class", "average")
- .attr("height", average_component.height)
- .attr("width", average_component.width)
- .style("fill", "black")
- .on("mouseover", function(){tooltip.style("visibility", "visible");})
- .on("mousemove", function(){tooltip.style("top",
- (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
- .on("mouseout", function(){tooltip.style("visibility", "hidden");})
- .transition()
- .duration(500)
- .attr(average_component.trasition_attr, average_component.trasition_value);
- };
- },
- /* TODO rewrite below as components */
- /* FIXME use some pretty tooltip from some library we will use
- * this one is just temporary */
- append_tooltip: function(tooltip, html_content){
- return tooltip
- .style("position", "absolute")
- .style("z-index", "10")
- .style("visibility", "hidden")
- .style("min-width", "100px")
- .style("max-width", "200px")
- .style("min-height", "30px")
- .style("max-height", "150px")
- .style("border", "1px ridge grey")
- .style("padding", "8px")
- .style("padding-top", "5px")
- .style("background-color", "white")
- .html(html_content);
- },
- append_unused: function(bar, base_component, tooltip_free){
- bar.append("rect")
- .attr("y", 0)
- .attr("width", base_component.w)
- .attr("height", base_component.h)
- .attr("rx", base_component.lvl_curve)
- .attr("ry", base_component.lvl_curve)
- .style("fill", base_component.bkgrnd)
- .style("stroke", "#e0e0e0")
- .style("stroke-width", 1)
- .on("mouseover", function(d){tooltip_free.style("visibility", "visible");})
- .on("mousemove", function(d){tooltip_free.style("top",
- (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
- .on("mouseout", function(d){tooltip_free.style("visibility", "hidden");});
- },
- // TODO This have to be enhanced, so this library can replace jtomasek capacity charts
- append_text: function(bar, base_component, tooltip){
- bar.append("text")
- .text("FREE")
- .attr("y", base_component.h/2)
- .attr("x", 3)
- .attr("dominant-baseline", "middle")
- .attr("font-size", 13)
- .on("mouseover", function(d){tooltip.style("visibility", "visible");})
- .on("mousemove", function(d){tooltip.style("top",
- (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
- .on("mouseout", function(d){tooltip.style("visibility", "hidden");})
- .transition()
- .duration(500)
- .attr("x", function() {
- // FIXME when another panel is active, this page is hidden and used_px return 0
- // text is then badly positioned, quick fix will be to refresh charts when panel
- // is switched. Need to find better solution.
- if (base_component.total_used_perc > 90 && base_component.used_px > 25)
- return base_component.used_px - 20;
- else
- return base_component.used_px + 20;
- });
- },
- append_border: function(bar){
- bar.append("rect")
- .attr("x", 0)
- .attr("y", 0)
- .attr("height", '100%')
- .attr("width", '100%')
- .style("stroke", "#bebebe")
- .style("fill", "none")
- .style("stroke-width", 1);
- },
- // INIT
- init: function() {
- var self = this;
- this.single_bars = $('div[data-single-bar-used]');
-
- this.single_bars.each(function() {
- self.refresh(this);
- });
- },
- refresh: function(html_element){
- var chart = new this.SingleBarChart(this, html_element);
- // FIXME save chart objects somewhere so I can use them again when
- // e.g. I am swithing tabs, or if I want to update them
- // via web sockets
- // this.charts.add_or_update(chart)
- chart.refresh();
- },
- render: function(html_element, data) {
- var jquery_element = $(html_element);
-
- // Initialize base_component
- var base_component = new this.BaseComponent(data);
-
- // Bar
- var bar_html = d3.select(html_element);
-
- // Tooltips
- var tooltip_average = bar_html.append("div");
- if (data.popup_average)
- tooltip_average = this.append_tooltip(tooltip_average, data.popup_average);
-
- var tooltip_free = bar_html.append("div");
- if (data.popup_free)
- tooltip_free = this.append_tooltip(tooltip_free, data.popup_free);
-
- var tooltip_used = bar_html.append("div");
- if (data.popup_used)
- tooltip_used = this.append_tooltip(tooltip_used, data.popup_used);
-
- // append layout for bar chart
- var bar = bar_html.append("svg:svg")
- .attr("class", "chart")
- .attr("width", base_component.w)
- .attr("height", base_component.h)
- .style("background-color", "white")
- .append("g");
- var used_component;
-
- // append Unused resources Bar
- this.append_unused(bar, base_component, tooltip_free);
-
- if (base_component.used_multi()){
- // If Used is shown as multiple values in one chart
- for (var i = 0; i < base_component.percentage_used.length; ++i) {
- // FIXME write proper iterator
- base_component.used_multi_iterator = i;
-
- // Use general tooltip, content of tooltip will be changed
- // by inner used compoentnts on their hover
- tooltip_used = this.append_tooltip(tooltip_used, "");
-
- // append used so it will be shown as multiple values in one chart
- used_component = new this.UsedComponent(base_component);
- used_component.append(bar, tooltip_used);
-
- // append Used resources to Bar
- base_component.total_used_perc += base_component.percentage_used_value();
- }
-
- // append Text to Bar
- this.append_text(bar, base_component, tooltip_free);
-
- } else {
- // used is show as one value it the chart
- used_component = new this.UsedComponent(base_component);
- used_component.append(bar, tooltip_used);
-
- // append average value to Bar
- var average_component = new this.AverageComponent(base_component);
- average_component.append(bar, tooltip_average);
- }
- // append border of whole Bar
- this.append_border(bar);
- }
-};
-
-
-horizon.addInitFunction(function () {
- horizon.d3_single_bar_chart.init();
-});
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_live.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_live.js
deleted file mode 100644
index 22e4f2f5..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_live.js
+++ /dev/null
@@ -1,17 +0,0 @@
-tuskar.deployment_live = (function () {
- 'use strict';
-
- var module = {};
-
- module.init = function () {
- $("#overcloudrc").on("hide.bs.collapse", function(){
- $("span.overcloudrc").html('Show <i class="fa fa-angle-down"></i>');
- });
- $("#overcloudrc").on("show.bs.collapse", function(){
- $("span.overcloudrc").html('Hide <i class="fa fa-angle-up"></i>');
- });
- };
-
- horizon.addInitFunction(module.init);
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_progress.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_progress.js
deleted file mode 100644
index 66829379..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_progress.js
+++ /dev/null
@@ -1,43 +0,0 @@
-tuskar.deployment_progress = (function () {
- 'use strict';
-
- var module = {};
-
- module.init = function () {
- if (!$('div.deployment-box div.progress').length) { return; }
- this.interval = setInterval(function () {
- module.check_progress();
- }, 30000);
- module.events_template = Hogan.compile($('#events-template').html() || '');
- module.roles_template = Hogan.compile($('#roles-template').html() || '');
- };
-
- module.check_progress = function () {
- var $form = $('form.deployment-roles-form');
- $.ajax({
- type: 'GET',
- headers: {'X-Horizon-Progress': 'true'},
- url: $form.attr('action'),
- dataType: 'json',
- async: true,
- success: this.update_progress
- });
- };
-
- module.update_progress = function (data) {
- if (data.progress >= 100 || data.progress <= 0) {
- window.location.reload(true);
- }
- var $bar = $('div.deployment-box div.progress div.progress-bar');
- $bar.css('width', '' + data.progress + '%');
- if (data.show_last_events) {
- $('div.deploy-last-events').html(module.events_template.render(data));
- } else {
- $('div.deploy-last-events').html('');
- }
- $('div.deploy-role-status').html(module.roles_template.render(data));
- };
-
- horizon.addInitFunction(module.init);
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.edit_plan.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.edit_plan.js
deleted file mode 100644
index 96ab5459..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.edit_plan.js
+++ /dev/null
@@ -1,74 +0,0 @@
-tuskar.edit_plan = (function () {
- 'use strict';
-
- var module = {};
-
- module.debounce_timer = null;
- module.ICON_CLASSES = (
- 'fa-spinner ' +
- 'fa-spin ' +
- 'fa-cloud ' +
- 'fa-exclamation-circle ' +
- 'fa-check-circle ' +
- ''
- );
-
- module.init = function () {
- if (!$('form.deployment-roles-form').length) { return; }
- // Attach event listeners and hide the submit button.
- $('form.deployment-roles-form input.number-picker'
- ).change(module.on_change);
- $('form.deployment-roles-form [type=submit]').hide();
- // Compile the templates.
- module.message_template = Hogan.compile(
- $('#message-template').html() || '');
- module.title_template = Hogan.compile(
- $('#title-template').html() || '');
- };
-
- module.on_change = function () {
- // Only save when there was no activity for half a second.
- window.clearTimeout(module.debounce_timer);
- module.debounce_timer = window.setTimeout(module.save_form, 500);
- };
-
- module.save_form = function () {
- // Save the current plan and get validation results.
- var $form = $('form.deployment-roles-form');
- module.update_messages(null);
- $.ajax({
- type: 'POST',
- headers: {'X-Horizon-Validate': 'true'},
- url: $form.attr('action'),
- data: $form.serialize(),
- dataType: 'json',
- async: true,
- success: module.update_messages,
- });
- };
-
- module.update_messages = function (data) {
- if (data === null) {
- $('div.deployment-buttons a.btn-primary').addClass('disabled');
- $('div.deployment-icon i').removeClass(module.ICON_CLASSES
- ).addClass('fa-spinner fa-spin');
- data = {validating:true};
- } else if (data.plan_invalid) {
- $('div.deployment-buttons a.btn-primary').addClass('disabled');
- $('div.deployment-icon i').removeClass(module.ICON_CLASSES
- ).addClass('fa-exclamation-circle');
- } else {
- $('div.deployment-buttons a.btn-primary').removeClass('disabled');
- $('div.deployment-icon i').removeClass(module.ICON_CLASSES
- ).addClass('fa-check-circle');
- }
- $('div.deployment-box h4').replaceWith(
- module.title_template.render(data));
- $('div.deployment-box ul').replaceWith(
- module.message_template.render(data));
- $('div.deployment-box a#collapse-steps').text(data.steps_message);
- };
-
- horizon.addInitFunction(module.init);
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.formset_table.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.formset_table.js
deleted file mode 100644
index 4cf225f1..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.formset_table.js
+++ /dev/null
@@ -1,87 +0,0 @@
-tuskar.formset_table = (function () {
- 'use strict';
-
- var module = {};
-
-
- // go through the whole table and fix the numbering of rows
- module.reenumerate_rows = function (table, prefix) {
- var count = 0;
- var input_name_re = new RegExp('^' + prefix + '-(\\d+|__prefix__)-');
- var input_id_re = new RegExp('^id_' + prefix + '-(\\d+|__prefix__)-');
-
- table.find('tbody tr').each(function () {
- $(this).find('input').each(function () {
- var input = $(this);
- input.attr('name', input.attr('name').replace(
- input_name_re, prefix + '-' + count + '-'));
- input.attr('id', input.attr('id').replace(
- input_id_re, 'id_' + prefix + '-' + count + '-'));
- });
- count += 1;
- });
- $('#id_' + prefix + '-TOTAL_FORMS').val(count);
- };
-
- // mark a row as deleted and hide it
- module.delete_row = function (e) {
- $(this).closest('tr').hide();
- $(this).prev('input[name$="-DELETE"]').attr('checked', true);
- };
-
- // replace the "Delete" checkboxes with × for deleting rows
- module.replace_delete = function (where) {
- where.find('input[name$="-DELETE"]').hide().after(
- $('<a href="#" class="close">×</a>').click(module.delete_row)
- );
- };
-
- // add more empty rows in the flavors table
- module.add_row = function (table, prefix, empty_row_html) {
- var new_row = $(empty_row_html);
- module.replace_delete(new_row);
- table.find('tbody').append(new_row);
- module.reenumerate_rows(table, prefix);
- };
-
- // prepare all the javascript for formset table
- module.init = function (prefix, empty_row_html, add_label) {
-
- var table = $('table#' + prefix);
-
- module.replace_delete(table);
-
- // if there are extra empty rows, add the button for new rows
- if (add_label) {
- var button = $('<a href="#" class="btn btn-small pull-right">' +
- add_label + '</a>');
- table.find('tfoot td').append(button);
- button.click(function () {
- module.add_row(table, prefix, empty_row_html);
- });
- }
-
- // if the formset is not empty and has no errors,
- // delete the empty extra rows from the end
- var initial_forms = +$('#id_' + prefix + '-INITIAL_FORMS').val();
- var total_forms = +$('#id_' + prefix + '-TOTAL_FORMS').val();
-
- if (table.find('tbody tr').length > 1 &&
- table.find('tbody td.error').length === 0 &&
- total_forms > initial_forms) {
- table.find('tbody tr').each(function (index) {
- if (index >= initial_forms) {
- $(this).remove();
- }
- });
- module.reenumerate_rows(table, prefix);
- $('#id_' + prefix + '-INITIAL_FORMS').val(
- $('#id_' + prefix + '-TOTAL_FORMS').val());
- }
-
- // enable tooltips
- table.find('td.error[title]').tooltip();
- };
-
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.js
deleted file mode 100644
index c9f29de6..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var Tuskar = function () {
- var tuskar = {};
- return tuskar;
-};
-
-var tuskar = new Tuskar();
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js
deleted file mode 100644
index a7021fe7..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js
+++ /dev/null
@@ -1,87 +0,0 @@
-tuskar.menu_formset = (function () {
- 'use strict';
-
- var module = {};
-
- module.init = function (prefix, empty_form_html) {
- var input_name_re = new RegExp('^' + prefix + '-(\\d+|__prefix__)-');
- var input_id_re = new RegExp('^id_' + prefix + '-(\\d+|__prefix__)-');
- var $content = $('#formset-' + prefix +' .tab-content');
- var $nav = $('#formset-' + prefix + ' .nav');
- var activated = false;
-
- function renumber_form($form, prefix, count) {
- $form.find('input, textarea, select').each(function () {
- var input = $(this);
- input.attr('name', input.attr('name').replace(
- input_name_re, prefix + '-' + count + '-'));
- input.attr('id', input.attr('id').replace(
- input_id_re, 'id_' + prefix + '-' + count + '-'));
- });
- }
-
- function reenumerate_forms($forms, prefix) {
- var count = 0;
- $forms.each(function () {
- renumber_form($(this), prefix, count);
- count += 1;
- });
- $('#id_' + prefix + '-TOTAL_FORMS').val(count);
- return count;
- }
-
- function add_delete_link($nav_item) {
- var $form = $content.find($nav_item.find('a').attr('href'));
- $nav_item.prepend('<span class="btn-small pull-right delete-icon"><i class="fa fa-times"></i></span>');
- $nav_item.find('span.delete-icon:first').click(function () {
- var count;
- $form.remove();
- $nav_item.remove();
- count = reenumerate_forms($content.find('.tab-pane'), prefix);
- if (count === 0) { add_node(); }
- });
- }
-
- function add_node() {
- var $new_form = $(empty_form_html);
- var count, id, $new_nav;
- $content.append($new_form);
- $new_form = $content.find('.tab-pane:last');
- count = reenumerate_forms($content.find('.tab-pane'), prefix);
- id = 'tab-' + prefix + '-' + count;
- $new_form.attr('id', id);
- $nav.append('<li><a href="#' + id + '" data-toggle="tab">Undefined node</a></li>');
- $new_nav = $nav.find('li > a:last');
- add_delete_link($new_nav.parent());
- $new_nav.click(function () {
- $(this).tab('show');
- $('select.switchable').trigger('change');
- });
- $new_nav.tab('show');
- $('select.switchable').trigger('change');
- horizon.forms.add_password_fields_reveal_buttons($new_form);
- }
-
- // Connect all signals.
- $('#add-node-link').click(add_node);
- $nav.find('li').each(function () {
- add_delete_link($(this));
- });
- $nav.find('li a').click(function () {
- window.setTimeout(function () {
- $('select.switchable').trigger('change');
- }, 0);
- });
-
- // Activate the first field that has errors.
- $content.find('.control-group.error').each(function () {
- if (!activated) {
- $nav.find('a[href="#' + $(this).closest('.tab-pane').attr('id') + '"]').tab('show');
- activated = true;
- }
- });
-
- };
-
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.number_picker.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.number_picker.js
deleted file mode 100644
index ac6ffda0..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.number_picker.js
+++ /dev/null
@@ -1,51 +0,0 @@
-tuskar.number_picker = (function () {
- 'use strict';
-
- var module = {};
-
- module.init = function () {
- $('input.number-picker').removeClass(
- 'form-control').wrap(
- '<div class="number_picker unselectable form-control">').before(
- '<a class="arrow-left" href="#">' +
- '<i class="fa fa-chevron-left"></i></a>').after(
- '<a class="arrow-right" href="#">' +
- '<i class="fa fa-chevron-right"></i></a>').each(
- function () {
- var $this = $(this);
- var $right_arrow = $this.next('a.arrow-right');
- var $left_arrow = $this.prev('a.arrow-left');
- if ($this.attr('readonly')) {
- $this.parent().addClass('readonly');
- }
- function change(step) {
- var value = +$this.val();
- var maximum = +$this.attr('max');
- var minimum = +$this.attr('min');
- value += step;
- if (!isNaN(maximum)) { value = Math.min(maximum, value); }
- if (!isNaN(minimum)) { value = Math.max(minimum, value); }
- $right_arrow.toggleClass('disabled', (value === maximum));
- $left_arrow.toggleClass('disabled', (value === minimum));
- $this.val(value);
- $this.trigger('change');
- }
- $right_arrow.click(function () {
- var step = +($this.attr('step') || 1);
- change(step);
- });
- $left_arrow.click(function () {
- var step = -($this.attr('step') || 1);
- change(step);
- });
- change(0);
- var step = +($this.attr('step') || 1);
- if (step !== 1) {
- $this.after('<span class="step">+' + step + '</span>');
- }
- });
- };
-
- horizon.addInitFunction(module.init);
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.performance.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.performance.js
deleted file mode 100644
index 23658f10..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.performance.js
+++ /dev/null
@@ -1,25 +0,0 @@
-tuskar.performance = (function () {
- 'use strict';
-
- var module = {};
-
- module.show_hide_datepickers = function () {
- var date_options = $("#date_options");
- date_options.change(function(evt) {
- if ($(this).find("option:selected").val() === "other"){
- evt.stopPropagation();
- $("#date_from, #date_to").val('');
- $("#date_from_group, #date_to_group").show();
- } else {
- $("#date_from_group, #date_to_group").hide();
- }
- });
- if (date_options.find("option:selected").val() === "other"){
- $("#date_from_group, #date_to_group").show();
- } else {
- $("#date_from_group, #date_to_group").hide();
- }
- };
-
- return module;
-} ());
diff --git a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.templates.js b/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.templates.js
deleted file mode 100644
index 5f8e6862..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/js/tuskar.templates.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* Namespace for core functionality related to client-side templating. */
-tuskar.templates = {
- template_ids: ["#modal_chart_template"],
-};
-
-/* Pre-loads and compiles the client-side templates. */
-tuskar.templates.compile_templates = function () {
- $.each(tuskar.templates.template_ids, function (ind, template_id) {
- horizon.templates.compiled_templates[template_id] = Hogan.compile($(template_id).html());
- });
-};
-
-horizon.addInitFunction(function () {
- // Load client-side template fragments and compile them.
- tuskar.templates.compile_templates();
-});
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_breadcrumbs.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_breadcrumbs.scss
deleted file mode 100644
index 6a4e24f7..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_breadcrumbs.scss
+++ /dev/null
@@ -1,31 +0,0 @@
-// breadcrumbs
-
-.breadcrumbs {
- font-size: 85%;
- margin: 0 0 25px 0;
-
- a {
- color: rgb(170, 170, 170);
- text-decoration: underline;
-
- &:hover {
- text-decoration: none;
- color: rgb(100, 100, 100);
- }
- }
-
- .separator {
- color: rgb(200, 200, 200);
-
- &:before {
- display: inline;
- content: ">>";
- margin: 0 7px;
- }
-
- &:last-child:after {
- display: inline;
- content: "...";
- }
- }
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_buttons.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_buttons.scss
deleted file mode 100644
index 0a62aa05..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_buttons.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-// buttons
-.btn-default:not(.btn-danger, .btn-primary) {
- background: rgb(243, 243, 243);
- &:hover {
- background: rgb( 235, 235, 235);
- }
-}
-
-.btn-toolbar.pull-right {
- margin: 0;
-}
-
-.btn-no-border {
- border: none;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_capacities.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_capacities.scss
deleted file mode 100644
index 91b2a1f5..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_capacities.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-// capacities
-table.capacities {
- &.overall_usage {
- margin-top: 5px;
- }
-
- td {
- padding: 3px;
-
- &.capacity_label {
- width: 60px;
- padding-right: 5px;
- color: rgb(160,160,160);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- & div.capacity_bar {
- line-height: 0;
- width: 120px;
- }
- }
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_charts.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_charts.scss
deleted file mode 100644
index 1a20c71c..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_charts.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-form.performance_charts {
- .pull-right {
- input {
- margin-left: 0;
- }
- }
-}
-.overview_chart {
- .chart_container {
- .spinner_wrapper {
- margin-top: 12px;
- margin-left: 12px;
- }
- }
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_detail_pages.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_detail_pages.scss
deleted file mode 100644
index 7c3d096a..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_detail_pages.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.dl-horizontal-left {
- dt {
- width: 120px;
- text-align: left;
- color: rgb(160, 160, 160);
- font-weight: normal;
- }
- dd {
- margin-left: 130px;
- padding-bottom: .5em;
- }
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_flavor_usages.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_flavor_usages.scss
deleted file mode 100644
index 639be9ec..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_flavor_usages.scss
+++ /dev/null
@@ -1,89 +0,0 @@
-// flavor usages
-div.flavor_usage_bar {
- max-width: 550px;
-}
-
-table.flavor_usages {
- width: 100%;
- max-width: 550px;
-
- td {
- text-align: center;
- padding: 3px;
-
- &.flavor_usage_label {
- width: 60px;
- text-align: center;
- }
-
- &.flavor_usage_text {
- width: 60px;
- text-align: center;
- line-height: 1;
- }
-
- & div.flavor_usage_bar {
- width: auto;
- text-align: center;
- line-height: 0;
- height: 120px;
- }
- }
-}
-
-#interval_selector {
- position: absolute;
- right: 15px;
- top: 15px;
-
- li {
- display: inline;
- list-style-type: none;
- padding-right: 5px;
-
- &.active {
- font-weight: bold;
-
- a {
- color: black;
-
- &:hover {
- text-decoration:none;
- }
- }
- }
- }
-}
-
-svg {
- .axis {
- path, line {
- fill: none;
- stroke: #000;
- shape-rendering: crispEdges;
- }
- }
-}
-
-.communication_chart_wrapper {
- display:inline-block;
- vertical-align: middle;
- width: 38%;
-}
-
-.communication_chart_connection {
- display:inline-block;
- width: 60px;
- height: 30px;
- vertical-align: middle;
- background: url('/static/dashboard/img/communication_flow.png') no-repeat 50% 50%;
- background-size: 40px 20px;
-}
-
-.circles_chart_time_picker {
- float: right;
-}
-
-.csv_rack_table {
- padding-top: 40px;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_formsets.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_formsets.scss
deleted file mode 100644
index cb1f3b59..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_formsets.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-// formsets
-.datatable {
- th.narrow {
- width: 1em;
- }
-
- input {
- padding: 2px 5px;
- margin: 0;
- }
-
- input.number_input_slim {
- width: 4em;
- text-align: right;
- }
-
- th span.required:after {
- // Copied from horizon, because there is no way to reuse their class.
- content: "*";
- font-weight: bold;
- line-height: 0;
- padding-left: 4px;
- color: #428bca;
- }
-}
-
-.param-section {
- padding-bottom: 15px;
-}
-
-#collapse-upload-form {
- text-align: center;
- padding-bottom: 30px;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_icons.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_icons.scss
deleted file mode 100644
index e5ea1f78..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_icons.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.text-warning .fa,
-.alert-warning .fa,
-.text-warning.fa {
- color: #eea236;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss
deleted file mode 100644
index b9cdfcdd..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss
+++ /dev/null
@@ -1,271 +0,0 @@
-#nodes-formset-datatable .datatable tbody {
- input {
- padding: 2px 1px;
- }
- input.number_input_slim {
- width: 3em;
- }
- td {
- padding: 2px;
- text-align: center;
- a.close {
- margin-right: 4px;
- }
- }
-}
-
-$link-color: #428bca;
-
-// Register nodes formset
-.register-nodes-formset {
- a.node-icon {
- display: block;
- float: right;
- padding-left: 10px;
- }
-
- .nav-pills > .active > a {
- color: #fff;
- background-color: $link-color;
- }
-
- ul.nav-pills > li span.delete-icon {
- display: none;
- }
-
- ul.nav-pills > li.active span.delete-icon {
- display: block;
- cursor: default;
- color: #fff;
- z-index: 1000;
- position: absolute;
- right: 16px;
- top: 10px;
- }
-
- ul.nav-pills > li {
- position: relative;
- }
-
- ul.nav-pills > li.active {
- width: 107%;
- }
-
- ul.nav-pills > li.active:after {
- display: block;
- content: '';
- position: absolute;
- top: 1px;
- right: -7px;
- border-top: 18px solid transparent;
- border-bottom: 18px solid transparent;
- border-left: 8px solid $link-color;
- }
-
- .register-nav-head {
- margin-top: 19px;
- margin-bottom: 5px;
-
- h4 {
- margin: 0;
- }
- }
-
- .form h5 {
- margin: 0.5em 0 0.75em 0;
- }
-
- .form label.checkbox {
- font-weight: normal;
- }
-
- .panel {
- margin: 8px -8px 8px -8px;
- padding: 8px;
- .panel-heading {
- margin: -8px -8px 0 -8px;
- }
- }
-
- fieldset .form-group {
- width: 100%;
- input, textarea, select {
- width: 100%;
- margin-left: 5px;
- }
- }
-
- .required label:after {
- content: "*";
- font-weight: bold;
- line-height: 0;
- padding-left: 4px;
- color: #428bca;
- }
-}
-
-
-#detail__overview, #collapseParameters {
- ul {
- list-style: disc;
- margin: 0 0 9px 25px;
- }
-}
-
-// Power state icons
-.fa.powerstate {
- display: inline-block;
- vertical-align: middle;
- &.fa-play {
- color: $brand-success;
- }
-}
-
-// Node detail view
-.node-details {
- .chart svg {
- path.undefined {
- stroke-width: 1px;
- stroke: #ff7f0e;
- }
-
- .y_ticks text {
- display: none;
- }
- }
-}
-
-// Node overview
-
-.nodes {
- .widget-info {
- padding: 0 30px 0 30px;
- text-align: center;
- span {
- font-size: 300%;
- }
- }
-
- .d3_pie_chart_distribution {
- .legend {
- position: relative;
- }
- }
-}
-
-// Plan overview
-
-.deployment-icon {
- vertical-align: middle;
- float: left;
- text-align: center;
- color: #ddd;
- background: #fff;
-}
-
-.deployment-box {
- border-left: 4px solid #eee;
- margin-left: 24px;
- margin-bottom: 12px;
- padding-left: 20px;
-}
-
-.deployment-buttons {
- margin-top: 12px;
-}
-
-.deployment-roles-label {
- font-weight: bold;
- display: block;
- margin: 14px 0 0 0;
-}
-
-.deploy-role-icon {
- a {
- display: inline-block;
- margin: 7px 0 0 0;
- }
- i {
- display: inline-block;
- margin: 14px 0 0 0;
- }
-}
-.deploy-role-count {
- text-align: right;
- vertical-align: middle;
- font-size: 32px;
- small {
- font-size: 28px;
- }
-}
-
-#collapseDriver {
- margin-left: -50px;
-}
-
-/* Configuration parameters */
-table .data-table-config-label {
- width: 20em;
- vertical-align: top;
-}
-table .data-table-config-value {
- font-weight: bold;
-}
-table.definition-list-table > thead {
- display: none;
-}
-table.definition-list-table > tbody > tr > td {
- border: 0;
-}
-
-
-ul.nav-arrow {
- padding-top: 30px;
- & > li {
- z-index: 1000;
- position: relative;
- margin-right: -30px;
- & > a {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
- &.active {
- margin-right: -31px;
- &:after {
- display: block;
- content: '';
- position: absolute;
- top: 0px;
- right: -12px;
- border-top: 19px solid transparent;
- border-bottom: 19px solid transparent;
- border-left: 12px solid $link-color;
- }
- }
- }
-}
-
-.configuration-panel {
- padding: 15px 0 0px 30px !important;
- border: none;
- border-left: 1px solid $border-color;
- border-radius: 0;
- box-shadow: none;
-}
-
-.password-button + div.popover {
- white-space: pre-wrap;
- word-wrap: break-word;
-}
-
-.form-horizontal label .popover {
- min-width: 250px;
- .popover-content {
- font-weight: normal;
- }
-}
-
-// hacky positioning of advanced service config form buttons next to header
-.page_form_actions {
- margin-top: -5em;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_numberpicker.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_numberpicker.scss
deleted file mode 100644
index d443c41e..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_numberpicker.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-// Number picker widget
-div.number_picker {
- text-align: left;
- margin: 5px 14px;
- width: 35px;
- height: 35px;
- padding: 0;
- display: inline-block;
- position: relative;
- vertical-align: middle;
- input {
- border: 0;
- width: 20px;
- display: block;
- margin: 8px auto;
- padding: 0;
- text-align: center;
- outline: 0;
- }
- a {
- display: block;
- width: 14px;
- position: absolute;
- top: 8px;
- cursor: default;
- &.disabled {
- cursor: default;
- }
- &.arrow-left {
- left: -12px;
- }
- &.arrow-right {
- right: -16px;
- }
- }
- a.disabled {
- opacity: 0.25;
- }
- &.readonly a {
- display: none;
- }
- span.step {
- display: block;
- font-size: 75%;
- position: absolute;
- top: -2px;
- right: -14px;
- }
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/_tables.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/_tables.scss
deleted file mode 100644
index d881ef4c..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/_tables.scss
+++ /dev/null
@@ -1,74 +0,0 @@
-tr.table_actions_row {
- th.table_header {
- background: #f9f9f9;
- border-top-width: 2px !important;
- .table_actions {
- float: none;
- .table_search, .table_filter {
- .filter_select {
- display: inline-block;
- position: relative;
- background: rgb(243, 243, 243);
- margin-right: -4px;
- &:after, &:before {
- left: 100%;
- top: 50%;
- border: solid transparent;
- content: " ";
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- z-index: 3;
- }
-
- &:after {
- border-color: rgba(243, 243, 243, 0);
- border-left-color: #f3f3f3;
- border-width: 14px;
- margin-top: -14px;
- }
- &:before {
- border-color: rgba(204, 204, 204, 0);
- border-left-color: #cccccc;
- border-width: 16px;
- margin-top: -16px;
- }
- select {
- overflow:hidden;
- width: 120%;
- border-right: none;
- box-shadow: none;
- background: transparent;
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
- .caret {
- position: absolute;
- right: 0;
- top: 47%;
- }
- }
- input[type="text"] {
- padding-right: 5px;
- }
- input.filter_select_input {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- border-left: none;
- padding-left: 20px;
- &:focus {
- box-shadow: none;
- }
- }
- }
- }
- }
-}
-
-.table > thead > tr > th {
- border-bottom: none;
-}
-tr.column_headers th {
- border: 1px solid $gray-light;
-}
diff --git a/tuskar_ui/infrastructure/static/infrastructure/scss/infrastructure.scss b/tuskar_ui/infrastructure/static/infrastructure/scss/infrastructure.scss
deleted file mode 100644
index 3194e008..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/scss/infrastructure.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Additional CSS for infrastructure. */
-@import "/dashboard/scss/variables";
-@import "/bootstrap/scss/bootstrap/variables";
-
-@import "numberpicker";
-@import "breadcrumbs";
-@import "buttons";
-@import "capacities";
-@import "flavor_usages";
-@import "formsets";
-@import "individual_pages";
-@import "tables";
-@import "charts";
-@import "icons";
-@import "detail_pages"
diff --git a/tuskar_ui/infrastructure/static/infrastructure/tests/formset_table.js b/tuskar_ui/infrastructure/static/infrastructure/tests/formset_table.js
deleted file mode 100644
index 184d7706..00000000
--- a/tuskar_ui/infrastructure/static/infrastructure/tests/formset_table.js
+++ /dev/null
@@ -1,60 +0,0 @@
-horizon.addInitFunction(function () {
- module("Formset table (tuskar.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');
- tuskar.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');
- tuskar.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);
- tuskar.formset_table.replace_delete(row);
- var x = input.next('a');
- tuskar.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 = '<tr><td><input id="id_flavors-__prefix__-name" name="flavors-__prefix__-name"></td></tr>';
-
- equal(table.find('tbody tr').length, 3);
- equal(html.find('#id_flavors-TOTAL_FORMS').val(), 3);
- tuskar.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');
-
- tuskar.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');
-
- tuskar.formset_table.init('flavors', '', '');
- equal(table.find('tfoot tr a').length, 0);
- });
-});
diff --git a/tuskar_ui/infrastructure/templates/client_side/_modal_chart.html b/tuskar_ui/infrastructure/templates/client_side/_modal_chart.html
deleted file mode 100644
index e26a7d4d..00000000
--- a/tuskar_ui/infrastructure/templates/client_side/_modal_chart.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "horizon/client_side/template.html" %}
-{% load horizon %}
-
-{% block id %}modal_chart_template{% endblock %}
-
-{% block template %}
-{% jstemplate %}
-<div class="[[classes]]" style="top: 80px; display: block;">
- <ul id="interval_selector">
- <li><a href="#" data-interval="12h">12h</a></li>
- <li><a href="#" data-interval="24h">24h</a></li>
- <li class="active"><a href="#" data-interval="1w">1w</a></li>
- <li><a href="#" data-interval="1m">1m</a></li>
- <li><a href="#" data-interval="1y">1y</a></li>
- </ul>
- <div id="modal_chart"></div>
-</div>
-{% endjstemplate %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/client_side/templates.html b/tuskar_ui/infrastructure/templates/client_side/templates.html
deleted file mode 100644
index c1646585..00000000
--- a/tuskar_ui/infrastructure/templates/client_side/templates.html
+++ /dev/null
@@ -1 +0,0 @@
-{% include "client_side/_modal_chart.html" %}
diff --git a/tuskar_ui/infrastructure/templates/formset_table/_row.html b/tuskar_ui/infrastructure/templates/formset_table/_row.html
deleted file mode 100644
index 5409a0b5..00000000
--- a/tuskar_ui/infrastructure/templates/formset_table/_row.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<tr{{ row.attr_string|safe }}>
- {% for cell in row %}
- <td{{ cell.attr_string|safe }}>
- {% if cell.field %}
- {{ cell.field }}
- {% else %}
- {%if cell.wrap_list %}<ul>{% endif %}{{ cell.value }}{%if cell.wrap_list %}</ul>{% endif %}
- {% endif %}
- {% if forloop.first %}
- {% for field in row.form.hidden_fields %}
- {{ field }}
- {% for error in field.errors %}
- <span class="help-inline">{{ field.name }}: {{ error }}</span>
- {% endfor %}
- {% endfor %}
- {% if row.form.non_field_errors %}
- <div class="alert alert-error">
- {{ row.form.non_field_errors }}
- </div>
- {% endif %}
- {% endif %}
- </td>
- {% endfor %}
-</tr>
diff --git a/tuskar_ui/infrastructure/templates/formset_table/_table.html b/tuskar_ui/infrastructure/templates/formset_table/_table.html
deleted file mode 100644
index be90b786..00000000
--- a/tuskar_ui/infrastructure/templates/formset_table/_table.html
+++ /dev/null
@@ -1,43 +0,0 @@
-{% extends 'horizon/common/_data_table.html' %}
-{% load i18n %}
-
-{% block table_columns %}
- {% if not table.is_browser_table %}
- <tr>
- {% for column in columns %}
- <th {{ column.attr_string|safe }}><span
- {% if column.name in table.get_required_columns %}
- class="required"
- {% endif %}
- >{{ column }}</span></th>
- {% endfor %}
- </tr>
- {% endif %}
-{% endblock table_columns %}
-
-{% block table %}
- {% with table.get_formset as formset %}
- {{ formset.management_form }}
- {% if formset.non_field_errors %}
- <div class="alert alert-error">
- {{ formset.non_field_errors }}
- </div>
- {% endif %}
- {% endwith %}
- {{ block.super }}
-
-<script type="text/javascript">
- (window.$ || window.addHorizonLoadEvent)(function () {
- // prepare the js-enabled parts of the formset data table
- var prefix = '{{ table.name|escapejs }}';
- var empty_row_html = '{% filter escapejs %}{% include "formset_table/_row.html" with row=table.get_empty_row %}{% endfilter %}';
- {% if table.formset_class.extra %}
- var add_label = '{% filter escapejs %}{% trans "Add a row" %}{% endfilter %}';
- {% else %}
- var add_label = '';
- {% endif %}
-
- tuskar.formset_table.init(prefix, empty_row_html, add_label);
- });
-</script>
-{% endblock table %}
diff --git a/tuskar_ui/infrastructure/templates/formset_table/menu_formset.html b/tuskar_ui/infrastructure/templates/formset_table/menu_formset.html
deleted file mode 100644
index e9568066..00000000
--- a/tuskar_ui/infrastructure/templates/formset_table/menu_formset.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% load i18n %}
-{{ formset.management_form }}
-{% for error in formset.non_form_errors %}
- <div class="alert alert-error error">{{ error }}</div>
-{% endfor %}
-<div class="row register-nodes-formset" id="formset-{{ formset.prefix }}">
- <div class="col-xs-5">
- <div class="clearfix register-nav-head">
- <a class="node-icon" data-toggle="collapse" href="#collapse-upload-form">
- <i class="fa fa-lg fa-upload"></i>
- </a>
- <a id="add-node-link" class="node-icon" href="#">
- <i class="fa fa-lg fa-plus"></i>
- </a>
- <h4>Nodes to register</h4>
- </div>
- <div id="collapse-upload-form" class="panel-collapse collapse">
- {% include 'infrastructure/nodes/_upload.html' with form=upload_form %}
- </div>
- <ul class="nav nav-pills nav-stacked">
- {% for form in formset %}
- <li {% if forloop.first %}class="active"{% endif %}>
- <a href="#tab-{{ form.prefix }}" data-toggle="tab">
- {{ form.get_name }}
- </a>
- </li>
- {% endfor %}
- </ul>
- </div>
- <div class="col-xs-7">
- <div class="tab-content">
- {% for form in formset %}
- {% include form_template with form=form active=forloop.first %}
- {% endfor %}
- </div>
- </div>
-</div>
-<script type="text/javascript">
-(window.$ || window.addHorizonLoadEvent)(function () {
- var prefix = '{{ formset.prefix|escapejs }}';
- var empty_form_html = '{% filter escapejs %}{% include form_template with form=formset.empty_form %}{% endfilter %}';
- tuskar.menu_formset.init(prefix, empty_form_html);
-});
-</script>
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown.html b/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown.html
deleted file mode 100644
index d719b5d8..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% if action.method != "GET" %}
- <button {{ action.attr_string|safe }} name="action" value="{{ action.table.name }}__{{ action.name }}__{{ row_id }}" type="submit">
- {% if action.icon != None %}<span class="fa fa-{{ action.icon }}"></span> {% endif %}
- {{ action.verbose_name }}
- </button>
-{% else %}
- <a href='{{ action.bound_url }}' {{ action.attr_string|safe }}>
- {% if action.icon != None %}<span class="fa fa-{{ action.icon }}"></span> {% endif %}
- {{ action.verbose_name }}
- </a>
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown_first_item.html b/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown_first_item.html
deleted file mode 100644
index 1596e247..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown_first_item.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% if action.method != "GET" %}
- <button {{ action.attr_string|safe }} name="action" value="{{ action.table.name }}__{{ action.name }}__{{ row_id }}" type="submit">
- {% if action.icon != None %}
- <span class="fa fa-{{ action.icon }}" title="{{ action.verbose_name }}"></span>
- {% else %}
- {{ action.verbose_name }}
- {% endif %}
- </button>
-{% else %}
- <a href='{{ action.bound_url }}' {{ action.attr_string|safe }}>
- {% if action.icon != None %}
- <span class="fa fa-{{ action.icon }}" title="{{ action.verbose_name }}"></span>
- {% else %}
- {{ action.verbose_name }}
- {% endif %}
- </a>
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_actions_dropdown.html b/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_actions_dropdown.html
deleted file mode 100644
index 3c842b68..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_actions_dropdown.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% load horizon i18n %}
-
-{% spaceless %} {# This makes sure whitespace doesn't affect positioning for dropdown. #}
-{% if row_actions|length > 1 %}
-<div class="btn-group {% if pull_right %}pull-right{% endif %}">
- {% for action in row_actions %}
- {% if forloop.first %}
- {% include "horizon/common/_data_table_row_action_dropdown_first_item.html" %}
- <a class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" href="#">
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu row_actions clearfix">
- {% else %}
- <li class="clearfix">
- {% include "horizon/common/_data_table_row_action_dropdown.html" %}
- </li>
- {% endif %}
- {% if forloop.last %}
- </ul>
- {% endif %}
- {% endfor %}
-</div>
-{% endif %}
-{% if row_actions|length == 1%}
- {% include "horizon/common/_data_table_row_action_dropdown_first_item.html" with action=row_actions.0%}
-{% endif %}
-{% endspaceless %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_table_actions.html b/tuskar_ui/infrastructure/templates/horizon/common/_data_table_table_actions.html
deleted file mode 100644
index 4cc4afe3..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_data_table_table_actions.html
+++ /dev/null
@@ -1,68 +0,0 @@
-{% load i18n %}
-<div class="table_actions">
-{% block table_filter %}
- {% if filter.filter_type == 'fixed' %}
- <div class="table_filter btn-group" data-toggle="buttons-radio">
- {% for button in filter.fixed_buttons %}
- <button name="{{ filter.get_param_name }}" type="submit" value="{{ button.value }}" class="btn btn-default btn-sm{% ifequal button.value filter.filter_string %} active{% endifequal %}">{% if button.icon %}<i class="{{ button.icon }}"></i> {% endif %}{{ button.text }}{% if button.count >= 0 %} ({{ button.count }}){% endif %}</button>
- {% endfor %}
- </div>
- {% elif filter.filter_type == 'query' %}
- <div class="table_search client">
- <div class="form-group input-group">
- <input class="form-control" value="{{ filter.filter_string|default:'' }}" type="text" name="{{ filter.get_param_name }}" />
- <span class="input-group-btn">
- <button class="btn btn-default" type="submit" {{ filter.attr_string|safe }}>
- <span class="fa fa-lg fa-filter"></span>
- </button>
- </span>
- </div>
- </div>
- {% elif filter.filter_type == 'server' %}
- <div class="table_search">
- <div class="filter_select">
- <select name="{{ filter.get_param_name }}_field" class="form-control">
- {% for choice in filter.filter_choices %}
- <option value="{{ choice.0 }}" {% if choice.0 == filter.filter_field %} selected{% endif %}>{{ choice.1 }}</option>
- {% endfor %}
- </select>
- <span class="caret"></span>
- </div>
- <div class="form-group input-group">
- <input class="form-control filter_select_input" value="{{ filter.filter_string|default:'' }}" type="text" name="{{ filter.get_param_name }}" />
- <span class="input-group-btn">
- <button class="btn btn-default" type="submit" {{ filter.attr_string|safe }}>
- <span class="glyphicon glyphicon-search"></span>
- </button>
- </span>
- </div>
- </div>
- {% endif %}
-{% endblock table_filter %}
-{% block table_actions %}
- <div class="table_actions pull-right">
- {% for action in table_actions_buttons %}
- {% include "horizon/common/_data_table_table_action.html" %}
- {% endfor %}
- {% if table_actions_menu|length > 0 %}
- <div class="btn-group table_actions_menu">
- <a class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" href="#">
- {% if table_actions_buttons|length > 0 %}
- {% trans "More Actions" %}
- {% else %}
- {% trans "Actions" %}
- {% endif %}
- <span class="caret"></span>
- </a>
- <ul class="dropdown-menu clearfix">
- {% for action in table_actions_menu %}
- <li class="clearfix">
- {% include "horizon/common/_data_table_table_action.html" %}
- </li>
- {% endfor %}
- </ul>
- </div>
- {% endif %}
- </div>
-{% endblock table_actions %}
-</div>
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_definition_list_data_table.html b/tuskar_ui/infrastructure/templates/horizon/common/_definition_list_data_table.html
deleted file mode 100644
index 064c5ac6..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_definition_list_data_table.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "horizon/common/_enhanced_data_table.html" %}
-
-{% block table_css_classes %}table table-condensed definition-list-table {{ table.css_classes }}{% endblock %}
-
-{% block table_footer %}{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_enhanced_data_table.html b/tuskar_ui/infrastructure/templates/horizon/common/_enhanced_data_table.html
deleted file mode 100644
index 0fb7fccb..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_enhanced_data_table.html
+++ /dev/null
@@ -1,54 +0,0 @@
-{% extends "horizon/common/_data_table.html" %}
-
-
-{% block table %}
- <table id="{{ table.slugify_name }}" class="{% block table_css_classes %}table table-striped datatable {{ table.css_classes }}{% endblock %}">
- <thead>
- {% block table_caption %}
- {% if not hidden_title %}
- <tr class='table_caption'>
- <th class='table_header' colspan='{{ columns|length }}'>
- <h3 class='table_title'>{{ table }}</h3>
- </th>
- </tr>
- {% endif %}
- {% if table.get_table_actions %}
- <tr class='table_actions_row'>
- <th class='table_header' colspan='{{ columns|length }}'>
- {{ table.render_table_actions }}
- </th>
- </tr>
- {% endif %}
- {% endblock table_caption %}
-
- {% block table_breadcrumb %}
- {{ block.super }}
- {% endblock table_breadcrumb %}
-
- {% block table_columns %}
- {% if not table.is_browser_table %}
- <tr class="column_headers">
- {% for column in columns %}
- <th {{ column.attr_string|safe }}>
- {{ column }}
- {% if column.help_text %}
- <span class="help-icon" data-toggle="tooltip" title="{{ column.help_text }}">
- <span class="glyphicon glyphicon-question-sign"></span>
- </span>
- {% endif %}
- </th>
- {% endfor %}
- </tr>
- {% endif %}
- {% endblock table_columns %}
- </thead>
-
- {% block table_body %}
- {{ block.super }}
- {% endblock table_body %}
-
- {% block table_footer %}
- {{ block.super }}
- {% endblock table_footer %}
- </table>
-{% endblock table %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_horizontal_field.html b/tuskar_ui/infrastructure/templates/horizon/common/_horizontal_field.html
deleted file mode 100644
index f15f9284..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_horizontal_field.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% load form_helpers %}
-
-<div class="form-group{% if field.errors %} has-error{% endif %} {{ field.css_classes }}">
- <label class="control-label col-lg-4 col-md-5 {% if field.field.required or field.field.parameter.is_required %}{{ form.required_css_class }}{% endif %}" for="{{ field.id_for_label }}">{{ field.label }}</label>
- <div class="col-lg-8 col-md-7 {{ classes.value }} {{ field|wrapper_classes }}">
- {{ field|add_bootstrap_class }}
- {% for error in field.errors %}
- <span class="help-block alert alert-danger {{ form.error_css_class }}">{{ error }}</span>
- {% empty %}
- {% comment %}
- Escape help_text a second time here, to avoid an XSS issue in bootstrap.js.
- This can most likely be removed once we upgrade bootstrap.js past 2.0.2.
- Note: the spaces are necessary here.
- {% endcomment %}
- {% if field.help_text %}
- <span class="help-block">{% filter force_escape %} {{ field.help_text }} {% endfilter %} </span>
- {% endif %}
- {% endfor %}
- </div>
-</div>
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_items_count_domain_page_header.html b/tuskar_ui/infrastructure/templates/horizon/common/_items_count_domain_page_header.html
deleted file mode 100644
index dafe4007..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_items_count_domain_page_header.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-{% block page_header %}
- <div class='page-header'>
- {% include 'infrastructure/header_actions.html' %}
- <h1>
- {% if request.session.domain_context_name %}
- <em>{{ request.session.domain_context_name }}:</em>
- {% endif %}
- {{ title }}
- {% if items_count or items_count == 0%}
- <span class="badge">
- {{ items_count }}
- </span>
- {% endif %}
- </h1>
- </div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/horizon/common/_items_count_tab_group.html b/tuskar_ui/infrastructure/templates/horizon/common/_items_count_tab_group.html
deleted file mode 100644
index 1d873133..00000000
--- a/tuskar_ui/infrastructure/templates/horizon/common/_items_count_tab_group.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% with tab_group.get_tabs as tabs %}
-{% if tabs %}
-
- {# Tab Navigation #}
- <ul id="{{ tab_group.get_id }}" {{ tab_group.attr_string|safe }}>
- {% for tab in tabs %}
- <li {{ tab.attr_string|safe }}>
- <a href="?{{ tab_group.param_name}}={{ tab.get_id }}" data-toggle="tab" data-target="#{{ tab.get_id }}" data-loaded='{{ tab.load|yesno:"true,false" }}'>
- {{ tab.name }} {% if tab.get_items_count %} ({{ tab.get_items_count }}) {% endif %}
- </a>
- </li>
- {% endfor %}
- </ul>
-
- {# Tab Content #}
- <div class="tab-content">
- {% for tab in tabs %}
- <div id="{{ tab.get_id }}" class="tab-pane{% if tab.is_active %} active{% endif %}">
- {{ tab.render }}
- </div>
- {% endfor %}
- </div>
-
-{% endif %}
-{% endwith %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow.html b/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow.html
deleted file mode 100644
index f7870a1b..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow.html
+++ /dev/null
@@ -1,37 +0,0 @@
-{% load i18n %}
-{% with workflow.get_entry_point as entry_point %}
-<div class="row">
- <form class="form form-horizontal col-xs-12" {{ workflow.attr_string|safe }} action="{{ workflow.get_absolute_url }}" {% if add_to_field %}data-add-to-field="{{ add_to_field }}"{% endif %} method="POST"{% if workflow.multipart %} enctype="multipart/form-data"{% endif %}>
- {% csrf_token %}
- {% if REDIRECT_URL %}<input type="hidden" name="{{ workflow.redirect_param_name }}" value="{{ REDIRECT_URL }}"/>{% endif %}
- <div class="fullscreen-workflow-body">
- <div class="actions pull-right">
- {% block workflow-buttons %}
- <input class="btn btn-primary pull-right"
- type="submit"
- value="{{ workflow.finalize_button_name }}">
- {% endblock %}
- </div>
- {% block workflow-body %}
- <ul class="nav nav-tabs">
- {% for step in workflow.steps %}
- <li class="{% if entry_point == step.slug %}active{% endif %}{% if step.has_errors %} error{% endif %}{% if step.has_required_fields %} required{% endif %}">
- <a href="#{{ step.get_id }}" data-toggle="tab" data-target="#{{ step.get_id }}">{{ step }}</a>
- </li>
- {% endfor %}
- </ul>
- <div class="tab-content">
- {% for step in workflow.steps %}
- <fieldset id="{{ step.get_id }}" class="js-tab-pane{% if entry_point == step.slug %} active{% endif %}">
- {{ step.render }}
- </fieldset>
- {% if not forloop.last %}
- <noscript><hr /></noscript>
- {% endif %}
- {% endfor %}
- </div>
- {% endblock %}
- </div>
- </form>
-</div>
-{% endwith %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow_base.html b/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow_base.html
deleted file mode 100644
index 35dc70e7..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow_base.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/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 'infrastructure/_fullscreen_workflow.html' %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart.html b/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart.html
deleted file mode 100644
index f3806712..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<h4>{{ label }}</h4>
-<div class="overview_chart">
- <div class="chart_container">
- <div class="chart"
- data-chart-type="line_chart"
- data-url="{{ url }}"
- data-form-selector='#linechart_general_form'
- data-settings='{ "auto_size": false, "axes_x" : false, "axes_y" : false {% if y_max %}, "yMax": {{ y_max }} {% endif %} }'>
- </div>
- </div>
- <div class="bar_chart_container">
- <div class="chart"
- data-chart-type="overview_bar_chart">
- </div>
- </div>
-</div>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart_box.html b/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart_box.html
deleted file mode 100644
index 1ad01607..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_performance_chart_box.html
+++ /dev/null
@@ -1,68 +0,0 @@
-{% load i18n %}
-{% load url from future%}
-
-{% if meter_conf %}
- <div class="nodes">
- <div id="ceilometer-stats">
- <form class="form-inline performance_charts" id="linechart_general_form">
- <div class="pull-right">
- <div class="form-group">
- <div class="input-group input-group-sm">
- <div class="input-group-addon"><i class="fa fa-clock-o"></i></div>
- <select data-line-chart-command="select_box_change"
- id="date_options"
- name="date_options"
- class="form-control">
- <option value="0.041666">{% trans "Last hour" %}</option>
- <option value="0.25">{% trans "Last 6 hours" %}</option>
- <option value="0.5">{% trans "Last 12 hours" %}</option>
- <option value="1" selected="selected">{% trans "Last day" %}</option>
- <option value="7">{% trans "Last week" %}</option>
- <option value="{% now 'j' %}">{% trans "Month to date" %}</option>
- <option value="15">{% trans "Last 15 days" %}</option>
- <option value="30">{% trans "Last 30 days" %}</option>
- <option value="365">{% trans "Last year" %}</option>
- <option value="other">{% trans "Other" %}</option>
- </select>
- </div>
- </div>
- <div class="form-group" id="date_from_group">
- <div class="input-group input-group-sm">
- <div class="input-group-addon">{% trans "From" %}</div>
- <input data-line-chart-command="date_picker_change"
- type="text"
- id="date_from"
- name="date_from"
- class="form-control"/>
- </div>
- </div>
- <div class="form-group" id="date_to_group">
- <div class="input-group input-group-sm">
- <div class="input-group-addon">{% trans "To" %}</div>
- <input data-line-chart-command="date_picker_change"
- type="text"
- id="date_to"
- name="date_to"
- class="form-control"/>
- </div>
- </div>
- </div>
- </form>
- </div>
- </div>
-
- <script type="text/javascript">
- (window.$ || window.addHorizonLoadEvent)(
- tuskar.performance.show_hide_datepickers);
- </script>
-
- <div id="node-charts" class="nodes row">
- {% for meter_label, url_part, y_max in meter_conf %}
- <div class="col-xs-{{col_size}}">
- {% include "infrastructure/_performance_chart.html" with label=meter_label y_max=y_max url=node_perf_url|add:"?"|add:url_part only %}
- </div>
- {% endfor %}
- </div>
-{% else %}
- <p>{% trans 'Metering service is not enabled.' %}</p>
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_scripts.html b/tuskar_ui/infrastructure/templates/infrastructure/_scripts.html
deleted file mode 100644
index ff190f01..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_scripts.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% block custom_js_files %}
- <script src='{{ STATIC_URL }}infrastructure/js/horizon.capacity.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/horizon.d3circleschart.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/horizon.d3singlebarchart.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.templates.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.formset_table.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.menu_formset.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.number_picker.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.edit_plan.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.deployment_progress.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.performance.js' type='text/javascript' charset='utf-8'></script>
- <script src='{{ STATIC_URL }}infrastructure/js/tuskar.deployment_live.js' type='text/javascript' charset='utf-8'></script>
-{% endblock %}
-
-{% comment %} Tuskar-UI Client-side Templates (These should *not* be inside the "compress" tag.) {% endcomment %}
-{% include "client_side/templates.html" %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_top_5_box.html b/tuskar_ui/infrastructure/templates/infrastructure/_top_5_box.html
deleted file mode 100644
index 1ac664d7..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_top_5_box.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="row">
- <div class="col-xs-2 col-xs-offset-3">{% include "infrastructure/_top_5_chart.html" with top_5=top_5.fan%}</div>
- <div class="col-xs-2">{% include "infrastructure/_top_5_chart.html" with top_5=top_5.voltage %}</div>
- <div class="col-xs-2">{% include "infrastructure/_top_5_chart.html" with top_5=top_5.temperature%}</div>
-</div>
-<div class="row">
- <div class="col-xs-2 col-xs-offset-3">{% include "infrastructure/_top_5_chart.html" with top_5=top_5.current %}</div>
-</div>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_top_5_chart.html b/tuskar_ui/infrastructure/templates/infrastructure/_top_5_chart.html
deleted file mode 100644
index d4d730cf..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_top_5_chart.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% load i18n %}
-{% load chart_helpers %}
-
-<h4>
- {% trans 'Top 5 Nodes' %} ({{ top_5.label }}):
-</h4>
-{% if top_5.data %}
-<table width="100%">
- {% for d in top_5.data %}
- <tr>
- <td>
- <a href="{% url 'horizon:infrastructure:nodes:node_detail' d.node_uuid %}">
- {{ d.node_uuid|truncatechars:6 }}
- </a>
- </td>
- <td>
- {{ d.value}} {{ top_5.unit }}
- </td>
- <td>
- {%if d.direction %}
- <i class="fa fa-arrow-{{d.direction}}"></i>
- {% endif %}
- </td>
- </tr>
- {% endfor %}
-</table>
-{% else %}
-{% trans 'No data available.' %}
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/_workflow_base.html b/tuskar_ui/infrastructure/templates/infrastructure/_workflow_base.html
deleted file mode 100644
index 5aa1a050..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/_workflow_base.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/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 %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/base.html b/tuskar_ui/infrastructure/templates/infrastructure/base.html
deleted file mode 100644
index c5ebbc4c..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/base.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends 'base.html' %}
-
-{% block css %}
- {{block.super}}
-
- {% load compress %}
- {% compress css %}
- <link href='{{ STATIC_URL }}infrastructure/scss/infrastructure.scss' type='text/scss' media='screen' rel='stylesheet' />
- <link href="{{ STATIC_URL }}horizon/lib/font-awesome/css/font-awesome.css" type="text/css" media="screen" rel="stylesheet" />
-
- {% endcompress %}
-{% endblock %}
-
-{% block js %}
- {{ block.super }}
- {% include "infrastructure/_scripts.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/base_detail.html b/tuskar_ui/infrastructure/templates/infrastructure/base_detail.html
deleted file mode 100644
index 591c5362..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/base_detail.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-
-{% block main %}
-
-<div class="row">
- <div class="col-xs-12">
- {% block breadcrumbs %}{% endblock %}
-
- <div class="pull-right btn-toolbar">
- {% block actions %}{% endblock %}
- </div>
-
- <h3>{% block name %}{% endblock %}</h3>
- </div>
-</div>
-
-<div class="row">
- <div class="col-xs-12">
- <div class="pull-right">
- {% block overall_usage %}{% endblock %}
- </div>
-
- {{ tab_group.render }}
- </div>
-</div>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/header_actions.html b/tuskar_ui/infrastructure/templates/infrastructure/header_actions.html
deleted file mode 100644
index 6544f295..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/header_actions.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<div class="actions pull-right">
- {% for action in header_actions %}
- <a href="{{ action.url }}"
- title="{{ action.name }}"
- class="btn btn-default btn-lg btn-no-border {% if action.ajax_modal %}ajax-modal{% endif %}">
- {% if action.icon %}<span class="fa {{ action.icon }}"></span>{% endif %}
- {% if action.show_name %}{{ action.name }}{% endif %}
- </a>
- {% endfor %}
-</div>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_auto_discover_csv.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_auto_discover_csv.html
deleted file mode 100644
index b6d7f14b..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_auto_discover_csv.html
+++ /dev/null
@@ -1,23 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}autodiscover_csv_nodes_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:nodes:register' %}{% endblock %}
-
-{% block modal_id %}autodiscover_csv_nodes_modal{% endblock %}
-{% block modal-header %}{% trans "Upload Nodes" %}{% endblock %}
-{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
-
-{% block modal-body %}
- {% include "horizon/common/_form_fields.html" %}
-{% endblock %}
-
-{% block modal-footer %}
- <button class="btn btn-primary pull-right" type="submit">
- <i class="fa fa-upload"></i>
- {% trans "Upload" %}
- </button>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}"
- class="btn secondary cancel close">{% trans "Cancel" %}</a>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_detail_overview.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_detail_overview.html
deleted file mode 100644
index e000da05..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_detail_overview.html
+++ /dev/null
@@ -1,105 +0,0 @@
-{% load i18n %}
-{% load icon_helpers %}
-
-<div class="row node-details">
- <div class="col-lg-6 col-xs-12">
- <h4> {{ node.power_state|iconized_ironic_node_state|safe }} </h4>
-
- <h3>{% trans "Inventory" %}</h3>
- <dl class="dl-horizontal dl-horizontal-left">
- <dt>{% trans "Node UUID" %}</dt>
- <dd>{{ node.uuid|default:"&mdash;" }}</dd>
- <dt>{% trans "Driver" %}</dt>
- <dd>
- <a data-toggle="collapse" href="#collapseDriver">
- {{ node.driver|default:"&mdash;" }}
- <span class="caret"></span>
- </a>
- <div id="collapseDriver" class="panel-collapse collapse">
- <dl>
- {% if node.driver == 'pxe_ssh' %}
- <dt>IP Address</dt> <dd>{{ node.driver_info.ssh_address|default:"&mdash;" }}</dd>
- <dt>IPMI User</dt> <dd>{{ node.driver_info.ssh_username|default:"&mdash;" }}</dd>
- <dt>SSH Key</dt> <dd>{{ node.driver_info.ssh_key_contents|default:"&mdash;" }}</dd>
- {% else %}
- <dt>IP Address</dt> <dd>{{ node.driver_info.ipmi_address|default:"&mdash;" }}</dd>
- <dt>IPMI User</dt> <dd>{{ node.driver_info.ipmi_username|default:"&mdash;" }}</dd>
- <dt>IPMI Password</dt>
- <dd><form><fieldset><div class="form-group"><div>
- <input
- class="form-control"
- type="password"
- value="{{ node.driver_info.ipmi_password }}"
- disabled="true"
- readonly="true"/>
- </div></div></fieldset></form></dd>
- {% endif %}
- </dl>
- </div>
- </dd>
- <dt>{% trans "Network Cards" %}</dt>
- <dd>
- <a data-toggle="collapse" href="#collapseNetworkCards">
- {% blocktrans count addresses_length=node.addresses|length %}
- {{ addresses_length }} interface
- {% plural %}
- {{ addresses_length }} interfaces
- {% endblocktrans %}
- <span class="caret"></span>
- </a>
- <div id="collapseNetworkCards" class="panel-collapse collapse">
- <ul>
- {% for address in node.addresses %}
- <li>{{ address }}</li>
- {% endfor %}
- </ul>
- </div>
- </dd>
- <dt>{% trans "Registered HW" %}</dt>
- <dd>
- {{ node.cpu_arch|default:"&mdash;" }}<br />
- {{ node.cpus|default:"&mdash;" }} {% trans "CPU" %}<br />
- {{ node.memory_mb|default:"&mdash;" }} {% trans "RAM (MB)" %}<br />
- {{ node.local_gb|default:"&mdash;" }} {% trans "HDD (GB)" %}
- </dd>
- </dl>
-
- <h3>{% trans "Deployment" %}</h3>
- <dl class="dl-horizontal dl-horizontal-left">
- <dt>{% trans "Deployment Role" %}</dt>
- {% if stack and role %}
- <dd><a href="{% url 'horizon:infrastructure:roles:detail' role.id %}">{{ role.name }}</a></dd>
- {% else %}
- <dd>&mdash;</dd>
- {% endif %}
- <dt>{% trans "Provisioning" %}</dt>
- <dd>
- {{ node.provisioning_status|default:"&mdash;" }}
- {% if node.instance_uuid %}
- <br />{{ node.instance.created }}
- {% endif %}
- </dd>
- <dt>{% trans "Image" %}</dt>
- <dd>{{ node.image_name|default:"&mdash;" }}</dd>
- <dt>{% trans "Instance UUID" %}</dt>
- <dd>{{ node.instance_uuid|default:"&mdash;" }}</dd>
- </dl>
-
- <h3>{% trans "Deployment Images" %}</h3>
- <dl class="dl-horizontal dl-horizontal-left">
- <dt>{% trans "Kernel" %}</dt>
- <dd>{{ kernel_image.name|default:"&mdash;" }}</dd>
- <dt>{% trans "Ramdisk" %}</dt>
- <dd>{{ ramdisk_image.name|default:"&mdash;" }}</dd>
- </dl>
-
- </div>
- <div class="col-lg-6 col-xs-12">
- <h3>{% trans "Performance & Metrics" %}</h3>
- {% url 'horizon:infrastructure:nodes:performance' node.uuid as node_perf_url %}
- {% include "infrastructure/_performance_chart_box.html" with meter_conf=meter_conf node_perf_url=node_perf_url col_size=6 %}
- </div>
-</div>
-
-{% block additional_data %}
-{% endblock %} \ No newline at end of file
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_field.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_field.html
deleted file mode 100644
index 1b1c489a..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_field.html
+++ /dev/null
@@ -1,15 +0,0 @@
-{% load form_helpers %}
-<div class="form-group">
- <div class="row{% if field.errors %} error{% endif %}{% if required or field.required %} required{% endif %}">
- <label class="col-xs-4">{{ field.label }}</label>
- <div class="col-xs-6">{{ field|add_bootstrap_class }}</div>
- <div class="col-xs-2 muted">{{ extra_text|default:'' }}</div>
- </div>
- {% if field.errors %}
- <div class="error">
- {% for error in field.errors %}
- <span class="text-danger">{{ error }}</span>
- {% endfor %}
- </div>
- {% endif %}
-</div>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_form.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_form.html
deleted file mode 100644
index c828125d..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_form.html
+++ /dev/null
@@ -1,49 +0,0 @@
-{% load i18n %}
-{% load form_helpers %}
-
-<div class="container-fluid tab-pane{% if active %} active{% endif %}"
- id="tab-{{ form.prefix }}">
- <div class="form form-inline"><fieldset class="well">
- {% include 'horizon/common/_form_errors.html' with form=form %}
- <h4>{% trans "Node Detail" %}</h4>
- <div class="param-section">
- <h5>{% trans "Power Management" %}</h5>
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.driver required=True %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_address required=True %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_username %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ipmi_password %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_address required=True %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_username required=True %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.ssh_key_contents required=True %}
- </div>
- <div class="param-section">
- <h5>{% trans "Networking" %}</h5>
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.mac_addresses required=True %}
- </div>
- <div class="param-section">
- <h5>{% trans "Hardware" %}</h5>
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpu_arch %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.cpus extra_text=_('units') %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.memory_mb extra_text=_('MB') %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.local_gb extra_text=_('GB') %}
- </div>
- <div class="param-section">
- <h5>{% trans "Deployment Images" %}</h5>
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_kernel %}
- {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_ramdisk %}
- </div>
- </fieldset></div>
-</div>
-
-<script type="text/javascript">
-(window.$ || window.addHorizonLoadEvent)(function () {
- var form_prefix = '{{ form.prefix|escapejs }}';
- var $form = $('#tab-' + form_prefix);
- var undefined_name = '{{ form.get_name|escapejs }}';
-
- $form.find('input[name$="_address"]').change(function () {
- var $nav_link = $('a[href="#' + $form.attr('id') + '"]');
- $nav_link.html($(this).val() || undefined_name);
- });
-});
-</script>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_overview.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_overview.html
deleted file mode 100644
index 373084af..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_overview.html
+++ /dev/null
@@ -1,146 +0,0 @@
-{% load i18n %}
-{% load url from future%}
-
-<div class="nodes row">
- <div class="col-xs-4">
- <h3>{% trans 'Hardware Inventory' %}</h3>
- <hr>
- <div class="widget-info pull-left">
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__all">
- <span class="info">{{ nodes_all_count }}</span>
- <p>{% trans 'Nodes' %}</p>
- </a>
- </div>
- <div>
- <p>{{ cpus }} {% trans 'CPU cores' %}</p>
- <p>{{ memory_gb|floatformat:"0" }} {% trans 'GB of memory' %}</p>
- <p>{{ local_gb|floatformat:"0" }} {% trans 'GB of storage' %}</p>
- </div>
- </div>
- <div class="col-xs-4">
- <h3>{% trans 'Nodes Status' %}</h3>
- <hr>
- <div class="d3_pie_chart_distribution" data-used="{{ nodes_status_data }}"></div>
- </div>
- <div class="col-xs-4">
- <h3>{% trans 'Power Status' %}</h3>
- <hr>
- <div class="d3_pie_chart_distribution" data-used="Running={{ nodes_up_count }}|Stopped={{ nodes_down_count }}"></div>
- </div>
-</div>
-
-<h3>{% trans "Provisioned nodes" %}</h3>
-<hr>
-<div class="nodes row">
- <div class="col-xs-3">
- <div class="widget-info pull-left">
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__provisioned">
- <span class="info">{{ nodes_provisioned_count }}</span>
- <p>{% trans 'Provisioned' %}</p><p>{% trans 'Nodes' %}</p>
- </a>
- {% if nodes_provisioning_count %}
- <div class="widget-info">
- <i class="fa fa-spinner fa-spin text-info"></i>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__all">
- {% blocktrans count nodes_provisioning_count as counter %}
- {{ counter }} node
- {% plural %}
- {{ counter }} nodes
- {% endblocktrans %}
- </a>
- {% blocktrans count nodes_provisioning_count as counter %}
- is being provisioned
- {% plural %}
- are being provisioned
- {% endblocktrans %}
- </div>
- {% endif %}
- {% if nodes_deleting_count %}
- <div class="widget-info">
- <i class="fa fa-spinner fa-spin text-info"></i>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__all">
- {% blocktrans count nodes_deleting_count as counter %}
- {{ counter }} node
- {% plural %}
- {{ counter }} nodes
- {% endblocktrans %}
- </a>
- {% blocktrans count nodes_deleting_count as counter %}
- is being deleted
- {% plural %}
- are being deleted
- {% endblocktrans %}
- </div>
- {% endif %}
- {% if nodes_error_count %}
- <div class="widget-info">
- <i class="fa fa-exclamation-circle text-info"></i>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__all">
- {% blocktrans count nodes_error_count as counter %}
- {{ counter }} node
- {% plural %}
- {{ counter }} nodes
- {% endblocktrans %}
- </a>
- {% trans 'failed' %}
- </div>
- {% endif %}
- </div>
- </div>
- <div>
- {% if nodes_provisioned_count or nodes_provisioning_count %}
- {% url 'horizon:infrastructure:nodes:nodes_performance' as node_perf_url %}
- {% include "infrastructure/_performance_chart_box.html" with meter_conf=meter_conf node_perf_url=node_perf_url col_size=2 %}
- {% endif %}
- </div>
-</div>
-{% if nodes_provisioned_count or nodes_provisioning_count %}
-{% include "infrastructure/_top_5_box.html" %}
-{% endif %}
-{% if nodes_on_discovery_count or nodes_discovered_count or nodes_discovery_failed_count %}
-<h3>{% trans "Nodes Discovery" %}</h3>
-<hr>
-<div class="nodes row">
- <div class="col-xs-3">
- <div class="widget-info pull-left">
- {% if nodes_discovered_count %}
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__maintenance">
- <span class="info">{{ nodes_discovered_count }}</span>
- <p>{% trans 'Discovered' %}</p><p>{% trans 'Nodes' %}</p>
- </a>
- ({{ nodes_discovered_count }} {% trans 'waiting for activation' %})
- {% endif %}
- {% if nodes_on_discovery_count %}
- <div class="widget-info">
- <i class="fa fa-spinner fa-spin text-info"></i>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__maintenance">
- {% blocktrans count nodes_on_discovery_count as counter %}
- {{ counter }} node
- {% plural %}
- {{ counter }} nodes
- {% endblocktrans %}
- </a>
- {% blocktrans count nodes_on_discovery_count as counter %}
- is being discovered
- {% plural %}
- are being discovered
- {% endblocktrans %}
- </div>
- {% endif %}
- {% if nodes_discovery_failed_count %}
- <div class="widget-info">
- <i class="fa fa-exclamation-circle text-danger"></i>
- <a href="{% url 'horizon:infrastructure:nodes:index' %}?tab=nodes__maintenance">
- {% blocktrans count nodes_discovery_failed_count as counter %}
- {{ counter }} node
- {% plural %}
- {{ counter }} nodes
- {% endblocktrans %}
- </a>
- {% trans 'discovery failed' %}
- </div>
- {% endif %}
- </div>
- </div>
-</div>
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_register.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_register.html
deleted file mode 100644
index 1a1486a8..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_register.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}register_nodes_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:nodes:register' %}{% endblock %}
-{% block modal_id %}register_nodes_modal{% endblock %}
-{% block modal-header %}{% trans "Register Nodes" %}{% endblock %}
-{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
-
-{% block modal-body %}
-{% include "formset_table/menu_formset.html" with formset=form form_template="infrastructure/nodes/_nodes_formset_form.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_upload.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/_upload.html
deleted file mode 100644
index 7de46ec6..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/_upload.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% load i18n %}
-<div class="form-group">
- {% include 'horizon/common/_form_field.html' with field=form.csv_file %}
-</div>
-
-<button class="btn btn-primary">{% trans 'Upload Nodes' %}</button>
-<a href="#collapse-upload-form" data-toggle="collapse" class="btn btn-default">{% trans 'Cancel' %}</a>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/detail.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/detail.html
deleted file mode 100644
index bbbe8fe1..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/detail.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Node Details" %}{% endblock %}
-{% block page_header %}
-{% include "horizon/common/_page_header.html" %}
-{% endblock page_header %}
-{% block main %}
-<div class="row">
- <div class="col-sm-12">
- {{ tab_group.render }}
- </div>
-</div>
-{% endblock %} \ No newline at end of file
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/index.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/index.html
deleted file mode 100644
index 302d4418..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% load url from future %}
-{% block title %}{% trans 'Nodes' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_items_count_domain_page_header.html' with title=_('Nodes') %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-xs-12">
- {{ tab_group.render }}
- </div>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/nodes/register.html b/tuskar_ui/infrastructure/templates/infrastructure/nodes/register.html
deleted file mode 100644
index 3e9cb1f5..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/nodes/register.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Register Nodes" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Register Nodes") %}
-{% endblock %}
-
-{% block main %}
- {% include "infrastructure/nodes/_register.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/_deploy_confirmation.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/_deploy_confirmation.html
deleted file mode 100644
index b3daed67..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/_deploy_confirmation.html
+++ /dev/null
@@ -1,40 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}provision_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:overview:deploy_confirmation' %}{% endblock %}
-
-{% block modal_id %}provision_modal{% endblock %}
-{% block modal-header %}{% trans "Deployment Confirmation" %}{% endblock %}
-
-{% block modal-body %}
-<div>
- <p>{% trans "You are about deploy your overcloud." %}</p>
- {% if autogenerated_parameters %}
- <i class="fa fa-info-circle"></i> &nbsp;
- {{ autogenerated_parameters|length }} parameters will be randomly generated.
- <a data-toggle="collapse" href="#collapseParameters">
- <span class="caret"></span>
- </a>
- <div id="collapseParameters" class="panel-collapse collapse">
- <ul><li>
- {{ autogenerated_parameters|join:"</li><li>" }}
- </li></ul>
- </div>
- {% endif %}
-</div>
-<div>
- <fieldset>
- <div class="left">
- {% include "horizon/common/_form_fields.html" %}
- </div>
- </fieldset>
- <br /><br />
- <div class="alert alert-warning">
- <i class="fa fa-exclamation-triangle fa-3x pull-left text-warning"></i>
- <p>{% trans "This operation cannot be undone." %}</p>
- <p>{% trans "Are you sure you want to deploy changes?" %}</p>
- </div>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/_last_events.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/_last_events.html
deleted file mode 100644
index e0659d6a..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/_last_events.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% if show_last_events %}
-<strong>{{ last_events_title }}</strong>
-<div>
- <dl>
- {% for event in last_events %}
- <dd>
- <time datetime="{{ event.event_time }}">{{ event.event_time }}</time>
- <br />
- {{ event.resource_name }} |
- {{ event.resource_status }} |
- {{ event.resource_status_reason }}
- </dd>
- <br />
- {% endfor %}
- </dl>
-</div>
-<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
-{% endif %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/_post_deploy_init.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/_post_deploy_init.html
deleted file mode 100644
index d0f9c52a..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/_post_deploy_init.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}post_deploy_init_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:overview:post_deploy_init' %}{% endblock %}
-
-{% block modal_id %}provision_modal{% endblock %}
-{% block modal-header %}{% trans "Initialize Overcloud" %}{% endblock %}
-
-{% block modal-body %}
-<div>
- <fieldset>
- <div class="left">
- {% include "horizon/common/_form_fields.html" %}
- </div>
- <div class="right">
- {% trans "Your OpenStack cloud nodes are deployed. They need to be initialized before your cloud will be live."%}
- </div>
- </fieldset>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/_scale_out.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/_scale_out.html
deleted file mode 100644
index 550aa42d..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/_scale_out.html
+++ /dev/null
@@ -1,41 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-{% load form_helpers %}
-
-{% block form_id %}scale_out_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:overview:scale_out' %}{% endblock %}
-
-{% block modal_id %}scale_out_modal{% endblock %}
-{% block modal-header %}{% trans "Scale-out Deployment" %}{% endblock %}
-
-{% block modal-body %}
-<div class="scale-out-form">
- {% include 'horizon/common/_form_errors.html' with form=form %}
-{% for role in roles %}
- <div class="form-group well well-sm clearfix{% if role.field.errors %} error{% endif %} {{ role.field.css_classes }}">
- <div class="col-xs-8 deploy-role-name">
- <span class="deployment-roles-label">{{ role.name|capfirst }}</span>
- {% for error in role.field.errors %}
- <span class="help-block"><span class="text-danger">
- {{ error }}
- </span></span>
- {% endfor %}
- </div>
- <div class="col-xs-2 deploy-role-count">
- {{ role.planned_node_count }} →
- </div>
- <div class="col-xs-2 deploy-role-count">
- {{ role.field|add_bootstrap_class }}
- </div>
- </div>
-{% endfor %}
-</div>
-
-<script type="text/javascript">
-(window.$ || window.addHorizonLoadEvent)(function () {
- tuskar.number_picker.init();
-});
-</script>
-
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/_undeploy_confirmation.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/_undeploy_confirmation.html
deleted file mode 100644
index 50b2c943..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/_undeploy_confirmation.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "horizon/common/_modal_form.html" %}
-{% load i18n %}
-{% load url from future %}
-
-{% block form_id %}provision_form{% endblock %}
-{% block form_action %}{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}{% endblock %}
-
-{% block modal_id %}provision_modal{% endblock %}
-{% block modal-header %}{% trans "Undeployment Confirmation" %}{% endblock %}
-
-{% block modal-body %}
-<div>
- <p>{% trans "You are about undeploy your overcloud" %}
- </p>
- <p>{% trans "This operation cannot be undone. Are you sure you want to do that?" %}</p>
- <fieldset>
- {% include "horizon/common/_form_fields.html" %}
- </fieldset>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deploy_confirmation.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deploy_confirmation.html
deleted file mode 100644
index 74f6dc86..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deploy_confirmation.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Deploy overcloud" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Deploy overcloud") %}
-{% endblock page_header %}
-
-{% block infrastructure_main %}
- {% include "infrastructure/overview/_deploy_confirmation.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_base.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_base.html
deleted file mode 100644
index 04bf8eef..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_base.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% load i18n %}
-{% load url from future%}
-
-<div class="deployment-heading">
- {% block deployment-heading %}{% endblock %}
-</div>
-<div class="clear"></div>
-<div class="deployment-icon">
-<i class="fa {% block deployment-icon %}fa-cloud{% endblock %} fa-3x fa-fw"></i>
-</div>
-<div class="deployment-box clearfix">
- <h4>{% block deployment-title %}{% endblock %}</h4>
- {% block deployment-info %}{% endblock %}
- <div class="deployment-buttons clearfix">
- {% block deployment-buttons %}
- <a
- href="{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}"
- class="btn btn-danger ajax-modal">
- <i class="fa fa-lg fa-fire"></i> {% trans "Undeploy" %}
- </a>
- {% endblock %}
- </div>
-</div>
-
-{% block deployment-overcloudrc %}{% endblock %}
-
-{% block templates %}{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_failed.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_failed.html
deleted file mode 100644
index c78acc23..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_failed.html
+++ /dev/null
@@ -1,25 +0,0 @@
-{% extends "infrastructure/overview/deployment_base.html" %}
-
-{% load i18n %}
-{% load url from future%}
-
-{% block deployment-icon %}fa-exclamation-circle{% endblock %}
-
-{% block deployment-title %}
- {% if stack.is_delete_failed %}
- {% trans "Undeploying failed" %}
- {% elif stack.is_failed %}
- {% trans "Deployment failed" %}
- {% else %}
- {% trans "Failure" %}
- {% endif %}
-{% endblock %}
-
-{% block deployment-info %}
-{% include "infrastructure/overview/_last_events.html" %}
-{% endblock %}
-
-{% block deployment-buttons %}
- {{ block.super }}
-{% endblock %}
-
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_initialize.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_initialize.html
deleted file mode 100644
index af29031e..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_initialize.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "infrastructure/overview/deployment_base.html" %}
-
-{% load i18n %}
-{% load url from future%}
-
-{% block deployment-icon %}fa-exclamation-triangle{% endblock %}
-
-{% block deployment-title %}{% trans "Initialization Needed" %}{% endblock %}
-
-{% block deployment-info %}
-<p>{% trans "Your OpenStack cloud is successfully deployed. Before using the cloud you must now initialize it." %}</p>
-{% endblock %}
-
-{% block deployment-buttons %}
- {{ block.super }}
- <a href="{% url 'horizon:infrastructure:overview:post_deploy_init' %}"
- class="btn btn-primary ajax-modal">
- <i class="fa fa-lg fa-cogs"></i>
- {% trans "Initialize" %}
- </a>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_live.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_live.html
deleted file mode 100644
index af6dfa81..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_live.html
+++ /dev/null
@@ -1,113 +0,0 @@
-{% extends "infrastructure/overview/deployment_base.html" %}
-
-{% load i18n %}
-{% load url from future %}
-
-{% block deployment-heading %}
- <div class="deployment-icon">
- <i class="fa fa-check fa-3x fa-fw"></i>
- </div>
- <div class="deployment-box clearfix">
- <h4>{% trans "Deployment is live" %}</h4>
- </div>
-{% endblock %}
-
-{% block deployment-icon %}fa-key{% endblock %}
-{% block deployment-title %}{% trans "Access Information" %}{% endblock %}
-{% block deployment-info %}
-<div>
- {% for dashboard_url in dashboard_urls %}
- <div class="row">
- <div class="col-xs-4">{% trans "Horizon URL:" %}</div>
- <div class="col-xs-8"><a href="{{ dashboard_url }}">{{ dashboard_url }}</a></div>
- </div>
- {% endfor %}
- <div class="row">
- <div class="col-xs-4">{% trans "User name:" %}</div>
- <div class="col-xs-8">admin</div>
- </div>
- <div class="row">
- <div class="col-xs-4">{% trans "Password" %}</div>
- <div class="col-xs-8">
- <span class="btn btn-xs btn-default password-button"
- data-content="{{ admin_password }}"
- ><i class="fa fa-eye"></i> {% trans "Reveal" %}</span>
- </div>
- </div>
-</div>
-
-<script type="text/javascript">
-(window.$ || window.addHorizonLoadEvent)(function () {
- $('span.password-button').popover({
- trigger: 'click',
- placement: 'top'
- });
-});
-</script>
-{% endblock %}
-
-{% block deployment-buttons %}
- {{ block.super }}
-<a
- href="{% url 'horizon:infrastructure:overview:scale_out' %}"
- class="btn ajax-modal btn-default"
->
- <i class="fa fa-lg fa-arrows-alt"></i> {% trans "Scale-out" %}
-</a>
-
-{% endblock %}
-
-{% block deployment-overcloudrc %}
- <div class="clear"></div>
- <div class="deployment-icon">
- <i class="fa fa-key fa-3x fa-fw"></i>
- </div>
- <div class="deployment-box clearfix">
- <h4>{% trans "Overcloudrc Information" %}</h4><a data-toggle="collapse" href="#overcloudrc" aria-expanded="false" aria-controls="overcloudrc" id="collapse-rc"><span class='overcloudrc'>{% trans "Show" %}<i class="fa fa-angle-down"></i></span></a>
- <div class="collapse" id="overcloudrc">
- <div class="row">
- <div class="col-xs-4">NOVA_VERSION:</div>
- <div class="col-xs-8">1.1</div>
- </div>
- <div class="row">
- <div class="col-xs-4">OS_USERNAME:</div>
- <div class="col-xs-8">admin</div>
- </div>
- <div class="row">
- <div class="col-xs-4">OS_PASSWORD:</div>
- <div class="col-xs-8">{{ admin_password }}</div>
- </div>
- <div class="row">
- <div class="col-xs-4">OS_AUTH_URL:</div>
- <div class="col-xs-8">{{ auth_url }}</div>
- </div>
- <div class="row">
- <div class="col-xs-4">OS_TENANT_NAME:</div>
- <div class="col-xs-8">admin</div>
- </div>
- <div class="row">
- <div class="col-xs-4">COMPUTE_API_VERSION:</div>
- <div class="col-xs-8">1.1</div>
- </div>
- <div class="row">
- <div class="col-xs-4">OS_NO_CACHE:</div>
- <div class="col-xs-8">True</div>
- </div>
- <div class="row">
- <div class="col-xs-4">no_proxy:</div>
- <div class="col-xs-8">{{ no_proxy }}</div>
- </div>
- <div class="row">
- <div class="col-xs-4">{% trans "Download" %}</div>
- <div class="col-xs-8">
- <a
- href="{% url 'horizon:infrastructure:overview:download_overcloudrc' %}"
- class="btn btn-default"
- >
- <i class="fa fa-lg fa-download"></i> {% trans "Download" %}
- </a>
- </div>
- </div>
- </div>
- </div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_plan.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_plan.html
deleted file mode 100644
index 52b3c275..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_plan.html
+++ /dev/null
@@ -1,51 +0,0 @@
-{% extends "infrastructure/overview/deployment_base.html" %}
-
-{% load i18n %}
-{% load url from future%}
-{% load horizon %}
-
-{% block deployment-icon %}
-{% if plan_invalid %}fa-exclamation-circle{% else %}fa-check-circle{% endif %}
-{% endblock %}
-
-{% block deployment-title %}
- {% trans "Deployment Checklist" %}
-{% endblock %}
-
-{% block deployment-info %}
-<a data-toggle="collapse" href="#messages" aria-expanded="false" aria-controls="messages" id="collapse-steps">{{ steps_message }}</a>
-<ul class="fa-ul collapse" id="messages">
- {% for message in plan_messages %}
- <li style="margin-left:{{ message.indent|default:0 }}em">
- <i class="fa-li fa {{ message.classes }}" ></i>
- {{ message.text }}
- </li>
- {% endfor %}
-</ul>
-{% endblock %}
-
-{% block deployment-buttons %}
-<a href="{% url 'horizon:infrastructure:overview:deploy_confirmation' %}"
-class="btn btn-primary ajax-modal btn-default {% if plan_invalid %}disabled{% endif %}">
-<i class="fa fa-lg fa-rocket"></i> {% trans "Verify and Deploy" %}
-</a>
-{% endblock %}
-
-{% block templates %}
-<script type="text/html" id="title-template">{% spaceless %}{% jstemplate %}
-<h4>
-[[#validating]]{% trans "Validating the Plan..." %}[[/validating]]
-[[^validating]]{% trans "Deployment Checklist" %}[[/validating]]
-</h4>
-{% endjstemplate %}{% endspaceless %}</script>
-<script type="text/html" id="message-template">{% spaceless %}{% jstemplate %}
-<ul class="fa-ul collapse" id="messages">
-[[#messages]]
- <li style="margin-left: [[indent]]em">
- <i class="fa-li fa [[classes]]"></i>
- [[text]]
- </li>
-[[/messages]]
-</ul>
-{% endjstemplate %}{% endspaceless %}</script>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_progress.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_progress.html
deleted file mode 100644
index acfdd9dd..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_progress.html
+++ /dev/null
@@ -1,69 +0,0 @@
-{% extends "infrastructure/overview/deployment_base.html" %}
-
-{% load i18n %}
-{% load url from future%}
-{% load horizon %}
-
-{% block deployment-icon %}fa-spinner fa-spin{% endblock %}
-
-{% block deployment-title %}
- {% if stack.is_deleting %}
- {% trans "Undeploying..." %}
- {% elif stack.is_deploying %}
- {% trans "Deploying..." %}
- {% elif stack.is_updating %}
- {% trans "Updating..." %}
- {% endif %}
-{% endblock %}
-
-{% block deployment-info %}
-{% if progress %}
- <div class="progress">
- <div
- class="progress-bar progress-bar-striped active"
- role="progressbar"
- aria-valuenow="{{ progress }}"
- aria-valuemin="0"
- aria-valuemax="100"
- style="width: {{ progress }}%"
- ><span class="sr-only">{{ progress }}% {% trans "Complete" %}</span></div>
- </div>
-{% endif %}
-<div class="deploy-last-events">
- {% include "infrastructure/overview/_last_events.html" %}
-</div>
-{% endblock %}
-
-{% block deployment-buttons %}
- {% if stack.is_deploying or stack.is_updating %}
- <a
- href="{% url 'horizon:infrastructure:overview:undeploy_confirmation' %}"
- class="btn btn-danger ajax-modal">
- <i class="fa fa-lg fa-times"></i>
- {% trans "Stop" %}
- </a>
- {% endif %}
-{% endblock %}
-
-{% block templates %}
-<script type="text/html" id="events-template">{% spaceless %}{% jstemplate %}
-[[#show_last_events]]
-<strong>[[ last_events_title ]]</strong>
-<div>
- <dl>
- [[#last_events]]
- <dd>
- <time datetime="[[ event_time ]]">[[ event_time ]]</time>
- <br />
- [[ resource_name ]] |
- [[ resource_status ]] |
- [[ resource_status_reason ]]
- </dd>
- <br />
- [[/last_events]]
- </dl>
-</div>
-[[/show_last_events]]
-<p><a href="{% url 'horizon:infrastructure:history:index' %}">See full log</a></p>
-{% endjstemplate %}{% endspaceless %}</script>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/index.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/index.html
deleted file mode 100644
index fa4273cc..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/index.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% load url from future %}
-
-{% block title %}{% trans 'My OpenStack Deployment' %}{% endblock %}
-
-{% block page_header %}
- {% include 'horizon/common/_domain_page_header.html' with title=_('My OpenStack Deployment') %}
-{% endblock page_header %}
-
-{% block main %}
-<div class="row">
- <div class="col-xs-4">
- {% if stack %}
- {% if stack.is_deleting or stack.is_deploying or stack.is_updating %}
- {% include "infrastructure/overview/deployment_progress.html" %}
- {% elif stack.is_delete_failed or stack.is_failed %}
- {% include "infrastructure/overview/deployment_failed.html" %}
- {% elif stack.is_deployed and not stack.is_initialized %}
- {% include "infrastructure/overview/deployment_initialize.html" %}
- {% else %}
- {% include "infrastructure/overview/deployment_live.html" %}
- {% endif %}
- {% else %}
- {% include "infrastructure/overview/deployment_plan.html" %}
- {% endif %}
- </div>
- <div class="col-xs-8">
- {% if stack %}
- {% include "infrastructure/overview/role_nodes_status.html" %}
- {% else %}
- {% include "infrastructure/overview/role_nodes_edit.html" %}
- {% endif %}
- </div>
-</div>
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/overcloudrc.sh.template b/tuskar_ui/infrastructure/templates/infrastructure/overview/overcloudrc.sh.template
deleted file mode 100644
index 5977bb37..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/overcloudrc.sh.template
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-export NOVA_VERSION=1.1
-export OS_AUTH_URL={{ auth_url }}
-export OS_TENANT_NAME="admin"
-export OS_USERNAME="admin"
-export OS_PASSWORD={{ admin_password }}
-export COMPUTE_API_VERSION=1.1
-export OS_NO_CACHE=True
-export no_proxy={{ no_proxy }}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/post_deploy_init.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/post_deploy_init.html
deleted file mode 100644
index 835c0e52..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/post_deploy_init.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Initialize" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Initialize Overcloud") %}
-{% endblock page_header %}
-
-{% block infrastructure_main %}
- {% include "infrastructure/overview/_post_deploy_init.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_edit.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_edit.html
deleted file mode 100644
index 5a47a8f6..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_edit.html
+++ /dev/null
@@ -1,38 +0,0 @@
-{% load i18n %}
-{% load url from future %}
-{% load form_helpers %}
-
-<h4>{% trans "Deployment Roles" %}</h4>
-<form method="POST" action="." class="deployment-roles-form">
-{% csrf_token %}
-{% include 'horizon/common/_form_errors.html' with form=form %}
-{% for role in roles %}
- <div class="form-group well well-sm clearfix{% if field.errors %} error{% endif %} {{ field.css_classes }}">
- <div class="col-xs-2 deploy-role-icon">
- <a
- href="{% url 'horizon:infrastructure:roles:update' role_id=role.id %}"
- class="btn"
- ><b class="fa fa-lg fa-fw fa-pencil"></b></a>
- </div>
- <div class="col-xs-8 deploy-role-name">
- <a
- href="{% url 'horizon:infrastructure:roles:detail' role_id=role.id %}"
- class="deployment-roles-label"
- >{{ role.name }}</a>
- {% for error in role.field.errors %}
- <span class="help-block"><span class="text-danger">
- {{ error }}
- </span></span>
- {% endfor %}
- </div>
- <div class="col-xs-2 deploy-role-count">
- {{ role.field|add_bootstrap_class }}
- </div>
- </div>
-{% endfor %}
-<hr>
-<button type="submit" class="btn btn-default">
- <i class="fa fa-save"></i>
- {% trans "Save changes" %}
-</button>
-</form>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_status.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_status.html
deleted file mode 100644
index f31c6bab..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_status.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% load i18n %}
-{% load url from future %}
-{% load horizon %}
-
-<h4>{% trans "Deployment Roles" %}</h4>
-<div class="deploy-role-status">
-{% for role in roles %}
- <div class="well well-sm clearfix">
- <div class="col-xs-2 deploy-role-icon">
- {% if role.planned_node_count %}
- <i class="fa fa-fw fa-lg {{ role.icon }}"></i>
- {% endif %}
- </div>
- <div class="col-xs-8 deploy-role-name">
- <strong
- class="deployment-roles-label"
- >{{ role.name }}</strong>
- </div>
- <div class="col-xs-2 deploy-role-count">
- {% if role.finished %}
- {{ role.deployed_node_count }}
- {% else %}
- {{ role.deployed_node_count }}<small class="text-muted">/{{ role.planned_node_count }}</small>
- {% endif %}
- </div>
- </div>
-{% endfor %}
-</div>
-
-<script type="text/html" id="roles-template">{% spaceless %}{% jstemplate %}
-[[#roles]]
- <div class="well well-sm clearfix">
- <div class="col-xs-2 deploy-role-icon">
- [[#planned_node_count]]
- <i class="fa fa-fw fa-lg [[ icon ]]"></i>
- [[/planned_node_count]]
- </div>
- <div class="col-xs-8 deploy-role-name"><strong class="deployment-roles-label" >[[ name ]]</strong></div>
- <div class="col-xs-2 deploy-role-count">
- [[#finished]]
- [[ deployed_node_count ]]
- [[/finished]]
- [[^finished]]
- [[ deployed_node_count ]]<small class="text-muted">/[[ planned_node_count ]]</small>
- [[/finished]]
- </div>
-
- </div>
-[[/roles]]
-{% endjstemplate %}{% endspaceless %}</script>
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/scale_out.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/scale_out.html
deleted file mode 100644
index 4ef31c20..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/scale_out.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Scale-out Deployment" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Scale-out Deployment") %}
-{% endblock page_header %}
-
-{% block infrastructure_main %}
- {% include "infrastructure/overview/_scale_out.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/overview/undeploy_confirmation.html b/tuskar_ui/infrastructure/templates/infrastructure/overview/undeploy_confirmation.html
deleted file mode 100644
index b234ac95..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/overview/undeploy_confirmation.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends 'infrastructure/base.html' %}
-{% load i18n %}
-{% block title %}{% trans "Undeploy overcloud" %}{% endblock %}
-
-{% block page_header %}
- {% include "horizon/common/_page_header.html" with title=_("Undeploy overcloud") %}
-{% endblock page_header %}
-
-{% block infrastructure_main %}
- {% include "infrastructure/overview/_undeploy_confirmation.html" %}
-{% endblock %}
diff --git a/tuskar_ui/infrastructure/templates/infrastructure/qunit.html b/tuskar_ui/infrastructure/templates/infrastructure/qunit.html
deleted file mode 100644
index 71a7815f..00000000
--- a/tuskar_ui/infrastructure/templates/infrastructure/qunit.html
+++ /dev/null
@@ -1,189 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="UTF-8" />
- <title>Tuskar QUnit Test Suite</title>
- <link rel="stylesheet" href="{{ STATIC_URL }}horizon/lib/qunit/qunit.css" type="text/css" media="screen">
- <script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/jquery/jquery.js"></script>
- <script type="text/javascript" src="{{ STATIC_URL }}horizon/lib/qunit/qunit.js"></script>
- {% include "horizon/_conf.html" %}
-
- {% comment %}Load test modules here.{% endcomment %}
- <script type="text/javascript" src="{{ STATIC_URL }}infrastructure/tests/formset_table.js"></script>
- {% comment %}End test modules.{% endcomment %}
-
- {% include "horizon/_scripts.html" %}
- {% include "infrastructure/_scripts.html" %}
- </head>
- <body ng-app="hz">
- <h1 id="qunit-header">Tuskar JavaScript Tests</h1>
- <h2 id="qunit-banner"></h2>
- <div id="qunit-testrunner-toolbar"></div>
- <h2 id="qunit-userAgent"></h2>
- <ol id="qunit-tests"></ol>
- <div id="qunit-fixture">
- <!-- Test markup; will be hidden. -->
-
-<div class="table_wrapper">
- <input id="id_flavors-TOTAL_FORMS" name="flavors-TOTAL_FORMS"
- type="hidden" value="3"><input id="id_flavors-INITIAL_FORMS"
- name="flavors-INITIAL_FORMS" type="hidden" value="2"><input id=
- "id_flavors-MAX_NUM_FORMS" name="flavors-MAX_NUM_FORMS" type=
- "hidden" value="1000">
-
- <table id="flavors" class=
- "table table-bordered table-striped datatable">
- <thead>
- <tr class='table_caption'>
- <th class='table_header' colspan='8'>
- <h3 class='table_title'>Flavors</h3>
-
- <div class="table_actions clearfix"></div>
- </th>
- </tr>
-
- <tr>
- <th class="sortable normal_column"><span class=
- "required">Flavor Name</span></th>
-
- <th class="sortable normal_column"><span class=
- "required">VCPU</span></th>
-
- <th class="sortable normal_column"><span class=
- "required">RAM (MB)</span></th>
-
- <th class="sortable normal_column"><span class=
- "required">Root Disk (GB)</span></th>
-
- <th class="sortable normal_column"><span>Ephemeral Disk
- (GB)</span></th>
-
- <th class="sortable normal_column"><span>Swap Disk
- (MB)</span></th>
-
- <th class="sortable normal_column"><span>Max.
- VMs</span></th>
-
- <th class="sortable normal_column">
- <span>Delete</span></th>
- </tr>
- </thead>
-
- <tbody>
- <tr class="" data-display="yyy.1" id="flavors__row__14">
- <td class="sortable normal_column"><input class=
- "input input-small" id="id_flavors-0-name" maxlength="25"
- name="flavors-0-name" type="text" value="1"> <input id=
- "id_flavors-0-id" name="flavors-0-id" type="hidden"
- value="14"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-0-cpu" name=
- "flavors-0-cpu" type="number" value="1"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-0-memory" name=
- "flavors-0-memory" type="number" value="1"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-0-storage" name=
- "flavors-0-storage" type="number" value="1"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id=
- "id_flavors-0-ephemeral_disk" name=
- "flavors-0-ephemeral_disk" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-0-swap_disk"
- name="flavors-0-swap_disk" type="number"></td>
-
- <td class="sortable normal_column">-</td>
-
- <td class="sortable normal_column"><input id=
- "id_flavors-0-DELETE" name="flavors-0-DELETE" type=
- "checkbox"></td>
- </tr>
-
- <tr class="" data-display="yyy.2" id="flavors__row__15">
- <td class="sortable normal_column"><input class=
- "input input-small" id="id_flavors-1-name" maxlength="25"
- name="flavors-1-name" type="text" value="2"> <input id=
- "id_flavors-1-id" name="flavors-1-id" type="hidden"
- value="15"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-1-cpu" name=
- "flavors-1-cpu" type="number" value="2"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-1-memory" name=
- "flavors-1-memory" type="number" value="2"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-1-storage" name=
- "flavors-1-storage" type="number" value="2"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id=
- "id_flavors-1-ephemeral_disk" name=
- "flavors-1-ephemeral_disk" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-1-swap_disk"
- name="flavors-1-swap_disk" type="number"></td>
-
- <td class="sortable normal_column">-</td>
-
- <td class="sortable normal_column"><input id=
- "id_flavors-1-DELETE" name="flavors-1-DELETE" type=
- "checkbox"></td>
- </tr>
-
- <tr class="current_selected">
- <td class="sortable normal_column"><input class=
- "input input-small" id="id_flavors-2-name" maxlength="25"
- name="flavors-2-name" type="text"> <input id=
- "id_flavors-2-id" name="flavors-2-id" type="hidden"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-2-cpu" name=
- "flavors-2-cpu" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-2-memory" name=
- "flavors-2-memory" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-2-storage" name=
- "flavors-2-storage" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id=
- "id_flavors-2-ephemeral_disk" name=
- "flavors-2-ephemeral_disk" type="number"></td>
-
- <td class="sortable normal_column"><input class=
- "input number_input_slim" id="id_flavors-2-swap_disk"
- name="flavors-2-swap_disk" type="number"></td>
-
- <td class="sortable normal_column">-</td>
-
- <td class="sortable normal_column"><input id=
- "id_flavors-2-DELETE" name="flavors-2-DELETE" type=
- "checkbox"></td>
- </tr>
- </tbody>
-
- <tfoot>
- <tr>
- <td colspan="8"><span class="table_count">Displaying 3
- items</span></td>
- </tr>
- </tfoot>
- </table>
-</div>
-
- </div>
- </body>
-</html>
diff --git a/tuskar_ui/infrastructure/templatetags/__init__.py b/tuskar_ui/infrastructure/templatetags/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/infrastructure/templatetags/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/infrastructure/templatetags/chart_helpers.py b/tuskar_ui/infrastructure/templatetags/chart_helpers.py
deleted file mode 100644
index 2a3e0e20..00000000
--- a/tuskar_ui/infrastructure/templatetags/chart_helpers.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import json
-
-from django import template
-
-
-register = template.Library()
-
-
-@register.filter()
-def remaining_capacity_by_flavors(obj):
- flavors = obj.list_flavors
-
- decorated_obj = " ".join(
- [("<p><strong>{0}</strong> {1}</p>").format(
- str(flavor.used_instances),
- flavor.name)
- for flavor in flavors])
-
- decorated_obj = ("<p>Capacity remaining by flavors: </p>" +
- decorated_obj)
-
- return decorated_obj
-
-
-@register.filter()
-def all_used_instances(obj):
- flavors = obj.list_flavors
-
- all_used_instances_info = []
- for flavor in flavors:
- info = {}
- info['popup_used'] = (
- '<p> {0}% total,'
- ' <strong> {1} instances</strong> of {2}</p>'.format(
- flavor.used_instances,
- flavor.used_instances,
- flavor.name))
- info['used_instances'] = str(flavor.used_instances)
-
- all_used_instances_info.append(info)
-
- return json.dumps(all_used_instances_info)
diff --git a/tuskar_ui/infrastructure/templatetags/icon_helpers.py b/tuskar_ui/infrastructure/templatetags/icon_helpers.py
deleted file mode 100644
index b3a88476..00000000
--- a/tuskar_ui/infrastructure/templatetags/icon_helpers.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# 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 template
-from django.utils.translation import ugettext_lazy as _
-
-
-register = template.Library()
-
-IRONIC_NODE_STATE_STRING_DICT = {
- 'active': _('powered on'),
- 'wait call-back': _('waiting'),
- 'deploying': _('deploying'),
- 'deploy failed': _('deployment failed'),
- 'deploy complete': _('deployment complete'),
- 'deleting': _('deleting'),
- 'deleted': _('deleted'),
- 'error': _('error'),
- 'rebuild': _('rebuilding'),
- 'power on': _('powered on'),
- 'power off': _('powered off'),
- 'rebooting': _('rebooting'),
-}
-
-IRONIC_NODE_STATE_ICON_DICT = {
- 'active': 'fa-play',
- 'wait call-back': 'fa-spinner',
- 'deploying': 'fa-spinner',
- 'deploy failed': 'fa-warning',
- 'deploy complete': 'fa-ok',
- 'deleting': 'fa-spinner',
- 'deleted': 'fa-cancel',
- 'error': 'fa-warning',
- 'rebuild': 'fa-spinner',
- 'power on': 'fa-play',
- 'power off': 'fa-stop',
- 'rebooting': 'fa-spinner',
-}
-
-
-@register.filter(is_safe=True)
-def iconized_ironic_node_state(node_power_state):
- state = IRONIC_NODE_STATE_STRING_DICT.get(node_power_state, "&mdash;")
- icon = IRONIC_NODE_STATE_ICON_DICT.get(node_power_state, 'fa-question')
- html_string = (u"""<span class="fa %s powerstate"></span>"""
- u"""<span>%s</span> """) % (icon, unicode(state))
-
- return html_string
diff --git a/tuskar_ui/infrastructure/templatetags/tests.py b/tuskar_ui/infrastructure/templatetags/tests.py
deleted file mode 100644
index 3d338c47..00000000
--- a/tuskar_ui/infrastructure/templatetags/tests.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- 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
-import json
-
-from tuskar_ui.infrastructure.templatetags import chart_helpers
-from tuskar_ui.infrastructure.templatetags import icon_helpers
-from tuskar_ui.test import helpers
-
-
-Flavor = collections.namedtuple('Flavor', 'name used_instances')
-Flavors = collections.namedtuple('Flavors', 'list_flavors')
-
-
-class ChartHelpersTest(helpers.TestCase):
- def test_remaining_capacity_by_flavors(self):
- flavors = Flavors([
- Flavor('a', 0),
- Flavor('b', 1),
- ])
- ret = chart_helpers.remaining_capacity_by_flavors(flavors)
- self.assertEqual(
- ret,
- '<p>Capacity remaining by flavors: </p>'
- '<p><strong>0</strong> a</p> '
- '<p><strong>1</strong> b</p>'
- )
-
- def test_all_used_instances(self):
- flavors = Flavors([
- Flavor('a', 0),
- Flavor('b', 1),
- ])
- ret = chart_helpers.all_used_instances(flavors)
- self.assertEqual(ret, json.dumps([
- {
- 'popup_used': '<p> 0% total, '
- '<strong> 0 instances</strong> of a</p>',
- 'used_instances': '0',
- }, {
- 'popup_used': '<p> 1% total, '
- '<strong> 1 instances</strong> of b</p>',
- 'used_instances': '1',
- },
- ]))
-
-
-class IconHelpersTest(helpers.TestCase):
- def test_iconized_ironic_node_state(self):
- ret = icon_helpers.iconized_ironic_node_state('active')
- self.assertEqual(
- ret,
- u'<span class="fa fa-play powerstate"></span>'
- '<span>powered on</span> ',
- )
- ret = icon_helpers.iconized_ironic_node_state('')
- self.assertEqual(
- ret,
- u'<span class="fa fa-question powerstate"></span>'
- '<span>&mdash;</span> ',
- )
diff --git a/tuskar_ui/infrastructure/views.py b/tuskar_ui/infrastructure/views.py
deleted file mode 100644
index b22aade3..00000000
--- a/tuskar_ui/infrastructure/views.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- 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.
-
-from horizon.utils import memoized
-
-from tuskar_ui import api
-
-
-class ItemCountMixin(object):
- def get_items_count(self):
- return len(self.get_data())
-
- def get_context_data(self, **kwargs):
- context = super(ItemCountMixin, self).get_context_data(**kwargs)
- context['items_count'] = self.get_items_count()
- return context
-
-
-class StackMixin(object):
- @memoized.memoized
- def get_plan(self):
- return api.tuskar.Plan.get_the_plan(self.request)
-
- @memoized.memoized
- def get_stack(self):
- return api.heat.Stack.get_by_plan(self.request, self.get_plan())
-
-
-class RoleMixin(object):
- @memoized.memoized
- def get_role(self, redirect=None):
- role_id = self.kwargs['role_id']
- role = api.tuskar.Role.get(self.request, role_id,
- _error_redirect=redirect)
- return role
diff --git a/tuskar_ui/tables.py b/tuskar_ui/tables.py
deleted file mode 100644
index c780bbb5..00000000
--- a/tuskar_ui/tables.py
+++ /dev/null
@@ -1,177 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-import itertools
-import logging
-import sys
-
-from django import template
-from django.template import loader
-from django.utils import datastructures
-from horizon.tables import base as horizon_tables
-
-
-LOG = logging.getLogger(__name__)
-STRING_SEPARATOR = "__"
-
-
-class FormsetCell(horizon_tables.Cell):
- """A DataTable cell that knows about its field from the fieldset."""
-
- def __init__(self, *args, **kwargs):
- super(FormsetCell, self).__init__(*args, **kwargs)
- try:
- self.field = (self.row.form or {})[self.column.name]
- except KeyError:
- self.field = None
- else:
- if self.field.errors:
- self.attrs['class'] = (
- '%s error control-group' % self.attrs.get('class', ''))
- self.attrs['title'] = ' '.join(
- unicode(error) for error in self.field.errors)
-
-
-class FormsetRow(horizon_tables.Row):
- """A DataTable row that knows about its form from the fieldset."""
-
- template_path = 'formset_table/_row.html'
-
- def __init__(self, column, datum, form):
- self.form = form
- super(FormsetRow, self).__init__(column, datum)
- if self.cells == []:
- # We need to be able to handle empty rows, because there may
- # be extra empty forms in a formset. The original DataTable breaks
- # on this, because it sets self.cells to [], but later expects a
- # SortedDict. We just fill self.cells with empty Cells.
- cells = []
- for column in self.table.columns.values():
- cell = self.table._meta.cell_class(None, column, self)
- cells.append((column.name or column.auto, cell))
- self.cells = datastructures.SortedDict(cells)
-
- def render(self):
- return loader.render_to_string(self.template_path,
- {"row": self, "form": self.form})
-
-
-class FormsetDataTableMixin(object):
- """A mixin for DataTable to support Django Formsets.
-
- This works the same as the ``FormsetDataTable`` below, but can be used
- to add to existing DataTable subclasses.
- """
- formset_class = None
-
- def __init__(self, *args, **kwargs):
- super(FormsetDataTableMixin, self).__init__(*args, **kwargs)
- self._formset = None
-
- # Override Meta settings, because we need custom Form and Cell classes,
- # and also our own template.
- self._meta.row_class = FormsetRow
- self._meta.cell_class = FormsetCell
- self._meta.template = 'formset_table/_table.html'
-
- def get_required_columns(self):
- """Lists names of columns that have required fields."""
- required_columns = []
- if self.formset_class:
- empty_form = self.get_formset().empty_form
- for column in self.columns.values():
- field = empty_form.fields.get(column.name)
- if field and field.required:
- required_columns.append(column.name)
- return required_columns
-
- def _get_formset_data(self):
- """Formats the self.filtered_data in a way suitable for a formset."""
- data = []
- for datum in self.filtered_data:
- form_data = {}
- for column in self.columns.values():
- value = column.get_data(datum)
- form_data[column.name] = value
- form_data['id'] = self.get_object_id(datum)
- data.append(form_data)
- return data
-
- def get_formset(self):
- """Provide the formset corresponding to this DataTable.
-
- Use this to validate the formset and to get the submitted data back.
- """
- if self._formset is None:
- self._formset = self.formset_class(
- self.request.POST or None,
- initial=self._get_formset_data(),
- prefix=self._meta.name)
- return self._formset
-
- def get_empty_row(self):
- """Return a row with no data, for adding at the end of the table."""
- return self._meta.row_class(self, None, self.get_formset().empty_form)
-
- def get_rows(self):
- """Return the row data for this table broken out by columns.
-
- The row objects get an additional ``form`` parameter, with the
- formset form corresponding to that row.
- """
- try:
- rows = []
- if self.formset_class is None:
- formset = []
- else:
- formset = self.get_formset()
- formset.is_valid()
- for datum, form in itertools.izip_longest(self.filtered_data,
- formset):
- row = self._meta.row_class(self, datum, form)
- if self.get_object_id(datum) == self.current_item_id:
- self.selected = True
- row.classes.append('current_selected')
- rows.append(row)
- except Exception:
- # Exceptions can be swallowed at the template level here,
- # re-raising as a TemplateSyntaxError makes them visible.
- LOG.exception("Error while rendering table rows.")
- exc_info = sys.exc_info()
- raise template.TemplateSyntaxError, exc_info[1], exc_info[2]
- return rows
-
- def get_object_id(self, datum):
- # We need to support ``None`` when there are more forms than data.
- if datum is None:
- return None
- return super(FormsetDataTableMixin, self).get_object_id(datum)
-
-
-class FormsetDataTable(FormsetDataTableMixin, horizon_tables.DataTable):
- """A DataTable with support for Django Formsets.
-
- Note that :attr:`~horizon.tables.DataTableOptions.row_class` and
- :attr:`~horizon.tables.DataTaleOptions.cell_class` are overwritten in this
- class, so setting them in ``Meta`` has no effect.
-
- .. attribute:: formset_class
-
- A classs made with :function:`~django.forms.formsets.formset_factory`
- containing the definition of the formset to use with this data table.
-
- The columns that are named the same as the formset fields will be
- replaced with form widgets in the table. Any hidden fields from the
- formset will also be included. The fields that are not hidden and
- don't correspond to any column will not be included in the form.
- """
diff --git a/tuskar_ui/test/__init__.py b/tuskar_ui/test/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/test/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/test/api_tests/__init__.py b/tuskar_ui/test/api_tests/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/test/api_tests/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/test/api_tests/heat_tests.py b/tuskar_ui/test/api_tests/heat_tests.py
deleted file mode 100644
index 235ba8f1..00000000
--- a/tuskar_ui/test/api_tests/heat_tests.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#
-# 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
-
-from django.utils import unittest
-from heatclient.v1 import events
-import mock
-from mock import patch # noqa
-
-from tuskar_ui import api
-import tuskar_ui.api.heat # noqa
-from tuskar_ui.test import helpers as test
-
-
-class HeatAPITests(test.APITestCase):
- def test_stack_list(self):
- stacks = self.heatclient_stacks.list()
-
- with patch('openstack_dashboard.api.heat.stacks_list',
- return_value=(stacks, None, None)):
- stacks = api.heat.Stack.list(self.request)
- for stack in stacks:
- self.assertIsInstance(stack, api.heat.Stack)
- self.assertEqual(1, len(stacks))
-
- def test_stack_get(self):
- stack = self.heatclient_stacks.first()
-
- with patch('openstack_dashboard.api.heat.stack_get',
- return_value=stack):
- ret_val = api.heat.Stack.get(self.request, stack.id)
- self.assertIsInstance(ret_val, api.heat.Stack)
-
- def test_stack_plan(self):
- stack = api.heat.Stack(self.heatclient_stacks.first(),
- self.request)
- plan = self.tuskarclient_plans.first()
-
- with patch('tuskarclient.v2.plans.PlanManager.list',
- return_value=[plan]):
- ret_val = stack.plan
- self.assertIsInstance(ret_val, api.tuskar.Plan)
-
- def test_stack_events(self):
- event_list = self.heatclient_events.list()
- stack = self.heatclient_stacks.first()
-
- with patch('openstack_dashboard.api.heat.events_list',
- return_value=event_list):
- ret_val = api.heat.Stack(stack).events
- for e in ret_val:
- self.assertIsInstance(e, events.Event)
- self.assertEqual(8, len(ret_val))
-
- def test_stack_is_deployed(self):
- stack = api.heat.Stack(self.heatclient_stacks.first())
- ret_val = stack.is_deployed
- self.assertFalse(ret_val)
-
- def test_stack_is_updating(self):
- stack = api.heat.Stack(self.heatclient_stacks.first())
- ret_val = stack.is_updating
- self.assertFalse(ret_val)
-
- def test_stack_is_deploying(self):
- stack = api.heat.Stack(self.heatclient_stacks.first())
- ret_val = stack.is_deploying
- self.assertFalse(ret_val)
-
- @unittest.skip("Add appropriate test data to deal with nested stacks.")
- def test_stack_resources(self):
- stack = api.heat.Stack(self.heatclient_stacks.first(),
- request=self.request)
- resources = self.heatclient_resources.list()
- nodes = self.baremetalclient_nodes.list()
- instances = []
-
- with patch('openstack_dashboard.api.base.is_service_enabled',
- return_value=False):
- with patch('openstack_dashboard.api.heat.resources_list',
- return_value=resources):
- with patch('openstack_dashboard.api.nova.server_list',
- return_value=(instances, None)):
- with patch('novaclient.v2.contrib.baremetal.'
- 'BareMetalNodeManager.list',
- return_value=nodes):
- ret_val = stack.resources()
-
- for i in ret_val:
- self.assertIsInstance(i, api.heat.Resource)
- self.assertEqual(4, len(ret_val))
-
- def test_stack_keystone_ip(self):
- stack = api.heat.Stack(self.heatclient_stacks.first())
-
- self.assertEqual('192.0.2.23', stack.keystone_ip)
-
- def test_stack_dashboard_url(self):
- stack = api.heat.Stack(self.heatclient_stacks.first())
- stack.plan = api.tuskar.Plan(self.tuskarclient_plans.first())
-
- mocked_service = mock.Mock(id='horizon_id')
- mocked_service.name = 'horizon'
-
- services = [mocked_service]
- endpoints = [mock.Mock(service_id='horizon_id',
- adminurl='http://192.0.2.23:/admin'), ]
-
- services_obj = mock.Mock(
- **{'list.return_value': services, })
-
- endpoints_obj = mock.Mock(
- **{'list.return_value': endpoints, })
-
- overcloud_keystone_client = mock.Mock(
- services=services_obj,
- endpoints=endpoints_obj)
-
- with patch('tuskar_ui.api.heat.overcloud_keystoneclient',
- return_value=overcloud_keystone_client) as client_get:
- self.assertEqual(['http://192.0.2.23:/admin'],
- stack.dashboard_urls)
- self.assertEqual(client_get.call_count, 1)
diff --git a/tuskar_ui/test/api_tests/node_tests.py b/tuskar_ui/test/api_tests/node_tests.py
deleted file mode 100644
index 7bde594b..00000000
--- a/tuskar_ui/test/api_tests/node_tests.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#
-# 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 novaclient.v2 import servers
-
-from tuskar_ui import api
-import tuskar_ui.api.node
-from tuskar_ui.test import helpers as test
-
-
-def mock_ironicclient(node=None, nodes=()):
- return mock.patch(
- 'tuskar_ui.api.node.ironicclient',
- return_value=mock.MagicMock(node=mock.MagicMock(**{
- 'create.return_value': node,
- 'get.return_value': node,
- 'get_by_instance_uuid.return_value': node,
- 'find.return_value': node,
- 'list.return_value': nodes,
- 'delete.return_value': None,
- })),
- )
-
-
-class NodeAPITests(test.APITestCase):
- def test_node_create(self):
- node = tuskar_ui.api.node.Node(self.ironicclient_nodes.first())
-
- with mock_ironicclient(node=node):
- ret_val = api.node.Node.create(
- self.request,
- node.driver_info['ipmi_address'],
- 'i386',
- node.cpus,
- node.memory_mb,
- node.local_gb,
- ['aa:aa:aa:aa:aa:aa'],
- ipmi_username='admin',
- ipmi_password='password')
-
- self.assertIsInstance(ret_val, api.node.Node)
-
- def test_node_get(self):
- node = self.ironicclient_nodes.first()
- instance = self.novaclient_servers.first()
-
- with mock_ironicclient(node=node), mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=([instance], False),
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_get',
- return_value=instance,
- ):
- ret_val = api.node.Node.get(self.request, node.uuid)
- ret_instance = ret_val.instance
-
- self.assertIsInstance(ret_val, api.node.Node)
- self.assertIsInstance(ret_instance, servers.Server)
-
- def test_node_get_by_instance_uuid(self):
- instance = self.novaclient_servers.first()
- node = self.ironicclient_nodes.first()
- nodes = self.ironicclient_nodes.list()
-
- with mock_ironicclient(
- node=node,
- nodes=nodes,
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_get',
- return_value=instance,
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=([instance], False),
- ):
- ret_val = api.node.Node.get_by_instance_uuid(self.request,
- node.instance_uuid)
- ret_instance = ret_val.instance
- self.assertIsInstance(ret_val, api.node.Node)
- self.assertIsInstance(ret_instance, servers.Server)
-
- def test_node_list(self):
- instances = self.novaclient_servers.list()
- node = self.ironicclient_nodes.first()
- nodes = self.ironicclient_nodes.list()
-
- with mock_ironicclient(
- node=node,
- nodes=nodes
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=(instances, None),
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_get',
- return_value=instances[0],
- ):
- ret_val = api.node.Node.list(self.request)
-
- for node in ret_val:
- self.assertIsInstance(node, api.node.Node)
- self.assertEqual(9, len(ret_val))
-
- def test_node_delete(self):
- node = self.ironicclient_nodes.first()
- with mock_ironicclient(node=node):
- api.node.Node.delete(self.request, node.uuid)
-
- def test_node_set_maintenance(self):
- node = self.ironicclient_nodes.first()
- with mock_ironicclient(node=node):
- api.node.Node.set_maintenance(self.request, node.uuid, False)
-
- def test_node_set_power_state(self):
- node = self.ironicclient_nodes.first()
- with mock_ironicclient(node=node):
- api.node.Node.set_power_state(self.request, node.uuid, 'on')
-
- def test_node_instance(self):
- node = self.ironicclient_nodes.first()
- instance = self.novaclient_servers.first()
-
- with mock.patch(
- 'openstack_dashboard.api.nova.server_get',
- return_value=instance,
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=([instance], False),
- ):
- ret_val = api.node.Node(node).instance
- self.assertIsInstance(ret_val, servers.Server)
-
- def test_node_image_name(self):
- node = self.ironicclient_nodes.first()
- instance = self.novaclient_servers.first()
- image = self.glanceclient_images.first()
-
- with mock.patch(
- 'openstack_dashboard.api.nova.server_get',
- return_value=instance,
- ), mock.patch(
- 'openstack_dashboard.api.glance.image_get',
- return_value=image,
- ), mock.patch(
- 'openstack_dashboard.api.nova.server_list',
- return_value=([instance], False),
- ):
- ret_val = api.node.Node(node).image_name
- self.assertEqual(ret_val, 'overcloud-full')
diff --git a/tuskar_ui/test/api_tests/tuskar_tests.py b/tuskar_ui/test/api_tests/tuskar_tests.py
deleted file mode 100644
index 3979bd73..00000000
--- a/tuskar_ui/test/api_tests/tuskar_tests.py
+++ /dev/null
@@ -1,321 +0,0 @@
-#
-# 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 contextlib
-
-from mock import patch, call # noqa
-
-from tuskar_ui import api
-from tuskar_ui.test import helpers as test
-
-
-class TuskarAPITests(test.APITestCase):
- def test_plan_create(self):
- plan = self.tuskarclient_plans.first()
-
- with patch('tuskarclient.v2.plans.PlanManager.create',
- return_value=plan):
- ret_val = api.tuskar.Plan.create(self.request, {}, {})
- self.assertIsInstance(ret_val, api.tuskar.Plan)
-
- def test_plan_list(self):
- plans = self.tuskarclient_plans.list()
-
- with patch('tuskarclient.v2.plans.PlanManager.list',
- return_value=plans):
- ret_val = api.tuskar.Plan.list(self.request)
- for plan in ret_val:
- self.assertIsInstance(plan, api.tuskar.Plan)
- self.assertEqual(1, len(ret_val))
-
- def test_plan_get(self):
- plan = self.tuskarclient_plans.first()
-
- with patch('tuskarclient.v2.plans.PlanManager.get',
- return_value=plan):
- ret_val = api.tuskar.Plan.get(self.request, plan.uuid)
-
- self.assertIsInstance(ret_val, api.tuskar.Plan)
-
- def test_plan_get_the_plan(self):
- plan = self.tuskarclient_plans.first()
-
- with patch('tuskarclient.v2.plans.PlanManager.list',
- return_value=[plan]):
- with patch('tuskarclient.v2.plans.PlanManager.create',
- return_value=plan):
- ret_val = api.tuskar.Plan.get_the_plan(self.request)
-
- self.assertIsInstance(ret_val, api.tuskar.Plan)
-
- def test_plan_delete(self):
- plan = self.tuskarclient_plans.first()
-
- with patch('tuskarclient.v2.plans.PlanManager.delete',
- return_value=None):
- api.tuskar.Plan.delete(self.request, plan.uuid)
-
- def test_plan_role_list(self):
- with patch('tuskarclient.v2.plans.PlanManager.get',
- return_value=[]):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first(),
- self.request)
-
- with patch('tuskarclient.v2.roles.RoleManager.list',
- return_value=self.tuskarclient_roles.list()):
- ret_val = plan.role_list
- self.assertEqual(4, len(ret_val))
- for r in ret_val:
- self.assertIsInstance(r, api.tuskar.Role)
-
- def test_role_list(self):
- roles = self.tuskarclient_roles.list()
-
- with patch('tuskarclient.v2.roles.RoleManager.list',
- return_value=roles):
- ret_val = api.tuskar.Role.list(self.request)
-
- for r in ret_val:
- self.assertIsInstance(r, api.tuskar.Role)
- self.assertEqual(4, len(ret_val))
-
- def test_role_get(self):
- roles = self.tuskarclient_roles.list()
-
- with patch('tuskarclient.v2.roles.RoleManager.list',
- return_value=roles):
- ret_val = api.tuskar.Role.get(self.request,
- roles[0].uuid)
- self.assertIsInstance(ret_val, api.tuskar.Role)
-
- def test_role_get_by_image(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- image = self.glanceclient_images.first()
- roles = self.tuskarclient_roles.list()
-
- with patch('tuskarclient.v2.roles.RoleManager.list',
- return_value=roles):
- ret_val = api.tuskar.Role.get_by_image(
- self.request, plan, image)
- self.assertIsInstance(ret_val, list)
- self.assertEqual(len(ret_val), 3)
-
- def test_parameter_stripped_name(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- param = plan.parameter('Controller-1::count')
-
- ret_val = param.stripped_name
- self.assertEqual(ret_val, 'count')
-
- def test_parameter_role(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first(),
- request=self.request)
- param = plan.parameter('Controller-1::count')
- roles = self.tuskarclient_roles.list()
-
- with patch('tuskarclient.v2.roles.RoleManager.list',
- return_value=roles):
- ret_val = param.role
- self.assertIsInstance(ret_val, api.tuskar.Role)
- self.assertEqual(ret_val.name, 'Controller')
-
- def test_list_generated_parameters(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.parameter_list',
- return_value=plan.parameters),
- ) as (mock_parameter_list, ):
- ret_val = plan.list_generated_parameters()
-
- self.assertEqual(
- ret_val,
- {'Controller-1::KeystoneCACertificate': {
- 'description': 'Keystone CA CertificateAdmin',
- 'hidden': True,
- 'name': 'Controller-1::KeystoneCACertificate',
- 'value': 'unset',
- 'default': '',
- 'label': 'Keystone CA CertificateAdmin',
- 'parameter_type': 'string',
- 'constraints': []},
- 'Controller-1::SnmpdReadonlyUserPassword': {
- 'description': 'Snmpd password',
- 'hidden': True,
- 'name': 'Controller-1::SnmpdReadonlyUserPassword',
- 'value': '',
- 'default': '',
- 'label': 'Snmpd password',
- 'parameter_type': 'string',
- 'constraints': []},
- 'Controller-1::AdminPassword': {
- 'description': 'Admin password',
- 'hidden': True,
- 'name': 'Controller-1::AdminPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Admin Password',
- 'parameter_type': 'string',
- 'constraints': []},
- 'Controller-1::AdminToken': {
- 'description': 'Admin Token',
- 'hidden': True,
- 'name': 'Controller-1::AdminToken',
- 'value': '',
- 'default': '',
- 'label': 'Admin Token',
- 'parameter_type': 'string',
- 'constraints': []},
- 'Compute-1::SnmpdReadonlyUserPassword': {
- 'description': 'Snmpd password',
- 'hidden': True,
- 'name': 'Compute-1::SnmpdReadonlyUserPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Snmpd password',
- 'parameter_type': 'string',
- 'constraints': []}})
-
- mock_parameter_list.assert_called_once_with()
-
- def test_list_generated_parameters_without_prefix(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.parameter_list',
- return_value=plan.parameters),
- ) as (mock_parameter_list, ):
- ret_val = plan.list_generated_parameters(with_prefix=False)
-
- self.assertEqual(
- ret_val,
- {'SnmpdReadonlyUserPassword': {
- 'description': 'Snmpd password',
- 'hidden': True,
- 'name': 'Compute-1::SnmpdReadonlyUserPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Snmpd password',
- 'parameter_type': 'string',
- 'constraints': []},
- 'KeystoneCACertificate': {
- 'description': 'Keystone CA CertificateAdmin',
- 'hidden': True,
- 'name': 'Controller-1::KeystoneCACertificate',
- 'value': 'unset',
- 'default': '',
- 'label': 'Keystone CA CertificateAdmin',
- 'parameter_type': 'string',
- 'constraints': []},
- 'AdminToken': {
- 'description': 'Admin Token',
- 'hidden': True,
- 'name': 'Controller-1::AdminToken',
- 'value': '',
- 'default': '',
- 'label': 'Admin Token',
- 'parameter_type': 'string',
- 'constraints': []},
- 'AdminPassword': {
- 'description': 'Admin password',
- 'hidden': True,
- 'name': 'Controller-1::AdminPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Admin Password',
- 'parameter_type': 'string',
- 'constraints': []}})
-
- mock_parameter_list.assert_called_once_with()
-
- def test_make_keystone_certificates(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
- with contextlib.nested(
- patch('os_cloud_config.keystone_pki.create_ca_pair',
- return_value=('ca_key_pem', 'ca_cert_pem')),
- patch('os_cloud_config.keystone_pki.create_signing_pair',
- return_value=('signing_key_pem', 'signing_cert_pem'))
- ) as (mock_create_ca_pair, mock_create_signing_pair):
- ret_val = plan._make_keystone_certificates(
- {'KeystoneSigningCertificate': {}})
-
- self.assertEqual(
- ret_val,
- {'KeystoneCACertificate': 'ca_cert_pem',
- 'KeystoneSigningCertificate': 'signing_cert_pem',
- 'KeystoneSigningKey': 'signing_key_pem'})
-
- mock_create_ca_pair.assert_called_once_with()
- mock_create_signing_pair.assert_called_once_with(
- 'ca_key_pem', 'ca_cert_pem')
-
- def test_make_generated_parameters(self):
- plan = api.tuskar.Plan(self.tuskarclient_plans.first())
-
- with contextlib.nested(
- patch('tuskar_ui.api.tuskar.Plan.parameter_list',
- return_value=plan.parameters),
- patch('tuskar_ui.api.tuskar.Plan._make_keystone_certificates',
- return_value={'KeystoneCACertificate': 'ca_cert_pem'}),
- patch('tuskar_ui.api.tuskar.password_generator',
- return_value='generated_password')
- ) as (mock_parameter_list, mock_make_keystone_certificates,
- mock_password_generator):
- ret_val = plan.make_generated_parameters()
-
- self.assertEqual(
- ret_val,
- {'Controller-1::KeystoneCACertificate': 'ca_cert_pem',
- 'Controller-1::SnmpdReadonlyUserPassword': 'generated_password',
- 'Controller-1::AdminPassword': 'generated_password',
- 'Controller-1::AdminToken': 'generated_password',
- 'Compute-1::SnmpdReadonlyUserPassword': 'generated_password'})
-
- mock_parameter_list.assert_has_calls([
- call(), call()])
- mock_make_keystone_certificates.assert_called_once_with({
- 'SnmpdReadonlyUserPassword': {
- 'description': 'Snmpd password',
- 'hidden': True,
- 'name': 'Compute-1::SnmpdReadonlyUserPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Snmpd password',
- 'parameter_type': 'string',
- 'constraints': []},
- 'KeystoneCACertificate': {
- 'description': 'Keystone CA CertificateAdmin',
- 'hidden': True, 'name':
- 'Controller-1::KeystoneCACertificate',
- 'value': 'unset',
- 'default': '',
- 'label': 'Keystone CA CertificateAdmin',
- 'parameter_type': 'string',
- 'constraints': []},
- 'AdminToken': {
- 'description': 'Admin Token',
- 'hidden': True,
- 'name': 'Controller-1::AdminToken',
- 'value': '',
- 'default': '',
- 'label': 'Admin Token',
- 'parameter_type': 'string',
- 'constraints': []},
- 'AdminPassword': {
- 'description': 'Admin password',
- 'hidden': True,
- 'name': 'Controller-1::AdminPassword',
- 'value': 'unset',
- 'default': '',
- 'label': 'Admin Password',
- 'parameter_type': 'string',
- 'constraints': []}})
diff --git a/tuskar_ui/test/formset_table_tests.py b/tuskar_ui/test/formset_table_tests.py
deleted file mode 100644
index d50eb6af..00000000
--- a/tuskar_ui/test/formset_table_tests.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# 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
-
-import tuskar_ui.tables
-from tuskar_ui.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(tuskar_ui.tables.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)
diff --git a/tuskar_ui/test/helpers.py b/tuskar_ui/test/helpers.py
deleted file mode 100644
index aa5d1695..00000000
--- a/tuskar_ui/test/helpers.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# 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 warnings
-
-from django.utils import unittest
-from openstack_dashboard.test import helpers
-
-from tuskar_ui.test.test_data import utils
-
-
-# Silences the warning about with statements.
-warnings.filterwarnings('ignore', 'With-statements now directly support '
- 'multiple context managers', DeprecationWarning,
- r'^tuskar_ui[.].*[._]tests$')
-
-
-def create_stubs(stubs_to_create={}):
- return helpers.create_stubs(stubs_to_create)
-
-
-class TuskarTestsMixin(object):
- def _setup_test_data(self):
- super(TuskarTestsMixin, self)._setup_test_data()
- utils.load_test_data(self)
-
- def add_panel_mocks(self):
- pass
-
-
-@unittest.skipIf(os.environ.get('SKIP_UNITTESTS', False),
- "The SKIP_UNITTESTS env variable is set.")
-class TestCase(TuskarTestsMixin, helpers.TestCase):
- pass
-
-
-class BaseAdminViewTests(TuskarTestsMixin, helpers.BaseAdminViewTests):
- pass
-
-
-class APITestCase(TuskarTestsMixin, helpers.APITestCase):
- pass
diff --git a/tuskar_ui/test/selenium.py b/tuskar_ui/test/selenium.py
deleted file mode 100644
index 2eebd8ca..00000000
--- a/tuskar_ui/test/selenium.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012 Nebula, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from __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_qunit(self):
- url = "%s%s" % (self.live_server_url, "/qunit_tuskar/")
- self.selenium.get(url)
- wait = self.ui.WebDriverWait(self.selenium, 10)
-
- def qunit_done(driver):
- text = driver.find_element_by_id("qunit-testresult").text
- return "Tests completed" in text
-
- wait.until(qunit_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 qunit 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))
diff --git a/tuskar_ui/test/settings.py b/tuskar_ui/test/settings.py
deleted file mode 100644
index 33f52bb0..00000000
--- a/tuskar_ui/test/settings.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#
-# 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
-
-from django.utils.translation import ugettext_lazy as _
-
-from horizon.test.settings import * # noqa
-from horizon.utils import secret_key as secret_key_utils
-
-from tuskar_ui import exceptions
-
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-TEST_DIR = os.path.dirname(os.path.abspath(__file__))
-ROOT_PATH = os.path.abspath(os.path.join(TEST_DIR, ".."))
-
-MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media'))
-MEDIA_URL = '/media/'
-STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static'))
-STATIC_URL = '/static/'
-
-SECRET_KEY = secret_key_utils.generate_or_read_from_file(
- os.path.join(TEST_DIR, '.secret_key_store'))
-ROOT_URLCONF = 'tuskar_ui.test.urls'
-TEMPLATE_DIRS = (
- os.path.join(TEST_DIR, 'templates'),
-)
-
-TEMPLATE_CONTEXT_PROCESSORS += (
- 'openstack_dashboard.context_processors.openstack',
-)
-
-INSTALLED_APPS = (
- 'django.contrib.contenttypes',
- 'django.contrib.auth',
- 'django.contrib.sessions',
- 'django.contrib.staticfiles',
- 'django.contrib.messages',
- 'django.contrib.humanize',
- 'django_nose',
- 'openstack_auth',
- 'compressor',
- 'horizon',
- 'openstack_dashboard',
- 'tuskar_ui.infrastructure',
- 'openstack_dashboard.dashboards.settings',
-)
-
-AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
-
-SITE_BRANDING = 'OpenStack'
-
-HORIZON_CONFIG = {
- 'dashboards': ('infrastructure', 'settings'),
- 'default_dashboard': 'infrastructure',
- "password_validator": {
- "regex": '^.{8,18}$',
- "help_text": _("Password must be between 8 and 18 characters.")
- },
- 'user_home': None,
- 'help_url': "http://docs.openstack.org",
- 'exceptions': {'recoverable': exceptions.RECOVERABLE,
- 'not_found': exceptions.NOT_FOUND,
- 'unauthorized': exceptions.UNAUTHORIZED},
-}
-
-# Set to True to allow users to upload images to glance via Horizon server.
-# When enabled, a file form field will appear on the create image form.
-# See documentation for deployment considerations.
-HORIZON_IMAGES_ALLOW_UPLOAD = True
-
-AVAILABLE_REGIONS = [
- ('http://localhost:5000/v2.0', 'local'),
- ('http://remote:5000/v2.0', 'remote'),
-]
-
-OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0"
-OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member"
-
-OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
-OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'test_domain'
-
-OPENSTACK_KEYSTONE_BACKEND = {
- 'name': 'native',
- 'can_edit_user': True,
- 'can_edit_group': True,
- 'can_edit_project': True,
- 'can_edit_domain': True,
- 'can_edit_role': True
-}
-
-OPENSTACK_NEUTRON_NETWORK = {
- 'enable_lb': True
-}
-
-OPENSTACK_HYPERVISOR_FEATURES = {
- 'can_set_mount_point': True,
-
- # NOTE: as of Grizzly this is not yet supported in Nova so enabling this
- # setting will not do anything useful
- 'can_encrypt_volumes': False
-}
-
-LOGGING['loggers']['openstack_dashboard'] = {
- 'handlers': ['test'],
- 'propagate': False,
-}
-
-LOGGING['loggers']['selenium'] = {
- 'handlers': ['test'],
- 'propagate': False,
-}
-
-LOGGING['loggers']['tuskar_ui'] = {
- 'handlers': ['test'],
- 'propagate': False,
-}
-
-SECURITY_GROUP_RULES = {
- 'all_tcp': {
- 'name': 'ALL TCP',
- 'ip_protocol': 'tcp',
- 'from_port': '1',
- 'to_port': '65535',
- },
- 'http': {
- 'name': 'HTTP',
- 'ip_protocol': 'tcp',
- 'from_port': '80',
- 'to_port': '80',
- },
-}
-
-NOSE_ARGS = ['--nocapture',
- '--nologcapture',
- '--cover-package=openstack_dashboard',
- '--cover-inclusive',
- '--all-modules']
-
-TUSKAR_ENDPOINT_URL = "http://127.0.0.1:8585"
-
-OVERCLOUD_CREDS = {
- 'enabled': True,
- 'user': 'admin',
- 'password': 'password',
- 'tenant': 'admin',
- 'auth_url': 'http://localhost:5000/v2.0/',
-}
diff --git a/tuskar_ui/test/test_data/__init__.py b/tuskar_ui/test/test_data/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/test/test_data/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/test/test_data/exceptions.py b/tuskar_ui/test/test_data/exceptions.py
deleted file mode 100644
index 2b56032a..00000000
--- a/tuskar_ui/test/test_data/exceptions.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# 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 tuskarclient.openstack.common.apiclient import exceptions as tuskarclient
-
-
-def data(TEST):
- TEST.exceptions = exceptions.data
-
- tuskar_exception = tuskarclient.ClientException
- TEST.exceptions.tuskar = exceptions.create_stubbed_exception(
- tuskar_exception)
diff --git a/tuskar_ui/test/test_data/flavor_data.py b/tuskar_ui/test/test_data/flavor_data.py
deleted file mode 100644
index 290a11e6..00000000
--- a/tuskar_ui/test/test_data/flavor_data.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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 novaclient.v2 import flavors
-from openstack_dashboard.test.test_data import utils as test_data_utils
-
-
-def data(TEST):
-
- # Nova flavors
- # Do not include fields irrelevant for our usage
- TEST.novaclient_flavors = test_data_utils.TestDataContainer()
- flavor_1 = flavors.Flavor(
- flavors.FlavorManager(None),
- {'id': '1',
- 'name': 'flavor-1',
- 'vcpus': 2,
- 'ram': 2048,
- 'disk': 20})
- flavor_1.get_keys = lambda: {'cpu_arch': 'amd64'}
- flavor_2 = flavors.Flavor(
- flavors.FlavorManager(None),
- {'id': '2',
- 'name': 'flavor-2',
- 'vcpus': 4,
- 'ram': 4096,
- 'disk': 60})
- flavor_2.get_keys = lambda: {'cpu_arch': 'i386'}
- TEST.novaclient_flavors.add(flavor_1, flavor_2)
diff --git a/tuskar_ui/test/test_data/heat_data.py b/tuskar_ui/test/test_data/heat_data.py
deleted file mode 100644
index 303bcc0a..00000000
--- a/tuskar_ui/test/test_data/heat_data.py
+++ /dev/null
@@ -1,248 +0,0 @@
-# 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 glanceclient.v1 import images
-from heatclient.v1 import events
-from heatclient.v1 import resources
-from heatclient.v1 import stacks
-from novaclient.v2 import servers
-from openstack_dashboard.test.test_data import utils as test_data_utils
-
-
-def data(TEST):
-
- # Stack
- TEST.heatclient_stacks = test_data_utils.TestDataContainer()
- stack_1 = stacks.Stack(
- stacks.StackManager(None),
- {'id': 'stack-id-1',
- 'stack_name': 'overcloud',
- 'stack_status': 'RUNNING',
- 'outputs': [{
- 'output_key': 'KeystoneURL',
- 'output_value': 'http://192.0.2.23:5000/v2',
- }],
- 'parameters': {
- 'plan_id': 'plan-1',
- 'one': 'one',
- 'two': 'two',
- }})
- TEST.heatclient_stacks.add(stack_1)
-
- # Events
- TEST.heatclient_events = test_data_utils.TestDataContainer()
- event_1 = events.Event(
- events.EventManager(None),
- {'id': 1,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Controller',
- 'resource_status': 'CREATE_IN_PROGRESS',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:26:15Z'})
- event_2 = events.Event(
- events.EventManager(None),
- {'id': 2,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute0',
- 'resource_status': 'CREATE_IN_PROGRESS',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:26:27Z'})
- event_3 = events.Event(
- events.EventManager(None),
- {'id': 3,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute1',
- 'resource_status': 'CREATE_IN_PROGRESS',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:26:44Z'})
- event_4 = events.Event(
- events.EventManager(None),
- {'id': 4,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute0',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:27:14Z'})
- event_5 = events.Event(
- events.EventManager(None),
- {'id': 5,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute2',
- 'resource_status': 'CREATE_IN_PROGRESS',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:27:31Z'})
- event_6 = events.Event(
- events.EventManager(None),
- {'id': 6,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute1',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:28:01Z'})
- event_7 = events.Event(
- events.EventManager(None),
- {'id': 7,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Controller',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:28:59Z'})
- event_8 = events.Event(
- events.EventManager(None),
- {'id': 8,
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute2',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_status_reason': 'state changed',
- 'event_time': '2014-01-01T07:29:11Z'})
- TEST.heatclient_events.add(event_1, event_2, event_3, event_4,
- event_5, event_6, event_7, event_8)
-
- # Resource
- TEST.heatclient_resources = test_data_utils.TestDataContainer()
- resource_1 = resources.Resource(
- resources.ResourceManager(None),
- {'id': '1-resource-id',
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute0',
- 'logical_resource_id': 'Compute0',
- 'physical_resource_id': 'aa',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_type': 'OS::Nova::Server'})
- resource_2 = resources.Resource(
- resources.ResourceManager(None),
- {'id': '2-resource-id',
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Controller',
- 'logical_resource_id': 'Controller',
- 'physical_resource_id': 'bb',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_type': 'OS::Nova::Server'})
- resource_3 = resources.Resource(
- resources.ResourceManager(None),
- {'id': '3-resource-id',
- 'stack_id': 'stack-id-1',
- 'resource_name': 'Compute1',
- 'logical_resource_id': 'Compute1',
- 'physical_resource_id': 'cc',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_type': 'OS::Nova::Server'})
- resource_4 = resources.Resource(
- resources.ResourceManager(None),
- {'id': '4-resource-id',
- 'stack_id': 'stack-id-4',
- 'resource_name': 'Compute2',
- 'logical_resource_id': 'Compute2',
- 'physical_resource_id': 'dd',
- 'resource_status': 'CREATE_COMPLETE',
- 'resource_type': 'OS::Nova::Server'})
- TEST.heatclient_resources.add(resource_1,
- resource_2,
- resource_3,
- resource_4)
-
- # Server
- TEST.novaclient_servers = test_data_utils.TestDataContainer()
- s_1 = servers.Server(
- servers.ServerManager(None),
- {'id': 'aa',
- 'name': 'Compute',
- 'created': '2014-06-26T20:38:06Z',
- 'image': {'id': '1'},
- 'flavor': {
- 'id': '1',
- },
- 'status': 'ACTIVE',
- 'public_ip': '192.168.1.1'})
- s_2 = servers.Server(
- servers.ServerManager(None),
- {'id': 'bb',
- 'name': 'Controller',
- 'created': '2014-06-27T20:38:06Z',
- 'image': {'id': '2'},
- 'flavor': {
- 'id': '2',
- },
- 'status': 'ACTIVE',
- 'public_ip': '192.168.1.2'})
- s_3 = servers.Server(
- servers.ServerManager(None),
- {'id': 'cc',
- 'name': 'Compute',
- 'created': '2014-06-28T20:38:06Z',
- 'image': {'id': '1'},
- 'flavor': {
- 'id': '1',
- },
- 'status': 'BUILD',
- 'public_ip': '192.168.1.3'})
- s_4 = servers.Server(
- servers.ServerManager(None),
- {'id': 'dd',
- 'name': 'Compute',
- 'created': '2014-06-29T20:38:06Z',
- 'image': {'id': '1'},
- 'flavor': {
- 'id': '1',
- },
- 'status': 'ERROR',
- 'public_ip': '192.168.1.4'})
- TEST.novaclient_servers.add(s_1, s_2, s_3, s_4)
-
- # Image
- TEST.glanceclient_images = test_data_utils.TestDataContainer()
- image_1 = images.Image(
- images.ImageManager(None),
- {'id': '1',
- 'name': 'overcloud-full',
- 'is_public': True,
- 'protected': False,
- 'properties': {
- 'type': 'overcloud provisioning'
- }})
- image_2 = images.Image(
- images.ImageManager(None),
- {'id': '2',
- 'name': 'Discovery Ramdisk',
- 'is_public': True,
- 'protected': False,
- 'properties': {
- 'type': 'discovery ramdisk'
- }})
- image_3 = images.Image(
- images.ImageManager(None),
- {'id': '3',
- 'name': 'Discovery Kernel',
- 'is_public': True,
- 'protected': False,
- 'properties': {
- 'type': 'discovery kernel'
- }})
- image_4 = images.Image(
- images.ImageManager(None),
- {'id': '4',
- 'name': 'Baremetal Deployment Kernel',
- 'is_public': True,
- 'protected': False,
- 'properties': {
- 'type': 'deploy kernel'
- }})
- image_5 = images.Image(
- images.ImageManager(None),
- {'id': '5',
- 'name': 'Baremetal Deployment Ramdisk',
- 'is_public': True,
- 'protected': False,
- 'properties': {
- 'type': 'deploy ramdisk'
- }})
- TEST.glanceclient_images.add(image_1, image_2, image_3, image_4, image_5)
diff --git a/tuskar_ui/test/test_data/keystone_data.py b/tuskar_ui/test/test_data/keystone_data.py
deleted file mode 100644
index 356c2d6f..00000000
--- a/tuskar_ui/test/test_data/keystone_data.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# 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.
-
-
-def data(TEST):
-
- # Add tuskar to the keystone data
- TEST.service_catalog.append(
- {"type": "management",
- "name": "tuskar",
- "endpoints_links": [],
- "endpoints": [
- {"region": "RegionOne",
- "adminURL": "http://admin.tuskar.example.com:8585/v2",
- "internalURL": "http://int.tuskar.example.com:8585/v2",
- "publicURL": "http://public.tuskar.example.com:8585/v2"},
- {"region": "RegionTwo",
- "adminURL": "http://admin.tuskar2.example.com:8585/v2",
- "internalURL": "http://int.tuskar2.example.com:8585/v2",
- "publicURL": "http://public.tuskar2.example.com:8585/v2"}]},
- )
diff --git a/tuskar_ui/test/test_data/node_data.py b/tuskar_ui/test/test_data/node_data.py
deleted file mode 100644
index 7b54b079..00000000
--- a/tuskar_ui/test/test_data/node_data.py
+++ /dev/null
@@ -1,289 +0,0 @@
-# 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 ironicclient.v1 import node
-from ironicclient.v1 import port
-from openstack_dashboard.test.test_data import utils as test_data_utils
-
-
-def data(TEST):
- # IronicNode
- TEST.ironicclient_nodes = test_data_utils.TestDataContainer()
- node_1 = node.Node(
- node.NodeManager(None),
- {'id': '1',
- 'uuid': 'aa-11',
- 'instance_uuid': 'aa',
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '1.1.1.1',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.2',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': None,
- 'newly_discovered': None,
- 'provision_state': 'active',
- 'extra': {}
- })
- node_2 = node.Node(
- node.NodeManager(None),
- {'id': '2',
- 'uuid': 'bb-22',
- 'instance_uuid': 'bb',
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '2.2.2.2',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.3',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '16',
- 'memory_mb': '4096',
- 'local_gb': '100',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': None,
- 'newly_discovered': None,
- 'provision_state': 'active',
- 'extra': {}
- })
- node_3 = node.Node(
- node.NodeManager(None),
- {'id': '3',
- 'uuid': 'cc-33',
- 'instance_uuid': 'cc',
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '3.3.3.3',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.4',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '32',
- 'memory_mb': '8192',
- 'local_gb': '1',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'rebooting',
- 'target_power_state': 'on',
- 'maintenance': None,
- 'newly_discovered': None,
- 'provision_state': 'deploying',
- 'extra': {}
- })
- node_4 = node.Node(
- node.NodeManager(None),
- {'id': '4',
- 'uuid': 'cc-44',
- 'instance_uuid': 'cc',
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '4.4.4.4',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.5',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': None,
- 'newly_discovered': None,
- 'provision_state': 'deploying',
- 'extra': {}
- })
- node_5 = node.Node(
- node.NodeManager(None),
- {'id': '5',
- 'uuid': 'dd-55',
- 'instance_uuid': 'dd',
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '5.5.5.5',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.6',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'error',
- 'target_power_state': 'on',
- 'provision_state': 'error',
- 'maintenance': None,
- 'newly_discovered': None,
- 'extra': {}
- })
- node_6 = node.Node(
- node.NodeManager(None),
- {'id': '6',
- 'uuid': 'ff-66',
- 'instance_uuid': None,
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '5.5.5.5',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.6',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': None,
- 'newly_discovered': None,
- 'provision_state': 'active',
- 'extra': {}
- })
- node_7 = node.Node(
- node.NodeManager(None),
- {'id': '7',
- 'uuid': 'gg-77',
- 'instance_uuid': None,
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '7.7.7.7',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.7',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'off',
- 'target_power_state': 'on',
- 'maintenance': True,
- 'newly_discovered': None,
- 'provision_state': 'deploying',
- 'extra': {}
- })
- node_8 = node.Node(
- node.NodeManager(None),
- {'id': '8',
- 'uuid': 'hh-88',
- 'instance_uuid': None,
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '8.8.8.8',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.8',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '8',
- 'memory_mb': '4096',
- 'local_gb': '10',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': True,
- 'newly_discovered': True,
- 'provision_state': 'active',
- 'extra': {}
- })
- node_9 = node.Node(
- node.NodeManager(None),
- {'id': '9',
- 'uuid': 'ii-99',
- 'instance_uuid': None,
- 'driver': 'pxe_ipmitool',
- 'driver_info': {
- 'ipmi_address': '9.9.9.9',
- 'ipmi_username': 'admin',
- 'ipmi_password': 'password',
- 'ip_address': '1.2.2.9',
- 'deploy_kernel': 'deploy-kernel-uuid',
- 'deploy_ramdisk': 'deploy-ramdisk-uuid',
- },
- 'properties': {
- 'cpus': '16',
- 'memory_mb': '8192',
- 'local_gb': '1000',
- 'cpu_arch': 'x86_64',
- },
- 'power_state': 'on',
- 'target_power_state': 'on',
- 'maintenance': True,
- 'newly_discovered': True,
- 'provision_state': 'active',
- 'extra': {}
- })
- TEST.ironicclient_nodes.add(node_1, node_2, node_3, node_4, node_5, node_6,
- node_7, node_8, node_9)
-
- # Ports
- TEST.ironicclient_ports = test_data_utils.TestDataContainer()
- port_1 = port.Port(
- port.PortManager(None),
- {'id': '1-port-id',
- 'type': 'port',
- 'address': 'aa:aa:aa:aa:aa:aa'})
- port_2 = port.Port(
- port.PortManager(None),
- {'id': '2-port-id',
- 'type': 'port',
- 'address': 'bb:bb:bb:bb:bb:bb'})
- port_3 = port.Port(
- port.PortManager(None),
- {'id': '3-port-id',
- 'type': 'port',
- 'address': 'cc:cc:cc:cc:cc:cc'})
- port_4 = port.Port(
- port.PortManager(None),
- {'id': '4-port-id',
- 'type': 'port',
- 'address': 'dd:dd:dd:dd:dd:dd'})
- TEST.ironicclient_ports.add(port_1, port_2, port_3, port_4)
diff --git a/tuskar_ui/test/test_data/tuskar_data.py b/tuskar_ui/test/test_data/tuskar_data.py
deleted file mode 100644
index dae72f1c..00000000
--- a/tuskar_ui/test/test_data/tuskar_data.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from openstack_dashboard.test.test_data import utils as test_data_utils
-from tuskarclient.v2 import plans
-from tuskarclient.v2 import roles
-
-planmanager = plans.PlanManager(None)
-rolemanager = roles.RoleManager(None)
-
-
-def data(TEST):
-
- # Plan
- TEST.tuskarclient_plans = test_data_utils.TestDataContainer()
- plan_1 = plans.Plan(planmanager, {
- 'uuid': 'plan-1',
- 'name': 'overcloud',
- 'description': 'this is an overcloud deployment plan',
- 'template': '',
- 'created_at': '2014-05-27T21:11:09Z',
- 'modified_at': '2014-05-30T21:11:09Z',
- 'roles': [
- {
- 'uuid': 'role-1',
- 'name': 'Controller',
- 'version': 1,
- }, {
- 'uuid': 'role-2',
- 'name': 'Compute',
- 'version': 1,
- }, {
- 'uuid': 'role-3',
- 'name': 'Object Storage',
- 'version': 1,
- }, {
- 'uuid': 'role-4',
- 'name': 'Block Storage',
- 'version': 1,
- }],
- 'parameters': [{
- 'name': 'Controller-1::count',
- 'label': 'Controller Node Count',
- 'description': 'Controller node count',
- 'hidden': False,
- 'default': '',
- 'value': 1,
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Compute-1::count',
- 'label': 'Compute Node Count',
- 'description': 'Compute node count',
- 'hidden': False,
- 'default': '',
- 'value': 42,
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Block Storage-1::count',
- 'label': 'Block Sorage Node Count',
- 'description': 'Block storage node count',
- 'hidden': False,
- 'default': '',
- 'value': 5,
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::Flavor',
- 'label': 'Controller Flavor',
- 'description': 'Controller flavor',
- 'hidden': False,
- 'default': '',
- 'value': 'flavor-1',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Compute-1::Flavor',
- 'label': 'Compute Flavor',
- 'description': 'Compute flavor',
- 'hidden': False,
- 'default': '',
- 'value': 'flavor-1',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Block Storage-1::Flavor',
- 'label': 'Block Storage Flavor',
- 'description': 'Block storage flavor',
- 'hidden': False,
- 'default': '',
- 'value': 'flavor-2',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::Image',
- 'label': 'Controller Image ID',
- 'description': 'Controller image ID',
- 'hidden': False,
- 'default': '',
- 'value': 'overcloud-full',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Compute-1::Image',
- 'label': 'Compute Image ID',
- 'description': 'Compute image ID',
- 'hidden': False,
- 'default': '',
- 'value': 'overcloud-full',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Block Storage-1::Image',
- 'label': 'Block Storage Image ID',
- 'description': 'Block storage image ID',
- 'hidden': False,
- 'default': '',
- 'value': 'overcloud-full',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::KeystoneCACertificate',
- 'label': 'Keystone CA CertificateAdmin',
- 'description': 'Keystone CA CertificateAdmin',
- 'hidden': True,
- 'default': '',
- 'value': 'unset',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::AdminPassword',
- 'label': 'Admin Password',
- 'description': 'Admin password',
- 'hidden': True,
- 'default': '',
- 'value': 'unset',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::AdminToken',
- 'label': 'Admin Token',
- 'description': 'Admin Token',
- 'hidden': True,
- 'default': '',
- 'value': '',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::SnmpdReadonlyUserPassword',
- 'label': 'Snmpd password',
- 'description': 'Snmpd password',
- 'hidden': True,
- 'default': '',
- 'value': '',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Compute-1::SnmpdReadonlyUserPassword',
- 'label': 'Snmpd password',
- 'description': 'Snmpd password',
- 'hidden': True,
- 'default': '',
- 'value': 'unset',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Controller-1::ExtraConfig',
- 'label': 'Extra Config',
- 'description': 'Extra Config',
- 'hidden': False,
- 'default': '',
- 'value': '{}',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Compute-1::ExtraConfig',
- 'label': 'Extra Config',
- 'description': 'Extra Config',
- 'hidden': False,
- 'default': '',
- 'value': '{}',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Block Storage-1::ExtraConfig',
- 'label': 'Extra Config',
- 'description': 'Extra Config',
- 'hidden': False,
- 'default': '',
- 'value': '{}',
- 'parameter_type': 'string',
- 'constraints': [],
- }, {
- 'name': 'Object Storage-1::ExtraConfig',
- 'label': 'Extra Config',
- 'description': 'Extra Config',
- 'hidden': False,
- 'default': '',
- 'value': '{}',
- 'parameter_type': 'string',
- 'constraints': [],
- }],
- })
- TEST.tuskarclient_plans.add(plan_1)
-
- # Role
- TEST.tuskarclient_roles = test_data_utils.TestDataContainer()
- r_1 = roles.Role(rolemanager, {
- 'uuid': 'role-1',
- 'name': 'Controller',
- 'version': 1,
- 'description': 'controller role',
- 'created_at': '2014-05-27T21:11:09Z'
- })
- r_2 = roles.Role(rolemanager, {
- 'uuid': 'role-2',
- 'name': 'Compute',
- 'version': 1,
- 'description': 'compute role',
- 'created_at': '2014-05-27T21:11:09Z'
- })
- r_3 = roles.Role(rolemanager, {
- 'uuid': 'role-3',
- 'name': 'Object Storage',
- 'version': 1,
- 'description': 'object storage role',
- 'created_at': '2014-05-27T21:11:09Z'
- })
- r_4 = roles.Role(rolemanager, {
- 'uuid': 'role-4',
- 'name': 'Block Storage',
- 'version': 1,
- 'description': 'block storage role',
- 'created_at': '2014-05-27T21:11:09Z'
- })
- TEST.tuskarclient_roles.add(r_1, r_2, r_3, r_4)
diff --git a/tuskar_ui/test/test_data/utils.py b/tuskar_ui/test/test_data/utils.py
deleted file mode 100644
index ef61ed0d..00000000
--- a/tuskar_ui/test/test_data/utils.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-from openstack_dashboard.test.test_data import utils
-
-
-def load_test_data(load_onto=None):
- from openstack_dashboard.test.test_data import cinder_data
- from openstack_dashboard.test.test_data import glance_data
- from openstack_dashboard.test.test_data import heat_data
- from openstack_dashboard.test.test_data import keystone_data
- from openstack_dashboard.test.test_data import neutron_data
- from openstack_dashboard.test.test_data import nova_data
- from openstack_dashboard.test.test_data import swift_data
-
- from tuskar_ui.test.test_data import exceptions
- from tuskar_ui.test.test_data import flavor_data
- from tuskar_ui.test.test_data import heat_data as tuskar_heat_data
- from tuskar_ui.test.test_data import keystone_data as tuskar_keystone_data
- from tuskar_ui.test.test_data import node_data
- from tuskar_ui.test.test_data import tuskar_data
-
- # The order of these loaders matters, some depend on others.
- loaders = (exceptions.data,
- keystone_data.data,
- glance_data.data,
- nova_data.data,
- cinder_data.data,
- neutron_data.data,
- swift_data.data,
- heat_data.data,
- flavor_data.data,
- node_data.data,
- tuskar_heat_data.data,
- tuskar_keystone_data.data,
- tuskar_data.data)
- if load_onto:
- for data_func in loaders:
- data_func(load_onto)
- return load_onto
- else:
- return utils.TestData(*loaders)
diff --git a/tuskar_ui/test/test_forms.py b/tuskar_ui/test/test_forms.py
deleted file mode 100644
index 9b9a0cd3..00000000
--- a/tuskar_ui/test/test_forms.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- 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.
-
-
-from django.utils.translation import ugettext_lazy as _
-
-from tuskar_ui import forms
-from tuskar_ui.test import helpers as test
-
-
-class MultiMACFieldTests(test.TestCase):
- def test_empty(self):
- field = forms.MultiMACField(required=False)
- cleaned = field.clean("")
- self.assertEqual(cleaned, "")
-
- def test_required(self):
- field = forms.MultiMACField(required=True)
- with self.assertRaises(forms.forms.ValidationError) as raised:
- field.clean("")
- self.assertEqual(unicode(raised.exception.messages[0]),
- unicode(_('This field is required.')))
-
- def test_malformed(self):
- field = forms.MultiMACField(required=True)
- with self.assertRaises(forms.forms.ValidationError) as raised:
- field.clean("de.ad:be.ef:ca.fe")
- self.assertEqual(
- unicode(raised.exception.messages[0]),
- unicode(_(u"'de.ad:be.ef:ca.fe' is not a valid MAC address.")),
- )
-
- def test_single(self):
- field = forms.MultiMACField(required=False)
- cleaned = field.clean("de:AD:be:ef:Ca:FE")
- self.assertEqual(cleaned, "DE:AD:BE:EF:CA:FE")
-
- def test_multiple(self):
- field = forms.MultiMACField(required=False)
- cleaned = field.clean(
- "de:AD:be:ef:Ca:FC, de:AD:be:ef:Ca:FD de:AD:be:ef:Ca:FE\n"
- "de:AD:be:ef:Ca:FF",
- )
- self.assertEqual(
- cleaned,
- "DE:AD:BE:EF:CA:FC DE:AD:BE:EF:CA:FD DE:AD:BE:EF:CA:FE "
- "DE:AD:BE:EF:CA:FF",
- )
-
- def test_duplicated(self):
- field = forms.MultiMACField(required=False)
- cleaned = field.clean("DE:AD:BE:EF:CA:FC DE:AD:BE:EF:CA:FC")
-
- self.assertEqual(
- cleaned,
- "DE:AD:BE:EF:CA:FC",
- )
diff --git a/tuskar_ui/test/urls.py b/tuskar_ui/test/urls.py
deleted file mode 100644
index 8f09bbfb..00000000
--- a/tuskar_ui/test/urls.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# 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 django.views import generic
-import openstack_dashboard.urls
-
-urlpatterns = urls.patterns(
- '',
- urls.url(
- r'^qunit_tuskar',
- generic.TemplateView.as_view(
- template_name="infrastructure/qunit.html"),
- name='qunit_tests'),
- urls.url(r'', urls.include(openstack_dashboard.urls))
-)
diff --git a/tuskar_ui/utils/__init__.py b/tuskar_ui/utils/__init__.py
deleted file mode 100644
index e69de29b..00000000
--- a/tuskar_ui/utils/__init__.py
+++ /dev/null
diff --git a/tuskar_ui/utils/metering.py b/tuskar_ui/utils/metering.py
deleted file mode 100644
index 2f9927ef..00000000
--- a/tuskar_ui/utils/metering.py
+++ /dev/null
@@ -1,289 +0,0 @@
-# -*- 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 copy
-
-from django.utils.http import urlencode
-from django.utils.translation import ugettext_lazy as _
-from horizon import exceptions
-from openstack_dashboard.api import ceilometer
-from openstack_dashboard.utils import metering
-
-SETTINGS = {
- 'settings': {
- 'renderer': 'StaticAxes',
- 'xMin': None,
- 'xMax': None,
- 'higlight_last_point': True,
- 'auto_size': False,
- 'auto_resize': False,
- 'axes_x': False,
- 'axes_y': True,
- 'axes_y_label': False,
- 'bar_chart_settings': {
- 'orientation': 'vertical',
- 'used_label_placement': 'left',
- 'width': 30,
- 'color_scale_domain': [0, 80, 80, 100],
- 'color_scale_range': [
- '#0000FF',
- '#0000FF',
- '#FF0000',
- '#FF0000'
- ],
- 'average_color_scale_domain': [0, 100],
- 'average_color_scale_range': ['#0000FF', '#0000FF']
- }
- },
- 'stats': {
- 'average': None,
- 'used': None,
- 'tooltip_average': None,
- }
-}
-
-LABELS = {
- 'hardware.cpu.load.1min': _("CPU load 1 min average"),
- 'hardware.system_stats.cpu.util': _("CPU utilization"),
- 'hardware.system_stats.io.outgoing.blocks': _("IO raw sent"),
- 'hardware.system_stats.io.incoming.blocks': _("IO raw received"),
- 'hardware.network.ip.outgoing.datagrams': _("IP out requests"),
- 'hardware.network.ip.incoming.datagrams': _("IP in requests"),
- 'hardware.memory.swap.util': _("Swap utilization"),
- 'hardware.ipmi.fan': _("Fan Speed"),
- 'hardware.ipmi.voltage': _("Voltage"),
- 'hardware.ipmi.temperature': _("Temperature"),
- 'hardware.ipmi.current': _("Current")
-}
-
-
-# TODO(lsmola) this should probably live in Horizon common
-def query_data(request,
- date_from,
- date_to,
- group_by,
- meter,
- period=None,
- query=None,
- additional_query=None):
-
- if not period:
- period = metering.calc_period(date_from, date_to, 50)
- if additional_query is None:
- additional_query = []
- if date_from:
- additional_query += [{'field': 'timestamp',
- 'op': 'ge',
- 'value': date_from}]
- if date_to:
- additional_query += [{'field': 'timestamp',
- 'op': 'le',
- 'value': date_to}]
-
- # TODO(lsmola) replace this by logic implemented in I1 in bugs
- # 1226479 and 1226482, this is just a quick fix for RC1
- ceilometer_usage = ceilometer.CeilometerUsage(request)
- try:
- if group_by:
- resources = ceilometer_usage.resource_aggregates_with_statistics(
- query, [meter], period=period, stats_attr=None,
- additional_query=additional_query)
- else:
- resources = ceilometer_usage.resources_with_statistics(
- query, [meter], period=period, stats_attr=None,
- additional_query=additional_query)
- except Exception:
- resources = []
- exceptions.handle(request,
- _('Unable to retrieve statistics.'))
- return resources
-
-
-def url_part(meter_name, barchart):
- d = {'meter': meter_name}
- if barchart:
- d['barchart'] = True
- return urlencode(d)
-
-
-def get_meter_name(meter):
- return meter.replace('.', '_')
-
-
-def get_meter_list_and_unit(request, meter):
- try:
- meter_list = [m for m in ceilometer.meter_list(request)
- if m.name == meter]
- unit = meter_list[0].unit
- except Exception:
- meter_list = []
- unit = ''
-
- return meter_list, unit
-
-
-def get_meters(meters):
- return [(m, get_meter_name(m)) for m in meters]
-
-
-def get_barchart_stats(series, unit):
- values = [point['y'] for point in series[0]['data']]
- average = sum(values) / len(values)
- used = values[-1]
- first_date = series[0]['data'][0]['x']
- last_date = series[0]['data'][-1]['x']
- tooltip_average = _('Average %(average)s %(unit)s<br> From: '
- '%(first_date)s, to: %(last_date)s') % (
- dict(average=average, unit=unit,
- first_date=first_date,
- last_date=last_date))
- return average, used, tooltip_average
-
-
-def create_json_output(series, barchart, unit, date_from, date_to):
- start_datetime = end_datetime = ''
- if date_from:
- start_datetime = date_from.strftime("%Y-%m-%dT%H:%M:%S")
- if date_to:
- end_datetime = date_to.strftime("%Y-%m-%dT%H:%M:%S")
-
- settings = copy.deepcopy(SETTINGS)
- settings['settings']['xMin'] = start_datetime
- settings['settings']['xMax'] = end_datetime
-
- if series and barchart:
- average, used, tooltip_average = get_barchart_stats(series, unit)
- settings['settings']['yMin'] = 0
- settings['settings']['yMax'] = 100
- settings['stats']['average'] = average
- settings['stats']['used'] = used
- settings['stats']['tooltip_average'] = tooltip_average
- else:
- del settings['settings']['bar_chart_settings']
- del settings['stats']
-
- json_output = {'series': series}
- json_output = dict(json_output.items() + settings.items())
- return json_output
-
-
-def get_nodes_stats(request, node_uuid, instance_uuids, meter,
- date_options=None, date_from=None, date_to=None,
- stats_attr=None, barchart=None, group_by=None):
- series = []
- meter_list, unit = get_meter_list_and_unit(request, meter)
-
- if instance_uuids:
- if 'ipmi' in meter:
- # For IPMI metrics, a resource ID is made of node UUID concatenated
- # with the metric description. E.g:
- # 1dcf1896-f581-4027-9efa-973eef3380d2-fan_2a_tach_(0x42)
- resource_ids = [m.resource_id for m in meter_list
- if m.resource_id.startswith(node_uuid)]
- queries = [
- [{'field': 'resource_id',
- 'op': 'eq',
- 'value': resource_id}]
- for resource_id in resource_ids
- ]
- else:
- # For SNMP metrics, a resource ID matches exactly the UUID of the
- # associated instance
- queries = [
- [{'field': 'resource_id',
- 'op': 'eq',
- 'value': instance_uuid}]
- for instance_uuid in instance_uuids
- ]
- else:
- # query will be aggregated across all resources
- group_by = "all"
- query = {'all': []}
- queries = [query]
-
- # Disk and Network I/O: data from 2 meters in one chart
- if meter == 'disk-io':
- meters = get_meters([
- 'hardware.system_stats.io.outgoing.blocks',
- 'hardware.system_stats.io.incoming.blocks'
- ])
- elif meter == 'network-io':
- meters = get_meters([
- 'hardware.network.ip.outgoing.datagrams',
- 'hardware.network.ip.incoming.datagrams'
- ])
- else:
- meters = get_meters([meter])
-
- date_from, date_to = metering.calc_date_args(
- date_from,
- date_to,
- date_options)
-
- for meter_id, meter_name in meters:
- label = unicode(LABELS.get(meter_id, meter_name))
-
- for query in queries:
- resources = query_data(
- request=request,
- date_from=date_from,
- date_to=date_to,
- group_by=group_by,
- meter=meter_id,
- query=query)
- s = metering.series_for_meter(request, resources, group_by,
- meter_id, meter_name, stats_attr,
- unit, label)
- series += s
-
- series = metering.normalize_series_by_unit(series)
-
- json_output = create_json_output(
- series,
- barchart,
- unit,
- date_from,
- date_to)
-
- return json_output
-
-
-def get_top_5(request, meter):
- ceilometer_usage = ceilometer.CeilometerUsage(request)
- meter_list, unit = get_meter_list_and_unit(request, meter)
- top_5 = {'unit': unit, 'label': LABELS.get(meter, meter)}
- data = []
-
- for m in meter_list:
- query = [{'field': 'resource_id', 'op': 'eq', 'value': m.resource_id}]
- resource = ceilometer_usage.resources_with_statistics(query, [m.name],
- period=600)[0]
- node_uuid = resource.metadata['node']
- old_value, value = resource.get_meter(get_meter_name(m.name))[-2:]
- old_value, value = old_value.max, value.max
-
- if value > old_value:
- direction = 'up'
- elif value < old_value:
- direction = 'down'
- else:
- direction = None
-
- data.append({
- 'node_uuid': node_uuid,
- 'value': value,
- 'direction': direction
- })
-
- top_5['data'] = sorted(data, key=lambda d: d['value'], reverse=True)[:5]
- return top_5
diff --git a/tuskar_ui/utils/tests.py b/tuskar_ui/utils/tests.py
deleted file mode 100644
index 71a59e30..00000000
--- a/tuskar_ui/utils/tests.py
+++ /dev/null
@@ -1,298 +0,0 @@
-# -*- 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
-import datetime
-
-from django.utils.translation import ugettext_lazy as _
-import mock
-
-from tuskar_ui.test import helpers
-from tuskar_ui.utils import metering
-from tuskar_ui.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)
-
- def test_parse_correct_csv_file(self):
- correct_file = [
- 'pxe_ipmitool,ipmi_address,ipmi_username,ipmi_password,'
- 'mac_addresses,cpu_arch,cpus,memory_mb,local_gb',
- 'pxe_ipmitool,,,,MAC_ADDRESS,,CPUS,,LOCAL_GB',
- 'pxe_ssh,ssh_address,ssh_username,ssh_key_contents,mac_addresses'
- ',cpu_arch,cpus,memory_mb,local_gb',
- 'pxe_ssh,SSH,USER,KEY',
- 'pxe_ssh,SSH,USER,,,CPU_ARCH',
- ]
-
- correct_data = utils.parse_csv_file(correct_file)
-
- self.assertSequenceEqual(
- correct_data, [
- {
- 'driver': 'pxe_ipmitool',
- 'ipmi_address': 'ipmi_address',
- 'ipmi_username': 'ipmi_username',
- 'ipmi_password': 'ipmi_password',
- 'mac_addresses': 'mac_addresses',
- 'cpu_arch': 'cpu_arch',
- 'cpus': 'cpus',
- 'memory_mb': 'memory_mb',
- 'local_gb': 'local_gb',
- }, {
- 'driver': 'pxe_ipmitool',
- 'ipmi_address': '',
- 'ipmi_username': '',
- 'ipmi_password': '',
- 'mac_addresses': 'MAC_ADDRESS',
- 'cpu_arch': '',
- 'cpus': 'CPUS',
- 'memory_mb': '',
- 'local_gb': 'LOCAL_GB',
- }, {
- 'driver': 'pxe_ssh',
- 'ssh_address': 'ssh_address',
- 'ssh_username': 'ssh_username',
- 'ssh_key_contents': 'ssh_key_contents',
- 'mac_addresses': 'mac_addresses',
- 'cpu_arch': 'cpu_arch',
- 'cpus': 'cpus',
- 'memory_mb': 'memory_mb',
- 'local_gb': 'local_gb',
- },
- {
- 'driver': 'pxe_ssh',
- 'ssh_address': 'SSH',
- 'ssh_username': 'USER',
- 'ssh_key_contents': 'KEY',
- },
- {
- 'driver': 'pxe_ssh',
- 'ssh_address': 'SSH',
- 'ssh_username': 'USER',
- 'ssh_key_contents': '',
- 'mac_addresses': '',
- 'cpu_arch': 'CPU_ARCH',
- },
- ]
- )
-
- def test_parse_csv_file_wrong(self):
- no_csv_file = [
- '',
- 'File with first empty line -- it\'s not a CSV file.',
- ]
-
- with self.assertRaises(ValueError) as raised:
- utils.parse_csv_file(no_csv_file)
-
- self.assertEqual(unicode(raised.exception.message),
- unicode(_("Unable to parse the CSV file.")))
-
- def test_parse_wrong_driver_file(self):
- wrong_driver_file = [
- 'wrong_driver,ssh_address,ssh_user',
- ]
-
- with self.assertRaises(ValueError) as raised:
- utils.parse_csv_file(wrong_driver_file)
-
- self.assertEqual(unicode(raised.exception.message),
- unicode(_("Unknown driver: %s.") % 'wrong_driver'))
-
-
-class MeteringTests(helpers.TestCase):
- def test_query_data(self):
- Meter = collections.namedtuple('Meter', 'name unit')
- request = 'request'
- from_date = datetime.datetime(2015, 1, 1, 13, 45)
- to_date = datetime.datetime(2015, 1, 2, 13, 45)
- with mock.patch(
- 'openstack_dashboard.api.ceilometer.meter_list',
- return_value=[Meter('foo.bar', u'µD')],
- ), mock.patch(
- 'openstack_dashboard.api.ceilometer.CeilometerUsage',
- return_value=mock.MagicMock(**{
- 'resource_aggregates_with_statistics.return_value': 'plonk',
- }),
- ):
- ret = metering.query_data(request, from_date, to_date,
- 'all', 'foo.bar')
- self.assertEqual(ret, 'plonk')
-
- def test_url_part(self):
- ret = metering.url_part('foo_bar_baz', True)
- self.assertTrue('meter=foo_bar_baz' in ret)
- self.assertTrue('barchart=True' in ret)
- ret = metering.url_part('foo_bar_baz', False)
- self.assertTrue('meter=foo_bar_baz' in ret)
- self.assertFalse('barchart=True' in ret)
-
- def test_get_meter_name(self):
- ret = metering.get_meter_name('foo.bar.baz')
- self.assertEqual(ret, 'foo_bar_baz')
-
- def test_get_meters(self):
- ret = metering.get_meters(['foo.bar', 'foo.baz'])
- self.assertEqual(ret, [('foo.bar', 'foo_bar'), ('foo.baz', 'foo_baz')])
-
- def test_get_barchart_stats(self):
- series = [
- {'data': [{'x': 1, 'y': 1}, {'x': 4, 'y': 4}]},
- {'data': [{'x': 2, 'y': 2}, {'x': 5, 'y': 5}]},
- {'data': [{'x': 3, 'y': 3}, {'x': 6, 'y': 6}]},
- ]
- # Arrogance is measured in IT in micro-Dijkstras, µD.
- average, used, tooltip_average = metering.get_barchart_stats(series,
- u'µD')
- self.assertEqual(average, 2)
- self.assertEqual(used, 4)
- self.assertEqual(tooltip_average, u'Average 2 µD<br> From: 1, to: 4')
-
- def test_create_json_output(self):
- ret = metering.create_json_output([], False, u'µD', None, None)
- self.assertEqual(ret, {
- 'series': [],
- 'settings': {
- 'higlight_last_point': True,
- 'axes_x': False,
- 'axes_y': True,
- 'xMin': '',
- 'renderer': 'StaticAxes',
- 'xMax': '',
- 'axes_y_label': False,
- 'auto_size': False,
- 'auto_resize': False,
- },
- })
-
- series = [
- {'data': [{'x': 1, 'y': 1}, {'x': 4, 'y': 4}]},
- {'data': [{'x': 2, 'y': 2}, {'x': 5, 'y': 5}]},
- {'data': [{'x': 3, 'y': 3}, {'x': 6, 'y': 6}]},
- ]
- ret = metering.create_json_output(series, True, u'µD', None, None)
- self.assertEqual(ret, {
- 'series': series,
- 'stats': {
- 'average': 2,
- 'used': 4,
- 'tooltip_average': u'Average 2 µD<br> From: 1, to: 4',
- },
- 'settings': {
- 'yMin': 0,
- 'yMax': 100,
- 'higlight_last_point': True,
- 'axes_x': False,
- 'axes_y': True,
- 'bar_chart_settings': {
- 'color_scale_domain': [0, 80, 80, 100],
- 'orientation': 'vertical',
- 'color_scale_range': [
- '#0000FF',
- '#0000FF',
- '#FF0000',
- '#FF0000',
- ],
- 'width': 30,
- 'average_color_scale_domain': [0, 100],
- 'used_label_placement': 'left',
- 'average_color_scale_range': ['#0000FF', '#0000FF'],
- },
- 'xMin': '',
- 'renderer': 'StaticAxes',
- 'xMax': '',
- 'axes_y_label': False,
- 'auto_size': False,
- 'auto_resize': False,
- },
- })
-
- def test_get_nodes_stats(self):
- request = 'request'
- with mock.patch(
- 'tuskar_ui.utils.metering.create_json_output',
- return_value='',
- ) as create_json_output, mock.patch(
- 'tuskar_ui.utils.metering.query_data',
- return_value=[],
- ), mock.patch(
- 'openstack_dashboard.utils.metering.series_for_meter',
- return_value=[],
- ), mock.patch(
- 'openstack_dashboard.utils.metering.calc_date_args',
- return_value=('from date', 'to date'),
- ):
- ret = metering.get_nodes_stats(
- request=request,
- node_uuid='abc',
- instance_uuids=['def'],
- meter='foo.bar')
- self.assertEqual(ret, '')
- self.assertEqual(create_json_output.call_args_list, [
- mock.call([], None, '', 'from date', 'to date')
- ])
diff --git a/tuskar_ui/utils/utils.py b/tuskar_ui/utils/utils.py
deleted file mode 100644
index 8921d09b..00000000
--- a/tuskar_ui/utils/utils.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- 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 base64
-import csv
-from itertools import izip
-import os
-import re
-import struct
-import time
-
-from django.utils.translation import ugettext_lazy as _
-
-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, type):
- """Check if image 'type' property matches passed-in 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', type) == 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 '<Item index=%d>' % self.index
- >>> items = [Item(i) for i in range(7)]
- >>> list(filter_items(items, index=1))
- [<Item index=1>]
- >>> list(filter_items(items, index__in=(1, 2, 3)))
- [<Item index=1>, <Item index=2>, <Item index=3>]
- >>> list(filter_items(items, index__not_in=(1, 2, 3)))
- [<Item index=0>, <Item index=4>, <Item index=5>, <Item index=6>]
- """
- 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
-
-
-def parse_csv_file(csv_file):
- """Parses given CSV file.
-
- If there is no error, it returns list of dicts. When something went wrong,
- list is empty, but warning contains appropriate information about
- possible problems.
- """
-
- parsed_data = []
-
- for row in csv.reader(csv_file):
- try:
- driver = row[0].strip()
- except IndexError:
- raise ValueError(_("Unable to parse the CSV file."))
-
- if driver in ('pxe_ssh', 'pxe_ipmitool'):
- node_keys = (
- 'mac_addresses', 'cpu_arch', 'cpus', 'memory_mb', 'local_gb')
-
- if driver == 'pxe_ssh':
- driver_keys = (
- 'driver', 'ssh_address', 'ssh_username',
- 'ssh_key_contents'
- )
-
- elif driver == 'pxe_ipmitool':
- driver_keys = (
- 'driver', 'ipmi_address', 'ipmi_username',
- 'ipmi_password'
- )
-
- node = dict(izip(driver_keys+node_keys, row))
-
- parsed_data.append(node)
-
- else:
- raise ValueError(_("Unknown driver: %s.") % driver)
-
- return parsed_data
-
-
-def create_cephx_key():
- # NOTE(gfidente): Taken from
- # https://github.com/ceph/ceph-deploy/blob/master/ceph_deploy/new.py#L21
- key = os.urandom(16)
- header = struct.pack("<hiih", 1, int(time.time()), 0, len(key))
- return base64.b64encode(header + key)