From 31e0bb84f6a412777a69781d1e6438b56a28c64b Mon Sep 17 00:00:00 2001 From: Dougal Matthews Date: Thu, 21 Jan 2016 15:12:37 +0000 Subject: Retire the Tuskar UI codebase Change-Id: I469fdc1339d4991586bf2e1d62d99fd5b68289eb Depends-On: I904b2f27591333e104bf9080bb8c3876fcb3596c --- .mailmap | 12 - .pylintrc | 42 -- HACKING.rst | 60 --- LICENSE | 176 ------ MANIFEST.in | 19 - Makefile | 24 - README | 10 + README.rst | 41 -- _10_admin.py.example | 2 - _20_project.py.example | 2 - _30_identity.py.example | 2 - _50_tuskar.py.example | 12 - dev_env.sh | 56 -- doc/Makefile | 153 ------ doc/make.bat | 190 ------- doc/source/HACKING.rst | 1 - doc/source/README.rst | 1 - doc/source/conf.py | 242 --------- doc/source/index.rst | 20 - doc/source/install.rst | 133 ----- doc/source/user_guide.rst | 16 - manage.py | 11 - nodes.sh | 20 - requirements.txt | 6 - run_tests.sh | 552 ------------------- setup.cfg | 41 -- setup.py | 29 - test-requirements.txt | 37 -- tools/install_venv.py | 154 ------ tools/with_venv.sh | 4 - tox.ini | 68 --- tuskar_ui/__init__.py | 0 tuskar_ui/api/__init__.py | 0 tuskar_ui/api/flavor.py | 121 ----- tuskar_ui/api/heat.py | 553 ------------------- tuskar_ui/api/node.py | 445 --------------- tuskar_ui/api/tuskar.py | 558 ------------------- tuskar_ui/cached_property.py | 63 --- tuskar_ui/exceptions.py | 22 - tuskar_ui/forms.py | 174 ------ tuskar_ui/handle_errors.py | 71 --- tuskar_ui/infrastructure/__init__.py | 0 tuskar_ui/infrastructure/dashboard.py | 34 -- tuskar_ui/infrastructure/flavors/__init__.py | 0 tuskar_ui/infrastructure/flavors/panel.py | 33 -- tuskar_ui/infrastructure/flavors/tables.py | 157 ------ .../flavors/templates/flavors/create.html | 11 - .../flavors/templates/flavors/details.html | 31 -- .../flavors/templates/flavors/index.html | 27 - tuskar_ui/infrastructure/flavors/tests.py | 265 --------- tuskar_ui/infrastructure/flavors/urls.py | 28 - tuskar_ui/infrastructure/flavors/utils.py | 121 ----- tuskar_ui/infrastructure/flavors/views.py | 103 ---- tuskar_ui/infrastructure/flavors/workflows.py | 79 --- tuskar_ui/infrastructure/history/__init__.py | 0 tuskar_ui/infrastructure/history/panel.py | 26 - tuskar_ui/infrastructure/history/tables.py | 37 -- .../history/templates/history/index.html | 16 - tuskar_ui/infrastructure/history/tests.py | 53 -- tuskar_ui/infrastructure/history/urls.py | 23 - tuskar_ui/infrastructure/history/views.py | 31 -- tuskar_ui/infrastructure/images/__init__.py | 0 tuskar_ui/infrastructure/images/forms.py | 17 - tuskar_ui/infrastructure/images/panel.py | 26 - tuskar_ui/infrastructure/images/tables.py | 74 --- .../images/templates/images/_create.html | 15 - .../images/templates/images/_update.html | 14 - .../images/templates/images/create.html | 12 - .../images/templates/images/index.html | 16 - .../images/templates/images/update.html | 12 - tuskar_ui/infrastructure/images/tests.py | 168 ------ tuskar_ui/infrastructure/images/urls.py | 25 - tuskar_ui/infrastructure/images/views.py | 109 ---- tuskar_ui/infrastructure/nodes/__init__.py | 0 tuskar_ui/infrastructure/nodes/forms.py | 319 ----------- tuskar_ui/infrastructure/nodes/panel.py | 26 - tuskar_ui/infrastructure/nodes/tables.py | 269 ---------- tuskar_ui/infrastructure/nodes/tabs.py | 378 ------------- tuskar_ui/infrastructure/nodes/tests.py | 596 --------------------- tuskar_ui/infrastructure/nodes/urls.py | 31 -- tuskar_ui/infrastructure/nodes/views.py | 199 ------- tuskar_ui/infrastructure/overview/__init__.py | 0 tuskar_ui/infrastructure/overview/forms.py | 488 ----------------- tuskar_ui/infrastructure/overview/panel.py | 26 - tuskar_ui/infrastructure/overview/tests.py | 364 ------------- tuskar_ui/infrastructure/overview/urls.py | 38 -- tuskar_ui/infrastructure/overview/views.py | 390 -------------- tuskar_ui/infrastructure/parameters/__init__.py | 0 tuskar_ui/infrastructure/parameters/forms.py | 281 ---------- tuskar_ui/infrastructure/parameters/panel.py | 26 - .../parameters/_simple_service_config.html | 26 - .../parameters/advanced_service_config.html | 88 --- .../parameters/templates/parameters/index.html | 76 --- .../parameters/simple_service_config.html | 11 - tuskar_ui/infrastructure/parameters/tests.py | 130 ----- tuskar_ui/infrastructure/parameters/urls.py | 29 - tuskar_ui/infrastructure/parameters/views.py | 107 ---- tuskar_ui/infrastructure/roles/__init__.py | 0 tuskar_ui/infrastructure/roles/panel.py | 26 - tuskar_ui/infrastructure/roles/tables.py | 66 --- .../roles/templates/roles/config.html | 24 - .../roles/templates/roles/detail.html | 39 -- .../roles/templates/roles/index.html | 16 - .../infrastructure/roles/templates/roles/info.html | 11 - tuskar_ui/infrastructure/roles/tests.py | 166 ------ tuskar_ui/infrastructure/roles/urls.py | 29 - tuskar_ui/infrastructure/roles/views.py | 191 ------- tuskar_ui/infrastructure/roles/workflows.py | 180 ------- .../static/infrastructure/images/chevron.png | Bin 348 -> 0 bytes .../static/infrastructure/images/power.png | Bin 606 -> 0 bytes .../static/infrastructure/js/horizon.capacity.js | 160 ------ .../infrastructure/js/horizon.d3circleschart.js | 282 ---------- .../infrastructure/js/horizon.d3singlebarchart.js | 475 ---------------- .../infrastructure/js/tuskar.deployment_live.js | 17 - .../js/tuskar.deployment_progress.js | 43 -- .../static/infrastructure/js/tuskar.edit_plan.js | 74 --- .../infrastructure/js/tuskar.formset_table.js | 87 --- .../static/infrastructure/js/tuskar.js | 6 - .../infrastructure/js/tuskar.menu_formset.js | 87 --- .../infrastructure/js/tuskar.number_picker.js | 51 -- .../static/infrastructure/js/tuskar.performance.js | 25 - .../static/infrastructure/js/tuskar.templates.js | 16 - .../static/infrastructure/scss/_breadcrumbs.scss | 31 -- .../static/infrastructure/scss/_buttons.scss | 15 - .../static/infrastructure/scss/_capacities.scss | 24 - .../static/infrastructure/scss/_charts.scss | 15 - .../static/infrastructure/scss/_detail_pages.scss | 12 - .../static/infrastructure/scss/_flavor_usages.scss | 89 --- .../static/infrastructure/scss/_formsets.scss | 34 -- .../static/infrastructure/scss/_icons.scss | 5 - .../infrastructure/scss/_individual_pages.scss | 271 ---------- .../static/infrastructure/scss/_numberpicker.scss | 49 -- .../static/infrastructure/scss/_tables.scss | 74 --- .../static/infrastructure/scss/infrastructure.scss | 15 - .../static/infrastructure/tests/formset_table.js | 60 --- .../templates/client_side/_modal_chart.html | 19 - .../templates/client_side/templates.html | 1 - .../templates/formset_table/_row.html | 24 - .../templates/formset_table/_table.html | 43 -- .../templates/formset_table/menu_formset.html | 44 -- .../common/_data_table_row_action_dropdown.html | 11 - ..._data_table_row_action_dropdown_first_item.html | 17 - .../common/_data_table_row_actions_dropdown.html | 27 - .../horizon/common/_data_table_table_actions.html | 68 --- .../common/_definition_list_data_table.html | 5 - .../horizon/common/_enhanced_data_table.html | 54 -- .../horizon/common/_horizontal_field.html | 20 - .../common/_items_count_domain_page_header.html | 17 - .../horizon/common/_items_count_tab_group.html | 25 - .../infrastructure/_fullscreen_workflow.html | 37 -- .../infrastructure/_fullscreen_workflow_base.html | 11 - .../infrastructure/_performance_chart.html | 16 - .../infrastructure/_performance_chart_box.html | 68 --- .../templates/infrastructure/_scripts.html | 17 - .../templates/infrastructure/_top_5_box.html | 8 - .../templates/infrastructure/_top_5_chart.html | 29 - .../templates/infrastructure/_workflow_base.html | 11 - .../templates/infrastructure/base.html | 17 - .../templates/infrastructure/base_detail.html | 27 - .../templates/infrastructure/header_actions.html | 10 - .../infrastructure/nodes/_auto_discover_csv.html | 23 - .../infrastructure/nodes/_detail_overview.html | 105 ---- .../infrastructure/nodes/_nodes_formset_field.html | 15 - .../infrastructure/nodes/_nodes_formset_form.html | 49 -- .../templates/infrastructure/nodes/_overview.html | 146 ----- .../templates/infrastructure/nodes/_register.html | 13 - .../templates/infrastructure/nodes/_upload.html | 7 - .../templates/infrastructure/nodes/detail.html | 13 - .../templates/infrastructure/nodes/index.html | 16 - .../templates/infrastructure/nodes/register.html | 11 - .../overview/_deploy_confirmation.html | 40 -- .../infrastructure/overview/_last_events.html | 18 - .../infrastructure/overview/_post_deploy_init.html | 22 - .../infrastructure/overview/_scale_out.html | 41 -- .../overview/_undeploy_confirmation.html | 20 - .../overview/deploy_confirmation.html | 11 - .../infrastructure/overview/deployment_base.html | 27 - .../infrastructure/overview/deployment_failed.html | 25 - .../overview/deployment_initialize.html | 21 - .../infrastructure/overview/deployment_live.html | 113 ---- .../infrastructure/overview/deployment_plan.html | 51 -- .../overview/deployment_progress.html | 69 --- .../templates/infrastructure/overview/index.html | 36 -- .../overview/overcloudrc.sh.template | 9 - .../infrastructure/overview/post_deploy_init.html | 11 - .../infrastructure/overview/role_nodes_edit.html | 38 -- .../infrastructure/overview/role_nodes_status.html | 50 -- .../infrastructure/overview/scale_out.html | 11 - .../overview/undeploy_confirmation.html | 11 - .../templates/infrastructure/qunit.html | 189 ------- tuskar_ui/infrastructure/templatetags/__init__.py | 0 .../infrastructure/templatetags/chart_helpers.py | 55 -- .../infrastructure/templatetags/icon_helpers.py | 58 -- tuskar_ui/infrastructure/templatetags/tests.py | 73 --- tuskar_ui/infrastructure/views.py | 46 -- tuskar_ui/tables.py | 177 ------ tuskar_ui/test/__init__.py | 0 tuskar_ui/test/api_tests/__init__.py | 0 tuskar_ui/test/api_tests/heat_tests.py | 134 ----- tuskar_ui/test/api_tests/node_tests.py | 161 ------ tuskar_ui/test/api_tests/tuskar_tests.py | 321 ----------- tuskar_ui/test/formset_table_tests.py | 57 -- tuskar_ui/test/helpers.py | 53 -- tuskar_ui/test/selenium.py | 55 -- tuskar_ui/test/settings.py | 161 ------ tuskar_ui/test/test_data/__init__.py | 0 tuskar_ui/test/test_data/exceptions.py | 23 - tuskar_ui/test/test_data/flavor_data.py | 38 -- tuskar_ui/test/test_data/heat_data.py | 248 --------- tuskar_ui/test/test_data/keystone_data.py | 30 -- tuskar_ui/test/test_data/node_data.py | 289 ---------- tuskar_ui/test/test_data/tuskar_data.py | 246 --------- tuskar_ui/test/test_data/utils.py | 52 -- tuskar_ui/test/test_forms.py | 68 --- tuskar_ui/test/urls.py | 26 - tuskar_ui/utils/__init__.py | 0 tuskar_ui/utils/metering.py | 289 ---------- tuskar_ui/utils/tests.py | 298 ----------- tuskar_ui/utils/utils.py | 152 ------ 219 files changed, 10 insertions(+), 17306 deletions(-) delete mode 100644 .mailmap delete mode 100644 .pylintrc delete mode 100644 HACKING.rst delete mode 100644 LICENSE delete mode 100644 MANIFEST.in delete mode 100644 Makefile create mode 100644 README delete mode 100644 README.rst delete mode 100644 _10_admin.py.example delete mode 100644 _20_project.py.example delete mode 100644 _30_identity.py.example delete mode 100644 _50_tuskar.py.example delete mode 100644 dev_env.sh delete mode 100644 doc/Makefile delete mode 100644 doc/make.bat delete mode 120000 doc/source/HACKING.rst delete mode 120000 doc/source/README.rst delete mode 100644 doc/source/conf.py delete mode 100644 doc/source/index.rst delete mode 100644 doc/source/install.rst delete mode 100644 doc/source/user_guide.rst delete mode 100755 manage.py delete mode 100755 nodes.sh delete mode 100644 requirements.txt delete mode 100755 run_tests.sh delete mode 100644 setup.cfg delete mode 100644 setup.py delete mode 100644 test-requirements.txt delete mode 100644 tools/install_venv.py delete mode 100755 tools/with_venv.sh delete mode 100644 tox.ini delete mode 100644 tuskar_ui/__init__.py delete mode 100644 tuskar_ui/api/__init__.py delete mode 100644 tuskar_ui/api/flavor.py delete mode 100644 tuskar_ui/api/heat.py delete mode 100644 tuskar_ui/api/node.py delete mode 100644 tuskar_ui/api/tuskar.py delete mode 100644 tuskar_ui/cached_property.py delete mode 100644 tuskar_ui/exceptions.py delete mode 100644 tuskar_ui/forms.py delete mode 100644 tuskar_ui/handle_errors.py delete mode 100644 tuskar_ui/infrastructure/__init__.py delete mode 100644 tuskar_ui/infrastructure/dashboard.py delete mode 100644 tuskar_ui/infrastructure/flavors/__init__.py delete mode 100644 tuskar_ui/infrastructure/flavors/panel.py delete mode 100644 tuskar_ui/infrastructure/flavors/tables.py delete mode 100644 tuskar_ui/infrastructure/flavors/templates/flavors/create.html delete mode 100644 tuskar_ui/infrastructure/flavors/templates/flavors/details.html delete mode 100644 tuskar_ui/infrastructure/flavors/templates/flavors/index.html delete mode 100644 tuskar_ui/infrastructure/flavors/tests.py delete mode 100644 tuskar_ui/infrastructure/flavors/urls.py delete mode 100644 tuskar_ui/infrastructure/flavors/utils.py delete mode 100644 tuskar_ui/infrastructure/flavors/views.py delete mode 100644 tuskar_ui/infrastructure/flavors/workflows.py delete mode 100644 tuskar_ui/infrastructure/history/__init__.py delete mode 100644 tuskar_ui/infrastructure/history/panel.py delete mode 100644 tuskar_ui/infrastructure/history/tables.py delete mode 100644 tuskar_ui/infrastructure/history/templates/history/index.html delete mode 100644 tuskar_ui/infrastructure/history/tests.py delete mode 100644 tuskar_ui/infrastructure/history/urls.py delete mode 100644 tuskar_ui/infrastructure/history/views.py delete mode 100644 tuskar_ui/infrastructure/images/__init__.py delete mode 100644 tuskar_ui/infrastructure/images/forms.py delete mode 100644 tuskar_ui/infrastructure/images/panel.py delete mode 100644 tuskar_ui/infrastructure/images/tables.py delete mode 100644 tuskar_ui/infrastructure/images/templates/images/_create.html delete mode 100644 tuskar_ui/infrastructure/images/templates/images/_update.html delete mode 100644 tuskar_ui/infrastructure/images/templates/images/create.html delete mode 100644 tuskar_ui/infrastructure/images/templates/images/index.html delete mode 100644 tuskar_ui/infrastructure/images/templates/images/update.html delete mode 100644 tuskar_ui/infrastructure/images/tests.py delete mode 100644 tuskar_ui/infrastructure/images/urls.py delete mode 100644 tuskar_ui/infrastructure/images/views.py delete mode 100644 tuskar_ui/infrastructure/nodes/__init__.py delete mode 100644 tuskar_ui/infrastructure/nodes/forms.py delete mode 100644 tuskar_ui/infrastructure/nodes/panel.py delete mode 100644 tuskar_ui/infrastructure/nodes/tables.py delete mode 100644 tuskar_ui/infrastructure/nodes/tabs.py delete mode 100644 tuskar_ui/infrastructure/nodes/tests.py delete mode 100644 tuskar_ui/infrastructure/nodes/urls.py delete mode 100644 tuskar_ui/infrastructure/nodes/views.py delete mode 100644 tuskar_ui/infrastructure/overview/__init__.py delete mode 100644 tuskar_ui/infrastructure/overview/forms.py delete mode 100644 tuskar_ui/infrastructure/overview/panel.py delete mode 100644 tuskar_ui/infrastructure/overview/tests.py delete mode 100644 tuskar_ui/infrastructure/overview/urls.py delete mode 100644 tuskar_ui/infrastructure/overview/views.py delete mode 100644 tuskar_ui/infrastructure/parameters/__init__.py delete mode 100644 tuskar_ui/infrastructure/parameters/forms.py delete mode 100644 tuskar_ui/infrastructure/parameters/panel.py delete mode 100644 tuskar_ui/infrastructure/parameters/templates/parameters/_simple_service_config.html delete mode 100644 tuskar_ui/infrastructure/parameters/templates/parameters/advanced_service_config.html delete mode 100644 tuskar_ui/infrastructure/parameters/templates/parameters/index.html delete mode 100644 tuskar_ui/infrastructure/parameters/templates/parameters/simple_service_config.html delete mode 100644 tuskar_ui/infrastructure/parameters/tests.py delete mode 100644 tuskar_ui/infrastructure/parameters/urls.py delete mode 100644 tuskar_ui/infrastructure/parameters/views.py delete mode 100644 tuskar_ui/infrastructure/roles/__init__.py delete mode 100644 tuskar_ui/infrastructure/roles/panel.py delete mode 100644 tuskar_ui/infrastructure/roles/tables.py delete mode 100644 tuskar_ui/infrastructure/roles/templates/roles/config.html delete mode 100644 tuskar_ui/infrastructure/roles/templates/roles/detail.html delete mode 100644 tuskar_ui/infrastructure/roles/templates/roles/index.html delete mode 100644 tuskar_ui/infrastructure/roles/templates/roles/info.html delete mode 100644 tuskar_ui/infrastructure/roles/tests.py delete mode 100644 tuskar_ui/infrastructure/roles/urls.py delete mode 100644 tuskar_ui/infrastructure/roles/views.py delete mode 100644 tuskar_ui/infrastructure/roles/workflows.py delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/images/chevron.png delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/images/power.png delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/horizon.capacity.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3circleschart.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/horizon.d3singlebarchart.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_live.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.deployment_progress.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.edit_plan.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.formset_table.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.menu_formset.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.number_picker.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.performance.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/js/tuskar.templates.js delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_breadcrumbs.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_buttons.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_capacities.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_charts.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_detail_pages.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_flavor_usages.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_formsets.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_icons.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_individual_pages.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_numberpicker.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/_tables.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/scss/infrastructure.scss delete mode 100644 tuskar_ui/infrastructure/static/infrastructure/tests/formset_table.js delete mode 100644 tuskar_ui/infrastructure/templates/client_side/_modal_chart.html delete mode 100644 tuskar_ui/infrastructure/templates/client_side/templates.html delete mode 100644 tuskar_ui/infrastructure/templates/formset_table/_row.html delete mode 100644 tuskar_ui/infrastructure/templates/formset_table/_table.html delete mode 100644 tuskar_ui/infrastructure/templates/formset_table/menu_formset.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_action_dropdown_first_item.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_data_table_row_actions_dropdown.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_data_table_table_actions.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_definition_list_data_table.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_enhanced_data_table.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_horizontal_field.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_items_count_domain_page_header.html delete mode 100644 tuskar_ui/infrastructure/templates/horizon/common/_items_count_tab_group.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_fullscreen_workflow_base.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_performance_chart.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_performance_chart_box.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_scripts.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_top_5_box.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_top_5_chart.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/_workflow_base.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/base.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/base_detail.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/header_actions.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_auto_discover_csv.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_detail_overview.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_field.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_nodes_formset_form.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_overview.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_register.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/_upload.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/detail.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/index.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/nodes/register.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/_deploy_confirmation.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/_last_events.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/_post_deploy_init.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/_scale_out.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/_undeploy_confirmation.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deploy_confirmation.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_base.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_failed.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_initialize.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_live.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_plan.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/deployment_progress.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/index.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/overcloudrc.sh.template delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/post_deploy_init.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_edit.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/role_nodes_status.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/scale_out.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/overview/undeploy_confirmation.html delete mode 100644 tuskar_ui/infrastructure/templates/infrastructure/qunit.html delete mode 100644 tuskar_ui/infrastructure/templatetags/__init__.py delete mode 100644 tuskar_ui/infrastructure/templatetags/chart_helpers.py delete mode 100644 tuskar_ui/infrastructure/templatetags/icon_helpers.py delete mode 100644 tuskar_ui/infrastructure/templatetags/tests.py delete mode 100644 tuskar_ui/infrastructure/views.py delete mode 100644 tuskar_ui/tables.py delete mode 100644 tuskar_ui/test/__init__.py delete mode 100644 tuskar_ui/test/api_tests/__init__.py delete mode 100644 tuskar_ui/test/api_tests/heat_tests.py delete mode 100644 tuskar_ui/test/api_tests/node_tests.py delete mode 100644 tuskar_ui/test/api_tests/tuskar_tests.py delete mode 100644 tuskar_ui/test/formset_table_tests.py delete mode 100644 tuskar_ui/test/helpers.py delete mode 100644 tuskar_ui/test/selenium.py delete mode 100644 tuskar_ui/test/settings.py delete mode 100644 tuskar_ui/test/test_data/__init__.py delete mode 100644 tuskar_ui/test/test_data/exceptions.py delete mode 100644 tuskar_ui/test/test_data/flavor_data.py delete mode 100644 tuskar_ui/test/test_data/heat_data.py delete mode 100644 tuskar_ui/test/test_data/keystone_data.py delete mode 100644 tuskar_ui/test/test_data/node_data.py delete mode 100644 tuskar_ui/test/test_data/tuskar_data.py delete mode 100644 tuskar_ui/test/test_data/utils.py delete mode 100644 tuskar_ui/test/test_forms.py delete mode 100644 tuskar_ui/test/urls.py delete mode 100644 tuskar_ui/utils/__init__.py delete mode 100644 tuskar_ui/utils/metering.py delete mode 100644 tuskar_ui/utils/tests.py delete mode 100644 tuskar_ui/utils/utils.py diff --git a/.mailmap b/.mailmap deleted file mode 100644 index f59873d1..00000000 --- a/.mailmap +++ /dev/null @@ -1,12 +0,0 @@ -# Format is: -# -# - - - - - - -Zhongyue Luo -Joe Gordon -Kun Huang 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 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 `__. -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: - -``__ - -We use OpenStack's Gerrit for the code contributions: - -``__ - -and we follow the `OpenStack Gerrit Workflow `__. - -If you're interested in the code, here are some key places to start: - -* `tuskar_ui/api.py `_ - - This file contains all the API calls made to the Tuskar API - (through python-tuskarclient). -* `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 `__, a management API for -OpenStack deployments. It is a plugin for `OpenStack -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! `_ - - -Installation Guide ------------------- - -Use the `Installation Guide `_ 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` " - -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 ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/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 ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. 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 -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = '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 `_. - -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 `_ -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 []" - echo " Just run PEP8 and HACKING compliance check" - echo " on files changed since HEAD~1 (or )" - echo " -P, --no-pep8 Don't run pep8 by default" - echo " -t, --tabs Check for tab characters in files." - echo " -y, --pylint Just run pylint" - echo " -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 diff --git a/tuskar_ui/api/__init__.py b/tuskar_ui/api/__init__.py deleted file mode 100644 index e69de29b..00000000 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} ', - 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('

