├── .coveragerc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .gitreview ├── .mailmap ├── .zuul.yaml ├── CONTRIBUTING.rst ├── HACKING.rst ├── LICENSE ├── MANIFEST.in ├── README.rst ├── babel-django.cfg ├── babel-djangojs.cfg ├── bindep.txt ├── devstack ├── plugin.sh └── settings ├── doc ├── requirements.txt └── source │ ├── conf.py │ ├── contributor │ ├── contributing.rst │ └── index.rst │ ├── index.rst │ └── install │ ├── index.rst │ ├── installation.rst │ └── uninstallation.rst ├── ironic_ui ├── __init__.py ├── api │ ├── __init__.py │ ├── ironic.py │ └── ironic_rest_api.py ├── content │ ├── __init__.py │ └── ironic │ │ ├── __init__.py │ │ ├── panel.py │ │ ├── templates │ │ └── ironic │ │ │ ├── index.html │ │ │ └── node_detail.html │ │ ├── urls.py │ │ └── views.py ├── enabled │ ├── _2200_ironic.py │ └── __init__.py ├── karma.conf.js ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ ├── django.po │ │ │ └── djangojs.po │ ├── en_GB │ │ └── LC_MESSAGES │ │ │ ├── django.po │ │ │ └── djangojs.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── djangojs.po │ ├── id │ │ └── LC_MESSAGES │ │ │ ├── django.po │ │ │ └── djangojs.po │ ├── ja │ │ └── LC_MESSAGES │ │ │ └── djangojs.po │ ├── ko_KR │ │ └── LC_MESSAGES │ │ │ ├── django.po │ │ │ └── djangojs.po │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ └── djangojs.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ └── djangojs.po │ ├── tr_TR │ │ └── LC_MESSAGES │ │ │ └── djangojs.po │ └── zh_Hans │ │ └── LC_MESSAGES │ │ ├── django.po │ │ └── djangojs.po ├── static │ └── dashboard │ │ └── admin │ │ └── ironic │ │ ├── auto-focus.directive.js │ │ ├── base-node │ │ ├── base-node.controller.js │ │ ├── base-node.controller.spec.js │ │ ├── base-node.html │ │ ├── base-node.service.js │ │ └── base-node.spec.js │ │ ├── base-port │ │ ├── base-port.controller.js │ │ ├── base-port.controller.spec.js │ │ └── base-port.html │ │ ├── base-portgroup │ │ ├── base-portgroup.controller.js │ │ ├── base-portgroup.controller.spec.js │ │ └── base-portgroup.html │ │ ├── bootdevice │ │ ├── bootdevice.controller.js │ │ ├── bootdevice.controller.spec.js │ │ ├── bootdevice.html │ │ ├── bootdevice.service.js │ │ └── bootdevice.service.spec.js │ │ ├── clean-node │ │ ├── clean-node.controller.js │ │ ├── clean-node.html │ │ └── clean-node.service.js │ │ ├── create-port │ │ ├── create-port.controller.js │ │ ├── create-port.controller.spec.js │ │ └── create-port.service.js │ │ ├── create-portgroup │ │ ├── create-portgroup.controller.js │ │ ├── create-portgroup.controller.spec.js │ │ └── create-portgroup.service.js │ │ ├── driver-property.service.js │ │ ├── driver-property.service.spec.js │ │ ├── edit-node │ │ ├── edit-node.controller.js │ │ ├── edit-node.controller.spec.js │ │ └── edit-node.service.js │ │ ├── edit-port │ │ ├── edit-port.controller.js │ │ ├── edit-port.controller.spec.js │ │ └── edit-port.service.js │ │ ├── edit-portgroup │ │ ├── edit-portgroup.controller.js │ │ ├── edit-portgroup.controller.spec.js │ │ └── edit-portgroup.service.js │ │ ├── empty-to-pristine.directive.js │ │ ├── enroll-node │ │ ├── enroll-node.controller.js │ │ ├── enroll-node.controller.spec.js │ │ └── enroll-node.service.js │ │ ├── form-field.directive.js │ │ ├── form-field.html │ │ ├── form-field.service.js │ │ ├── form-field.service.spec.js │ │ ├── graph.service.js │ │ ├── ironic.backend-mock.service.js │ │ ├── ironic.module.js │ │ ├── ironic.service.js │ │ ├── ironic.service.spec.js │ │ ├── maintenance │ │ ├── maintenance.controller.js │ │ ├── maintenance.html │ │ └── maintenance.service.js │ │ ├── modal-draggable.directive.js │ │ ├── node-actions.service.js │ │ ├── node-details │ │ ├── node-details.controller.js │ │ ├── node-details.controller.spec.js │ │ ├── node-details.html │ │ └── sections │ │ │ ├── configuration.html │ │ │ ├── overview.html │ │ │ ├── port-details.html │ │ │ └── portgroup-details.html │ │ ├── node-error.service.js │ │ ├── node-list │ │ ├── node-list.controller.js │ │ └── node-list.html │ │ ├── node-state-transition.service.js │ │ ├── node-state-transition.service.spec.js │ │ ├── postfix-expr.service.js │ │ ├── postfix-expr.service.spec.js │ │ ├── property-collection-editor.directive.js │ │ ├── property-collection-editor.html │ │ ├── property-collection.service.js │ │ ├── property-collection.service.spec.js │ │ ├── raidconfig │ │ ├── raidconfig.controller.js │ │ ├── raidconfig.controller.spec.js │ │ ├── raidconfig.html │ │ ├── raidconfig.service.js │ │ └── raidconfig.service.spec.js │ │ ├── test-data.spec.js │ │ └── update-patch.service.js └── test │ ├── __init__.py │ ├── integration │ ├── __init__.py │ ├── horizon.conf │ ├── pages │ │ ├── __init__.py │ │ └── admin │ │ │ ├── __init__.py │ │ │ └── system │ │ │ ├── __init__.py │ │ │ └── ironicbaremetalprovisioningpage.py │ └── test_basic.py │ ├── settings.py │ ├── tests │ ├── __init__.py │ └── test_registration.py │ └── urls.py ├── manage.py ├── package.json ├── releasenotes ├── notes │ ├── .placeholder │ ├── 2.0-release-3d8acae7c8de429b.yaml │ ├── 2.1.0-release-3391dfbcf087c883.yaml │ ├── add-console-support-ccffcedc845ca214.yaml │ ├── adding-driver-interfaces-while-node-enrollment-7cb82d6753074629.yaml │ ├── bug-1648548-6e846a0c1e1574aa.yaml │ ├── bug-1648550-32e28cf2a401bb02.yaml │ ├── bug-1648557-462ebfdf39efee1f.yaml │ ├── bug-1648563-e2bc262985873122.yaml │ ├── bug-1671567-a95d7cb0d21470e4.yaml │ ├── bug-1672709-b485a7a59ea1c129.yaml │ ├── drop-python2-0bf548c3946068af.yaml │ ├── edit-portgroup-92c62b1ae0cf5e54.yaml │ ├── get-client-e30e3b05eb9901ca.yaml │ ├── inject-nmi-0320453eaf1bda9b.yaml │ ├── remove-py38-bc44dcb4625e380f.yaml │ ├── unit-test-framework-f61ad7926413bf91.yaml │ ├── view-portgroups-a3efb4407536caf2.yaml │ └── wallaby-release-note-f502b2ba09932e4c.yaml └── source │ ├── 2023.1.rst │ ├── 2023.2.rst │ ├── 2024.1.rst │ ├── 2024.2.rst │ ├── 2025.1.rst │ ├── Makefile │ ├── conf.py │ ├── index.rst │ ├── locale │ ├── de │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── en_GB │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── id │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── ja │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── ko_KR │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ ├── ru │ │ └── LC_MESSAGES │ │ │ └── releasenotes.po │ └── zh_CN │ │ └── LC_MESSAGES │ │ └── releasenotes.po │ ├── make.bat │ ├── newton.rst │ ├── ocata.rst │ ├── pike.rst │ ├── queens.rst │ ├── rocky.rst │ ├── stein.rst │ ├── train.rst │ ├── unreleased.rst │ ├── ussuri.rst │ ├── victoria.rst │ ├── wallaby.rst │ ├── xena.rst │ ├── yoga.rst │ └── zed.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── test-shim.js └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = ironic_ui 4 | omit = ironic_ui/tests/* 5 | 6 | [report] 7 | ignore_errors = True 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | # Enable eslint-plugin-angular 2 | plugins: 3 | - angular 4 | 5 | extends: openstack 6 | 7 | # Set up globals 8 | globals: 9 | angular: false 10 | module: false 11 | 12 | env: 13 | browser: true 14 | jasmine: true 15 | 16 | rules: 17 | angular/no-private-call: 0 18 | angular/no-services: 19 | - 2 20 | - - $http 21 | - $resource 22 | - Restangular 23 | angular/no-service-method: 0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | cover 12 | .eggs 13 | eggs 14 | parts 15 | bin 16 | var 17 | sdist 18 | develop-eggs 19 | .installed.cfg 20 | lib 21 | lib64 22 | 23 | # Installer logs 24 | pip-log.txt 25 | 26 | # Unit test / coverage reports 27 | .coverage* 28 | coverage.xml 29 | nosetests.xml 30 | .tox 31 | .venv 32 | 33 | # Translations 34 | *.mo 35 | 36 | # Mr Developer 37 | .mr.developer.cfg 38 | .project 39 | .pydevproject 40 | 41 | # Complexity 42 | output/*.html 43 | output/*/index.html 44 | 45 | # Sphinx 46 | doc/build 47 | doc/source/contributor/api/* 48 | 49 | # pbr generates these 50 | AUTHORS 51 | ChangeLog 52 | 53 | # Editors 54 | *~ 55 | .*.swp 56 | .*sw? 57 | 58 | .secret_key_store 59 | *.lock 60 | 61 | # Node generated files 62 | package/ 63 | node_modules/ 64 | npm-debug.log 65 | 66 | # release notes build 67 | releasenotes/build 68 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/ironic-ui.git 5 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # Format is: 2 | # 3 | # 4 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - job: 2 | name: ironic-ui-integration-tests 3 | parent: horizon-integration-tests 4 | required-projects: 5 | - name: openstack/horizon 6 | - name: openstack/ironic 7 | - name: openstack/python-ironicclient 8 | - name: openstack/ironic-ui 9 | roles: 10 | - zuul: openstack-infra/devstack 11 | - zuul: openstack/horizon 12 | irrelevant-files: 13 | - ^.*\.rst$ 14 | - ^doc/.*$ 15 | - ^releasenotes/.*$ 16 | vars: 17 | devstack_plugins: 18 | ironic: https://opendev.org/openstack/ironic 19 | ironic-ui: https://opendev.org/openstack/ironic-ui 20 | devstack_services: 21 | horizon: true 22 | tox_envlist: integration 23 | 24 | - job: 25 | name: ironic-ui-tox-codespell 26 | parent: openstack-tox 27 | timeout: 7200 28 | vars: 29 | tox_envlist: codespell 30 | 31 | - project: 32 | templates: 33 | - check-requirements 34 | - openstack-cover-jobs 35 | - horizon-nodejs-jobs 36 | - horizon-non-primary-django-jobs 37 | - openstack-python3-jobs 38 | - publish-openstack-docs-pti 39 | - release-notes-jobs-python3 40 | check: 41 | jobs: 42 | - ironic-ui-integration-tests: 43 | voting: false 44 | - ironic-ui-tox-codespell: 45 | voting: false 46 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | If you would like to contribute to the development of OpenStack, you must 2 | follow the steps in this page: 3 | 4 | http://docs.openstack.org/infra/manual/developers.html 5 | 6 | If you already have a good understanding of how the system works and your 7 | OpenStack accounts are set up, you can skip to the development workflow 8 | section of this documentation to learn how changes to OpenStack should be 9 | submitted for review via the Gerrit tool: 10 | 11 | http://docs.openstack.org/infra/manual/developers.html#development-workflow 12 | 13 | Pull requests submitted through GitHub will be ignored. 14 | 15 | Bugs should be filed in StoryBoard, not GitHub: 16 | 17 | https://storyboard.openstack.org/#!/project/952 18 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | ironic-ui Style Commandments 2 | ============================ 3 | 4 | Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ 5 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include ChangeLog 3 | exclude .gitignore 4 | exclude .gitreview 5 | include setup.py 6 | 7 | recursive-include ironic_ui *.js *.html *.scss 8 | 9 | global-exclude *.pyc 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Team and repository tags 3 | ======================== 4 | 5 | .. image:: https://governance.openstack.org/tc/badges/ironic-ui.svg 6 | :target: https://governance.openstack.org/tc/reference/tags/index.html 7 | 8 | .. Change things from this point on 9 | 10 | ========= 11 | Ironic UI 12 | ========= 13 | 14 | The Ironic UI is a Horizon plugin that will allow users to view and manage bare 15 | metal nodes, ports and drivers. 16 | 17 | * Free software: Apache license 18 | * Documentation: https://docs.openstack.org/ironic-ui/latest 19 | * Release notes: https://docs.openstack.org/releasenotes/ironic-ui/ 20 | * Source: https://opendev.org/openstack/ironic-ui 21 | * Bugs: https://storyboard.openstack.org/#!/project/952 22 | 23 | Features 24 | -------- 25 | 26 | * View bare metal nodes 27 | * View node details 28 | * Apply maintenance and power on/off actions to the nodes 29 | -------------------------------------------------------------------------------- /babel-django.cfg: -------------------------------------------------------------------------------- 1 | [python: **.py] 2 | [django: **/templates/**.html] 3 | [django: **/templates/**.csv] 4 | -------------------------------------------------------------------------------- /babel-djangojs.cfg: -------------------------------------------------------------------------------- 1 | [javascript: **.js] 2 | [angular: **/static/**.html] 3 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | # selenium tests 2 | firefox [selenium] 3 | libfontconfig1 [nodejs platform:dpkg] 4 | xvfb [selenium platform:dpkg] 5 | # already part of xorg-x11-server on openSUSE 6 | xorg-x11-server-Xvfb [selenium platform:redhat] 7 | 8 | -------------------------------------------------------------------------------- /devstack/plugin.sh: -------------------------------------------------------------------------------- 1 | # plugin.sh - DevStack plugin.sh dispatch script ironic-ui 2 | 3 | function install_ironic_ui { 4 | # NOTE(crushil): workaround for devstack bug: 1540328 5 | # where devstack installs 'test-requirements' but should not do it 6 | # for ironic-ui project as it installs Horizon from url. 7 | # Remove following two 'mv' commands when mentioned bug is fixed. 8 | mv $IRONIC_UI_DIR/test-requirements.txt $IRONIC_UI_DIR/_test-requirements.txt 9 | 10 | setup_develop ${IRONIC_UI_DIR} 11 | 12 | mv $IRONIC_UI_DIR/_test-requirements.txt $IRONIC_UI_DIR/test-requirements.txt 13 | } 14 | 15 | # check for service enabled 16 | if is_service_enabled horizon && is_service_enabled ironic && is_service_enabled ironic-ui; then 17 | 18 | if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then 19 | # Set up system services 20 | # no-op 21 | : 22 | elif [[ "$1" == "stack" && "$2" == "install" ]]; then 23 | # Perform installation of service source 24 | echo_summary "Installing Ironic UI" 25 | install_ironic_ui 26 | elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then 27 | # Configure after the other layer 1 and 2 services have been configured 28 | echo_summary "Configuring Ironic UI" 29 | cp -a ${IRONIC_UI_DIR}/ironic_ui/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/ 30 | elif [[ "$1" == "stack" && "$2" == "extra" ]]; then 31 | # no-op 32 | : 33 | fi 34 | 35 | if [[ "$1" == "unstack" ]]; then 36 | # no-op 37 | : 38 | fi 39 | 40 | if [[ "$1" == "clean" ]]; then 41 | # Remove state and transient data 42 | # Remember clean.sh first calls unstack.sh 43 | # no-op 44 | : 45 | fi 46 | fi -------------------------------------------------------------------------------- /devstack/settings: -------------------------------------------------------------------------------- 1 | # settings file for ironic-ui plugin 2 | enable_service ironic-ui 3 | 4 | # set up default directories 5 | IRONIC_UI_DIR=${IRONIC_UI_DIR:=$DEST/ironic-ui} -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>=2.0.0,!=2.1.0 # BSD 2 | openstackdocstheme>=2.2.1 # Apache-2.0 3 | reno>=3.1.0 # Apache-2.0 4 | testtools>=2.2.0 # MIT 5 | testscenarios>=0.4 # Apache-2.0/BSD 6 | 7 | # The below is rewquired to build testing module reference 8 | 9 | selenium>=2.50.1 # Apache-2.0 10 | xvfbwrapper>=0.1.3 #license: MIT 11 | -------------------------------------------------------------------------------- /doc/source/contributor/contributing.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | How to Contribute 3 | ================= 4 | 5 | Contributor License Agreement 6 | ----------------------------- 7 | 8 | .. index:: 9 | single: license; agreement 10 | 11 | In order to contribute to the Ironic UI project, you need to have 12 | signed OpenStack's contributor's agreement. 13 | 14 | .. seealso:: 15 | 16 | * https://docs.openstack.org/infra/manual/developers.html 17 | * https://wiki.openstack.org/CLA 18 | 19 | Project Hosting Details 20 | ------------------------- 21 | 22 | Bug tracker 23 | https://storyboard.openstack.org/#!/project/952 24 | 25 | Mailing list (prefix subjects with ``[ironic]`` for faster responses) 26 | http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss 27 | 28 | Code Hosting 29 | https://opendev.org/openstack/ironic-ui 30 | 31 | Code Review 32 | https://review.opendev.org/#/q/status:open+project:openstack/ironic-ui,n,z 33 | -------------------------------------------------------------------------------- /doc/source/contributor/index.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Contributing to Ironic UI 3 | ========================= 4 | 5 | If you're interested in contributing to the Ironic UI project, 6 | the following will help get you started. 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | contributing 12 | 13 | Autogenerated API Documentation 14 | =============================== 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | 19 | api/autoindex 20 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | ===================================== 2 | Welcome to Ironic UI's documentation! 3 | ===================================== 4 | 5 | Introduction 6 | ============ 7 | 8 | The ironic UI is an OpenStack Horizon plugin that will allow users to view and 9 | manage their ironic bare metal nodes, ports and drivers. 10 | 11 | The documentation provided here is continually kept up-to-date based 12 | on the latest code that has been committed, and may not represent the state of 13 | the project at any specific prior release. 14 | 15 | For information on any current or prior version of Ironic, see `the release 16 | notes`_. 17 | 18 | .. _the release notes: https://docs.openstack.org/releasenotes/ironic-ui/ 19 | 20 | For more information on ironic, see `the ironic documentation`_. 21 | 22 | .. _the ironic documentation: https://docs.openstack.org/ironic/latest/ 23 | 24 | .. toctree:: 25 | :maxdepth: 1 26 | 27 | Introduction to ironic 28 | Installing the ironic UI 29 | Contributing 30 | Release notes 31 | -------------------------------------------------------------------------------- /doc/source/install/index.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | ironic-ui installation guide 3 | ============================ 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | installation 9 | uninstallation 10 | -------------------------------------------------------------------------------- /doc/source/install/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Ironic-UI Installation 4 | ====================== 5 | 6 | Manual Installation 7 | ------------------- 8 | 9 | Please note that the following instructions assume that you have an existing 10 | installation of the OpenStack Horizon dashboard application. For Horizon 11 | installation please see 12 | https://docs.openstack.org/horizon/latest/contributor/quickstart.html. 13 | 14 | 1. Clone the Ironic UI repository:: 15 | 16 | git clone https://opendev.org/openstack/ironic-ui 17 | 18 | 2. Change into the root directory of your horizon installation and 19 | activate the python virtualenv. Example:: 20 | 21 | source .venv/bin/activate 22 | 23 | .. NOTE:: The ``.venv`` folder is pre-installed when horizon is setup with 24 | ``./run_tests.sh``. Do not attempt to reinstall the virtual 25 | environment. 26 | 27 | 3. Copy the ``_2200_ironic.py`` file from ``ironic_ui/enabled/_2200_ironic.py`` 28 | file to ``horizon/openstack_dashboard/local/enabled`` directory. Example, 29 | set as if being executed from the root of the ironic-ui repository:: 30 | 31 | cp ./ironic_ui/enabled/_2200_ironic.py ../horizon/openstack_dashboard/local/enabled 32 | 33 | 4. Change into the ironic-ui repository and package the plugin:: 34 | 35 | pip install -r requirements.txt -e . 36 | 37 | This will build and install the ironic-ui plugin into the active virtual 38 | environment associated with your horizon installation. The plugin is installed 39 | in "editable" mode as a link back to your ironic-ui plugin directory. 40 | 41 | 5. Change back into the horizon repository and bring up your environment:: 42 | 43 | ./run_tests.sh --runserver 44 | 45 | The Bare Metal service should now be visible in the Horizon navigation. 46 | 47 | 6. Start the server in test mode with the ``npm run test`` command. 48 | 49 | 7. Access the test page in order to initiate tests. 50 | 51 | http://localhost:8000/jasmine/?spec=horizon.dashboard.admin.ironic 52 | 53 | Installation with DevStack 54 | -------------------------- 55 | 56 | In order to use the Ironic UI with devstack, you will need to enable 57 | the UI plugin separately in your installation local.conf file. 58 | 59 | This is done in a similar fashion to enabling Ironic for devstack. 60 | 61 | Make sure you have horizon enabled, which is the default in devstack. 62 | 63 | Then, enable the Ironic UI plugin appending the following line to the end of the local.conf file, 64 | just after Ironic plugin enablement:: 65 | 66 | enable_plugin ironic-ui https://github.com/openstack/ironic-ui 67 | 68 | After this, you can run ./stack.sh from the devstack directory. 69 | 70 | The Bare Metal Provisioning plugin should now be visible in the Horizon 71 | navigation. 72 | -------------------------------------------------------------------------------- /doc/source/install/uninstallation.rst: -------------------------------------------------------------------------------- 1 | .. _unstallation: 2 | 3 | Uninstallation 4 | ============== 5 | 6 | To uninstall, use ``pip uninstall ironic-ui`` from with-in the horizon 7 | virtual environment. You will also need to remove the 8 | ``openstack_dashboard/enabled/_2200_ironic.py`` file from the horizon 9 | installation. 10 | -------------------------------------------------------------------------------- /ironic_ui/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import pbr.version 14 | 15 | 16 | __version__ = pbr.version.VersionInfo( 17 | 'ironic-ui').version_string() 18 | -------------------------------------------------------------------------------- /ironic_ui/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/ironic-ui/01b67ed7bfdd30deb35d06a16ef543bfcd5fe046/ironic_ui/api/__init__.py -------------------------------------------------------------------------------- /ironic_ui/content/__init__.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import pbr.version 14 | 15 | 16 | __version__ = pbr.version.VersionInfo( 17 | 'ironic-ui').version_string() 18 | -------------------------------------------------------------------------------- /ironic_ui/content/ironic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/ironic-ui/01b67ed7bfdd30deb35d06a16ef543bfcd5fe046/ironic_ui/content/ironic/__init__.py -------------------------------------------------------------------------------- /ironic_ui/content/ironic/panel.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cisco Systems, Inc. 2 | # Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | from django.utils.translation import gettext_lazy as _ 17 | 18 | import horizon 19 | 20 | from openstack_dashboard.dashboards.admin import dashboard 21 | 22 | 23 | class Ironic(horizon.Panel): 24 | name = _("Ironic Bare Metal Provisioning") 25 | slug = 'ironic' 26 | permissions = ('openstack.roles.admin', 'openstack.services.baremetal',) 27 | 28 | 29 | dashboard.Admin.register(Ironic) 30 | -------------------------------------------------------------------------------- /ironic_ui/content/ironic/templates/ironic/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Ironic Bare Metal Provisioning" %}{% endblock %} 4 | 5 | {% block breadcrumb_nav %} 6 | 11 | {% endblock %} 12 | 13 | {% block page_header %} 14 | 15 | {% endblock %} 16 | 17 | {% block main %} 18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /ironic_ui/content/ironic/templates/ironic/node_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% load i18n %} 3 | {% block title %}{% trans "Node Details" %}{% endblock %} 4 | 5 | {% block breadcrumb_nav %} 6 | 12 | {% endblock %} 13 | 14 | {% block page_header %} 15 | 16 | {% endblock %} 17 | 18 | {% block main %} 19 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /ironic_ui/content/ironic/urls.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cisco Systems, Inc. 2 | # Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP 3 | # Copyright (c) 2016 Cray Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | from django.urls import re_path 18 | 19 | import ironic_ui.api.ironic_rest_api # noqa 20 | from ironic_ui.content.ironic import views 21 | 22 | urlpatterns = [ 23 | re_path(r'^$', views.IndexView.as_view(), name='index'), 24 | re_path(r'^([^/]+)/$', views.DetailView.as_view(), name='detail'), 25 | ] 26 | -------------------------------------------------------------------------------- /ironic_ui/content/ironic/views.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Cisco Systems, Inc. 2 | # Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP 3 | # Copyright (c) 2016 Cray Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from django.views import generic 18 | 19 | 20 | class IndexView(generic.TemplateView): 21 | template_name = 'admin/ironic/index.html' 22 | 23 | 24 | class DetailView(generic.TemplateView): 25 | template_name = 'admin/ironic/node_detail.html' 26 | -------------------------------------------------------------------------------- /ironic_ui/enabled/_2200_ironic.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP 2 | # Copyright (c) 2016 Cray Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 | # implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # The name of the panel to be added to HORIZON_CONFIG. Required. 18 | PANEL = 'ironic' 19 | # The name of the dashboard the PANEL associated with. Required. 20 | PANEL_DASHBOARD = 'admin' 21 | # The name of the panel group the PANEL is associated with. 22 | PANEL_GROUP = 'admin' 23 | # Python panel class of the PANEL to be added. 24 | ADD_PANEL = 'ironic_ui.content.ironic.panel.Ironic' 25 | # A list of applications to be prepended to INSTALLED_APPS 26 | ADD_INSTALLED_APPS = ['ironic_ui', ] 27 | # A list of AngularJS modules to be loaded when Angular bootstraps. 28 | ADD_ANGULAR_MODULES = ['horizon.dashboard.admin.ironic'] 29 | # Automatically discover static resources in installed apps 30 | AUTO_DISCOVER_STATIC_FILES = True 31 | -------------------------------------------------------------------------------- /ironic_ui/enabled/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/ironic-ui/01b67ed7bfdd30deb35d06a16ef543bfcd5fe046/ironic_ui/enabled/__init__.py -------------------------------------------------------------------------------- /ironic_ui/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Robert Simai , 2016. #zanata 2 | # Robert Simai , 2019. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ironic-ui VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2019-04-25 10:13+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2019-05-03 02:16+0000\n" 12 | "Last-Translator: Robert Simai \n" 13 | "Language-Team: German\n" 14 | "Language: de\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 17 | 18 | msgid "Admin" 19 | msgstr "Admin" 20 | 21 | msgid "Ironic Bare Metal Provisioning" 22 | msgstr "Ironic Bare Metal Provisionierung" 23 | 24 | msgid "Node Details" 25 | msgstr "Knoten Details" 26 | 27 | msgid "System" 28 | msgstr "System" 29 | -------------------------------------------------------------------------------- /ironic_ui/locale/en_GB/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Andi Chandler , 2016. #zanata 2 | # Andi Chandler , 2019. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ironic-ui VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2019-10-29 15:16+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2019-11-14 11:18+0000\n" 12 | "Last-Translator: Andi Chandler \n" 13 | "Language-Team: English (United Kingdom)\n" 14 | "Language: en_GB\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=2; plural=(n != 1)\n" 17 | 18 | msgid "Admin" 19 | msgstr "Admin" 20 | 21 | msgid "Ironic Bare Metal Provisioning" 22 | msgstr "Ironic Bare Metal Provisioning" 23 | 24 | msgid "Node Details" 25 | msgstr "Node Details" 26 | 27 | msgid "System" 28 | msgstr "System" 29 | -------------------------------------------------------------------------------- /ironic_ui/locale/id/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # suhartono , 2017. #zanata 2 | # suhartono , 2019. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ironic-ui VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2019-04-25 10:13+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2019-04-30 02:25+0000\n" 12 | "Last-Translator: suhartono \n" 13 | "Language-Team: Indonesian\n" 14 | "Language: id\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=1; plural=0\n" 17 | 18 | msgid "Admin" 19 | msgstr "Admin" 20 | 21 | msgid "Ironic Bare Metal Provisioning" 22 | msgstr "Ironic Bare Metal Provisioning" 23 | 24 | msgid "Node Details" 25 | msgstr "Node Details" 26 | 27 | msgid "System" 28 | msgstr "System" 29 | -------------------------------------------------------------------------------- /ironic_ui/locale/ko_KR/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Eunseop Shin , 2016. #zanata 2 | # ByungYeol Woo , 2019. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ironic-ui VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2019-04-25 10:13+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2019-05-01 10:51+0000\n" 12 | "Last-Translator: ByungYeol Woo \n" 13 | "Language-Team: Korean (South Korea)\n" 14 | "Language: ko_KR\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=1; plural=0\n" 17 | 18 | msgid "Admin" 19 | msgstr "관리자" 20 | 21 | msgid "Ironic Bare Metal Provisioning" 22 | msgstr "Ironic 베어메탈 프로비저닝" 23 | 24 | msgid "Node Details" 25 | msgstr "노드 세부사항" 26 | 27 | msgid "System" 28 | msgstr "시스템" 29 | -------------------------------------------------------------------------------- /ironic_ui/locale/zh_Hans/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # Shengjing Zhu , 2016. #zanata 2 | # Franco Tseng , 2019. #zanata 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ironic-ui VERSION\n" 6 | "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" 7 | "POT-Creation-Date: 2021-01-24 09:54+0000\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "PO-Revision-Date: 2019-03-30 01:32+0000\n" 12 | "Last-Translator: Franco Tseng \n" 13 | "Language-Team: Chinese (China)\n" 14 | "Language: zh_CN\n" 15 | "X-Generator: Zanata 4.3.3\n" 16 | "Plural-Forms: nplurals=1; plural=0\n" 17 | 18 | msgid "Admin" 19 | msgstr "管理员" 20 | 21 | msgid "Ironic Bare Metal Provisioning" 22 | msgstr "裸金属 Ironic 配置向导" 23 | 24 | msgid "Node Details" 25 | msgstr "节点详情" 26 | 27 | msgid "System" 28 | msgstr "系统" 29 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/auto-focus.directive.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function () { 17 | 'use strict'; 18 | 19 | angular 20 | .module('horizon.dashboard.admin.ironic') 21 | .directive('autoFocus', AutoFocus); 22 | 23 | AutoFocus.$inject = ['$timeout']; 24 | 25 | function AutoFocus($timeout) { 26 | return { 27 | restrict: 'AC', 28 | link: function(scope, elem) { 29 | $timeout(function() { 30 | elem[0].focus(); 31 | }, 1000); 32 | } 33 | }; 34 | } 35 | })(); 36 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-node/base-node.service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function() { 17 | 'use strict'; 18 | 19 | angular 20 | .module('horizon.dashboard.admin.ironic') 21 | .factory('horizon.dashboard.admin.ironic.base-node.service', 22 | baseNodeService); 23 | 24 | baseNodeService.$inject = []; 25 | 26 | function baseNodeService() { 27 | var service = { 28 | driverPropertyGroupHasRequired: driverPropertyGroupHasRequired, 29 | driverPropertyGroupsToString: driverPropertyGroupsToString, 30 | compareDriverPropertyGroups: compareDriverPropertyGroups 31 | }; 32 | 33 | /** 34 | * @description Check whether a group contains required properties 35 | * 36 | * @param {DriverProperty[]} group - Property group 37 | * @return {boolean} Return true if the group contains required 38 | * properties, false otherwise 39 | */ 40 | function driverPropertyGroupHasRequired(group) { 41 | var hasRequired = false; 42 | for (var i = 0; i < group.length; i++) { 43 | if (group[i].required) { 44 | hasRequired = true; 45 | break; 46 | } 47 | } 48 | return hasRequired; 49 | } 50 | 51 | /** 52 | * @description Convert array of driver property groups to a string 53 | * 54 | * @param {array[]} groups - Array of driver property groups 55 | * @return {string} Output string 56 | */ 57 | function driverPropertyGroupsToString(groups) { 58 | var output = []; 59 | angular.forEach(groups, function(group) { 60 | var groupStr = []; 61 | angular.forEach(group, function(property) { 62 | groupStr.push(property.name); 63 | }); 64 | groupStr = groupStr.join(", "); 65 | output.push(['[', groupStr, ']'].join("")); 66 | }); 67 | output = output.join(", "); 68 | return ['[', output, ']'].join(""); 69 | } 70 | 71 | /** 72 | * @description Comparison function used to sort driver property groups 73 | * 74 | * @param {DriverProperty[]} group1 - First group 75 | * @param {DriverProperty[]} group2 - Second group 76 | * @return {integer} Return: 77 | * < 0 if group1 should precede group2 in an ascending ordering 78 | * > 0 if group2 should precede group1 79 | * 0 if group1 and group2 are considered equal from ordering perspective 80 | */ 81 | function compareDriverPropertyGroups(group1, group2) { 82 | var group1HasRequired = driverPropertyGroupHasRequired(group1); 83 | var group2HasRequired = driverPropertyGroupHasRequired(group2); 84 | 85 | if (group1HasRequired === group2HasRequired) { 86 | if (group1.length === group2.length) { 87 | return group1[0].name.localeCompare(group2[0].name); 88 | } else { 89 | return group1.length - group2.length; 90 | } 91 | } else { 92 | return group1HasRequired ? -1 : 1; 93 | } 94 | return 0; 95 | } 96 | 97 | return service; 98 | } 99 | })(); 100 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-node/base-node.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2016 Cray Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | (function() { 18 | "use strict"; 19 | 20 | describe( 21 | 'horizon.dashboard.admin.ironic.base-node.service', 22 | function() { 23 | var service, driverPropertyService; 24 | 25 | beforeEach(module('horizon.dashboard.admin.ironic')); 26 | 27 | beforeEach(inject(function($injector) { 28 | service = 29 | $injector.get('horizon.dashboard.admin.ironic.base-node.service'); 30 | 31 | driverPropertyService = 32 | $injector.get('horizon.dashboard.admin.ironic.driver-property.service'); 33 | })); 34 | 35 | it('defines the service', function() { 36 | expect(service).toBeDefined(); 37 | }); 38 | 39 | describe('DriverPropertyGroup', function() { 40 | it('driverPropertyGroupHasRequired', function () { 41 | var dp1 = 42 | new driverPropertyService.DriverProperty("dp-1", " Required.", []); 43 | var dp2 = 44 | new driverPropertyService.DriverProperty("dp-2", " ", []); 45 | 46 | expect(service.driverPropertyGroupHasRequired).toBeDefined(); 47 | expect(service.driverPropertyGroupHasRequired([])).toBe(false); 48 | expect(service.driverPropertyGroupHasRequired([dp1])).toBe(true); 49 | expect(service.driverPropertyGroupHasRequired([dp2])).toBe(false); 50 | expect(service.driverPropertyGroupHasRequired([dp1, dp2])).toBe(true); 51 | }); 52 | 53 | it('driverPropertyGroupsToString', function () { 54 | var dp1 = 55 | new driverPropertyService.DriverProperty("dp-1", " Required.", []); 56 | var dp2 = 57 | new driverPropertyService.DriverProperty("dp-2", " ", []); 58 | 59 | expect(service.driverPropertyGroupsToString).toBeDefined(); 60 | expect(service.driverPropertyGroupsToString([])).toBe("[]"); 61 | expect(service.driverPropertyGroupsToString([[dp1]])) 62 | .toBe("[[dp-1]]"); 63 | expect(service.driverPropertyGroupsToString([[dp1], [dp2]])) 64 | .toBe("[[dp-1], [dp-2]]"); 65 | }); 66 | 67 | it('compareDriverPropertyGroups', function () { 68 | var dp1 = 69 | new driverPropertyService.DriverProperty("dp-1", " Required.", []); 70 | var dp2 = 71 | new driverPropertyService.DriverProperty("dp-2", " ", []); 72 | 73 | expect(service.compareDriverPropertyGroups).toBeDefined(); 74 | expect(service.compareDriverPropertyGroups([dp1], [dp1])).toBe(0); 75 | expect(service.compareDriverPropertyGroups([dp1], [dp2])).toBe(-1); 76 | expect(service.compareDriverPropertyGroups([dp2], [dp1])).toBe(1); 77 | // smaller group precedes larger group 78 | expect(service.compareDriverPropertyGroups([dp1], [dp1, dp2])) 79 | .toBe(-1); 80 | // group order decided on lexographic comparison of names of first 81 | // property 82 | expect(service.compareDriverPropertyGroups([dp2, dp1], [dp1, dp2])) 83 | .toBe(1); 84 | }); 85 | }); 86 | }); 87 | })(); 88 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-port/base-port.html: -------------------------------------------------------------------------------- 1 | 11 | 31 | 32 | 46 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-portgroup/base-portgroup.controller.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function() { 17 | 'use strict'; 18 | 19 | /** 20 | * Controller used to support operations on an Ironic portgroup 21 | */ 22 | angular 23 | .module('horizon.dashboard.admin.ironic') 24 | .controller('BasePortgroupController', BasePortgroupController); 25 | 26 | BasePortgroupController.$inject = [ 27 | '$uibModalInstance', 28 | 'horizon.dashboard.admin.ironic.validMacAddressPattern', 29 | 'horizon.dashboard.admin.ironic.validDatapathIdPattern', 30 | 'horizon.dashboard.admin.ironic.form-field.service', 31 | 'horizon.dashboard.admin.ironic.property-collection.service', 32 | 'ctrl' 33 | ]; 34 | 35 | function BasePortgroupController($uibModalInstance, 36 | validMacAddressPattern, 37 | validDatapathIdPattern, 38 | formFieldService, 39 | propertyCollectionService, 40 | ctrl) { 41 | 42 | ctrl.address = new formFieldService.FormField({ 43 | id: "macAddress", 44 | title: gettext("MAC address"), 45 | desc: gettext("MAC address for this portgroup."), 46 | pattern: new RegExp(validMacAddressPattern), 47 | value: null, 48 | autoFocus: true 49 | }); 50 | 51 | ctrl.name = new formFieldService.FormField({ 52 | id: "portgroupName", 53 | title: gettext("Name"), 54 | desc: gettext("Name for the portgroup.") 55 | }); 56 | 57 | ctrl.standalone_ports_supported = new formFieldService.FormField({ 58 | type: "radio", 59 | id: "standalonePorts", 60 | title: gettext("Standalone Ports Supported"), 61 | desc: gettext( 62 | "Specifies whether ports in this portgroup can be used as standalone ports."), // eslint-disable-line max-len 63 | options: [{label: 'True', value: true}, 64 | {label: 'False', value: false}], 65 | value: true}); 66 | 67 | var modeOptions = function(modes) { 68 | var options = []; 69 | angular.forEach(modes, function(mode) { 70 | options.push({label:mode, value: mode}); 71 | }); 72 | return options; 73 | }; 74 | 75 | ctrl.mode = new formFieldService.FormField({ 76 | type: "radio", 77 | id: "mode", 78 | title: gettext("Mode"), 79 | desc: gettext("Linux portgroup mode. For possible values refer to https://www.kernel.org/doc/Documentation/networking/bonding.txt"), // eslint-disable-line max-len 80 | options: modeOptions(['balance-rr', 81 | 'active-backup', 82 | 'balance-xor', 83 | 'broadcast', 84 | '802.3ad', 85 | 'balance-tlb', 86 | 'balance-alb']), 87 | value: 'active-backup'}); 88 | 89 | ctrl.properties = new propertyCollectionService.PropertyCollection({ 90 | id: 'properties', 91 | title: gettext('Properties'), 92 | addPropertyLabel: gettext('Add Property'), 93 | placeholder: gettext('Property Name') 94 | }); 95 | 96 | ctrl.extra = new propertyCollectionService.PropertyCollection({ 97 | id: 'extra', 98 | title: gettext('Extras'), 99 | addPropertyLabel: gettext('Add Extra'), 100 | placeholder: gettext('Property Name') 101 | }); 102 | 103 | /** 104 | * Cancel the modal 105 | * 106 | * @return {void} 107 | */ 108 | ctrl.cancel = function() { 109 | $uibModalInstance.dismiss('cancel'); 110 | }; 111 | } 112 | })(); 113 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-portgroup/base-portgroup.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function () { 17 | 'use strict'; 18 | 19 | describe('horizon.dashboard.admin.ironic.base-portgroup', function () { 20 | var uibModalInstance; 21 | var ctrl = {}; 22 | 23 | beforeEach(module('horizon.dashboard.admin.ironic')); 24 | 25 | beforeEach(module(function($provide) { 26 | $provide.value('$uibModal', {}); 27 | })); 28 | 29 | beforeEach(module(function($provide) { 30 | uibModalInstance = { 31 | dismiss: jasmine.createSpy() 32 | }; 33 | $provide.value('$uibModalInstance', uibModalInstance); 34 | })); 35 | 36 | beforeEach(inject(function($injector) { 37 | var controller = $injector.get('$controller'); 38 | controller('BasePortgroupController', {ctrl: ctrl}); 39 | })); 40 | 41 | it('controller should be defined', function () { 42 | expect(ctrl).toBeDefined(); 43 | }); 44 | 45 | it('base construction', function () { 46 | expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual( 47 | BASE_PORTGROUP_CONTROLLER_PROPERTIES.sort()); 48 | 49 | angular.forEach( 50 | ['address', 'name', 'standalone_ports_supported', 'mode'], 51 | function(propertyName) { 52 | expect(Object.keys(ctrl[propertyName])).toContain('value'); 53 | }); 54 | }); 55 | 56 | it('cancel', function () { 57 | ctrl.cancel(); 58 | expect(uibModalInstance.dismiss).toHaveBeenCalledWith('cancel'); 59 | }); 60 | }); 61 | })(); 62 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/base-portgroup/base-portgroup.html: -------------------------------------------------------------------------------- 1 | 11 | 23 | 24 | 38 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/bootdevice/bootdevice.controller.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function() { 17 | 'use strict'; 18 | 19 | /** 20 | * @ngdoc controller 21 | * @name horizon.dashboard.admin.ironic:BootDeviceController 22 | * @ngController 23 | * 24 | * @description 25 | * Controller used to prompt the user for information associated with 26 | * setting the boot device of a node 27 | */ 28 | angular 29 | .module('horizon.dashboard.admin.ironic') 30 | .controller('BootDeviceController', BootDeviceController); 31 | 32 | BootDeviceController.$inject = [ 33 | '$uibModalInstance', 34 | 'horizon.app.core.openstack-service-api.ironic', 35 | 'node' 36 | ]; 37 | 38 | function BootDeviceController($uibModalInstance, ironic, node) { 39 | var ctrl = this; 40 | 41 | ctrl.modalTitle = gettext("Set Boot Device"); 42 | 43 | ironic.getSupportedBootDevices(node.uuid).then( 44 | function(bootDevices) { 45 | ctrl.supportedBootDevices = bootDevices; 46 | }); 47 | 48 | // Initialize form fields to current values 49 | ctrl.bootDevice = null; 50 | ctrl.persistent = 'False'; 51 | ironic.getBootDevice(node.uuid).then(function(device) { 52 | ctrl.bootDevice = device.boot_device; 53 | ctrl.persistent = device.persistent ? 'True' : 'False'; 54 | }); 55 | 56 | ctrl.cancel = function() { 57 | $uibModalInstance.dismiss('cancel'); 58 | }; 59 | 60 | ctrl.setSelectedBootDevice = function() { 61 | $uibModalInstance.close({device: ctrl.bootDevice, 62 | persistent: ctrl.persistent === 'True'}); 63 | }; 64 | } 65 | })(); 66 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/bootdevice/bootdevice.controller.spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function () { 17 | 'use strict'; 18 | 19 | describe('horizon.dashboard.admin.ironic.BootDeviceController', function () { 20 | var BOOT_DEVICE_CONTROLLER_PROPERTIES = [ 21 | 'bootDevice', 22 | 'cancel', 23 | 'modalTitle', 24 | 'persistent', 25 | 'setSelectedBootDevice', 26 | 'supportedBootDevices' 27 | ]; 28 | var uibModalInstance, ironicBackendMockService, node; 29 | var ctrl = {}; 30 | 31 | beforeEach(module('horizon.dashboard.admin.ironic')); 32 | 33 | beforeEach(module('horizon.framework.util')); 34 | 35 | beforeEach(module(function($provide) { 36 | $provide.value('$uibModal', {}); 37 | })); 38 | 39 | beforeEach(module(function($provide) { 40 | uibModalInstance = { 41 | close: jasmine.createSpy(), 42 | dismiss: jasmine.createSpy() 43 | }; 44 | $provide.value('$uibModalInstance', uibModalInstance); 45 | })); 46 | 47 | beforeEach(module(function($provide) { 48 | $provide.value('horizon.framework.widgets.toast.service', 49 | {}); 50 | })); 51 | 52 | beforeEach(module('horizon.app.core.openstack-service-api')); 53 | 54 | beforeEach(inject(function($injector) { 55 | ironicBackendMockService = 56 | $injector.get('horizon.dashboard.admin.ironic.backend-mock.service'); 57 | ironicBackendMockService.init(); 58 | 59 | var ironicAPI = 60 | $injector.get('horizon.app.core.openstack-service-api.ironic'); 61 | 62 | ironicAPI.createNode( 63 | {driver: ironicBackendMockService.params.defaultDriver}) 64 | .then(function(response) { 65 | node = response.data; 66 | var controller = $injector.get('$controller'); 67 | ctrl = controller('BootDeviceController', {node: node}); 68 | }); 69 | ironicBackendMockService.flush(); 70 | })); 71 | 72 | it('controller should be defined', function () { 73 | expect(ctrl).toBeDefined(); 74 | expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual( 75 | BOOT_DEVICE_CONTROLLER_PROPERTIES.sort()); 76 | expect(ctrl.supportedBootDevices).toEqual( 77 | ironicBackendMockService.getNodeSupportedBootDevices(node.uuid)); 78 | var bootDevice = ironicBackendMockService.getNodeBootDevice(node.uuid); 79 | expect(ctrl.bootDevice).toEqual(bootDevice.boot_device); 80 | expect(ctrl.persistent).toEqual(bootDevice.persistent ? 'True' : 'False'); 81 | }); 82 | 83 | it('cancel', function () { 84 | ctrl.cancel(); 85 | expect(uibModalInstance.dismiss).toHaveBeenCalled(); 86 | }); 87 | 88 | it('setSelectedBootDevice', function () { 89 | ctrl.bootDevice = 'pxe'; 90 | ctrl.persistent = 'False'; 91 | ctrl.setSelectedBootDevice(); 92 | expect(uibModalInstance.close).toHaveBeenCalledWith( 93 | {device: ctrl.bootDevice, 94 | persistent: ctrl.persistent === 'True'}); 95 | }); 96 | }); 97 | })(); 98 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/bootdevice/bootdevice.html: -------------------------------------------------------------------------------- 1 | 11 | 43 | 44 | 59 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/bootdevice/bootdevice.service.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function() { 17 | 'use strict'; 18 | 19 | /* 20 | * @ngdoc service 21 | * @name horizon.dashboard.admin.ironic.bootdevice.service 22 | * @description Service for setting the boot device of a node 23 | */ 24 | angular 25 | .module('horizon.dashboard.admin.ironic') 26 | .factory('horizon.dashboard.admin.ironic.bootdevice.service', 27 | bootDeviceService); 28 | 29 | bootDeviceService.$inject = [ 30 | '$uibModal', 31 | 'horizon.dashboard.admin.ironic.basePath', 32 | 'horizon.app.core.openstack-service-api.ironic' 33 | ]; 34 | 35 | function bootDeviceService($uibModal, basePath, ironic) { 36 | var service = { 37 | setBootDevice: setBootDevice 38 | }; 39 | return service; 40 | 41 | /* 42 | * @description Set the boot device of a specified node 43 | * 44 | * @param {object} node - node object 45 | * @return {promise} 46 | */ 47 | function setBootDevice(node) { 48 | var promise; 49 | var options = { 50 | controller: "BootDeviceController as ctrl", 51 | backdrop: 'static', 52 | resolve: { 53 | node: function() { 54 | return node; 55 | } 56 | }, 57 | templateUrl: basePath + '/bootdevice/bootdevice.html' 58 | }; 59 | promise = $uibModal.open(options).result.then( 60 | function(result) { 61 | return ironic.nodeSetBootDevice(node.uuid, 62 | result.device, 63 | result.persistent); 64 | }); 65 | return promise; 66 | } 67 | } 68 | })(); 69 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.controller.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Cray Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | (function() { 17 | 'use strict'; 18 | 19 | /** 20 | * @ngdoc controller 21 | * @name horizon.dashboard.admin.ironic:CleanNodeController 22 | * @ngController 23 | * 24 | * @description 25 | * Controller used to prompt the user for a list of clean-steps 26 | * in JSON format that will be applied a node 27 | */ 28 | angular 29 | .module('horizon.dashboard.admin.ironic') 30 | .controller('CleanNodeController', CleanNodeController); 31 | 32 | CleanNodeController.$inject = [ 33 | '$uibModalInstance' 34 | ]; 35 | 36 | function CleanNodeController($uibModalInstance) { 37 | var ctrl = this; 38 | 39 | ctrl.errMsg = ''; 40 | 41 | ctrl.cancel = function() { 42 | $uibModalInstance.dismiss('cancel'); 43 | }; 44 | 45 | ctrl.clean = function(cleanSteps) { 46 | try { 47 | var steps = JSON.parse(cleanSteps); 48 | if (angular.isArray(steps) && steps.length > 0) { 49 | var valid = true; 50 | angular.forEach(steps, function(step) { 51 | if (angular.isUndefined(step.interface) || 52 | angular.isUndefined(step.step)) { 53 | valid = false; 54 | } 55 | }); 56 | if (valid) { 57 | $uibModalInstance.close(steps); 58 | } else { 59 | ctrl.errMsg = gettext('Each cleaning step must be an object that contains "interface" and "step" properties'); // eslint-disable-line max-len 60 | } 61 | } else { 62 | ctrl.errMsg = gettext('Clean steps should be an non-empty array'); 63 | } 64 | } catch (e) { 65 | ctrl.errMsg = gettext('Unable to validate the JSON input'); 66 | } 67 | }; 68 | } 69 | })(); 70 | -------------------------------------------------------------------------------- /ironic_ui/static/dashboard/admin/ironic/clean-node/clean-node.html: -------------------------------------------------------------------------------- 1 | 4 |