{0}

', - value) - - -class StaticTextPasswordWidget(forms.Widget): - def render(self, name, value, attrs=None): - if value is None or value == '': - return html.format_html(u'

') - else: - return html.format_html( - u'

' - u' {1}' - u'

', 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 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 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 %} -
-
-

{% trans "Hardware Info" %}

-
-
{% trans "Architecture" %}
-
{{ flavor.cpu_arch|default:"—" }}
-
{% trans "CPUs" %}
-
{{ flavor.vcpus|default:"—" }}
-
{% trans "Memory" %}
-
{{ flavor.ram_bytes|filesizeformat|default:"—" }}
-
{% trans "Disk" %}
-
{{ flavor.disk_bytes|filesizeformat|default:"—" }}
-
-
-
-
-
- {{ table.render }} -
-
- -{% 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 %} -
- -
-
- {{ suggested_flavors_table.render }} -
-
-
-{% endif %} - -
- {{ flavors_table.render }} -
-{% 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[^/]+)$', views.CreateView.as_view(), - name='create'), - urls.url(r'^create/$', views.CreateView.as_view(), name='create'), - urls.url(r'^(?P[^/]+)/$', 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 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 %} -
-
- {{ table.render }} -
-
- -{% 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 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 %} -

{% trans "Description" %}:

-

{% trans "Modify different properties of an image." %}

-{% 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 %} -

{% trans "Description" %}:

-

{% trans "Modify different properties of an image." %}

-{% 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 %} -
-
- {{ table.render }} -
-
- -{% 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[^/]+)/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 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[^/]+)/$', views.DetailView.as_view(), - name='node_detail'), - urls.url(r'^(?P[^/]+)/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 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 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 %} -
-
- {% include "horizon/common/_form_fields.html" %} -
-
-
-

{% trans "Description:" %}

-

- {% trans "Configure values that cannot be defaulted" %} -

-

- {% trans "These values cannot be defaulted. Please choose values for them and save them before you deploy your overcloud." %} -

-
-{% 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 %} -
-
{% csrf_token %} - - {% include 'horizon/common/_form_errors.html' with form=form %} - -
-
-
- {% for field in form.global_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.controller_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.compute_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.block_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.object_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.ceph_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
-
-
-
- - -{% 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 %} -
-
- {% include 'horizon/common/_form_errors.html' with form=form %} - -
-
-
- {% for field in form.global_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.controller_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.compute_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.block_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.object_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
- {% for field in form.ceph_storage_fieldset %} - {% include 'horizon/common/_horizontal_field.html' with field=field %} - {% endfor %} -
-
-
-
-
- - -{% 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 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 @@ - -
-
-
- {% include "horizon/common/_horizontal_fields.html" %} -
-
-
- 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 %} - -
-
-

{% trans "Properties" %}

-

{% blocktrans count counter=nodes|length %}{{ counter }} instance{% plural %}{{ counter }} instances{% endblocktrans %}

-
-
{% trans 'Flavor' %}
- {% if flavor %} -
{{ flavor.name }} {{ flavor.get_keys.cpu_arch }} | {{ flavor.vcpus }} {% trans "CPU" %} | {{ flavor.ram }} {% trans "MB RAM" %} | {{ flavor.disk }} {% trans "GB HDD" %}
- {% else %} -
{% trans 'No flavor associated' %}
- {% endif %} -
{% trans 'Image' %}
- {% if image %} -
{{ image.name }}
- {% else %} -
{% trans 'No image associated' %}
- {% endif %} -
-
-
-

{% trans "Performance & Metrics" %}

- {% 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 %} -
-
- -{{ 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 %} -
-
- {{ table.render }} -
-
- -{% 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 @@ - -
-
-
- {% include "horizon/common/_horizontal_fields.html" %} -
-
-
- {{ step.get_help_text }} -
-
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[^/]+)/$', views.DetailView.as_view(), - name='detail'), - urls.url(r'^(?P[^/]+)/edit$', views.UpdateView.as_view(), - name='update'), - urls.url(r'^(?P[^/]+)/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 Binary files a/tuskar_ui/infrastructure/static/infrastructure/images/chevron.png and /dev/null 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 Binary files a/tuskar_ui/infrastructure/static/infrastructure/images/power.png and /dev/null 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: - -
-
- - 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: -
-
- - 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 -
- -
-
- - 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. - - -*/ - - -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 + "
" + 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: -
-
- - 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 '); - }); - $("#overcloudrc").on("show.bs.collapse", function(){ - $("span.overcloudrc").html('Hide '); - }); - }; - - 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( - $('×').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 = $('' + - add_label + ''); - 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(''); - $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('
  • Undefined node
  • '); - $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( - '
    ').before( - '' + - '').after( - '' + - '').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('+' + step + ''); - } - }); - }; - - 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 = ''; - - 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 %} -
    - - -
    -{% 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 @@ - - {% for cell in row %} - - {% if cell.field %} - {{ cell.field }} - {% else %} - {%if cell.wrap_list %}
      {% endif %}{{ cell.value }}{%if cell.wrap_list %}
    {% endif %} - {% endif %} - {% if forloop.first %} - {% for field in row.form.hidden_fields %} - {{ field }} - {% for error in field.errors %} - {{ field.name }}: {{ error }} - {% endfor %} - {% endfor %} - {% if row.form.non_field_errors %} -
    - {{ row.form.non_field_errors }} -
    - {% endif %} - {% endif %} - - {% endfor %} - 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 %} - - {% for column in columns %} - {{ column }} - {% endfor %} - - {% endif %} -{% endblock table_columns %} - -{% block table %} - {% with table.get_formset as formset %} - {{ formset.management_form }} - {% if formset.non_field_errors %} -
    - {{ formset.non_field_errors }} -
    - {% endif %} - {% endwith %} - {{ block.super }} - - -{% endblock table %} 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 %} -
    {{ error }}
    -{% endfor %} -
    -
    -
    - - - - - - -

    Nodes to register

    -
    -
    - {% include 'infrastructure/nodes/_upload.html' with form=upload_form %} -
    - -
    -
    -
    - {% for form in formset %} - {% include form_template with form=form active=forloop.first %} - {% endfor %} -
    -
    -
    - 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" %} - -{% else %} - - {% if action.icon != None %} {% endif %} - {{ action.verbose_name }} - -{% 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" %} - -{% else %} - - {% if action.icon != None %} - - {% else %} - {{ action.verbose_name }} - {% endif %} - -{% 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 %} -
    - {% for action in row_actions %} - {% if forloop.first %} - {% include "horizon/common/_data_table_row_action_dropdown_first_item.html" %} - - - - - {% endif %} - {% endfor %} -
    -{% 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 %} -
    -{% block table_filter %} - {% if filter.filter_type == 'fixed' %} -
    - {% for button in filter.fixed_buttons %} - - {% endfor %} -
    - {% elif filter.filter_type == 'query' %} - - {% elif filter.filter_type == 'server' %} - - {% endif %} -{% endblock table_filter %} -{% block table_actions %} -
    - {% for action in table_actions_buttons %} - {% include "horizon/common/_data_table_table_action.html" %} - {% endfor %} - {% if table_actions_menu|length > 0 %} -
    - - {% if table_actions_buttons|length > 0 %} - {% trans "More Actions" %} - {% else %} - {% trans "Actions" %} - {% endif %} - - - -
    - {% endif %} -
    -{% endblock table_actions %} -
    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 %} - - - {% block table_caption %} - {% if not hidden_title %} - - - - {% endif %} - {% if table.get_table_actions %} - - - - {% endif %} - {% endblock table_caption %} - - {% block table_breadcrumb %} - {{ block.super }} - {% endblock table_breadcrumb %} - - {% block table_columns %} - {% if not table.is_browser_table %} - - {% for column in columns %} - - {% endfor %} - - {% endif %} - {% endblock table_columns %} - - - {% block table_body %} - {{ block.super }} - {% endblock table_body %} - - {% block table_footer %} - {{ block.super }} - {% endblock table_footer %} -
    -

    {{ table }}

    -
    - {{ table.render_table_actions }} -
    - {{ column }} - {% if column.help_text %} - - - - {% endif %} -
    -{% 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 %} - -
    - -
    - {{ field|add_bootstrap_class }} - {% for error in field.errors %} - {{ error }} - {% 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 %} - {% filter force_escape %} {{ field.help_text }} {% endfilter %} - {% endif %} - {% endfor %} -
    -
    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 %} - -{% 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 #} - - - {# Tab Content #} -
    - {% for tab in tabs %} -
    - {{ tab.render }} -
    - {% endfor %} -
    - -{% 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 %} -
    -
    - {% csrf_token %} - {% if REDIRECT_URL %}{% endif %} -
    -
    - {% block workflow-buttons %} - - {% endblock %} -
    - {% block workflow-body %} - -
    - {% for step in workflow.steps %} -
    - {{ step.render }} -
    - {% if not forloop.last %} - - {% endif %} - {% endfor %} -
    - {% endblock %} -
    -
    -
    -{% endwith %} 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 @@ -

    {{ label }}

    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    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 %} -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    {% trans "From" %}
    - -
    -
    -
    -
    -
    {% trans "To" %}
    - -
    -
    -
    -
    -
    -
    - - - -
    - {% for meter_label, url_part, y_max in meter_conf %} -
    - {% include "infrastructure/_performance_chart.html" with label=meter_label y_max=y_max url=node_perf_url|add:"?"|add:url_part only %} -
    - {% endfor %} -
    -{% else %} -

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

    -{% endif %} 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 %} - - - - - - - - - - - - -{% 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 @@ -
    -
    {% include "infrastructure/_top_5_chart.html" with top_5=top_5.fan%}
    -
    {% include "infrastructure/_top_5_chart.html" with top_5=top_5.voltage %}
    -
    {% include "infrastructure/_top_5_chart.html" with top_5=top_5.temperature%}
    -
    -
    -
    {% include "infrastructure/_top_5_chart.html" with top_5=top_5.current %}
    -
    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 %} - -

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

    -{% if top_5.data %} - - {% for d in top_5.data %} - - - - - - {% endfor %} -
    - - {{ d.node_uuid|truncatechars:6 }} - - - {{ d.value}} {{ top_5.unit }} - - {%if d.direction %} - - {% endif %} -
    -{% else %} -{% trans 'No data available.' %} -{% endif %} 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 %} - - - - {% 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 %} - -
    -
    - {% block breadcrumbs %}{% endblock %} - -
    - {% block actions %}{% endblock %} -
    - -

    {% block name %}{% endblock %}

    -
    -
    - -
    -
    -
    - {% block overall_usage %}{% endblock %} -
    - - {{ tab_group.render }} -
    -
    - -{% 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 @@ - 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 %} - - {% trans "Cancel" %} -{% 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 %} - -
    -
    -

    {{ node.power_state|iconized_ironic_node_state|safe }}

    - -

    {% trans "Inventory" %}

    -
    -
    {% trans "Node UUID" %}
    -
    {{ node.uuid|default:"—" }}
    -
    {% trans "Driver" %}
    -
    - - {{ node.driver|default:"—" }} - - -
    -
    - {% if node.driver == 'pxe_ssh' %} -
    IP Address
    {{ node.driver_info.ssh_address|default:"—" }}
    -
    IPMI User
    {{ node.driver_info.ssh_username|default:"—" }}
    -
    SSH Key
    {{ node.driver_info.ssh_key_contents|default:"—" }}
    - {% else %} -
    IP Address
    {{ node.driver_info.ipmi_address|default:"—" }}
    -
    IPMI User
    {{ node.driver_info.ipmi_username|default:"—" }}
    -
    IPMI Password
    -
    - -
    - {% endif %} -
    -
    -
    -
    {% trans "Network Cards" %}
    -
    - - {% blocktrans count addresses_length=node.addresses|length %} - {{ addresses_length }} interface - {% plural %} - {{ addresses_length }} interfaces - {% endblocktrans %} - - -
    -
      - {% for address in node.addresses %} -
    • {{ address }}
    • - {% endfor %} -
    -
    -
    -
    {% trans "Registered HW" %}
    -
    - {{ node.cpu_arch|default:"—" }}
    - {{ node.cpus|default:"—" }} {% trans "CPU" %}
    - {{ node.memory_mb|default:"—" }} {% trans "RAM (MB)" %}
    - {{ node.local_gb|default:"—" }} {% trans "HDD (GB)" %} -
    -
    - -

    {% trans "Deployment" %}

    -
    -
    {% trans "Deployment Role" %}
    - {% if stack and role %} -
    {{ role.name }}
    - {% else %} -
    - {% endif %} -
    {% trans "Provisioning" %}
    -
    - {{ node.provisioning_status|default:"—" }} - {% if node.instance_uuid %} -
    {{ node.instance.created }} - {% endif %} -
    -
    {% trans "Image" %}
    -
    {{ node.image_name|default:"—" }}
    -
    {% trans "Instance UUID" %}
    -
    {{ node.instance_uuid|default:"—" }}
    -
    - -

    {% trans "Deployment Images" %}

    -
    -
    {% trans "Kernel" %}
    -
    {{ kernel_image.name|default:"—" }}
    -
    {% trans "Ramdisk" %}
    -
    {{ ramdisk_image.name|default:"—" }}
    -
    - -
    -
    -

    {% trans "Performance & Metrics" %}

    - {% 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 %} -
    -
    - -{% 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 %} -
    -
    - -
    {{ field|add_bootstrap_class }}
    -
    {{ extra_text|default:'' }}
    -
    - {% if field.errors %} -
    - {% for error in field.errors %} - {{ error }} - {% endfor %} -
    - {% endif %} -
    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 %} - -
    -
    - {% include 'horizon/common/_form_errors.html' with form=form %} -

    {% trans "Node Detail" %}

    -
    -
    {% trans "Power Management" %}
    - {% 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 %} -
    -
    -
    {% trans "Networking" %}
    - {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.mac_addresses required=True %} -
    -
    -
    {% trans "Hardware" %}
    - {% 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') %} -
    -
    -
    {% trans "Deployment Images" %}
    - {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_kernel %} - {% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_ramdisk %} -
    -
    -
    - - 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%} - -
    -
    -

    {% trans 'Hardware Inventory' %}

    -
    - -
    -

    {{ cpus }} {% trans 'CPU cores' %}

    -

    {{ memory_gb|floatformat:"0" }} {% trans 'GB of memory' %}

    -

    {{ local_gb|floatformat:"0" }} {% trans 'GB of storage' %}

    -
    -
    -
    -

    {% trans 'Nodes Status' %}

    -
    -
    -
    -
    -

    {% trans 'Power Status' %}

    -
    -
    -
    -
    - -

    {% trans "Provisioned nodes" %}

    -
    -
    -
    -
    - - {{ nodes_provisioned_count }} -

    {% trans 'Provisioned' %}

    {% trans 'Nodes' %}

    -
    - {% if nodes_provisioning_count %} -
    - - - {% blocktrans count nodes_provisioning_count as counter %} - {{ counter }} node - {% plural %} - {{ counter }} nodes - {% endblocktrans %} - - {% blocktrans count nodes_provisioning_count as counter %} - is being provisioned - {% plural %} - are being provisioned - {% endblocktrans %} -
    - {% endif %} - {% if nodes_deleting_count %} -
    - - - {% blocktrans count nodes_deleting_count as counter %} - {{ counter }} node - {% plural %} - {{ counter }} nodes - {% endblocktrans %} - - {% blocktrans count nodes_deleting_count as counter %} - is being deleted - {% plural %} - are being deleted - {% endblocktrans %} -
    - {% endif %} - {% if nodes_error_count %} - - {% endif %} -
    -
    -
    - {% 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 %} -
    -
    -{% 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 %} -

    {% trans "Nodes Discovery" %}

    -
    -
    -
    -
    - {% if nodes_discovered_count %} - - {{ nodes_discovered_count }} -

    {% trans 'Discovered' %}

    {% trans 'Nodes' %}

    -
    - ({{ nodes_discovered_count }} {% trans 'waiting for activation' %}) - {% endif %} - {% if nodes_on_discovery_count %} -
    - - - {% blocktrans count nodes_on_discovery_count as counter %} - {{ counter }} node - {% plural %} - {{ counter }} nodes - {% endblocktrans %} - - {% blocktrans count nodes_on_discovery_count as counter %} - is being discovered - {% plural %} - are being discovered - {% endblocktrans %} -
    - {% endif %} - {% if nodes_discovery_failed_count %} - - {% endif %} -
    -
    -
    -{% 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 %} -
    - {% include 'horizon/common/_form_field.html' with field=form.csv_file %} -
    - - -{% trans 'Cancel' %} 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 %} -
    -
    - {{ tab_group.render }} -
    -
    -{% 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 %} -
    -
    - {{ tab_group.render }} -
    -
    -{% 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 %} -
    -

    {% trans "You are about deploy your overcloud." %}

    - {% if autogenerated_parameters %} -   - {{ autogenerated_parameters|length }} parameters will be randomly generated. - - - -
    -
    • - {{ autogenerated_parameters|join:"
    • " }} -
    -
    - {% endif %} -
    -
    -
    -
    - {% include "horizon/common/_form_fields.html" %} -
    -
    -

    -
    - -

    {% trans "This operation cannot be undone." %}

    -

    {% trans "Are you sure you want to deploy changes?" %}

    -
    -
    -{% 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 %} -{{ last_events_title }} -
    -
    - {% for event in last_events %} -
    - -
    - {{ event.resource_name }} | - {{ event.resource_status }} | - {{ event.resource_status_reason }} -
    -
    - {% endfor %} -
    -
    -

    See full log

    -{% 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 %} -
    -
    -
    - {% include "horizon/common/_form_fields.html" %} -
    -
    - {% trans "Your OpenStack cloud nodes are deployed. They need to be initialized before your cloud will be live."%} -
    -
    -
    -{% 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 %} -
    - {% include 'horizon/common/_form_errors.html' with form=form %} -{% for role in roles %} -
    -
    - {{ role.name|capfirst }} - {% for error in role.field.errors %} - - {{ error }} - - {% endfor %} -
    -
    - {{ role.planned_node_count }} → -
    -
    - {{ role.field|add_bootstrap_class }} -
    -
    -{% endfor %} -
    - - - -{% 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 %} -
    -

    {% trans "You are about undeploy your overcloud" %} -

    -

    {% trans "This operation cannot be undone. Are you sure you want to do that?" %}

    -
    - {% include "horizon/common/_form_fields.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 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%} - -
    - {% block deployment-heading %}{% endblock %} -
    -
    -
    - -
    -
    -

    {% block deployment-title %}{% endblock %}

    - {% block deployment-info %}{% endblock %} -
    - {% block deployment-buttons %} - - {% trans "Undeploy" %} - - {% endblock %} -
    -
    - -{% 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 %} -

    {% trans "Your OpenStack cloud is successfully deployed. Before using the cloud you must now initialize it." %}

    -{% endblock %} - -{% block deployment-buttons %} - {{ block.super }} - - - {% trans "Initialize" %} - -{% 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 %} -
    - -
    -
    -

    {% trans "Deployment is live" %}

    -
    -{% endblock %} - -{% block deployment-icon %}fa-key{% endblock %} -{% block deployment-title %}{% trans "Access Information" %}{% endblock %} -{% block deployment-info %} -
    - {% for dashboard_url in dashboard_urls %} -
    -
    {% trans "Horizon URL:" %}
    - -
    - {% endfor %} -
    -
    {% trans "User name:" %}
    -
    admin
    -
    -
    -
    {% trans "Password" %}
    -
    - {% trans "Reveal" %} -
    -
    -
    - - -{% endblock %} - -{% block deployment-buttons %} - {{ block.super }} - - {% trans "Scale-out" %} - - -{% endblock %} - -{% block deployment-overcloudrc %} -
    -
    - -
    -
    -

    {% trans "Overcloudrc Information" %}

    -
    -
    -
    NOVA_VERSION:
    -
    1.1
    -
    -
    -
    OS_USERNAME:
    -
    admin
    -
    -
    -
    OS_PASSWORD:
    -
    {{ admin_password }}
    -
    -
    -
    OS_AUTH_URL:
    -
    {{ auth_url }}
    -
    -
    -
    OS_TENANT_NAME:
    -
    admin
    -
    -
    -
    COMPUTE_API_VERSION:
    -
    1.1
    -
    -
    -
    OS_NO_CACHE:
    -
    True
    -
    -
    -
    no_proxy:
    -
    {{ no_proxy }}
    -
    -
    -
    {% trans "Download" %}
    - -
    -
    -
    -{% 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 %} - -
      - {% for message in plan_messages %} -
    • - - {{ message.text }} -
    • - {% endfor %} -
    -{% endblock %} - -{% block deployment-buttons %} - - {% trans "Verify and Deploy" %} - -{% endblock %} - -{% block templates %} - - -{% 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 %} -
    -
    {{ progress }}% {% trans "Complete" %}
    -
    -{% endif %} -
    - {% include "infrastructure/overview/_last_events.html" %} -
    -{% endblock %} - -{% block deployment-buttons %} - {% if stack.is_deploying or stack.is_updating %} - - - {% trans "Stop" %} - - {% endif %} -{% endblock %} - -{% block templates %} - -{% 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 %} -
    -
    - {% 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 %} -
    -
    - {% if stack %} - {% include "infrastructure/overview/role_nodes_status.html" %} - {% else %} - {% include "infrastructure/overview/role_nodes_edit.html" %} - {% endif %} -
    -
    -{% 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 %} - -

    {% trans "Deployment Roles" %}

    -
    -{% csrf_token %} -{% include 'horizon/common/_form_errors.html' with form=form %} -{% for role in roles %} -
    -
    - -
    -
    - {{ role.name }} - {% for error in role.field.errors %} - - {{ error }} - - {% endfor %} -
    -
    - {{ role.field|add_bootstrap_class }} -
    -
    -{% endfor %} -
    - -
    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 %} - -

    {% trans "Deployment Roles" %}

    -
    -{% for role in roles %} -
    -
    - {% if role.planned_node_count %} - - {% endif %} -
    -
    - {{ role.name }} -
    -
    - {% if role.finished %} - {{ role.deployed_node_count }} - {% else %} - {{ role.deployed_node_count }}/{{ role.planned_node_count }} - {% endif %} -
    -
    -{% endfor %} -
    - - 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 @@ - - - - - Tuskar QUnit Test Suite - - - - {% include "horizon/_conf.html" %} - - {% comment %}Load test modules here.{% endcomment %} - - {% comment %}End test modules.{% endcomment %} - - {% include "horizon/_scripts.html" %} - {% include "infrastructure/_scripts.html" %} - - -

    Tuskar JavaScript Tests

    -

    -
    -

    -
      -
      - - -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      -

      Flavors

      - -
      -
      Flavor NameVCPURAM (MB)Root Disk (GB)Ephemeral Disk - (GB)Swap Disk - (MB)Max. - VMs - Delete
      -
      -
      -
      Displaying 3 - items
      -
      - -
      - - diff --git a/tuskar_ui/infrastructure/templatetags/__init__.py b/tuskar_ui/infrastructure/templatetags/__init__.py deleted file mode 100644 index e69de29b..00000000 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( - [("

      {0} {1}

      ").format( - str(flavor.used_instances), - flavor.name) - for flavor in flavors]) - - decorated_obj = ("

      Capacity remaining by flavors:

      " + - 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'] = ( - '

      {0}% total,' - ' {1} instances of {2}

      '.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, "—") - icon = IRONIC_NODE_STATE_ICON_DICT.get(node_power_state, 'fa-question') - html_string = (u"""""" - u"""%s """) % (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, - '

      Capacity remaining by flavors:

      ' - '

      0 a

      ' - '

      1 b

      ' - ) - - 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': '

      0% total, ' - ' 0 instances of a

      ', - 'used_instances': '0', - }, { - 'popup_used': '

      1% total, ' - ' 1 instances of b

      ', - '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'' - 'powered on ', - ) - ret = icon_helpers.iconized_ironic_node_state('') - self.assertEqual( - ret, - u'' - ' ', - ) 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 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 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 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 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
      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
      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
      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 '' % self.index - >>> items = [Item(i) for i in range(7)] - >>> list(filter_items(items, index=1)) - [] - >>> list(filter_items(items, index__in=(1, 2, 3))) - [, , ] - >>> list(filter_items(items, index__not_in=(1, 2, 3))) - [, , , ] - """ - for item in items: - for name, value in kwargs.items(): - if name.endswith('__in'): - if getattr(item, name[:-len('__in')]) not in value: - break - elif name.endswith('__not_in'): - if getattr(item, name[:-len('__not_in')]) in value: - break - else: - if getattr(item, name) != value: - break - else: - yield item - - -def safe_int_cast(value): - try: - return int(value) - except (TypeError, ValueError): - return 0 - - -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("