Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I6319cb6aa2b8c5847ede4372e222b02a23b02ee3
This commit is contained in:
parent
4315f1e64a
commit
89490553d0
|
@ -1,6 +0,0 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = ironic-ui
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
|
@ -1,2 +0,0 @@
|
|||
node_modules
|
||||
dist
|
23
.eslintrc
23
.eslintrc
|
@ -1,23 +0,0 @@
|
|||
# Enable eslint-plugin-angular
|
||||
plugins:
|
||||
- angular
|
||||
|
||||
extends: openstack
|
||||
|
||||
# Set up globals
|
||||
globals:
|
||||
angular: false
|
||||
module: false
|
||||
|
||||
env:
|
||||
browser: true
|
||||
jasmine: true
|
||||
|
||||
rules:
|
||||
angular/no-private-call: 0
|
||||
angular/no-services:
|
||||
- 2
|
||||
- - $http
|
||||
- $resource
|
||||
- Restangular
|
||||
angular/no-service-method: 0
|
|
@ -1,66 +0,0 @@
|
|||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
cover
|
||||
.eggs
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.venv
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.*sw?
|
||||
|
||||
.secret_key_store
|
||||
*.lock
|
||||
|
||||
# Node generated files
|
||||
package/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
|
||||
# release notes build
|
||||
releasenotes/build
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/ironic-ui.git
|
3
.mailmap
3
.mailmap
|
@ -1,3 +0,0 @@
|
|||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
|
@ -1,7 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -1,17 +0,0 @@
|
|||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/ironic-ui
|
|
@ -1,4 +0,0 @@
|
|||
ironic-ui Style Commandments
|
||||
===============================================
|
||||
|
||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
176
LICENSE
176
LICENSE
|
@ -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.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
include setup.py
|
||||
|
||||
recursive-include ironic_ui *.js *.html *.scss
|
||||
|
||||
global-exclude *.pyc
|
|
@ -0,0 +1,14 @@
|
|||
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 ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
27
README.rst
27
README.rst
|
@ -1,27 +0,0 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/ironic-ui.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
=========
|
||||
Ironic UI
|
||||
=========
|
||||
|
||||
The Ironic UI is a Horizon plugin that will allow users to view and manage bare
|
||||
metal nodes, ports and drivers.
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: http://docs.openstack.org/ironic-ui/latest
|
||||
* Source: https://git.openstack.org/cgit/openstack/ironic-ui
|
||||
* Bugs: http://bugs.launchpad.net/ironic-ui
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* View bare metal nodes
|
||||
* View node details
|
||||
* Apply maintenance and power on/off actions to the nodes
|
|
@ -1,6 +0,0 @@
|
|||
[extractors]
|
||||
django = django_babel.extract:extract_django
|
||||
|
||||
[python: **.py]
|
||||
[django: templates/**.html]
|
||||
[django: **/templates/**.csv]
|
|
@ -1,14 +0,0 @@
|
|||
[extractors]
|
||||
# We use a custom extractor to find translatable strings in AngularJS
|
||||
# templates. The extractor is included in horizon.utils for now.
|
||||
# See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for
|
||||
# details on how this works.
|
||||
angular = horizon.utils.babel_extract_angular:extract_angular
|
||||
|
||||
[javascript: **.js]
|
||||
|
||||
# We need to look into all static folders for HTML files.
|
||||
# The **/static ensures that we also search within
|
||||
# /openstack_dashboard/dashboards/XYZ/static which will ensure
|
||||
# that plugins are also translated.
|
||||
[angular: **/static/**.html]
|
|
@ -1,46 +0,0 @@
|
|||
# plugin.sh - DevStack plugin.sh dispatch script ironic-ui
|
||||
|
||||
function install_ironic_ui {
|
||||
# NOTE(crushil): workaround for devstack bug: 1540328
|
||||
# where devstack installs 'test-requirements' but should not do it
|
||||
# for ironic-ui project as it installs Horizon from url.
|
||||
# Remove following two 'mv' commands when mentioned bug is fixed.
|
||||
mv $IRONIC_UI_DIR/test-requirements.txt $IRONIC_UI_DIR/_test-requirements.txt
|
||||
|
||||
setup_develop ${IRONIC_UI_DIR}
|
||||
|
||||
mv $IRONIC_UI_DIR/_test-requirements.txt $IRONIC_UI_DIR/test-requirements.txt
|
||||
}
|
||||
|
||||
# check for service enabled
|
||||
if is_service_enabled horizon && is_service_enabled ironic && is_service_enabled ironic-ui; then
|
||||
|
||||
if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
|
||||
# Set up system services
|
||||
# no-op
|
||||
:
|
||||
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
# Perform installation of service source
|
||||
echo_summary "Installing Ironic UI"
|
||||
install_ironic_ui
|
||||
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
# Configure after the other layer 1 and 2 services have been configured
|
||||
echo_summary "Configuring Ironic UI"
|
||||
cp -a ${IRONIC_UI_DIR}/ironic_ui/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "unstack" ]]; then
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "clean" ]]; then
|
||||
# Remove state and transient data
|
||||
# Remember clean.sh first calls unstack.sh
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
fi
|
|
@ -1,5 +0,0 @@
|
|||
# settings file for ironic-ui plugin
|
||||
enable_service ironic-ui
|
||||
|
||||
# set up default directories
|
||||
IRONIC_UI_DIR=${IRONIC_UI_DIR:=$DEST/ironic-ui}
|
|
@ -1,81 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'ironic-ui'
|
||||
copyright = u'2016, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/ironic-ui'
|
||||
bug_project = 'ironic-ui'
|
||||
bug_tag = ''
|
||||
|
||||
# Must set this variable to include year, month, day, hours, and minutes.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -1,45 +0,0 @@
|
|||
=================
|
||||
How to Contribute
|
||||
=================
|
||||
|
||||
Contributor License Agreement
|
||||
-----------------------------
|
||||
|
||||
.. index::
|
||||
single: license; agreement
|
||||
|
||||
In order to contribute to the Ironic UI project, you need to have
|
||||
signed OpenStack's contributor's agreement.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* http://docs.openstack.org/infra/manual/developers.html
|
||||
* http://wiki.openstack.org/CLA
|
||||
|
||||
LaunchPad Project
|
||||
-----------------
|
||||
|
||||
Most of the tools used for OpenStack depend on a launchpad.net ID for
|
||||
authentication.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* https://launchpad.net
|
||||
* https://launchpad.net/ironic-ui
|
||||
|
||||
Project Hosting Details
|
||||
-------------------------
|
||||
|
||||
Bug tracker
|
||||
http://launchpad.net/ironic-ui
|
||||
|
||||
Mailing list (prefix subjects with ``[ironic-ui]`` for faster responses)
|
||||
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
|
||||
|
||||
Code Hosting
|
||||
https://git.openstack.org/cgit/openstack/ironic-ui
|
||||
|
||||
Code Review
|
||||
https://review.openstack.org/#/q/status:open+project:openstack/ironic-ui,n,z
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
=========================
|
||||
Contributing to Ironic UI
|
||||
=========================
|
||||
|
||||
If you're interested in contributing to the Ironic UI project,
|
||||
the following will help get you started.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
contributing
|
|
@ -1,30 +0,0 @@
|
|||
=====================================
|
||||
Welcome to Ironic UI's documentation!
|
||||
=====================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The ironic UI is an OpenStack Horizon plugin that will allow users to view and
|
||||
manage their ironic bare metal nodes, ports and drivers.
|
||||
|
||||
The documentation provided here is continually kept up-to-date based
|
||||
on the latest code that has been committed, and may not represent the state of
|
||||
the project at any specific prior release.
|
||||
|
||||
For information on any current or prior version of Ironic, see `the release
|
||||
notes`_.
|
||||
|
||||
.. _the release notes: http://docs.openstack.org/releasenotes/ironic-ui/
|
||||
|
||||
For more information on ironic, see `the ironic documentation`_.
|
||||
|
||||
.. _the ironic documentation: http://docs.openstack.org/developer/ironic/
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Introduction to ironic <http://docs.openstack.org/developer/ironic/deploy/user-guide.html>
|
||||
Installing the ironic UI <install/index>
|
||||
Contributing <contributor/index>
|
||||
Release notes <http://docs.openstack.org/releasenotes/ironic-ui>
|
|
@ -1,9 +0,0 @@
|
|||
============================
|
||||
ironic-ui installation guide
|
||||
============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
installation
|
||||
uninstallation
|
|
@ -1,68 +0,0 @@
|
|||
.. _installation:
|
||||
|
||||
Ironic-UI Installation
|
||||
======================
|
||||
|
||||
Please note that the following instructions assume that you have an existing
|
||||
installation of the OpenStack Horizon dashboard application. For Horizon
|
||||
installation please see http://docs.openstack.org/developer/horizon/quickstart.html
|
||||
|
||||
1. Clone the Ironic UI repository::
|
||||
|
||||
git clone https://git.openstack.org/openstack/ironic-ui
|
||||
|
||||
2. Change into the root directory of your horizon installation and
|
||||
activate the python virtualenv. Example::
|
||||
|
||||
source .venv/bin/activate
|
||||
|
||||
.. NOTE:: The ``.venv`` folder is pre-installed when horizon is setup with
|
||||
``./run_tests.sh``. Do not attempt to reinstall the virtual
|
||||
environment.
|
||||
|
||||
3. Copy the ``_2200_ironic.py`` file from ``ironic_ui/enabled/_2200_ironic.py``
|
||||
file to ``horizon/openstack_dashboard/local/enabled`` directory. Example,
|
||||
set as if being executed from the root of the ironic-ui repository::
|
||||
|
||||
cp ./ironic_ui/enabled/_2200_ironic.py ../horizon/openstack_dashboard/local/enabled
|
||||
|
||||
4. Change into the ironic-ui repository and package the plugin::
|
||||
|
||||
pip install -r requirements.txt -e .
|
||||
|
||||
This will build and install the ironic-ui plugin into the active virtual
|
||||
environment associated with your horizon installation. The plugin is installed
|
||||
in "editable" mode as a link back to your ironic-ui plugin directory.
|
||||
|
||||
5. Change back into the horizon repository and bring up your environment::
|
||||
|
||||
./run_tests.sh --runserver
|
||||
|
||||
The Bare Metal service should now be visible in the Horizon navigation.
|
||||
|
||||
Ironic-UI Installation with DevStack
|
||||
------------------------------------
|
||||
|
||||
In order to use the Ironic UI with devstack, you will need to enable
|
||||
the UI plugin separately in your installation local.conf file.
|
||||
|
||||
This is done in a similar fashion to enabling Ironic for devstack.
|
||||
|
||||
Make sure you have horizon enabled, which is the default in devstack.
|
||||
|
||||
Then, enable the Ironic UI plugin appending the following line to the end of the local.conf file,
|
||||
just after Ironic plugin enablement:
|
||||
|
||||
enable_plugin ironic-ui https://github.com/openstack/ironic-ui
|
||||
|
||||
After this, you can run ./stack.sh from the devstack directory.
|
||||
|
||||
The Ironic Bare Metal Provisioning plugin should now be visible in the Horizon
|
||||
navigation.
|
||||
|
||||
6. Run JavaScript unit tests by either:
|
||||
|
||||
Running the tests locally with npm run test.
|
||||
|
||||
Visiting http://localhost:8000/jasmine/?spec=horizon.dashboard.admin.ironic in your
|
||||
browser.
|
|
@ -1,9 +0,0 @@
|
|||
.. _unstallation:
|
||||
|
||||
Uninstallation
|
||||
==============
|
||||
|
||||
To uninstall, use ``pip uninstall ironic-ui`` from with-in the horizon
|
||||
virtual environment. You will also need to remove the
|
||||
``openstack_dashboard/enabled/_2200_ironic.py`` file from the horizon
|
||||
installation.
|
|
@ -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.
|
||||
|
||||
import pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'ironic-ui').version_string()
|
|
@ -1,371 +0,0 @@
|
|||
#
|
||||
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright 2016 Cray Inc.
|
||||
# Copyright 2017 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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 ironicclient import client
|
||||
from ironicclient.v1 import resource_fields as res_fields
|
||||
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
|
||||
DEFAULT_IRONIC_API_VERSION = '1.27'
|
||||
DEFAULT_INSECURE = False
|
||||
DEFAULT_CACERT = None
|
||||
IRONIC_CLIENT_CLASS_NAME = 'baremetal'
|
||||
|
||||
|
||||
@memoized
|
||||
def ironicclient(request):
|
||||
"""Returns a client connected to the Ironic backend.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: Ironic client.
|
||||
"""
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', DEFAULT_INSECURE)
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', DEFAULT_CACERT)
|
||||
ironic_url = base.url_for(request, IRONIC_CLIENT_CLASS_NAME)
|
||||
|
||||
return client.Client(1,
|
||||
ironic_url,
|
||||
os_ironic_api_version=DEFAULT_IRONIC_API_VERSION,
|
||||
project_id=request.user.project_id,
|
||||
token=request.user.token.id,
|
||||
insecure=insecure,
|
||||
cacert=cacert)
|
||||
|
||||
|
||||
def node_list(request):
|
||||
"""Retrieve a list of nodes.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: A list of nodes.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.list
|
||||
"""
|
||||
node_manager = ironicclient(request).node
|
||||
return node_manager.list(detail=True, limit=0)
|
||||
|
||||
|
||||
def node_get(request, node_id):
|
||||
"""Retrieve a node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get
|
||||
"""
|
||||
return ironicclient(request).node.get(node_id)
|
||||
|
||||
|
||||
def node_list_ports(request, node_id):
|
||||
"""List all the ports on a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: A full list of ports. (limit=0)
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.list_ports
|
||||
"""
|
||||
return ironicclient(request).node.list_ports(node_id, limit=0, detail=True)
|
||||
|
||||
|
||||
def node_set_power_state(request, node_id, state, soft=False):
|
||||
"""Set power state for a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param state: the power state to set ['on', 'off', 'reboot'].
|
||||
:param soft: flag for graceful power 'off' or reboot
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_power_state
|
||||
"""
|
||||
return ironicclient(request).node.set_power_state(node_id,
|
||||
state,
|
||||
soft)
|
||||
|
||||
|
||||
def node_set_provision_state(request, node_id, state, cleansteps=None):
|
||||
"""Set the target provision state for a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param state: the target provision state to set.
|
||||
:param cleansteps: Optional list of cleaning steps
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_provision_state
|
||||
"""
|
||||
node_manager = ironicclient(request).node
|
||||
return node_manager.set_provision_state(node_id,
|
||||
state,
|
||||
cleansteps=cleansteps)
|
||||
|
||||
|
||||
def node_set_console_mode(request, node_id, enabled):
|
||||
"""Start or stop the serial console for a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param enabled: True to start the console, False to stop it
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_console_mode
|
||||
"""
|
||||
return ironicclient(request).node.set_console_mode(node_id, enabled)
|
||||
|
||||
|
||||
def node_get_console(request, node_id):
|
||||
"""Get connection information for a node's console.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: Console connection information
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_console
|
||||
"""
|
||||
return ironicclient(request).node.get_console(node_id)
|
||||
|
||||
|
||||
def node_set_maintenance(request, node_id, state, maint_reason=None):
|
||||
"""Set the maintenance mode on a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param state: The maintenance state to set.
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_maintenance
|
||||
"""
|
||||
return ironicclient(request).node.set_maintenance(
|
||||
node_id,
|
||||
state,
|
||||
maint_reason=maint_reason)
|
||||
|
||||
|
||||
def node_create(request, params):
|
||||
"""Create a node
|
||||
|
||||
:param request: HTTP request.
|
||||
:param params: Dictionary of node parameters
|
||||
"""
|
||||
node_manager = ironicclient(request).node
|
||||
node = node_manager.create(**params)
|
||||
return dict([(f, getattr(node, f, ''))
|
||||
for f in res_fields.NODE_DETAILED_RESOURCE.fields])
|
||||
|
||||
|
||||
def node_delete(request, node_id):
|
||||
"""Delete a node from inventory.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.delete
|
||||
"""
|
||||
return ironicclient(request).node.delete(node_id)
|
||||
|
||||
|
||||
def node_update(request, node_id, patch):
|
||||
"""Update a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param patch: Sequence of update operations
|
||||
:return: node.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.update
|
||||
"""
|
||||
node = ironicclient(request).node.update(node_id, patch)
|
||||
return dict([(f, getattr(node, f, ''))
|
||||
for f in res_fields.NODE_DETAILED_RESOURCE.fields])
|
||||
|
||||
|
||||
def node_validate(request, node_id):
|
||||
"""Validate a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: List of dictionaries, each containing an interface status
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.validate
|
||||
"""
|
||||
ifaces = ironicclient(request).node.validate(node_id)
|
||||
result = []
|
||||
for interface, status in ifaces.to_dict().items():
|
||||
data = {'interface': interface}
|
||||
data.update(status)
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
|
||||
def node_get_boot_device(request, node_id):
|
||||
"""Get the boot device for a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: Dictionary with keys "boot_device" and "persistent"
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_boot_device
|
||||
"""
|
||||
return ironicclient(request).node.get_boot_device(node_id)
|
||||
|
||||
|
||||
def node_set_boot_device(request, node_id, device, persistent):
|
||||
"""Set the boot device for a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:param device: boot device.
|
||||
:param persistent: True or False.
|
||||
:return: null.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.set_boot_device
|
||||
"""
|
||||
return ironicclient(request).node.set_boot_device(node_id,
|
||||
device,
|
||||
persistent)
|
||||
|
||||
|
||||
def node_get_supported_boot_devices(request, node_id):
|
||||
"""Get the list of supported boot devices for a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: List of supported boot devices (strings)
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.node.html#ironicclient.v1.node.NodeManager.get_boot_device
|
||||
"""
|
||||
result = ironicclient(request).node.get_supported_boot_devices(node_id)
|
||||
return result.get('supported_boot_devices', [])
|
||||
|
||||
|
||||
def driver_list(request):
|
||||
"""Retrieve a list of drivers.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: A list of drivers.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.driver.html#ironicclient.v1.driver.DriverManager.list
|
||||
"""
|
||||
return ironicclient(request).driver.list()
|
||||
|
||||
|
||||
def driver_properties(request, driver_name):
|
||||
"""Retrieve the properties of a specified driver
|
||||
|
||||
:param request: HTTP request
|
||||
:param driver_name: Name of the driver
|
||||
:return: Property list
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.driver.html#ironicclient.v1.driver.DriverManager.properties
|
||||
"""
|
||||
return ironicclient(request).driver.properties(driver_name)
|
||||
|
||||
|
||||
def port_create(request, params):
|
||||
"""Create network port
|
||||
|
||||
:param request: HTTP request
|
||||
:param params: Port creation parameters
|
||||
:return: Port
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.port.html#ironicclient.v1.port.PortManager.create
|
||||
"""
|
||||
port_manager = ironicclient(request).port
|
||||
return port_manager.create(**params)
|
||||
|
||||
|
||||
def port_delete(request, port_uuid):
|
||||
"""Delete a network port
|
||||
|
||||
:param request: HTTP request
|
||||
:param port_uuid: Port uuid
|
||||
:return: Port
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.port.html#ironicclient.v1.port.PortManager.delete
|
||||
"""
|
||||
return ironicclient(request).port.delete(port_uuid)
|
||||
|
||||
|
||||
def port_update(request, port_uuid, patch):
|
||||
"""Update a specified port.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param port_id: The UUID of the port.
|
||||
:param patch: Sequence of update operations
|
||||
:return: Port.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.port.html#ironicclient.v1.port.PortManager.update
|
||||
"""
|
||||
port = ironicclient(request).port.update(port_uuid, patch)
|
||||
return dict([(f, getattr(port, f, ''))
|
||||
for f in res_fields.PORT_DETAILED_RESOURCE.fields])
|
||||
|
||||
|
||||
def portgroup_list(request, node_id):
|
||||
"""List the portgroups associated with a given node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: The UUID or name of the node.
|
||||
:return: A full list of portgroups. (limit=0)
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.list_portgroups
|
||||
"""
|
||||
return ironicclient(request).portgroup.list(node_id, limit=0, detail=True)
|
||||
|
||||
|
||||
def portgroup_create(request, params):
|
||||
"""Create a portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param params: Portgroup creation parameters.
|
||||
:return: Portgroup.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.create
|
||||
"""
|
||||
portgroup_manager = ironicclient(request).portgroup
|
||||
return portgroup_manager.create(**params)
|
||||
|
||||
|
||||
def portgroup_delete(request, portgroup_id):
|
||||
"""Delete a portgroup from the DB.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param portgroup_id: The UUID or name of the portgroup.
|
||||
:return: Portgroup.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.delete
|
||||
"""
|
||||
return ironicclient(request).portgroup.delete(portgroup_id)
|
||||
|
||||
|
||||
def portgroup_get_ports(request, portgroup_id):
|
||||
"""Get the ports associated with a specified portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param portgroup_id: The UUID or name of the portgroup.
|
||||
:return: List of ports.
|
||||
|
||||
http://docs.openstack.org/developer/python-ironicclient/api/ironicclient.v1.portgroup.html#ironicclient.v1.portgroup.PortgroupManager.list_ports
|
||||
"""
|
||||
return ironicclient(request).portgroup.list_ports(portgroup_id)
|
|
@ -1,401 +0,0 @@
|
|||
#
|
||||
# Copyright 2015, 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright 2016 Cray Inc.
|
||||
# Copyright 2017 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT 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.views import generic
|
||||
|
||||
from ironic_ui.api import ironic
|
||||
|
||||
from openstack_dashboard.api.rest import urls
|
||||
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
|
||||
LOGICAL_NAME_PATTERN = '[a-zA-Z0-9-._~]+'
|
||||
|
||||
|
||||
@urls.register
|
||||
class Nodes(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get the list of nodes.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: nodes.
|
||||
"""
|
||||
nodes = ironic.node_list(request)
|
||||
return {
|
||||
'nodes': [i.to_dict() for i in nodes]
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create an Ironic node
|
||||
|
||||
:param request: HTTP request
|
||||
"""
|
||||
params = request.DATA.get('node')
|
||||
return ironic.node_create(request, params)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
"""Delete an Ironic node from inventory
|
||||
|
||||
:param request: HTTP request
|
||||
"""
|
||||
params = request.DATA.get('node')
|
||||
return ironic.node_delete(request, params)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Node(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})$'.format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_id):
|
||||
"""Get information on a specific node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node id.
|
||||
:return: node.
|
||||
"""
|
||||
return ironic.node_get(request, node_id).to_dict()
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def patch(self, request, node_id):
|
||||
"""Update an Ironic node
|
||||
|
||||
:param request: HTTP request
|
||||
:param node_uuid: Node uuid.
|
||||
"""
|
||||
patch = request.DATA.get('patch')
|
||||
return ironic.node_update(request, node_id, patch)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Ports(generic.View):
|
||||
|
||||
url_regex = r'ironic/ports/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get the list of ports associated with a specified node.
|
||||
|
||||
:param request: HTTP request
|
||||
:return: List of ports.
|
||||
"""
|
||||
node_id = request.GET.get('node_id')
|
||||
ports = ironic.node_list_ports(request, node_id)
|
||||
return {
|
||||
'ports': [i.to_dict() for i in ports]
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a network port
|
||||
|
||||
:param request: HTTP request
|
||||
:return: Port
|
||||
"""
|
||||
port = request.DATA.get('port')
|
||||
return ironic.port_create(request, port).to_dict()
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
"""Delete a network port
|
||||
|
||||
:param request: HTTP request
|
||||
"""
|
||||
params = request.DATA.get('port_uuid')
|
||||
return ironic.port_delete(request, params)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Port(generic.View):
|
||||
|
||||
url_regex = r'ironic/ports/(?P<port_id>[0-9a-f-]+)$'
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def patch(self, request, port_id):
|
||||
"""Update an Ironic port
|
||||
|
||||
:param request: HTTP request
|
||||
:param port_id: Port id.
|
||||
"""
|
||||
patch = request.DATA.get('patch')
|
||||
return ironic.port_update(request, port_id, patch)
|
||||
|
||||
|
||||
@urls.register
|
||||
class StatesPower(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})/states/power$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def patch(self, request, node_id):
|
||||
"""Set the power state for a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: Return code
|
||||
"""
|
||||
return ironic.node_set_power_state(request,
|
||||
node_id,
|
||||
request.DATA.get('state'),
|
||||
request.DATA.get('soft'))
|
||||
|
||||
|
||||
@urls.register
|
||||
class StatesProvision(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_uuid>{})/states/provision$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, node_uuid):
|
||||
"""Set the provision state for a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node uuid
|
||||
:return: Return code
|
||||
"""
|
||||
verb = request.DATA.get('verb')
|
||||
clean_steps = request.DATA.get('clean_steps')
|
||||
return ironic.node_set_provision_state(request,
|
||||
node_uuid,
|
||||
verb,
|
||||
clean_steps)
|
||||
|
||||
|
||||
@urls.register
|
||||
class StatesConsole(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_uuid>{})/states/console$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_uuid):
|
||||
"""Get connection information for the node's console
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node uuid
|
||||
:return: Connection information
|
||||
"""
|
||||
return ironic.node_get_console(request, node_uuid)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, node_uuid):
|
||||
"""Start or stop the serial console.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node uuid
|
||||
:return: Return code
|
||||
"""
|
||||
return ironic.node_set_console_mode(request,
|
||||
node_uuid,
|
||||
request.DATA.get('enabled'))
|
||||
|
||||
|
||||
@urls.register
|
||||
class Maintenance(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})/maintenance$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def patch(self, request, node_id):
|
||||
"""Put a specified node into maintenance state
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: Return code
|
||||
"""
|
||||
maint_reason = request.DATA.get('maint_reason')
|
||||
return ironic.node_set_maintenance(
|
||||
request,
|
||||
node_id,
|
||||
'on',
|
||||
maint_reason=maint_reason)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, node_id):
|
||||
"""Take a specified node out of the maintenance state
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: Return code
|
||||
"""
|
||||
return ironic.node_set_maintenance(request, node_id, 'off')
|
||||
|
||||
|
||||
@urls.register
|
||||
class Validate(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})/validate$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_id):
|
||||
"""Validate a specified node
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: List of dictionaries of interface statuses
|
||||
"""
|
||||
return ironic.node_validate(request, node_id)
|
||||
|
||||
|
||||
@urls.register
|
||||
class BootDevice(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})/boot_device$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_id):
|
||||
"""Get the boot device for a specified node
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: Dictionary with keys "boot_device" and "persistent"
|
||||
"""
|
||||
return ironic.node_get_boot_device(request, node_id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, node_id):
|
||||
"""Set the boot device for a specific node
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: null.
|
||||
"""
|
||||
return ironic.node_set_boot_device(
|
||||
request,
|
||||
node_id,
|
||||
request.DATA.get('boot_device'),
|
||||
persistent=request.DATA.get('persistent'))
|
||||
|
||||
|
||||
@urls.register
|
||||
class SupportedBootDevices(generic.View):
|
||||
|
||||
url_regex = r'ironic/nodes/(?P<node_id>{})/boot_device/supported$' . \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_id):
|
||||
"""Get the list of supported boot devices for a specified node
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: Node name or uuid
|
||||
:return: List of supported boot devices
|
||||
"""
|
||||
return ironic.node_get_supported_boot_devices(request, node_id)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Drivers(generic.View):
|
||||
|
||||
url_regex = r'ironic/drivers/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get the list of drivers
|
||||
|
||||
:param request: HTTP request
|
||||
:return: drivers
|
||||
"""
|
||||
drivers = ironic.driver_list(request)
|
||||
return {
|
||||
'drivers': [i.to_dict() for i in drivers]
|
||||
}
|
||||
|
||||
|
||||
@urls.register
|
||||
class DriverProperties(generic.View):
|
||||
|
||||
url_regex = r'ironic/drivers/(?P<driver_name>[0-9a-zA-Z_-]+)/properties$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, driver_name):
|
||||
"""Get the properties associated with a specified driver
|
||||
|
||||
:param request: HTTP request
|
||||
:param driver_name: Driver name
|
||||
:return: Dictionary of properties
|
||||
"""
|
||||
return ironic.driver_properties(request, driver_name)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Portgroups(generic.View):
|
||||
|
||||
url_regex = r'ironic/portgroups/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get the list of portgroups associated with a specified node.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: List of portgroups.
|
||||
"""
|
||||
portgroups = ironic.portgroup_list(request,
|
||||
request.GET.get('node_id'))
|
||||
return {
|
||||
'portgroups': [i.to_dict() for i in portgroups]
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:return: Portgroup.
|
||||
"""
|
||||
return ironic.portgroup_create(request, request.DATA).to_dict()
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def delete(self, request):
|
||||
"""Delete a portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
"""
|
||||
return ironic.portgroup_delete(request,
|
||||
request.DATA.get('portgroup_id'))
|
||||
|
||||
|
||||
@urls.register
|
||||
class PortgroupPorts(generic.View):
|
||||
|
||||
url_regex = r'ironic/portgroups/(?P<portgroup_id>{})/ports$'. \
|
||||
format(LOGICAL_NAME_PATTERN)
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, portgroup_id):
|
||||
"""Get the ports for a specified portgroup.
|
||||
|
||||
:param request: HTTP request.
|
||||
:param node_id: UUID or name of portgroup.
|
||||
:return: List of port objects.
|
||||
"""
|
||||
ports = ironic.portgroup_get_ports(request, portgroup_id)
|
||||
return {
|
||||
'ports': [i.to_dict() for i in ports]
|
||||
}
|
|
@ -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.
|
||||
|
||||
import pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'ironic-ui').version_string()
|
|
@ -1,43 +0,0 @@
|
|||
# Copyright 2016 Cisco Systems, Inc.
|
||||
# Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 openstack_dashboard.api import base
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
|
||||
|
||||
class Ironic(horizon.Panel):
|
||||
name = _("Ironic Bare Metal Provisioning")
|
||||
slug = 'ironic'
|
||||
permissions = ('openstack.roles.admin',)
|
||||
|
||||
def allowed(self, context):
|
||||
request = context['request']
|
||||
if not base.is_service_enabled(request, 'baremetal'):
|
||||
return False
|
||||
else:
|
||||
return super(Ironic, self).allowed(context)
|
||||
|
||||
def nav(self, context):
|
||||
request = context['request']
|
||||
if not base.is_service_enabled(request, 'baremetal'):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
dashboard.Admin.register(Ironic)
|
|
@ -1,19 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Ironic Bare Metal Provisioning" %}{% endblock %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li>{% trans "Admin" %}</li>
|
||||
<li>{% trans "System" %}</li>
|
||||
<li class="active">{% trans "Ironic Bare Metal Provisioning" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
<hz-page-header header="{% trans "Ironic Bare Metal Provisioning" %}"></hz-page-header>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<ng-include src="'{{ STATIC_URL }}dashboard/admin/ironic/node-list/node-list.html'"></ng-include>
|
||||
{% endblock %}
|
|
@ -1,20 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Node Details" %}{% endblock %}
|
||||
|
||||
{% block breadcrumb_nav %}
|
||||
<ol class="breadcrumb">
|
||||
<li>{% trans "Admin" %}</li>
|
||||
<li>{% trans "System" %}</li>
|
||||
<li>{% trans "Ironic Bare Metal Provisioning" %}</li>
|
||||
<li class="active">{% trans "Node Details" %}</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
<hz-page-header header="{% trans "Node Details" %}"></hz-page-header>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<ng-include src="'{{ STATIC_URL }}dashboard/admin/ironic/node-details/node-details.html'"></ng-include>
|
||||
{% endblock %}
|
|
@ -1,25 +0,0 @@
|
|||
# Copyright 2016 Cisco Systems, Inc.
|
||||
# Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright (c) 2016 Cray 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 django.conf.urls import url
|
||||
|
||||
import ironic_ui.api.ironic_rest_api # noqa
|
||||
from ironic_ui.content.ironic import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^([^/]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Copyright 2016 Cisco Systems, Inc.
|
||||
# Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright (c) 2016 Cray 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 django.views import generic
|
||||
|
||||
|
||||
class IndexView(generic.TemplateView):
|
||||
template_name = 'admin/ironic/index.html'
|
||||
|
||||
|
||||
class DetailView(generic.TemplateView):
|
||||
template_name = 'admin/ironic/node_detail.html'
|
|
@ -1,30 +0,0 @@
|
|||
# Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
# Copyright (c) 2016 Cray 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.
|
||||
|
||||
# The name of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'ironic'
|
||||
# The name of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
# The name of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'admin'
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'ironic_ui.content.ironic.panel.Ironic'
|
||||
# A list of applications to be prepended to INSTALLED_APPS
|
||||
ADD_INSTALLED_APPS = ['ironic_ui', ]
|
||||
# A list of AngularJS modules to be loaded when Angular bootstraps.
|
||||
ADD_ANGULAR_MODULES = ['horizon.dashboard.admin.ironic']
|
||||
# Automatically discover static resources in installed apps
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
|
@ -1,154 +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.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
module.exports = function (config) {
|
||||
// This tox venv is setup in the post-install npm step
|
||||
var toxPath = '../.tox/py27/lib/python2.7/site-packages/';
|
||||
|
||||
config.set({
|
||||
preprocessors: {
|
||||
// Used to collect templates for preprocessing.
|
||||
// NOTE: the templates must also be listed in the files section below.
|
||||
'./static/**/*.html': ['ng-html2js'],
|
||||
// Used to indicate files requiring coverage reports.
|
||||
'./static/**/!(*.spec).js': ['coverage']
|
||||
},
|
||||
|
||||
// Sets up module to process templates.
|
||||
ngHtml2JsPreprocessor: {
|
||||
prependPrefix: '/',
|
||||
moduleName: 'templates'
|
||||
},
|
||||
|
||||
basePath: './',
|
||||
|
||||
// Contains both source and test files.
|
||||
files: [
|
||||
/*
|
||||
* shim, partly stolen from /i18n/js/horizon/
|
||||
* Contains expected items not provided elsewhere (dynamically by
|
||||
* Django or via jasmine template.
|
||||
*/
|
||||
'../test-shim.js',
|
||||
|
||||
// from jasmine.html
|
||||
toxPath + 'xstatic/pkg/jquery/data/jquery.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-route.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-mocks.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-cookies.js',
|
||||
toxPath + 'xstatic/pkg/angular_bootstrap/data/angular-bootstrap.js',
|
||||
toxPath + 'xstatic/pkg/angular_gettext/data/angular-gettext.js',
|
||||
toxPath + 'xstatic/pkg/angular/data/angular-sanitize.js',
|
||||
toxPath + 'xstatic/pkg/d3/data/d3.js',
|
||||
toxPath + 'xstatic/pkg/rickshaw/data/rickshaw.js',
|
||||
toxPath + 'xstatic/pkg/angular_smart_table/data/smart-table.js',
|
||||
toxPath + 'xstatic/pkg/angular_lrdragndrop/data/lrdragndrop.js',
|
||||
toxPath + 'xstatic/pkg/angular_fileupload/data/ng-file-upload-all.js',
|
||||
toxPath + 'xstatic/pkg/spin/data/spin.js',
|
||||
toxPath + 'xstatic/pkg/spin/data/spin.jquery.js',
|
||||
toxPath + 'xstatic/pkg/tv4/data/tv4.js',
|
||||
toxPath + 'xstatic/pkg/objectpath/data/ObjectPath.js',
|
||||
toxPath + 'xstatic/pkg/angular_schema_form/data/schema-form.js',
|
||||
toxPath + '/horizon/static/horizon/js/horizon.js',
|
||||
|
||||
/**
|
||||
* Include framework source code from horizon that we need.
|
||||
* Otherwise, karma will not be able to find them when testing.
|
||||
* These files should be mocked in the foreseeable future.
|
||||
*/
|
||||
toxPath + 'horizon/static/framework/**/*.module.js',
|
||||
toxPath + 'horizon/static/framework/**/!(*.spec|*.mock).js',
|
||||
toxPath + 'openstack_dashboard/static/**/*.module.js',
|
||||
toxPath + 'openstack_dashboard/static/**/!(*.spec|*.mock).js',
|
||||
toxPath + 'openstack_dashboard/dashboards/**/static/**/*.module.js',
|
||||
toxPath + 'openstack_dashboard/dashboards/**/static/**/!(*.spec|*.mock).js',
|
||||
|
||||
/**
|
||||
* First, list all the files that defines application's angular modules.
|
||||
* Those files have extension of `.module.js`. The order among them is
|
||||
* not significant.
|
||||
*/
|
||||
'./static/**/*.module.js',
|
||||
|
||||
/**
|
||||
* Followed by other JavaScript files that defines angular providers
|
||||
* on the modules defined in files listed above. And they are not mock
|
||||
* files or spec files defined below. The order among them is not
|
||||
* significant.
|
||||
*/
|
||||
'./static/**/!(*.spec|*.mock).js',
|
||||
|
||||
/**
|
||||
* Then, list files for mocks with `mock.js` extension. The order
|
||||
* among them should not be significant.
|
||||
*/
|
||||
toxPath + 'openstack_dashboard/static/**/*.mock.js',
|
||||
|
||||
/**
|
||||
* Finally, list files for spec with `spec.js` extension. The order
|
||||
* among them should not be significant.
|
||||
*/
|
||||
'./static/**/*.spec.js',
|
||||
|
||||
/**
|
||||
* Angular external templates
|
||||
*/
|
||||
'./static/**/*.html'
|
||||
],
|
||||
|
||||
autoWatch: true,
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
browserNoActivityTimeout: 60000,
|
||||
|
||||
phantomjsLauncher: {
|
||||
// Have phantomjs exit if a ResourceError is encountered
|
||||
// (useful if karma exits without killing phantom)
|
||||
exitOnResourceError: true
|
||||
},
|
||||
|
||||
reporters: ['progress', 'coverage', 'threshold'],
|
||||
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-ng-html2js-preprocessor',
|
||||
'karma-coverage',
|
||||
'karma-threshold-reporter'
|
||||
],
|
||||
|
||||
// Places coverage report in HTML format in the subdirectory below.
|
||||
coverageReporter: {
|
||||
type: 'html',
|
||||
dir: '../cover/karma/'
|
||||
},
|
||||
|
||||
// Coverage threshold values.
|
||||
thresholdReporter: {
|
||||
statements: 10, // target 100
|
||||
branches: 0, // target 100
|
||||
functions: 10, // target 100
|
||||
lines: 10 // target 100
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
|
@ -1,18 +0,0 @@
|
|||
# Robert Simai <robert.simai@suse.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.1.1.dev9\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-10-07 16:48+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-10-17 01:04+0000\n"
|
||||
"Last-Translator: Robert Simai <robert.simai@suse.com>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic Bare Metal Provisionierung"
|
|
@ -1,601 +0,0 @@
|
|||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Frank Kloeker <eumel@arcor.de>, 2016. #zanata
|
||||
# Robert Simai <robert.simai@suse.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2017. #zanata
|
||||
# Robert Simai <robert.simai@suse.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev70\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-18 16:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-04 10:43+0000\n"
|
||||
"Last-Translator: Robert Simai <robert.simai@suse.com>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr ""
|
||||
"Eine Anfrage wurde erstellt, um den Provisionierungszustand des Knotens %s "
|
||||
"zu ändern"
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "Bereinigung abbrechen"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "Aktionen"
|
||||
|
||||
msgid "Add Extra"
|
||||
msgstr "Extra hinzufügen"
|
||||
|
||||
msgid "Add Instance Property"
|
||||
msgstr "Instanz-Eigenschaft hinzufügen"
|
||||
|
||||
msgid "Add Property"
|
||||
msgstr "Eigenschaft hinzufügen"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Sind Sie sicher, dass Sie den Knoten \"%s\" löschen wollen? Diese Aktion "
|
||||
"kann nicht rückgängig gemacht werden."
|
||||
msgstr[1] ""
|
||||
"Sind Sie sicher, dass Sie die Knoten \"%s\" löschen wollen? Diese Aktion "
|
||||
"kann nicht rückgängig gemacht werden."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Sind Sie sicher, dass Sie den Port \"%s\" löschen wollen? Diese Aktion kann "
|
||||
"nicht rückgängig gemacht werden."
|
||||
msgstr[1] ""
|
||||
"Sind Sie sicher, dass Sie die Ports \"%s\" löschen wollen? Diese Aktion kann "
|
||||
"nicht rückgängig gemacht werden."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete portgroup \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete portgroups \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgstr[0] ""
|
||||
"Sind Sie sicher, dass Sie die Portgruppe \"%s\" löschen wollen? Diese Aktion "
|
||||
"kann nicht rückgängig gemacht werden."
|
||||
msgstr[1] ""
|
||||
"Sind Sie sicher, dass Sie die Portgruppen \"%s\" löschen wollen? Diese "
|
||||
"Aktion kann nicht rückgängig gemacht werden."
|
||||
|
||||
msgid "Attributes"
|
||||
msgstr "Attribute"
|
||||
|
||||
msgid "Boot Device"
|
||||
msgstr "Boot-Gerät"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Abbrechen"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "Chassis-ID"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "Wählen Sie ein Abbild"
|
||||
|
||||
msgid "Clean"
|
||||
msgstr "Bereinigen"
|
||||
|
||||
msgid "Clean Node"
|
||||
msgstr "Knoten bereinigen"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "Bereinigungsschritt"
|
||||
|
||||
msgid "Clean node"
|
||||
msgstr "Knoten bereinigen"
|
||||
|
||||
msgid "Clean steps should be an non-empty array"
|
||||
msgstr "Reinigungsschritte sollten ein nicht-leeres Array sein"
|
||||
|
||||
msgid "Click link to view console"
|
||||
msgstr "Klicken Sie auf den Link, um nur die Konsole anzuzeigen."
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Konfiguration"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "Konsole aktiviert"
|
||||
|
||||
msgid "Console Info."
|
||||
msgstr "Konsoleninformation."
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "Port erstellen"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "Erstellt am"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Standard ist ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "Knoten löschen"
|
||||
msgstr[1] "Knoten löschen"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "Port löschen"
|
||||
msgstr[1] "Ports löschen"
|
||||
|
||||
msgid "Delete Portgroup"
|
||||
msgid_plural "Delete Portgroups"
|
||||
msgstr[0] "Portgruppe löschen"
|
||||
msgstr[1] "Portgruppen löschen"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "Kernel bereitstellen"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "Ramdisk bereitstellen"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "Gerät"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "Treiber"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "Treiberdetails"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "Treiberinformationen"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "Treibervalidierung"
|
||||
|
||||
msgid ""
|
||||
"Each cleaning step must be an object that contains \"interface\" and \"step"
|
||||
"\" properties"
|
||||
msgstr ""
|
||||
"Jeder Reinigunsschritt muss ein Objekt sein, das \"interface\" und \"step\" "
|
||||
"Eigenschaften enthält"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "Knoten bearbeiten"
|
||||
|
||||
msgid "Edit Port"
|
||||
msgstr "Port bearbeiten"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "Knoten ausrollen"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "Extra Property Name"
|
||||
msgstr "Extra Eigenschaftname"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Extras"
|
||||
|
||||
msgid "General"
|
||||
msgstr "Allgemein"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "Abbildquelle"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "Inspizieren"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "Inspektion beenden um"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "Inspektion gestartet um"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "Instanz-ID"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "Instanzinformation"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "Instanzname"
|
||||
|
||||
msgid "Instance Property Name"
|
||||
msgstr "Instanzeigenschaftname"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "Interface"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "Letzter Fehler"
|
||||
|
||||
msgid "Local Link Connection"
|
||||
msgstr "Local Link Verbindung"
|
||||
|
||||
msgid "Local link connection"
|
||||
msgstr "Local Link Verbindung"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC-Adresse"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC-Adresse"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "MAC-Adresse für diesen Port. Erforderlich."
|
||||
|
||||
msgid "MAC address or OpenFlow datapath ID"
|
||||
msgstr "MAC Adresse oder OpenFlow datapath ID"
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "Wartung"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "Grund der Wartung"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "Verschieben nach"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
msgid "Network Interface"
|
||||
msgstr "Netzwerk Schnittstelle"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "Keine Instanz"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "Es wurden keine Netzwerkports definiert"
|
||||
|
||||
msgid "No portgroups have been defined"
|
||||
msgstr "Es wurden keine Portgruppen definiert"
|
||||
|
||||
msgid "No reason given."
|
||||
msgstr "Kein Grund angegeben."
|
||||
|
||||
msgid "Node"
|
||||
msgstr "Knoten"
|
||||
|
||||
#, python-format
|
||||
msgid "Node %s is already in target maintenance state."
|
||||
msgstr "Knoten %s ist bereits im beabsichtigten Wartungszustand."
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "Knotentreiber"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "Knoten-ID"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "Knoteninformation"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "Knotenname"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "Einer von diesen, (.*) muss spezifiziert sein\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Übersicht"
|
||||
|
||||
msgid "PXE Enabled"
|
||||
msgstr "PXE aktiviert"
|
||||
|
||||
msgid "PXE enabled"
|
||||
msgstr "PXE aktiviert"
|
||||
|
||||
msgid "Persistent"
|
||||
msgstr "Persistent"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "Port erfolgreich erstellt"
|
||||
|
||||
msgid "Portgroup"
|
||||
msgstr "Portgruppe"
|
||||
|
||||
msgid "Portgroup successfully created"
|
||||
msgstr "Portgruppe erfolgreich erstellt"
|
||||
|
||||
msgid "Portgroups"
|
||||
msgstr "Portgruppen"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "Ports"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "Zustand"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "Ausschalten"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "Einschalten"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "Eigenschaften"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "Eigenschaftname"
|
||||
|
||||
msgid "Provide a list of cleaning steps in JSON format"
|
||||
msgstr "Eine Liste der Reinigungsschritte im JSON-Format angeben"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"Geben Sie einen Grund an, warum Sie den/die ausgewählten Knoten in den "
|
||||
"Wartungsmodus setzen (optional)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "Bereitstellungszustand"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "Provisionierungszustand"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "Provisionierungsstatus"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "Knoten in Wartungsmodus setzen"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "Ramdisk"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Grund"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Neustart"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Aktualisieren"
|
||||
|
||||
msgid "Refresh page to see updated console details"
|
||||
msgstr "Seite neu laden, um aktualisierte Konsolendetails anzuzeigen"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "Aktualisiere Seite, um den Power Status zu sehen"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "Erforderlich"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "Reservierung"
|
||||
|
||||
msgid "Resource Class"
|
||||
msgstr "Ressourcen-Klasse"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "Root GB"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH Adresse"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH Schlüsselinhalt"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH Schlüsseldatei"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH Passwort"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH-Port"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH-Benutzername"
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH Terminal Port"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "Wählen Sie einen Treiber"
|
||||
|
||||
msgid "Soft power off"
|
||||
msgstr "Sanftes Ausschalten"
|
||||
|
||||
msgid "Soft reboot"
|
||||
msgstr "Sanfter Neustart"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Abschicken"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "Knoten \"%s\" erfolgreich gelöscht"
|
||||
msgstr[1] "Knoten \"%s\" erfolgreich gelöscht"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "Port \"%s\" erfolgreich gelöscht"
|
||||
msgstr[1] "Port \"%s\" erfolgreich gelöscht"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted portgroup \"%s\""
|
||||
msgid_plural "Successfully deleted portgroups \"%s\""
|
||||
msgstr[0] "Portgruppe \"%s\" erfolgreich gelöscht"
|
||||
msgstr[1] "Portgruppen \"%s\" erfolgreich gelöscht"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "Knoten %s erfolgreich aktualisiert"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated port %s"
|
||||
msgstr "Port %s erfolgreich aktualisiert"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "Ziel Power-Zustand"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "Ziel Bereitstellungszustand"
|
||||
|
||||
msgid ""
|
||||
"This field is disabled because a port cannot have any connectivity "
|
||||
"attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless "
|
||||
"its associated node is in an enroll, inspecting, mangeable state; or in "
|
||||
"maintenance mode."
|
||||
msgstr ""
|
||||
"Dieses Feld ist deaktiviert, denn bei einem Port können Verbindungsattribute "
|
||||
"(pxe_enabled, local_link_connection, portgroup_id) nur aktualisiert werden, "
|
||||
"wenn der zugewiesene Knoten im Enroll, Inspecting oder Manageable Status "
|
||||
"oder im Wartungsmodus ist."
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "Knoten-Aktualisierungspatch kann nicht erstellt werden."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "Knoten kann nicht erstellt werden: %s"
|
||||
|
||||
msgid "Unable to create port update patch."
|
||||
msgstr "Port Update Patch kann nicht erstellt werden."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "Port kann nicht erstellt werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create portgroup: %s"
|
||||
msgstr "Portgruppe kann nicht erstellt werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "Knoten \"%s\" kann nicht gelöscht werden"
|
||||
msgstr[1] "Knoten \"%s\" können nicht gelöscht werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "Knoten kann nicht gelöscht werden: %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "Port \"%s\" kann nicht gelöscht werden"
|
||||
msgstr[1] "Ports \"%s\" können nicht gelöscht werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "Port kann nicht gelöscht werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup \"%s\""
|
||||
msgid_plural "Unable to delete portgroups \"%s\""
|
||||
msgstr[0] "Portgruppe \"%s\" kann nicht gelöscht werden"
|
||||
msgstr[1] "Portgruppen \"%s\" können nicht gelöscht werden"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup: %s"
|
||||
msgstr "Portgruppe kann nicht gelöscht werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to get console for node %s: %s"
|
||||
msgstr "Konsole für Node %s kann nicht abgerufen werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "Konnte den Knoten nicht ausschalten: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Ironic Treiber können nicht abgerufen werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic node portgroups: %s"
|
||||
msgstr "Ironic Knotenportgruppen können nicht abgerufen werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "Konnte die Ironic Knoten nicht abrufen: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve boot device for Ironic node. %s"
|
||||
msgstr "Boot-Gerät für den Ironic-Knoten kann nicht abgerufen werden. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "Treibereigenschaften können nicht abgerufen werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Konnte die Ironic-Knotenports nicht abrufen: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Konnte den Ironic-Knoten nicht abrufen: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set Ironic node %s maintenance state: %s"
|
||||
msgstr "Konnte den Ironic Knoten %s nicht in den Wartungszustand setzen: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set console mode: %s"
|
||||
msgstr "Konsolenmodus kann nicht gesetzt werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "Kann Provisionierungszustand nicht setzen: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "Knoten %s kann nicht aktualisiert werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update port %s: %s"
|
||||
msgstr "Port %s kann nicht aktualisiert werden: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "Knoten %s kann nicht validiert werden: %s"
|
||||
|
||||
msgid "Unable to validate the JSON input"
|
||||
msgstr "JSON-Eingabe kann nicht validiert werden"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "Knoten aktualisieren"
|
||||
|
||||
msgid "Update Port"
|
||||
msgstr "Port aktualisieren"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "Aktualisiert am"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "Gültig"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "Virtualisierungssoftware"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Standard (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,18 +0,0 @@
|
|||
# Andi Chandler <andi@gowling.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 1.1.1.dev28\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-07-13 21:09+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-29 01:16+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
"Language: en-GB\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic Bare Metal Provisioning"
|
|
@ -1,18 +0,0 @@
|
|||
# Nicolas Fournier <nicolas.fournier3@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 1.1.1.dev9\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-05-05 12:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-04-30 05:13+0000\n"
|
||||
"Last-Translator: Nicolas Fournier <nicolas.fournier3@gmail.com>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Déploiement Bare Metal Ironic"
|
|
@ -1,347 +0,0 @@
|
|||
# Gael Rehault <gael01@gmail.com>, 2015. #zanata
|
||||
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
|
||||
# Martine Marin <mmarin@fr.ibm.com>, 2016. #zanata
|
||||
# Nicolas Fournier <nicolas.fournier3@gmail.com>, 2016. #zanata
|
||||
# Gérald LONLAS <g.lonlas@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev70\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-18 16:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-02-03 10:49+0000\n"
|
||||
"Last-Translator: Gérald LONLAS <g.lonlas@gmail.com>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr ""
|
||||
"Une requête a été envoyé pour changer l'état du provisionnement du nœud %s"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "Actions"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Etes-vous certain de vouloir supprimer le nœud \"%s\" ? Cette action est "
|
||||
"irréversible."
|
||||
msgstr[1] ""
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Etes-vous certain de vouloir supprimer le port \"%s\" ? Cette action est "
|
||||
"irréversible."
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "ID du chassis"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "Choisir une image"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "Console Activée"
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "Créer un port"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "Créé le"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Par défaut ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "Supprimer le nœud"
|
||||
msgstr[1] ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "Supprimer le port"
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "Kernel de déploiement"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "Ramdisk de déploiement"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "Pilote"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "Détails du pilote"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "Info du pilote"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "Editer le nœud"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "Enroller le nœud"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Extras"
|
||||
|
||||
msgid "General"
|
||||
msgstr "Général"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "Inspection terminée à"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "Inspection démarrée à"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "ID de l'instance"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "Infos de l'instance"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "Nom de l'instance"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "Noyau"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "Dernière Erreur"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "Adresse MAC"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "Adresse MAC"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "Adresse MAC pour ce port. Requis."
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "Maintenance"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "Raison de la maintenance"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "Pas d'instance"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "Aucun port réseau n'a été défini"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "Nœud"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "Pilote du nœud"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "ID du nœud"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "Info du nœud"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "Nom du nœud"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "Une des leurs (.*) doit être spécifié\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Vue d'ensemble"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "Port crée avec succès"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "Ports"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "État de l'alimentation"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "Éteindre"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "Allumer"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "Propriétés"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "Nom de la propriété"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"Fournir une raison pour avoir mis le(s) nœud(s) sélectionné(s) en mode "
|
||||
"maintenance (optionnel)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "État de déploiement"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "État de déploiement"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "Statut de Déploiement"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "Mettre le(s) nœud(s) en mode maintenance"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "Ramdisk"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Rafraichir"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "Rafraichir la page pour voir les statuts d'alimentation à jour"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "Obligatoire"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "Réservation"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "Port SSH"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "Utilisateur SSH"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "Sélectionner un pilote"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "Suppression avec succès du nœud \"%s\" "
|
||||
msgstr[1] ""
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "Suppression avec succès du port \"%s\" "
|
||||
msgstr[1] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "Mise à jour avec succès du nœud %s"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "État de l'alimentation de la cible"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "État de déploiement de la cible"
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "Impossible de créer le patch de mise à jour du nœud."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "Impossible de créer le nœud : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "Impossible de créer le port : %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "Impossible de supprimer le nœud \"%s\""
|
||||
msgstr[1] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "Impossible de supprimer le nœud %s : %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "Impossible de supprimer le port \"%s\""
|
||||
msgstr[1] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "Impossible de supprimer le port : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "Impossible d'éteindre le nœud : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Impossible de récupérer les pilotes Ironic : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "Impossible de récupérer les propriétés du pilote : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Impossible de récupérer les ports du nœud Ironic : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Impossible de récupérer le nœud Ironic : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "Impossible de configurer l'état du provisionnement : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "Impossible de mettre à jour le nœud %s : %s"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "Mettre à jour le nœud"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "Mis à jour à"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,18 +0,0 @@
|
|||
# suhartono <cloudsuhartono@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.1.1.dev45\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-01-17 12:34+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-01-10 02:16+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic Bare Metal Provisioning"
|
|
@ -1,615 +0,0 @@
|
|||
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev73\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-21 15:07+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-23 05:11+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr "Permintaan telah dibuat untuk mengubah keadaan provisioning node %s"
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "Membatalkan pembersihan"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "Aksi"
|
||||
|
||||
msgid "Add Extra"
|
||||
msgstr "Menambahkan Extra"
|
||||
|
||||
msgid "Add Instance Property"
|
||||
msgstr "Menambahkan Instance Property"
|
||||
|
||||
msgid "Add Property"
|
||||
msgstr "Menambahkan Property"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Apakah Anda yakin ingin menghapus node \"%s\"? Tindakan ini tidak bisa "
|
||||
"dibatalkan."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Apakah Anda yakin ingin menghapus port \"%s\"? Tindakan ini tidak bisa "
|
||||
"dibatalkan."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete portgroup \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete portgroups \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgstr[0] ""
|
||||
"Are you sure you want to delete portgroup \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
|
||||
msgid "Attributes"
|
||||
msgstr "Attributes"
|
||||
|
||||
msgid "Boot Device"
|
||||
msgstr "Boot Device"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Batal"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "Chassis ID"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "Pilih Image"
|
||||
|
||||
msgid "Clean"
|
||||
msgstr "Bersih"
|
||||
|
||||
msgid "Clean Node"
|
||||
msgstr "Clean Node"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "Membersihkan langkah"
|
||||
|
||||
msgid "Clean node"
|
||||
msgstr "Clean node"
|
||||
|
||||
msgid "Clean steps should be an non-empty array"
|
||||
msgstr "Langkah pembersihan harus array non-empty"
|
||||
|
||||
msgid "Click link to view console"
|
||||
msgstr "Klik tautan untuk melihat konsol"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Konfigurasi"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "Console Enabled (konsol diaktifkan)"
|
||||
|
||||
msgid "Console Info."
|
||||
msgstr "Console Info."
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "Buat Port"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "Created At"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Defaults ke ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "Delete Node (hapus node)"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "Delete Port (hapus port)"
|
||||
|
||||
msgid "Delete Portgroup"
|
||||
msgid_plural "Delete Portgroups"
|
||||
msgstr[0] "Delete Portgroup"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "Deploy Kernel (mengerahkan kernel)"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "Deploy Ramdisk (mengerahkan ramdisk)"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "Device"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "Driver"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "Driver Details (rincian driver)"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "Driver Info"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "Driver Validation (validasi driver)"
|
||||
|
||||
msgid ""
|
||||
"Each cleaning step must be an object that contains \"interface\" and \"step"
|
||||
"\" properties"
|
||||
msgstr ""
|
||||
"Setiap langkah pembersihan harus sebagai obyek yang berisi properti "
|
||||
"\"interface\" dan \"step\"."
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "Edit Node"
|
||||
|
||||
msgid "Edit Port"
|
||||
msgstr "Edit Port"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "Enroll Node"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "Extra Property Name"
|
||||
msgstr "Extra Property Name"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Extras"
|
||||
|
||||
msgid "General"
|
||||
msgstr "General (umum)"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "Image Source"
|
||||
|
||||
msgid "Indicates whether this port should be used when PXE booting this node"
|
||||
msgstr ""
|
||||
"Menunjukkan apakah port ini harus digunakan saat PXE melakukan booting node "
|
||||
"ini"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "Inspect (periksa)"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "Inspection Finished At (pemeriksaan selesai)"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "Inspection Started At (pemeriksaan dimulai)"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "ID Instances"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "Instance Info (info instance)"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "Nama Instance"
|
||||
|
||||
msgid "Instance Property Name"
|
||||
msgstr "Instance Property Name"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "Interface (antarmuka)"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "Kernel"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "Last Error (kesalahan terakhir)"
|
||||
|
||||
msgid "Local Link Connection"
|
||||
msgstr "Local Link Connection"
|
||||
|
||||
msgid "Local link connection"
|
||||
msgstr "Koneksi link lokal"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC Address"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC address"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "MAC address untuk port ini. Wajib."
|
||||
|
||||
msgid "MAC address or OpenFlow datapath ID"
|
||||
msgstr "Alamat MAC atau OpenFlow datapath ID"
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "Maintenance (pemeliharaan)"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "Maintenance Reason (alasan pemeliharaan)"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "Move to (pindah ke)"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Nama"
|
||||
|
||||
msgid "Network Interface"
|
||||
msgstr "Network Interface"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "Tak ada Instance"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "Tidak ada port jaringan telah didefinisikan"
|
||||
|
||||
msgid "No portgroups have been defined"
|
||||
msgstr "No portgroups telah didefinisikan"
|
||||
|
||||
msgid "No reason given."
|
||||
msgstr "Tidak ada alasan yang diberikan."
|
||||
|
||||
msgid "Node"
|
||||
msgstr "Node (simpul)"
|
||||
|
||||
#, python-format
|
||||
msgid "Node %s is already in target maintenance state."
|
||||
msgstr "Node %s sudah dalam target maintenance state."
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "Node Driver"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "Node ID"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "Node Info (info node)"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "Node Name (nama node)"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "One of this, (.*) must be specified\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Iktisar"
|
||||
|
||||
msgid "PXE Enabled"
|
||||
msgstr "PXE Diaktifkan"
|
||||
|
||||
msgid "PXE enabled"
|
||||
msgstr "PXE enabled"
|
||||
|
||||
msgid "Persistent"
|
||||
msgstr "Persistent"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "Port berhasil dibuat"
|
||||
|
||||
msgid "Portgroup"
|
||||
msgstr "Portgroup"
|
||||
|
||||
msgid "Portgroup successfully created"
|
||||
msgstr "Portgroup berhasil dibuat"
|
||||
|
||||
msgid "Portgroup that this port belongs to"
|
||||
msgstr "Portgroup yang dimiliki port ini"
|
||||
|
||||
msgid "Portgroups"
|
||||
msgstr "Portgroups"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "Port-Port"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "Power State"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "Power off"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "Power on"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "Property (sifat)"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "Property Name (nama property)"
|
||||
|
||||
msgid "Provide a list of cleaning steps in JSON format"
|
||||
msgstr "Berikan daftar langkah-langkah pembersihan dalam format JSON"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"Berikan alasan mengapa Anda menempatkan node yang dipilih ke dalam modus "
|
||||
"pemeliharaan (opsional)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "Provision State (keadaan penyediaan)"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "Provisioning State"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "Provisioning Status (status penyediaan)"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "Letakan Node(s) dalam Maintenance Mode"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "Ramdisk"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Reason (alasan)"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Reboot"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Refresh"
|
||||
|
||||
msgid "Refresh page to see set boot device"
|
||||
msgstr "Segarkan halaman untuk melihat perangkat booting yang ditetapkan"
|
||||
|
||||
msgid "Refresh page to see updated console details"
|
||||
msgstr "Segarkan halaman untuk melihat rincian konsol yang diperbarui"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "Refresh halaman untuk melihat status daya diperbarui"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "Wajib"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "Reservation"
|
||||
|
||||
msgid "Resource Class"
|
||||
msgstr "Resource Class"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "Root GB"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH Address"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH Key Contents (konten kunci SSH)"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH Key File (file kunci SSH)"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH Password (password SSH)"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH Port"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH Username "
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH terminal port (port terminal SSH)"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "Pilih Driver"
|
||||
|
||||
msgid "Select a boot device"
|
||||
msgstr "Pilih perangkat booting"
|
||||
|
||||
msgid "Select a portgroup"
|
||||
msgstr "Pilih portgroup"
|
||||
|
||||
msgid "Set Boot Device"
|
||||
msgstr "Set Boot Device (setel perangkat booting)"
|
||||
|
||||
msgid "Set boot device"
|
||||
msgstr "Setel perangkat booting"
|
||||
|
||||
msgid "Soft power off"
|
||||
msgstr "Soft power off"
|
||||
|
||||
msgid "Soft reboot"
|
||||
msgstr "Soft reboot"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Submit (menyampaikan)"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "Berhasil hapus node \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "Berhasil hapus port \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted portgroup \"%s\""
|
||||
msgid_plural "Successfully deleted portgroups \"%s\""
|
||||
msgstr[0] "Successfully deleted portgroup \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "Berhasil perbarui node %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated port %s"
|
||||
msgstr "Port berhasil diperbarui %s"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "Target Power State"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "Target Provision State (keadaab penyediaan sasaran)"
|
||||
|
||||
msgid ""
|
||||
"This field is disabled because a port cannot have any connectivity "
|
||||
"attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless "
|
||||
"its associated node is in an enroll, inspecting, mangeable state; or in "
|
||||
"maintenance mode."
|
||||
msgstr ""
|
||||
"Field ini dinonaktifkan karena port tidak dapat memiliki atribut "
|
||||
"konektivitas (pxe_enabled, local_link_connection, portgroup_id) yang "
|
||||
"diperbarui kecuali node yang terkait adalah dalam keadaan enroll, "
|
||||
"inspecting, mangeable; atau dalam modus pemeliharaan."
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "Tidak dapat membuat node update patch."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "Tidak dapat membuat node: %s"
|
||||
|
||||
msgid "Unable to create port update patch."
|
||||
msgstr "Tidak dapat membuat port update patch."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "Tidak dapat membuat port: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create portgroup: %s"
|
||||
msgstr "Tidak dapat membuat portgroup: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "Tidak dapat menghapus node \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "Tidak dapat menghapus node %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "Unable to delete port \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "Tidak dapat menghapus port: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup \"%s\""
|
||||
msgid_plural "Unable to delete portgroups \"%s\""
|
||||
msgstr[0] "Unable to delete portgroup \"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup: %s"
|
||||
msgstr "Tidak dapat menghapus portgroup: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to get console for node %s: %s"
|
||||
msgstr "Tidak dapat mendapatkan konsol untuk node %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "Tidak dapat daya mati pada node: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Tidak dapat mengambil Ironic driver: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic node portgroups: %s"
|
||||
msgstr "Unable to retrieve Ironic node portgroups: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "Tidak dapat mengambil node Ironis. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve boot device for Ironic node. %s"
|
||||
msgstr "Tidak dapat mengambil perangkat boot untuk simpul Ironis. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "Tidak dapat mengambil driver property: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve portgroup ports: %s"
|
||||
msgstr "Tidak dapat mengambil port portgroup: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve supported boot devices for Ironic node. %s"
|
||||
msgstr ""
|
||||
"Tidak dapat mengambil perangkat booting yang didukung untuk node Ironic. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Tidak dapat mengambil Ironic node port: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Tidak dapat mengambil Ironic node: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set Ironic node %s maintenance state: %s"
|
||||
msgstr "Unable to set Ironic node %s maintenance state: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set boot device: %s"
|
||||
msgstr "Tidak dapat mengatur perangkat booting: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set console mode: %s"
|
||||
msgstr "Tidak dapat mengatur mode konsol: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "Tidak dapat mengatur keadaan provision node: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "Tidak dapat memperbarui node %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update port %s: %s"
|
||||
msgstr "Tidak dapat memperbarui port %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "Tidak dapat memvalidasi node %s: %s"
|
||||
|
||||
msgid "Unable to validate the JSON input"
|
||||
msgstr "Tidak dapat memvalidasi input JSON"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "Update Node"
|
||||
|
||||
msgid "Update Port"
|
||||
msgstr "Update Port"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "Diperbarui pada"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "Valid (sah)"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "Virtualization Software (software virtualisasi)"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,18 +0,0 @@
|
|||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 1.1.1.dev9\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-05-05 12:53+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-04-01 04:04+0000\n"
|
||||
"Last-Translator: Shu Muto <shu-mutou@rf.jp.nec.com>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic ベアメタルプロビジョニング"
|
|
@ -1,419 +0,0 @@
|
|||
# Akihiro Motoki <amotoki@gmail.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Shu Muto <shu-mutou@rf.jp.nec.com>, 2016. #zanata
|
||||
# Yoshiki Eguchi <yoshiki.eguchi@gmail.com>, 2016. #zanata
|
||||
# Akihiro Motoki <amotoki@gmail.com>, 2017. #zanata
|
||||
# Shu Muto <shu-mutou@rf.jp.nec.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev70\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-18 16:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-06-30 03:16+0000\n"
|
||||
"Last-Translator: Akihiro Motoki <amotoki@gmail.com>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(デフォルト\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr "ノード %s のプロビジョニング状態を変更するための要求が作成されました。"
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "クリーニングの中止"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "アクション"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "ノード「%s」 を削除してよろしいですか?この操作は取り消せません。"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "ポート「%s」 を削除してよろしいですか?この操作は取り消せません。"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "取り消し"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "シャーシ ID"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "イメージの選択"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "クリーニングステップ"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "設定"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "コンソールの有効化"
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "ポートの作成"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "作成時刻"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "デフォルトを ([^\"\\. ]+|\"[^\"]+\") にします"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "ノードの削除"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "ポートの削除"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "カーネルのデプロイ"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "RAM ディスクのデプロイ"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "ドライバー"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "ドライバー詳細"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "ドライバー情報"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "ドライバーの検証"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "ノードの編集"
|
||||
|
||||
msgid "Edit Port"
|
||||
msgstr "ポートの編集"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "ノードの登録"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "拡張"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "拡張"
|
||||
|
||||
msgid "General"
|
||||
msgstr "一般"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "イメージソース"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "検査"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "検査終了時刻"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "検査開始時刻"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "インスタンス ID"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "インスタンス情報"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "インスタンス名"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "インターフェース"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "カーネル"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "最後のエラー"
|
||||
|
||||
msgid "Local link connection"
|
||||
msgstr "ローカルリンク接続"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC アドレス"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC アドレス"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "このポートの MAC アドレス。必須。"
|
||||
|
||||
msgid "MAC address or OpenFlow datapath ID"
|
||||
msgstr "MAC アドレスあるいは OpenFlow データパス ID"
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "メンテナンス"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "メンテナンスの理由"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "移動先"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "名前"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "インスタンスなし"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "ネットワークポートが定義されていません"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "ノード"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "ノードドライバー"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "ノード ID"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "ノード情報"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "ノード名"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "(.*) の1つを指定する必要があります。"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "概要"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "ポートが正常に作成されました"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "ポート"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "電源状態"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "電源オフ"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "電源オン"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "プロパティー"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "プロパティ名"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"選択したノードをメンテナンスモードにする理由を設定してください(オプション)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "プロビジョニング状態"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "プロビジョニング状態"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "プロビジョニング状態"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "ノードをメンテナンスモードにします"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "RAM ディスク"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "理由"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "最新表示"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "電源状態を更新するにはページをリフレッシュしてください"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "必須"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "予約"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "ルートディスク (GB)"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH アドレス"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH 鍵の内容"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH 鍵ファイル"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH パスワード"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH ポート"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH ユーザー名"
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH ターミナルのポート"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "ドライバーを選択してください"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "送信"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "ノード「%s」を正常に削除しました"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "ポート \"%s\" を正常に削除しました"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "ノード %s の更新に成功しました"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated port %s"
|
||||
msgstr "ポート %s の更新に成功しました"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "ターゲット電源状態"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "ターゲットプロビジョニング状態"
|
||||
|
||||
msgid ""
|
||||
"This field is disabled because a port cannot have any connectivity "
|
||||
"attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless "
|
||||
"its associated node is in an enroll, inspecting, mangeable state; or in "
|
||||
"maintenance mode."
|
||||
msgstr ""
|
||||
"ポートが接続のための属性 (PXE 有効、ローカルリンク接続、ポートグループ ID) の"
|
||||
"いずれも持たないため、関連付けられたノードが登録、検査、管理可能な状態に更新"
|
||||
"されるまで、あるいはメンテナンスモードになるまで、この項目は無効です。"
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "ノードを更新するパッチを作成できません。"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "ノードを作成できません: %s"
|
||||
|
||||
msgid "Unable to create port update patch."
|
||||
msgstr "ポートを更新するパッチを作成できません。"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "ポートを作成できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "ノード 「%s」を削除できません"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "ノード %s を削除できません: %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "ポート \"%s\" を削除できません"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "ポートを削除できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "ノードを電源OFFにできません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Ironic ドライバーの一覧を取得できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "Ironic ノードの一覧を取得できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "ドライバープロパティーの一覧を取得できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Ironic ノードのポート一覧を取得できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Ironic ノードを取得できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "ノードのプロビジョニング状態を設定できませんでした: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "ノード %s を更新できません: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update port %s: %s"
|
||||
msgstr "ポート %s の更新に失敗しました: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "ノード %s を検証できません: %s"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "ノードの更新"
|
||||
|
||||
msgid "Update Port"
|
||||
msgstr "ポートの更新"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "最終更新"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "有効"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "仮想化ソフトウェア"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "(?:value )?のデフォルトは ([^\"\\. ]+|\"[^\"]+\") です"
|
|
@ -1,18 +0,0 @@
|
|||
# Eunseop Shin <kairos9603@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.1.1.dev9\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-10-07 16:48+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-10-20 05:14+0000\n"
|
||||
"Last-Translator: Eunseop Shin <kairos9603@gmail.com>\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
"Language: ko-KR\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic 베어메탈 프로비저닝"
|
|
@ -1,395 +0,0 @@
|
|||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Eunseop Shin <kairos9603@khu.ac.kr>, 2016. #zanata
|
||||
# HYUNGBAI PARK <openstack.make@gmail.com>, 2016. #zanata
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2016. #zanata
|
||||
# Sungjin Kang <gang.sungjin@gmail.com>, 2016. #zanata
|
||||
# ChungYoung Cho <openstack.cho@gmail.com>, 2017. #zanata
|
||||
# Ian Y. Choi <ianyrchoi@gmail.com>, 2017. #zanata
|
||||
# Sungjin Kang <gang.sungjin@gmail.com>, 2017. #zanata
|
||||
# Wonil Choi <wonil0522@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev70\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-18 16:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-06-30 03:16+0000\n"
|
||||
"Last-Translator: Sungjin Kang <gang.sungjin@gmail.com>\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
"Language: ko-KR\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr "노드 %s의 프로비저닝 상태 변경을 위한 요청이 처리되었습니다."
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "클린업 취소"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "작업"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "노드 \"%s\" 를 삭제하시겠습니까? 이 작업은 취소할 수 없습니다."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "포트 \"%s\" 를 삭제하시겠습니까? 이 작업은 취소할 수 없습니다."
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "취소하기"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "Chassis ID"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "이미지 선택하기"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "클린업 단계"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "구성"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "콘솔 활성화"
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "포트 생성하기"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "생성 시점"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "노드 삭제"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "포트 삭제"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "커널 배치하기"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "램디스크 배치하기"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "드라이버"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "드라이버 세부사항"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "드라이버 정보"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "드라이버 확인"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "노드 편집"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "노드 등록하기"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "Extra Property Name"
|
||||
msgstr "Extra 속성 명칭"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Extra"
|
||||
|
||||
msgid "General"
|
||||
msgstr "일반"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "이미지 소스"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "검사"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "점검 종료시점"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "점검 시작 시점"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "인스턴스 ID"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "인스턴스 정보"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "인스턴스 이름"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "인터페이스"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "커널"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "마지막 에러"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC 주소"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC 주소"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "이 포트를 위한 MAC 주소. 필수항목."
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "유지보수"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "유지보수 사유"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "이동"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "이름"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "인스턴스 없음"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "네트워크 포트가 정의되지 않았습니다"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "노드"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "노드 드라이버"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "노드 ID"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "노드 정보"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "노드 명칭"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "One of this, (.*) must be specified\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "개요"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "포트가 성공적으로 생성됐습니다"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "포트들"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "전원 상태"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "전원 꺼짐"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "전원 켜짐"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "속성"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "속성 명칭"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr "선택된 노드를 유지보수 모드로 넣는지 사유를 제공합니다 (선택항목)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "권한설정 상태"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "권한설정 상태"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "권한설정 상태"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "노드(들)을 유지보수 모드로 넣기"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "램디스크"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "원인"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "갱신"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "갱신된 전원 상태를 보기 위해 페이지 새로 고치기"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "필수항목"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "예약"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "루트 GB"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH주소"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH 키콘텐츠"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH Key File"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH 패스워드"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH 포트"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH Username"
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH 터미널포트"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "드라이버 선택하기"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "제출"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "노드 \"%s\" 를 성공적으로 삭제했습니다"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "포트 \"%s\" 를 성공적으로 삭제했습니다"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "노드 %s가 성공적으로 업데이트 됨."
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "대상 전원 상태"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "대상 권한설정 상태"
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "노드 업데이트 패치를 만들수 없습니다."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "노드를 생성할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "포트를 생성할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "노드 \"%s\" 를 삭제할 수 없습니다"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "노드 %s를 삭제할 수 없습니다: %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "포트를 삭제할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "포트를 삭제할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "노드의 전원을 끌 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Ironic 드라이버를 되찾을 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "Ironic 노드를 가져올 수 없습니다: %s "
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve boot device for Ironic node. %s"
|
||||
msgstr "Ironic 노드를 위한 부트 장치를 가져올 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "드라이버 속성을 되찾을 수 없습니다 : %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Ironic 노드 포트를 가져올 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "해당 Ironic 노드를 가져올 수 없습니다: %s "
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "노드 공급 상태를 설정 할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "노드 %s를 업데이트 할 수 없습니다: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "노드 %s를 검증할수 없습니다: %s"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "노드 업데이트"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "업데이트 시점"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "유효"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "가상화 소프트트웨어"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,19 +0,0 @@
|
|||
# Alexander <ainikitenkov@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.1.1.dev9\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-10-07 16:48+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-10-19 02:08+0000\n"
|
||||
"Last-Translator: Alexander <ainikitenkov@gmail.com>\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Разворачивание на пустой системе - Ironic"
|
|
@ -1,354 +0,0 @@
|
|||
# Ilya Alekseyev <ilyaalekseyev@acm.org>, 2015. #zanata
|
||||
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
|
||||
# Alexander <ainikitenkov@gmail.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Fedor Tarasenko <feodor.tarasenko@gmail.com>, 2016. #zanata
|
||||
# Alexander <ainikitenkov@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev70\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-18 16:08+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-02-03 10:49+0000\n"
|
||||
"Last-Translator: Alexander <ainikitenkov@gmail.com>\n"
|
||||
"Language-Team: Russian\n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(По умолчанию\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr "Запрос был создал для изменения состояния подготовки узла %s"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "Действия"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Вы действительно хотите удалить узел \"%s\"? Это действие не может быть "
|
||||
"отменено."
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"Вы действительно хотите удалить порт \"%s\"? Это действие не может быть "
|
||||
"отменено."
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "ID устройства"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "Выберите образ"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Конфигурация"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "Консоль активирована"
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "Создать порт"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "Создано"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "По умолчанию ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "Удалить узел"
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "Удалить порт"
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "Развернуть ядро"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "Развернуть Ramdisk"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "Драйвер"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "Детали дравера"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "Информация о драйвере"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "Редактировать узел"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "Зарегистрировать узел"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Дополнительно"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Дополнительно"
|
||||
|
||||
msgid "General"
|
||||
msgstr "Общее"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "Проверка закончилась в"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "Проверка началась в"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "ID инстанса"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "Информация об инстансе"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "Имя инстанса"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "Ядро"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "Последняя ошибка"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC адрес"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC адрес"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "MAC адрес для этого порта. Обязательный параметр."
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "Техническое обслуживание"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "Причина технического обслуживания"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Имя"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "Нет инстанса"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "Сетевые порты не были заданы"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "Узел"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "Драйвер узла"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "ID узла"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "Информация об узле"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "Имя узла"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "Один из этих, (.*) должен быть определен\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Обзор"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "Порт создан успешно"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "Порты"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "Состояние"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "Выключить"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "Включить"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "Свойства"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "Имя свойства"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"Опишите причину по которой вы переводите выбранные узлы в режим обслуживания "
|
||||
"(необязательно)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "Статус развертывания"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "Статус развертывания"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "Статус развертывания"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "Перевести узел (узлы) в режим обслуживания"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "Ramdisk"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "Обновите страницу чтобы увидеть обновленный статус питания"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "Обязательно"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "Резервация"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "Порт SSH"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "Имя пользователя SSH"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "Выберите драйвер"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Отправить"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "Узел \"%s\" успешно удален"
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "Порт \"%s\" успешно удален"
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "Узел %s успешно обновлен"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "Целевое состояние"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "Целевой статус развертывания"
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "Невозможно создать патч обновления узла."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "Невозможно создать узел: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "Невозможно создать порт: %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "Невозможно удалить узел \"%s\""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "Невозможно удалить узел %s: %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "Невозможно удалить порт \"%s\""
|
||||
msgstr[1] ""
|
||||
msgstr[2] ""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "Невозможно удалить порт: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "Невозможно выключить узел: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Невозможно получить драйвера Ironic: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "Невозможно получить свойства драйвера: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Невозможно получить порты узла Ironic: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Невозможно получить узел Ironic: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "Не удается установить состояние подготовки узла: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "Невозможно обновить узел %s: %s"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "Обновить узел"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "Обновлено"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "По умолчанию (?:value )? - ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,18 +0,0 @@
|
|||
# işbaran akçayır <isbaran@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev39\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-06-20 18:25+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-05-22 12:06+0000\n"
|
||||
"Last-Translator: işbaran akçayır <isbaran@gmail.com>\n"
|
||||
"Language-Team: Turkish (Turkey)\n"
|
||||
"Language: tr-TR\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n>1)\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "Ironic Saf Metal Hazırlığı"
|
|
@ -1,516 +0,0 @@
|
|||
# Mücahit Büyükyılmaz <mucahit@deltanoc.com>, 2015. #zanata
|
||||
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Mücahit Büyükyılmaz <mucahit@deltanoc.com>, 2016. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2017. #zanata
|
||||
# işbaran akçayır <isbaran@gmail.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev53\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-06-30 15:16+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-06-30 03:16+0000\n"
|
||||
"Last-Translator: işbaran akçayır <isbaran@gmail.com>\n"
|
||||
"Language-Team: Turkish (Turkey)\n"
|
||||
"Language: tr-TR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"X-POOTLE-MTIME: 1495454551.000000\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr " ([^\" ]+|\"[^\"]+\") \\(Öntanımlı\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "Temizliği iptal et"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "İşlemler"
|
||||
|
||||
msgid "Add Extra"
|
||||
msgstr "Ek Ekle"
|
||||
|
||||
msgid "Add Instance Property"
|
||||
msgstr "Sunucu Özelliği Ekle"
|
||||
|
||||
msgid "Add Property"
|
||||
msgstr "Özellik Ekle"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"\"%s\" düğümünü silmek istediğinize emin misiniz? Bu eylem geri alınamaz."
|
||||
msgstr[1] ""
|
||||
"\"%s\" düğümlerini silmek istediğinize emin misiniz? Bu eylem geri alınamaz."
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] ""
|
||||
"\"%s\" bağlantı noktasını silmek istediğinize emin misiniz? Bu eylem geri "
|
||||
"alınamaz."
|
||||
msgstr[1] ""
|
||||
"\"%s\" bağlantı noktalarını silmek istediğinize emin misiniz? Bu eylem geri "
|
||||
"alınamaz."
|
||||
|
||||
msgid "Boot Device"
|
||||
msgstr "Önyükleme Aygıtı"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "İptal"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "Şasi ID'si"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "Bir İmaj seçin"
|
||||
|
||||
msgid "Clean"
|
||||
msgstr "Temizle"
|
||||
|
||||
msgid "Clean Node"
|
||||
msgstr "Düğümü Temizle"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "Temizlik Adımı"
|
||||
|
||||
msgid "Clean node"
|
||||
msgstr "Düğümü sil"
|
||||
|
||||
msgid "Clean steps should be an non-empty array"
|
||||
msgstr "Temizlik adımları boş-olmayan bir dizi olmalı"
|
||||
|
||||
msgid "Click link to view console"
|
||||
msgstr "Konsolu görüntülemek için bağlantıya tıklayın"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Yapılandırma"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "Konsol Etkin"
|
||||
|
||||
msgid "Console Info."
|
||||
msgstr "Konsol Bilgisi."
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "Bağlantı Noktası Oluştur"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "Oluşturulduğu zaman"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "Öntanımlı olarak ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "Düğümü Sil"
|
||||
msgstr[1] "Düğümleri Sil"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "Bağlantı Noktasını Sil"
|
||||
msgstr[1] "Bağlantı Noktalarını Sil"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "Çekirdeği Yerleştir"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "Ramdisk'i Yerleştir"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "Aygıt"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "Sürücü"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "Sürücü Ayrıntıları"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "Sürücü Bilgisi"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "Sürücü Onaylama"
|
||||
|
||||
msgid ""
|
||||
"Each cleaning step must be an object that contains \"interface\" and \"step"
|
||||
"\" properties"
|
||||
msgstr ""
|
||||
"Her temizlik adımı \"arayüz\" ve \"adım\" özelliklerinden birini içeren "
|
||||
"nesne olmalı"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "Düğümü Düzenle"
|
||||
|
||||
msgid "Edit Port"
|
||||
msgstr "Bağlantı Noktasını Düzenle"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "Düğümü Kaydet"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "Ek"
|
||||
|
||||
msgid "Extra Property Name"
|
||||
msgstr "Ek Özellik İsmi"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "Ekler"
|
||||
|
||||
msgid "General"
|
||||
msgstr "Genel"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "İmaj Kaynağı"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "İncele"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "İnceleme Şu Anda Bitti"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "İnceleme Şu Anda Başlatıldı"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "Instance ID"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "Sunucu Bilgisi"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "Instance Adı"
|
||||
|
||||
msgid "Instance Property Name"
|
||||
msgstr "Sunucu Özellik İsmi"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "Arayüz"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "Çekirdek"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "Son Hata"
|
||||
|
||||
msgid "Local link connection"
|
||||
msgstr "Yerel bağlantı"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC Adresi"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC adresi"
|
||||
|
||||
msgid "MAC address or OpenFlow datapath ID"
|
||||
msgstr "MAC adresi veya OpenFlow veriyolu ID'si"
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "Bakım"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "Bakım Sebebi"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "Taşı"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "İsim"
|
||||
|
||||
msgid "Network Interface"
|
||||
msgstr "Ağ Arayüzü"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "Sunucu Yok"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "Hiçbir ağ bağlantı noktası tanımlanmamış"
|
||||
|
||||
msgid "No reason given."
|
||||
msgstr "Sebep verilmemiş."
|
||||
|
||||
msgid "Node"
|
||||
msgstr "Düğüm"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "Düğüm Sürücüsü"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "Düğüm Kimliği"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "Düğüm Bilgisi"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "Düğüm İsmi"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "Bunlardan biri, (.*) belirtilmeli\\."
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "Önizleme"
|
||||
|
||||
msgid "PXE enabled"
|
||||
msgstr "PXE etkin"
|
||||
|
||||
msgid "Persistent"
|
||||
msgstr "Kalıcı"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "Bağlantı noktası başarıyla oluşturuldu"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "Portlar"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "Güç Durumu"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "Güç kapa"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "Güç aç"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "Özellikler"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "Özellik İsmi"
|
||||
|
||||
msgid "Provide a list of cleaning steps in JSON format"
|
||||
msgstr "JSON biçiminde temizlik adımları listesi sağlayın"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr ""
|
||||
"Seçili düğüm(ler)i neden bakım kipine soktuğunuzla ilgili bir sebep verin "
|
||||
"(isteğe bağlı)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "Hazırlık Durumu"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "Hazırlık Durumu"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "Hazırlık Durumu"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "Düğüm(ler)i Bakım Kipine Sok"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "Ramdisk"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "Sebep"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "Yeniden başlat"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Tazele"
|
||||
|
||||
msgid "Refresh page to see updated console details"
|
||||
msgstr "Güncel konsol ayrıntılarını görmek için sayfayı tazeleyin"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "Güncel güç durumunu görmek için sayfayı tazeleyin"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "Gerekli"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "Yer ayırmalar"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "Kök GB"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH Adresi"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH Anahtar İçeriği"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH Anahtar Dosyası"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH Parolası"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH Bağlantı Noktası"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH Kullanıcı adı"
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH terminal bağlantı noktası"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "Bir Sürücü Seçin"
|
||||
|
||||
msgid "Soft power off"
|
||||
msgstr "Yumuşak güç kapa"
|
||||
|
||||
msgid "Soft reboot"
|
||||
msgstr "Yumuşak yeniden başlatma"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "Gönder"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "\"%s\" düğümü başarıyla silindi."
|
||||
msgstr[1] "\"%s\" düğümleri başarıyla silindi."
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "\"%s\" bağlantı noktası başarıyla silindi"
|
||||
msgstr[1] "\"%s\" bağlantı noktaları başarıyla silindi"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "Düğüm %s başarıyla güncellendi"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated port %s"
|
||||
msgstr "Bağlantı noktası %s başarıyla güncellendi"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "Hedef Güç Durumu"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "Hedef Hazırlık Durumu"
|
||||
|
||||
msgid ""
|
||||
"This field is disabled because a port cannot have any connectivity "
|
||||
"attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless "
|
||||
"its associated node is in an enroll, inspecting, mangeable state; or in "
|
||||
"maintenance mode."
|
||||
msgstr ""
|
||||
"Bu alan kapalı çünkü bağlantı noktası bağlanırlık öznitelikleri "
|
||||
"(pxe_enabled, local_link_connection, portgroup_id), ilişkili düğümü kayıt, "
|
||||
"inceleme, yönetilir durum; veya bakım kipinde olmadan güncellenemez."
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "Düğüm güncelleme yaması oluşturulamadı."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "Düğüm oluşturulamıyor: %s"
|
||||
|
||||
msgid "Unable to create port update patch."
|
||||
msgstr "Bağlantı noktası güncelleme yaması oluşturulamadı."
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "Bağlantı noktası oluşturulamıyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "\"%s\" düğümü silinemedi"
|
||||
msgstr[1] "\"%s\" düğümleri silinemedi"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "Düğüm %s silinemiyor: %s"
|
||||
|
||||
#, fuzzy, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "\"%s\" bağlantı noktası silinemiyor"
|
||||
msgstr[1] "\"%s\" bağlantı noktaları silinemiyor"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "Bağlantı noktası silinemiyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to get console for node %s: %s"
|
||||
msgstr "Düğüm %s için konsol alınamadı: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "Düğümün gücü kesilemiyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "Ironic sürücüleri alınamıyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "Ironic düğümler alınamadı. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve boot device for Ironic node. %s"
|
||||
msgstr "Ironic düğüm için ön yükleme aygıtı alınamadı. %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "Sürücü özellikleri alınamıyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "Ironic düğümü bağlantı noktaları alınamadı: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "Ironic düğümü alınamadı: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set console mode: %s"
|
||||
msgstr "Konsol kipi ayarlanamıyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "Düğüm hazırlık durumu ayarlanamıyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "Düğüm %s güncellenemedi: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update port %s: %s"
|
||||
msgstr "Bağlantı noktası %s güncellenemiyor: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "Düğüm %s onaylanamıyor: %s"
|
||||
|
||||
msgid "Unable to validate the JSON input"
|
||||
msgstr "JSON girdisi onaylanamadı"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "Düğümü Güncelle"
|
||||
|
||||
msgid "Update Port"
|
||||
msgstr "Bağlantı Noktasını Güncelle"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "Güncelleme saati"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "Geçerli"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "Sanallaştırma Yazılımı"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "öntanımlı (?:değer )? ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,18 +0,0 @@
|
|||
# Shengjing Zhu <zsj950618@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 1.1.1.dev33\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2016-08-15 22:06+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-08-09 08:49+0000\n"
|
||||
"Last-Translator: Shengjing Zhu <zsj950618@gmail.com>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh-CN\n"
|
||||
"X-Generator: Zanata 3.7.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Ironic Bare Metal Provisioning"
|
||||
msgstr "裸金属 Ironic 配置向导"
|
|
@ -1,607 +0,0 @@
|
|||
# OpenStack Infra <zanata@openstack.org>, 2015. #zanata
|
||||
# zhangjingwen <zhangjingwen@cn.fujitsu.com>, 2015. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
|
||||
# Shengjing Zhu <zsj950618@gmail.com>, 2016. #zanata
|
||||
# sunanchen <KF.sunanchen@h3c.com>, 2016. #zanata
|
||||
# vuuv <froms2008@gmail.com>, 2016. #zanata
|
||||
# Bin <liubin@glab.cn>, 2017. #zanata
|
||||
# Gaoxiao Zhu <zhu.gaoxiao@h3c.com>, 2017. #zanata
|
||||
# liujunpeng <liujunpeng@inspur.com>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: ironic-ui 2.3.1.dev73\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
|
||||
"POT-Creation-Date: 2017-07-21 15:07+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-22 03:50+0000\n"
|
||||
"Last-Translator: Bin <liubin@glab.cn>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh-CN\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid " ([^\" ]+|\"[^\"]+\") \\(Default\\)"
|
||||
msgstr "([^\" ]+|\"[^\"]+\") \\(Default标签\\)"
|
||||
|
||||
msgid "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
msgstr "(?:[Oo]ne of )(?!this)((?:(?:\"[^\"]+\"|[^,\\. ]+)(?:, |\\.))+)"
|
||||
|
||||
#, python-format
|
||||
msgid "A request has been made to change the provisioning state of node %s"
|
||||
msgstr "更改节点 \"%s\" 服务状态的请求已提交。"
|
||||
|
||||
msgid "Abort cleaning"
|
||||
msgstr "放弃清理"
|
||||
|
||||
msgid "Actions"
|
||||
msgstr "动作"
|
||||
|
||||
msgid "Add Extra"
|
||||
msgstr "增加额外信息"
|
||||
|
||||
msgid "Add Instance Property"
|
||||
msgstr "添加实例属性"
|
||||
|
||||
msgid "Add Property"
|
||||
msgstr "添加属性"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete node \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete nodes \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "你确认要删除节点\"%s\"嘛?此操作将不可恢复"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete port \"%s\"? This action cannot be undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete ports \"%s\"? This action cannot be undone."
|
||||
msgstr[0] "你确认要删除端口\"%s\"吗?本操作无法恢复。"
|
||||
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Are you sure you want to delete portgroup \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgid_plural ""
|
||||
"Are you sure you want to delete portgroups \"%s\"? This action cannot be "
|
||||
"undone."
|
||||
msgstr[0] "你确定要删除端口组\"%s\"?本操作无法恢复。"
|
||||
|
||||
msgid "Attributes"
|
||||
msgstr "属性"
|
||||
|
||||
msgid "Boot Device"
|
||||
msgstr "引导设备"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
msgid "Chassis ID"
|
||||
msgstr "机架ID"
|
||||
|
||||
msgid "Choose an Image"
|
||||
msgstr "选择一个镜像"
|
||||
|
||||
msgid "Clean"
|
||||
msgstr "清理"
|
||||
|
||||
msgid "Clean Node"
|
||||
msgstr "清理节点"
|
||||
|
||||
msgid "Clean Step"
|
||||
msgstr "清理步骤"
|
||||
|
||||
msgid "Clean node"
|
||||
msgstr "清理节点"
|
||||
|
||||
msgid "Clean steps should be an non-empty array"
|
||||
msgstr "清理步骤应该是一个非空数组"
|
||||
|
||||
msgid "Click link to view console"
|
||||
msgstr "点击链接查看控制台"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "配置"
|
||||
|
||||
msgid "Console Enabled"
|
||||
msgstr "允许控制台"
|
||||
|
||||
msgid "Console Info."
|
||||
msgstr "控制台信息。"
|
||||
|
||||
msgid "Create Port"
|
||||
msgstr "创建端口"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "创建于"
|
||||
|
||||
msgid "Defaults to ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "默认为 ([^\"\\. ]+|\"[^\"]+\")"
|
||||
|
||||
msgid "Delete Node"
|
||||
msgid_plural "Delete Nodes"
|
||||
msgstr[0] "删除节点"
|
||||
|
||||
msgid "Delete Port"
|
||||
msgid_plural "Delete Ports"
|
||||
msgstr[0] "删除端口"
|
||||
|
||||
msgid "Delete Portgroup"
|
||||
msgid_plural "Delete Portgroups"
|
||||
msgstr[0] "删除端口组"
|
||||
|
||||
msgid "Deploy Kernel"
|
||||
msgstr "部署内核"
|
||||
|
||||
msgid "Deploy Ramdisk"
|
||||
msgstr "部署虚拟内存盘"
|
||||
|
||||
msgid "Device"
|
||||
msgstr "设备"
|
||||
|
||||
msgid "Driver"
|
||||
msgstr "驱动"
|
||||
|
||||
msgid "Driver Details"
|
||||
msgstr "驱动详情"
|
||||
|
||||
msgid "Driver Info"
|
||||
msgstr "驱动信息"
|
||||
|
||||
msgid "Driver Validation"
|
||||
msgstr "驱动验证"
|
||||
|
||||
msgid ""
|
||||
"Each cleaning step must be an object that contains \"interface\" and \"step"
|
||||
"\" properties"
|
||||
msgstr "每一步清理步骤必需是一个包含“接口”和“步骤”属性的对象"
|
||||
|
||||
msgid "Edit Node"
|
||||
msgstr "编辑节点"
|
||||
|
||||
msgid "Edit Port"
|
||||
msgstr "编辑端口"
|
||||
|
||||
msgid "Enroll Node"
|
||||
msgstr "注册节点"
|
||||
|
||||
msgid "Extra"
|
||||
msgstr "额外信息"
|
||||
|
||||
msgid "Extra Property Name"
|
||||
msgstr "额外属性名称"
|
||||
|
||||
msgid "Extras"
|
||||
msgstr "额外信息"
|
||||
|
||||
msgid "General"
|
||||
msgstr "概要信息"
|
||||
|
||||
msgid "Image Source"
|
||||
msgstr "镜像源"
|
||||
|
||||
msgid "Indicates whether this port should be used when PXE booting this node"
|
||||
msgstr "当PXE应到该节点时,表明此端口是否应该被使用"
|
||||
|
||||
msgid "Inspect"
|
||||
msgstr "检查"
|
||||
|
||||
msgid "Inspection Finished At"
|
||||
msgstr "检查结束于"
|
||||
|
||||
msgid "Inspection Started At"
|
||||
msgstr "检查开始于"
|
||||
|
||||
msgid "Instance ID"
|
||||
msgstr "云主机ID"
|
||||
|
||||
msgid "Instance Info"
|
||||
msgstr "实例信息"
|
||||
|
||||
msgid "Instance Name"
|
||||
msgstr "实例名字"
|
||||
|
||||
msgid "Instance Property Name"
|
||||
msgstr "实例属性名称"
|
||||
|
||||
msgid "Interface"
|
||||
msgstr "接口"
|
||||
|
||||
msgid "Kernel"
|
||||
msgstr "内核"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "最近的一次错误"
|
||||
|
||||
msgid "Local Link Connection"
|
||||
msgstr "本地链接连接"
|
||||
|
||||
msgid "Local link connection"
|
||||
msgstr "本地链接连接"
|
||||
|
||||
msgid "MAC Address"
|
||||
msgstr "MAC地址"
|
||||
|
||||
msgid "MAC address"
|
||||
msgstr "MAC地址"
|
||||
|
||||
msgid "MAC address for this port. Required."
|
||||
msgstr "需要该端口的MAC地址"
|
||||
|
||||
msgid "MAC address or OpenFlow datapath ID"
|
||||
msgstr "MAC地址或OpenFlow datapath ID"
|
||||
|
||||
msgid "Maintenance"
|
||||
msgstr "维护"
|
||||
|
||||
msgid "Maintenance Reason"
|
||||
msgstr "维护原因"
|
||||
|
||||
msgid "Move to"
|
||||
msgstr "移动"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "名称"
|
||||
|
||||
msgid "Network Interface"
|
||||
msgstr "网络接口"
|
||||
|
||||
msgid "No Instance"
|
||||
msgstr "没有实例"
|
||||
|
||||
msgid "No network ports have been defined"
|
||||
msgstr "未定义网络端口"
|
||||
|
||||
msgid "No portgroups have been defined"
|
||||
msgstr "未定义端口组"
|
||||
|
||||
msgid "No reason given."
|
||||
msgstr "没有原因提供。"
|
||||
|
||||
msgid "Node"
|
||||
msgstr "节点"
|
||||
|
||||
#, python-format
|
||||
msgid "Node %s is already in target maintenance state."
|
||||
msgstr "节点\"%s\"已经处于目标维护状态"
|
||||
|
||||
msgid "Node Driver"
|
||||
msgstr "节点驱动"
|
||||
|
||||
msgid "Node ID"
|
||||
msgstr "节点ID"
|
||||
|
||||
msgid "Node Info"
|
||||
msgstr "节点信息"
|
||||
|
||||
msgid "Node Name"
|
||||
msgstr "节点名称"
|
||||
|
||||
msgid "One of this, (.*) must be specified\\."
|
||||
msgstr "必须指定其中的一个 (.*)"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "概览"
|
||||
|
||||
msgid "PXE Enabled"
|
||||
msgstr "PXE启用"
|
||||
|
||||
msgid "PXE enabled"
|
||||
msgstr "PXE启用"
|
||||
|
||||
msgid "Persistent"
|
||||
msgstr "持久"
|
||||
|
||||
msgid "Port successfully created"
|
||||
msgstr "端口创建成功"
|
||||
|
||||
msgid "Portgroup"
|
||||
msgstr "端口组"
|
||||
|
||||
msgid "Portgroup successfully created"
|
||||
msgstr "端口组创建成功"
|
||||
|
||||
msgid "Portgroup that this port belongs to"
|
||||
msgstr "此端口所属的端口组"
|
||||
|
||||
msgid "Portgroups"
|
||||
msgstr "端口组"
|
||||
|
||||
msgid "Ports"
|
||||
msgstr "端口"
|
||||
|
||||
msgid "Power State"
|
||||
msgstr "电源状态"
|
||||
|
||||
msgid "Power off"
|
||||
msgstr "关闭电源"
|
||||
|
||||
msgid "Power on"
|
||||
msgstr "打开电源"
|
||||
|
||||
msgid "Properties"
|
||||
msgstr "属性"
|
||||
|
||||
msgid "Property Name"
|
||||
msgstr "属性名称"
|
||||
|
||||
msgid "Provide a list of cleaning steps in JSON format"
|
||||
msgstr "提供一个JSON格式的清理步骤列表"
|
||||
|
||||
msgid ""
|
||||
"Provide a reason for why you are putting the selected node(s) into "
|
||||
"maintenance mode (optional)"
|
||||
msgstr "提供一个你为什么选择将节点置于维护模式的原因(可选)"
|
||||
|
||||
msgid "Provision State"
|
||||
msgstr "配置状态"
|
||||
|
||||
msgid "Provisioning State"
|
||||
msgstr "配置状态"
|
||||
|
||||
msgid "Provisioning Status"
|
||||
msgstr "配置状态"
|
||||
|
||||
msgid "Put Node(s) Into Maintenance Mode"
|
||||
msgstr "将节点置于维护模式"
|
||||
|
||||
msgid "Ramdisk"
|
||||
msgstr "内存盘"
|
||||
|
||||
msgid "Reason"
|
||||
msgstr "原因"
|
||||
|
||||
msgid "Reboot"
|
||||
msgstr "重启"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "刷新"
|
||||
|
||||
msgid "Refresh page to see set boot device"
|
||||
msgstr "刷新页面来设置引导设备"
|
||||
|
||||
msgid "Refresh page to see updated console details"
|
||||
msgstr "刷新页面查看更新的控制台细节"
|
||||
|
||||
msgid "Refresh page to see updated power status"
|
||||
msgstr "刷新页面来确认电源状态是否更新"
|
||||
|
||||
msgid "Required"
|
||||
msgstr "必需的"
|
||||
|
||||
msgid "Reservation"
|
||||
msgstr "预留"
|
||||
|
||||
msgid "Resource Class"
|
||||
msgstr "资源类"
|
||||
|
||||
msgid "Root GB"
|
||||
msgstr "根磁盘 GB"
|
||||
|
||||
msgid "SSH Address"
|
||||
msgstr "SSH 地址"
|
||||
|
||||
msgid "SSH Key Contents"
|
||||
msgstr "SSH 密钥内容"
|
||||
|
||||
msgid "SSH Key File"
|
||||
msgstr "SSH 密钥文件"
|
||||
|
||||
msgid "SSH Password"
|
||||
msgstr "SSH 密码"
|
||||
|
||||
msgid "SSH Port"
|
||||
msgstr "SSH端口"
|
||||
|
||||
msgid "SSH Username"
|
||||
msgstr "SSH用户名"
|
||||
|
||||
msgid "SSH terminal port"
|
||||
msgstr "SSH 终端端口"
|
||||
|
||||
msgid "Select a Driver"
|
||||
msgstr "选择一种驱动"
|
||||
|
||||
msgid "Select a boot device"
|
||||
msgstr "选择一个引导设备"
|
||||
|
||||
msgid "Select a portgroup"
|
||||
msgstr "选择一个端口组"
|
||||
|
||||
msgid "Set Boot Device"
|
||||
msgstr "设置引导设备"
|
||||
|
||||
msgid "Set boot device"
|
||||
msgstr "设置引导设备"
|
||||
|
||||
msgid "Soft power off"
|
||||
msgstr "软关机"
|
||||
|
||||
msgid "Soft reboot"
|
||||
msgstr "软重启"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "提交"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted node \"%s\""
|
||||
msgid_plural "Successfully deleted nodes \"%s\""
|
||||
msgstr[0] "成功删除节点\"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted port \"%s\""
|
||||
msgid_plural "Successfully deleted ports \"%s\""
|
||||
msgstr[0] "删除端口\"%s\"成功"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully deleted portgroup \"%s\""
|
||||
msgid_plural "Successfully deleted portgroups \"%s\""
|
||||
msgstr[0] "成功删除端口组\"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated node %s"
|
||||
msgstr "成功更新节点:“%s”。"
|
||||
|
||||
#, python-format
|
||||
msgid "Successfully updated port %s"
|
||||
msgstr "成功更新端口:“%s”。"
|
||||
|
||||
msgid "Target Power State"
|
||||
msgstr "标记电源状态"
|
||||
|
||||
msgid "Target Provision State"
|
||||
msgstr "标记配置状态"
|
||||
|
||||
msgid ""
|
||||
"This field is disabled because a port cannot have any connectivity "
|
||||
"attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless "
|
||||
"its associated node is in an enroll, inspecting, mangeable state; or in "
|
||||
"maintenance mode."
|
||||
msgstr ""
|
||||
"这些域被禁用了,因为端口不能有任何已更新的连接属性((pxe_enabled, "
|
||||
"local_link_connection, portgroup_id),除非节点处于注册,检查,可管理状态或处"
|
||||
"于维护模式。"
|
||||
|
||||
msgid "UUID"
|
||||
msgstr "UUID"
|
||||
|
||||
msgid "Unable to create node update patch."
|
||||
msgstr "不能创建节点更新补丁"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create node: %s"
|
||||
msgstr "无法创建Ironic节点: %s"
|
||||
|
||||
msgid "Unable to create port update patch."
|
||||
msgstr "不能创建端口更新补丁"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create port: %s"
|
||||
msgstr "无法创建端口: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to create portgroup: %s"
|
||||
msgstr "无法创建端口组: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node \"%s\""
|
||||
msgid_plural "Unable to delete nodes \"%s\""
|
||||
msgstr[0] "无法删除节点\"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete node %s: %s"
|
||||
msgstr "无法删除Ironic节点\"%s\": %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port \"%s\""
|
||||
msgid_plural "Unable to delete ports \"%s\""
|
||||
msgstr[0] "无法删除端口\"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete port: %s"
|
||||
msgstr "无法删除端口: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup \"%s\""
|
||||
msgid_plural "Unable to delete portgroups \"%s\""
|
||||
msgstr[0] "无法删除端口组\"%s\""
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to delete portgroup: %s"
|
||||
msgstr "无法删除端口组:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to get console for node %s: %s"
|
||||
msgstr "无法获取节点的控制台 %s: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to power off the node: %s"
|
||||
msgstr "无法关闭节点电源: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic drivers: %s"
|
||||
msgstr "无法获取Ironic驱动: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic node portgroups: %s"
|
||||
msgstr "无法获取Ironic节点端口组信息: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve Ironic nodes. %s"
|
||||
msgstr "无法获取Ironic节点信息: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve boot device for Ironic node. %s"
|
||||
msgstr "无法获取Ironic节点引导设备信息。%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve driver properties: %s"
|
||||
msgstr "无法获取驱动属性: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve portgroup ports: %s"
|
||||
msgstr "无法获取端口组端口:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve supported boot devices for Ironic node. %s"
|
||||
msgstr "无法获取Ironic节点支持的引导设备信息。%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node ports: %s"
|
||||
msgstr "无法获取Ironic节点端口信息: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to retrieve the Ironic node: %s"
|
||||
msgstr "无法获取Ironic节点信息: %s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set Ironic node %s maintenance state: %s"
|
||||
msgstr "不能设置Ironic节点%s维护状态:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set boot device: %s"
|
||||
msgstr "不能设置引导设备:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set console mode: %s"
|
||||
msgstr "不能设置控制台模式:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to set node provision state: %s"
|
||||
msgstr "不能设置节点提供状态:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update node %s: %s"
|
||||
msgstr "无法更新节点%s:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to update port %s: %s"
|
||||
msgstr "无法更新端口%s:%s"
|
||||
|
||||
#, python-format
|
||||
msgid "Unable to validate node %s: %s"
|
||||
msgstr "无法验证节点%s:%s"
|
||||
|
||||
msgid "Unable to validate the JSON input"
|
||||
msgstr "无法验证JSON输入"
|
||||
|
||||
msgid "Update Node"
|
||||
msgstr "更新节点"
|
||||
|
||||
msgid "Update Port"
|
||||
msgstr "更新端口"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "已更新于"
|
||||
|
||||
msgid "Valid"
|
||||
msgstr "有效"
|
||||
|
||||
msgid "Virtualization Software"
|
||||
msgstr "虚拟化软件"
|
||||
|
||||
msgid "default (?:value )?is ([^\"\\. ]+|\"[^\"]+\")"
|
||||
msgstr "默认值 (?:value )?是 ([^\"\\. ]+|\"[^\"]+\")"
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.directive('autoFocus', AutoFocus);
|
||||
|
||||
AutoFocus.$inject = ['$timeout'];
|
||||
|
||||
function AutoFocus($timeout) {
|
||||
return {
|
||||
restrict: 'AC',
|
||||
link: function(scope, elem) {
|
||||
$timeout(function() {
|
||||
elem[0].focus();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,275 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Controller used to support operations on an Ironic node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('BaseNodeController', BaseNodeController);
|
||||
|
||||
BaseNodeController.$inject = [
|
||||
'$uibModalInstance',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.app.core.openstack-service-api.glance',
|
||||
'horizon.dashboard.admin.ironic.base-node.service',
|
||||
'horizon.dashboard.admin.ironic.validHostNamePattern',
|
||||
'$log',
|
||||
'ctrl'
|
||||
];
|
||||
|
||||
function BaseNodeController($uibModalInstance,
|
||||
ironic,
|
||||
glance,
|
||||
baseNodeService,
|
||||
validHostNamePattern,
|
||||
$log,
|
||||
ctrl) {
|
||||
ctrl.validHostNameRegex = new RegExp(validHostNamePattern);
|
||||
ctrl.drivers = null;
|
||||
ctrl.images = null;
|
||||
ctrl.loadingDriverProperties = false;
|
||||
// Object containing the set of properties associated with the currently
|
||||
// selected driver
|
||||
ctrl.driverProperties = null;
|
||||
ctrl.driverPropertyGroups = null;
|
||||
|
||||
ctrl.modalTitle = gettext("Node");
|
||||
ctrl.submitButtonTitle = gettext("Submit");
|
||||
|
||||
/* A property-collection is a set of properties that will be displayed
|
||||
in the node view as a minimal browser ui that supports:
|
||||
- adding new properties
|
||||
- displaying the list of properties in the set
|
||||
- changing the value of properties
|
||||
|
||||
Collection attributes:
|
||||
id: Name of the property inside the node object that is used
|
||||
to store the collection.
|
||||
formId: Name of the controller variable that can be used to
|
||||
access the property collection form.
|
||||
prompt: Label used to prompt the user to add properties
|
||||
to the collection.
|
||||
placeholder: Label used to guide the user in providiing property
|
||||
values.
|
||||
*/
|
||||
ctrl.propertyCollections = [
|
||||
{id: "properties",
|
||||
formId: "properties_form",
|
||||
title: gettext("Properties"),
|
||||
addPrompt: gettext("Add Property"),
|
||||
placeholder: gettext("Property Name")
|
||||
},
|
||||
{id: "extra",
|
||||
formId: "extra_form",
|
||||
title: gettext("Extras"),
|
||||
addPrompt: gettext("Add Extra"),
|
||||
placeholder: gettext("Extra Property Name")
|
||||
}];
|
||||
|
||||
// Node object suitable for Ironic api
|
||||
ctrl.node = {
|
||||
name: null,
|
||||
driver: null,
|
||||
driver_info: {},
|
||||
network_interface: null,
|
||||
resource_class: null
|
||||
};
|
||||
|
||||
angular.forEach(ctrl.propertyCollections, function(collection) {
|
||||
ctrl.node[collection.id] = {};
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Get the list of currently active Ironic drivers
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl._loadDrivers = function() {
|
||||
return ironic.getDrivers().then(function(drivers) {
|
||||
ctrl.drivers = drivers;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the list of images from Glance
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl._getImages = function() {
|
||||
glance.getImages().then(function(response) {
|
||||
ctrl.images = response.data.items;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Order driver properties in the form using the following
|
||||
* rules:
|
||||
*
|
||||
* (1) Properties that are related to one another should occupy adjacent
|
||||
* locations in the form
|
||||
*
|
||||
* (2) Required properties with no dependents should be located at the
|
||||
* top of the form
|
||||
*
|
||||
* @return {[]} Ordered list of groups of strongly related properties
|
||||
*/
|
||||
ctrl._sortDriverProperties = function() {
|
||||
// Build dependency graph between driver properties
|
||||
var graph = new baseNodeService.Graph();
|
||||
|
||||
// Create vertices
|
||||
angular.forEach(ctrl.driverProperties, function(property, name) {
|
||||
graph.addVertex(name, property);
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
// Create edges
|
||||
angular.forEach(ctrl.driverProperties,
|
||||
function(property, name) {
|
||||
var activators = property.getActivators();
|
||||
if (activators) {
|
||||
angular.forEach(activators,
|
||||
function(unused, activatorName) {
|
||||
graph.addEdge(name, activatorName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
// Perform depth-first-search to find groups of related properties
|
||||
var groups = [];
|
||||
graph.dfs(
|
||||
function(vertexList, components) {
|
||||
// Sort properties so that those with the largest number of
|
||||
// immediate dependents are the top of the list
|
||||
vertexList.sort(function(vertex1, vertex2) {
|
||||
return vertex2.adjacents.length - vertex1.adjacents.length;
|
||||
});
|
||||
|
||||
// Build component and add to list
|
||||
var component = new Array(vertexList.length);
|
||||
angular.forEach(vertexList, function(vertex, index) {
|
||||
component[index] = vertex.data;
|
||||
});
|
||||
components.push(component);
|
||||
},
|
||||
groups);
|
||||
groups.sort(baseNodeService.compareDriverPropertyGroups);
|
||||
|
||||
$log.debug("Found the following property groups: " +
|
||||
baseNodeService.driverPropertyGroupsToString(groups));
|
||||
return groups;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the properties associated with a specified driver
|
||||
*
|
||||
* @param {string} driverName - Name of driver
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.loadDriverProperties = function(driverName) {
|
||||
ctrl.node.driver = driverName;
|
||||
ctrl.node.driver_info = {};
|
||||
|
||||
ctrl.loadingDriverProperties = true;
|
||||
ctrl.driverProperties = null;
|
||||
ctrl.driverPropertyGroups = null;
|
||||
|
||||
return ironic.getDriverProperties(driverName).then(function(properties) {
|
||||
ctrl.driverProperties = {};
|
||||
angular.forEach(properties, function(desc, property) {
|
||||
ctrl.driverProperties[property] =
|
||||
new baseNodeService.DriverProperty(property,
|
||||
desc,
|
||||
ctrl.driverProperties);
|
||||
});
|
||||
ctrl.driverPropertyGroups = ctrl._sortDriverProperties();
|
||||
ctrl.loadingDriverProperties = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Cancel the current node operation
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.cancel = function() {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Check whether the specified property already exists
|
||||
*
|
||||
* @param {string} collectionId - Collection ID
|
||||
* @param {string} propertyName - Name of the property
|
||||
* @return {boolean} True if the property already exists,
|
||||
* otherwise false
|
||||
*/
|
||||
ctrl.collectionCheckPropertyUnique = function(collectionId, propertyName) {
|
||||
return !(propertyName in ctrl.node[collectionId]);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Delete a node metadata property
|
||||
*
|
||||
* @param {string} collectionId - Collection ID
|
||||
* @param {string} propertyName - Name of the property
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.collectionDeleteProperty = function(collectionId, propertyName) {
|
||||
delete ctrl.node[collectionId][propertyName];
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Check whether a specified driver property is
|
||||
* currently active
|
||||
*
|
||||
* @param {string} property - Driver property
|
||||
* @return {boolean} True if the property is active, false otherwise
|
||||
*/
|
||||
ctrl.isDriverPropertyActive = function(property) {
|
||||
return property.isActive();
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Check whether the node definition form is ready for
|
||||
* to be submitted.
|
||||
*
|
||||
* @return {boolean} True if the form is ready to be submitted,
|
||||
* otherwise false.
|
||||
*/
|
||||
ctrl.readyToSubmit = function() {
|
||||
var ready = true;
|
||||
if (ctrl.driverProperties) {
|
||||
for (var i = 0; i < ctrl.propertyCollections.length; i++) {
|
||||
var collection = ctrl.propertyCollections[i];
|
||||
if (ctrl[collection.formId].$invalid) {
|
||||
ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ready = false;
|
||||
}
|
||||
return ready;
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,107 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.base-node', function () {
|
||||
var ironicBackendMockService, uibModalInstance;
|
||||
var ctrl = {};
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
uibModalInstance = {
|
||||
dismiss: jasmine.createSpy()
|
||||
};
|
||||
$provide.value('$uibModalInstance', uibModalInstance);
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
|
||||
var controller = $injector.get('$controller');
|
||||
controller('BaseNodeController', {ctrl: ctrl});
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
ironicBackendMockService.postTest();
|
||||
});
|
||||
|
||||
it('controller should be defined', function () {
|
||||
expect(ctrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('base construction', function () {
|
||||
expect(ctrl.drivers).toBeNull();
|
||||
expect(ctrl.images).toBeNull();
|
||||
expect(ctrl.loadingDriverProperties).toBe(false);
|
||||
expect(ctrl.driverProperties).toBeNull();
|
||||
expect(ctrl.driverPropertyGroups).toBeNull();
|
||||
expect(ctrl.modalTitle).toBeDefined();
|
||||
angular.forEach(ctrl.propertyCollections, function(collection) {
|
||||
expect(Object.getOwnPropertyNames(collection).sort()).toEqual(
|
||||
PROPERTY_COLLECTION_PROPERTIES.sort());
|
||||
});
|
||||
expect(ctrl.propertyCollections)
|
||||
.toContain(jasmine.objectContaining({id: "properties"}));
|
||||
expect(ctrl.propertyCollections)
|
||||
.toContain(jasmine.objectContaining({id: "extra"}));
|
||||
expect(ctrl.node).toEqual({
|
||||
name: null,
|
||||
driver: null,
|
||||
driver_info: {},
|
||||
properties: {},
|
||||
extra: {},
|
||||
network_interface: null,
|
||||
resource_class: null});
|
||||
expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual(
|
||||
BASE_NODE_CONTROLLER_PROPERTIES.sort());
|
||||
});
|
||||
|
||||
it('_loadDrivers', function () {
|
||||
ctrl._loadDrivers();
|
||||
ironicBackendMockService.flush();
|
||||
expect(ctrl.drivers).toEqual(ironicBackendMockService.getDrivers());
|
||||
});
|
||||
|
||||
it('_getImages', function () {
|
||||
ctrl._getImages();
|
||||
ironicBackendMockService.flush();
|
||||
expect(ctrl.images).toEqual(ironicBackendMockService.getImages());
|
||||
});
|
||||
|
||||
it('cancel', function () {
|
||||
ctrl.cancel();
|
||||
expect(uibModalInstance.dismiss).toHaveBeenCalledWith('cancel');
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
|
@ -1,276 +0,0 @@
|
|||
<div class="modal-header" modal-draggable>
|
||||
<button type="button"
|
||||
class="close"
|
||||
ng-click="$dismiss()"
|
||||
aria-hidden="true"
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true" class="fa fa-times"></span>
|
||||
</button>
|
||||
<h3 class="modal-title">{$ ::ctrl.modalTitle $}</h3>
|
||||
</div>
|
||||
<!-- begin general node info modal -->
|
||||
<div class="modal-body">
|
||||
<div class="tabbable"> <!-- Only required for left/right tabs -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="required active">
|
||||
<a href=""
|
||||
data-target="#nodeInfo"
|
||||
data-toggle="tab"
|
||||
translate>Node Info</a></li>
|
||||
<li ng-if="!ctrl.driverProperties"
|
||||
class="disabled">
|
||||
<a data-target="#driverDetails"
|
||||
translate>Driver Details</a></li>
|
||||
<li ng-if="ctrl.driverProperties">
|
||||
<a href=""
|
||||
data-target="#driverDetails"
|
||||
data-toggle="tab"
|
||||
translate>Driver Details</a></li>
|
||||
</ul>
|
||||
|
||||
<!--base node form-->
|
||||
<form id="baseNodeForm"
|
||||
name="baseNodeForm">
|
||||
<!--tabbed content-->
|
||||
<div class="tab-content">
|
||||
<!-- node info tab-->
|
||||
<div class="tab-pane active" id="nodeInfo">
|
||||
<!--node name-->
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': baseNodeForm.name.$invalid &&
|
||||
baseNodeForm.name.$dirty}">
|
||||
<label for="name"
|
||||
class="control-label"
|
||||
translate>Node Name</label>
|
||||
<div>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
ng-model="ctrl.node.name"
|
||||
id="name"
|
||||
name="name"
|
||||
ng-pattern="ctrl.validHostNameRegex"
|
||||
placeholder="{$ ::'A unique node name. Optional.' | translate $}"/>
|
||||
</div>
|
||||
</div>
|
||||
<!--resource class-->
|
||||
<div class="form-group"
|
||||
ng-class="{'has-error': baseNodeForm.resource_class.$invalid &&
|
||||
baseNodeForm.resource_class.$dirty}">
|
||||
<label for="resource_class"
|
||||
class="control-label"
|
||||
translate>Resource Class</label>
|
||||
<div>
|
||||
<input type="text maxlength=80"
|
||||
class="form-control"
|
||||
ng-model="ctrl.node.resource_class"
|
||||
id="resource_class"
|
||||
name="resource_class"
|
||||
placeholder="{$ ::'A resource class name for future nova scheduling, in stand-alone usage only. Optional.' | translate $}"/>
|
||||
</div>
|
||||
</div>
|
||||
<!--network interface-->
|
||||
<div class="form-group">
|
||||
<label for="network_interface"
|
||||
class="control-label"
|
||||
translate>
|
||||
Network Interface
|
||||
</label>
|
||||
<span class="help-icon"
|
||||
data-container="body"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ ::'Network interface used for switching between provisioning, tenant, and cleaning networks.' | translate $}">
|
||||
<span class="fa fa-question-circle"></span>
|
||||
</span>
|
||||
<div>
|
||||
<div class="btn-group">
|
||||
<label class="btn btn-default"
|
||||
ng-repeat="opt in ['noop', 'flat', 'neutron']"
|
||||
ng-model="ctrl.node.network_interface"
|
||||
uib-btn-radio="opt">{$ opt $}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--node driver-->
|
||||
<div class="form-group required">
|
||||
<label for="driver"
|
||||
class="control-label"
|
||||
translate>Node Driver</label>
|
||||
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||
<div>
|
||||
<select id="driver"
|
||||
class="form-control"
|
||||
ng-options="driver as driver.name for driver in ctrl.drivers"
|
||||
ng-model="ctrl.selectedDriver"
|
||||
ng-change="ctrl.loadDriverProperties(ctrl.selectedDriver.name)">
|
||||
<option value="" disabled selected translate>Select a Driver</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!--property collections-->
|
||||
<div ng-repeat="collection in ctrl.propertyCollections">
|
||||
<form
|
||||
id="add-{$ collection.id $}-form"
|
||||
name="add-{$ collection.id $}-form">
|
||||
<div class="form-group">
|
||||
<label for="add-{$ collection.id $}-input"
|
||||
class="control-label">
|
||||
{$ collection.title $}</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon"
|
||||
style="width:25%;text-align:right">
|
||||
{$ collection.addPrompt $}:</span>
|
||||
<input class="form-control"
|
||||
id="add-{$ collection.id $}-input"
|
||||
type="text"
|
||||
ng-model="ctrl[collection.id]"
|
||||
placeholder="{$ collection.placeholder $}"/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
ng-disabled="!ctrl[collection.id] ||
|
||||
!ctrl.collectionCheckPropertyUnique(collection.id,
|
||||
ctrl[collection.id]) ||
|
||||
add-{$ collection.id $}-form.$invalid"
|
||||
ng-click="ctrl.node[collection.id][ctrl[collection.id]] = null;
|
||||
ctrl[collection.id] = null">
|
||||
<span class="fa fa-plus"> </span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form id="ctrl.{$ collection.formId $}"
|
||||
name="ctrl.{$ collection.formId $}">
|
||||
<div class="form-group">
|
||||
<div class="input-group input-group-sm"
|
||||
ng-repeat="(propertyName, propertyValue) in ctrl.node[collection.id]">
|
||||
<span class="input-group-addon"
|
||||
style="width:25%;text-align:right">
|
||||
{$ propertyName $}
|
||||
</span>
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
name="{$ propertyName $}"
|
||||
ng-model="ctrl.node[collection.id][propertyName]"
|
||||
ng-required="true"/>
|
||||
<div class="input-group-btn">
|
||||
<a class="btn btn-default"
|
||||
ng-click="ctrl.collectionDeleteProperty(collection.id, propertyName)">
|
||||
<span class="fa fa-minus"> </span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!--end node info tab-->
|
||||
|
||||
<!--driver details tab-->
|
||||
<div class="tab-pane" id="driverDetails">
|
||||
<p class="text-center"
|
||||
ng-if="ctrl.loadingDriverProperties">
|
||||
<small><em><i class="fa fa-spin fa-refresh"></i></em></small>
|
||||
</p>
|
||||
<div ng-repeat="propertyGroup in ctrl.driverPropertyGroups"
|
||||
ng-class="{'well': propertyGroup.length > 1}">
|
||||
<div class="form-group"
|
||||
ng-repeat="property in propertyGroup | filter:ctrl.isDriverPropertyActive"
|
||||
ng-init="name = property.name;
|
||||
selectOptions = property.getSelectOptions()"
|
||||
ng-class="{'has-error': baseNodeForm.{$ name $}.$invalid &&
|
||||
baseNodeForm.{$ name $}.$dirty}">
|
||||
<label for="{$ name $}"
|
||||
class="control-label"
|
||||
style="white-space: nowrap">
|
||||
{$ name $}
|
||||
<span ng-if="property.isRequired()"
|
||||
class="hz-icon-required fa fa-asterisk"></span>
|
||||
<span class="help-icon"
|
||||
data-container="body"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ property.getDescription() $}">
|
||||
<span class="fa fa-question-circle"></span>
|
||||
</span>
|
||||
</label>
|
||||
<div ng-if="!selectOptions"
|
||||
ng-class="{'input-group': name === 'deploy_kernel' ||
|
||||
name === 'deploy_ramdisk'}">
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="{$ name $}"
|
||||
name="{$ name $}"
|
||||
ng-model="property.inputValue"
|
||||
ng-pattern="property.getValidValueRegex()"
|
||||
placeholder="{$ property.defaultValue !== undefined ?
|
||||
property.defaultValue :
|
||||
property.getDescription() $}"
|
||||
ng-required="property.isRequired()"
|
||||
empty-to-pristine/>
|
||||
<div ng-if="name === 'deploy_kernel' ||
|
||||
name === 'deploy_ramdisk'"
|
||||
class="input-group-btn">
|
||||
<button type="button"
|
||||
class="btn btn-primary dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
translate>
|
||||
Choose an Image
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<a class="dropdown-item"
|
||||
ng-repeat="imageObj in ctrl.images"
|
||||
href="#"
|
||||
ng-click="property.inputValue = imageObj.id">{$ imageObj.name + ' [' + imageObj.id + ']' $}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="selectOptions" class="">
|
||||
<select ng-if="selectOptions.length > 4"
|
||||
id="{$ name $}"
|
||||
class="form-control"
|
||||
ng-options="opt for opt in selectOptions"
|
||||
ng-model="property.inputValue"
|
||||
ng-required="property.isRequired()">
|
||||
<option ng-if="property.defaultValue === undefined"
|
||||
value=""
|
||||
disabled
|
||||
selected>
|
||||
{$ property.getDescription() $}</option>
|
||||
</select>
|
||||
<div ng-if="selectOptions.length <= 4"
|
||||
class="btn-group">
|
||||
<label class="btn btn-default"
|
||||
ng-repeat="opt in selectOptions"
|
||||
ng-model="property.inputValue"
|
||||
uib-btn-radio="opt">{$ opt $}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end driver details tab-->
|
||||
</div>
|
||||
<!--end tabbed content-->
|
||||
</form>
|
||||
<!--end base node form-->
|
||||
</div>
|
||||
</div>
|
||||
<!--modal footer-->
|
||||
<div class="modal-footer ng-scope">
|
||||
<button class="btn btn-default"
|
||||
ng-click="ctrl.cancel()">
|
||||
<span class="fa fa-close"></span>
|
||||
<span class="ng-scope" translate>Cancel</span>
|
||||
</button>
|
||||
|
||||
<button type="submit"
|
||||
ng-disabled="!ctrl.readyToSubmit()"
|
||||
ng-click="ctrl.submit()"
|
||||
class="btn btn-primary">
|
||||
{$ ::ctrl.submitButtonTitle $}
|
||||
</button>
|
||||
</div>
|
|
@ -1,741 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var REQUIRED = " " + gettext("Required") + ".";
|
||||
|
||||
var SELECT_OPTIONS_REGEX =
|
||||
new RegExp(
|
||||
gettext('(?:[Oo]ne of )(?!this)((?:(?:"[^"]+"|[^,\\. ]+)(?:, |\\.))+)'));
|
||||
|
||||
var DEFAULT_IS_REGEX =
|
||||
new RegExp(gettext('default (?:value )?is ([^"\\. ]+|"[^"]+")'));
|
||||
|
||||
var DEFAULTS_TO_REGEX =
|
||||
new RegExp(gettext('Defaults to ([^"\\. ]+|"[^"]+")'));
|
||||
|
||||
var DEFAULT_IN_PARENS_REGEX =
|
||||
new RegExp(gettext(' ([^" ]+|"[^"]+") \\(Default\\)'));
|
||||
|
||||
var DEFAULT_REGEX_LIST = [DEFAULT_IS_REGEX,
|
||||
DEFAULTS_TO_REGEX,
|
||||
DEFAULT_IN_PARENS_REGEX];
|
||||
var ONE_OF_REGEX =
|
||||
new RegExp(gettext('One of this, (.*) must be specified\\.'));
|
||||
|
||||
var NOT_INSIDE_MATCH = -1;
|
||||
|
||||
var VALID_PORT_REGEX = new RegExp('^\\d+$');
|
||||
|
||||
var VALID_IPV4_ADDRESS = "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; // eslint-disable-line max-len
|
||||
|
||||
var VALID_IPV6_ADDRESS = "^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"; // eslint-disable-line max-len
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.base-node.service',
|
||||
baseNodeService);
|
||||
|
||||
baseNodeService.$inject = [
|
||||
'$uibModal',
|
||||
'$log',
|
||||
'horizon.dashboard.admin.ironic.validHostNamePattern',
|
||||
'horizon.dashboard.admin.ironic.validUuidPattern'
|
||||
];
|
||||
|
||||
function baseNodeService($uibModal,
|
||||
$log,
|
||||
validHostNamePattern,
|
||||
validUuidPattern) {
|
||||
var service = {
|
||||
DriverProperty: DriverProperty,
|
||||
PostfixExpr: PostfixExpr,
|
||||
Graph: Graph,
|
||||
driverPropertyGroupHasRequired: driverPropertyGroupHasRequired,
|
||||
driverPropertyGroupsToString: driverPropertyGroupsToString,
|
||||
compareDriverPropertyGroups: compareDriverPropertyGroups
|
||||
};
|
||||
|
||||
var VALID_ADDRESS_HOSTNAME_REGEX = new RegExp(VALID_IPV4_ADDRESS + "|" +
|
||||
VALID_IPV6_ADDRESS + "|" +
|
||||
validHostNamePattern);
|
||||
|
||||
var VALID_IMAGE_REGEX = new RegExp(validUuidPattern + "|" +
|
||||
"^(https?|file)://.+$");
|
||||
|
||||
/**
|
||||
The DriverProperty class is used to represent an ironic driver
|
||||
property. It is currently used by the base-node form to
|
||||
support property display, value assignment and validation.
|
||||
|
||||
The following rules are used to extract information about a property
|
||||
from the description returned by the driver.
|
||||
|
||||
1. If the description ends with " Required." a value must be
|
||||
supplied for the property.
|
||||
|
||||
2. The following syntax is used to extract default values
|
||||
from property descriptions.
|
||||
|
||||
Default is <value>(<space>|.)
|
||||
default is “<value>”
|
||||
default value is <value>(<space>|.)
|
||||
default value is “<value>”
|
||||
Defaults to <value>(<space>|.)
|
||||
Defaults to “<value>”
|
||||
<value> (Default)
|
||||
|
||||
3. The following syntax is used to determine whether a property
|
||||
is considered active. In the example below if the user specifies
|
||||
a value for <property-name-1>, properties 2 to n will be tagged
|
||||
inactive, and hidden from view. All properties are considered
|
||||
to be required.
|
||||
|
||||
One of this, <property-name-1>, <property-name-2>, …, or
|
||||
<property-name-n> must be specified.
|
||||
|
||||
4. The following syntax is used to determine whether a property
|
||||
is restricted to a set of enumerated values. The property will
|
||||
be displayed as an HTML select element.
|
||||
|
||||
[Oo]ne of <value-1>, "<value-2>", …, <value-n>.
|
||||
|
||||
5. The following syntax is used to determine whether a property is
|
||||
active and required based on the value of another property.
|
||||
If the property is not active it will not be displayed.
|
||||
|
||||
Required|Used only if <property-name> is set to <value-1>
|
||||
(or "<value-2>")*.
|
||||
|
||||
Notes:
|
||||
1. The properties "deploy_kernel" and "deploy_ramdisk" are
|
||||
assumed to accept Glance image uuids as valid values.
|
||||
|
||||
2. Property names ending in _port are assumed to only accept
|
||||
positive integer values
|
||||
|
||||
3. Property names ending in _address are assumed to only accept
|
||||
valid IPv4 and IPv6 addresses; and hostnames
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Construct a new driver property
|
||||
*
|
||||
* @class DriverProperty
|
||||
* @param {string} name - Name of property
|
||||
* @param {string} desc - Description of property
|
||||
* @param {object} propertySet - Set of properties to which this one belongs
|
||||
*
|
||||
* @property {string} defaultValue - Default value of the property
|
||||
* @property {string[]} selectOptions - If the property is limited to a
|
||||
* set of enumerated values then selectOptions will be an array of those
|
||||
* values, otherwise null
|
||||
* @property {boolean} required - Boolean value indicating whether a value
|
||||
* must be supplied for this property if it is active
|
||||
* @property {PostfixExpr} isActiveExpr - Null if this property is always
|
||||
* active; otherwise, a boolean expression that when evaluated will
|
||||
* return whether this variable is active. A property is considered
|
||||
* active if its role is not eliminated by the values of other
|
||||
* properties in the property-set.
|
||||
* @property {string} inputValue - User assigned value for this property
|
||||
* @property {regexp} validValueRegex - Regular expression used to
|
||||
* determine whether an input value is valid.
|
||||
* @returns {object} Driver property
|
||||
*/
|
||||
function DriverProperty(name, desc, propertySet) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.propertySet = propertySet;
|
||||
|
||||
// Determine whether this property should be presented as a selection
|
||||
this.selectOptions = this._analyzeSelectOptions();
|
||||
|
||||
this.required = null; // Initialize to unknown
|
||||
// Expression to be evaluated to determine whether property is active.
|
||||
// By default the property is considered active.
|
||||
this.isActiveExpr = null;
|
||||
var result = this._analyzeRequiredOnlyDependencies();
|
||||
if (result) {
|
||||
this.required = result[0];
|
||||
this.isActiveExpr = result[1];
|
||||
}
|
||||
if (!this.isActiveExpr) {
|
||||
result = this._analyzeOneOfDependencies();
|
||||
if (result) {
|
||||
this.required = result[0];
|
||||
this.isActiveExpr = result[1];
|
||||
}
|
||||
}
|
||||
if (this.required === null) {
|
||||
this.required = desc.endsWith(REQUIRED);
|
||||
}
|
||||
|
||||
this.defaultValue = this._getDefaultValue();
|
||||
this.inputValue = this.defaultValue;
|
||||
|
||||
// Infer that property is a boolean that can be represented as a
|
||||
// True/False selection
|
||||
if (this.selectOptions === null &&
|
||||
(this.defaultValue === "True" || this.defaultValue === "False")) {
|
||||
this.selectOptions = ["True", "False"];
|
||||
}
|
||||
|
||||
this.validValueRegex = _determineValidValueRegex(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Return a regular expression that can be used to
|
||||
* validate the value of a specified property
|
||||
*
|
||||
* @param {string} propertyName - Name of property
|
||||
* @return {regexp} Regular expression object or undefined
|
||||
*/
|
||||
function _determineValidValueRegex(propertyName) {
|
||||
var regex;
|
||||
if (propertyName.endsWith("_port")) {
|
||||
regex = VALID_PORT_REGEX;
|
||||
} else if (propertyName.endsWith("_address")) {
|
||||
regex = VALID_ADDRESS_HOSTNAME_REGEX;
|
||||
} else if (propertyName === "deploy_kernel") {
|
||||
regex = VALID_IMAGE_REGEX;
|
||||
} else if (propertyName === "deploy_ramdisk") {
|
||||
regex = VALID_IMAGE_REGEX;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
DriverProperty.prototype.isActive = function() {
|
||||
if (!this.isActiveExpr) {
|
||||
return true;
|
||||
}
|
||||
var ret = this.isActiveExpr.evaluate(this.propertySet);
|
||||
return ret[0] === PostfixExpr.status.OK &&
|
||||
typeof ret[1] === "boolean" ? ret[1] : true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get a regular expression object that can be used to
|
||||
* determine whether a value is valid for this property
|
||||
*
|
||||
* @return {regexp} Regular expression object or undefined
|
||||
*/
|
||||
DriverProperty.prototype.getValidValueRegex = function() {
|
||||
return this.validValueRegex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Must a value be provided for this property
|
||||
*
|
||||
* @return {boolean} True if a value must be provided for this property
|
||||
*/
|
||||
DriverProperty.prototype.isRequired = function() {
|
||||
return this.required;
|
||||
};
|
||||
|
||||
DriverProperty.prototype._analyzeSelectOptions = function() {
|
||||
var match = this.desc.match(SELECT_OPTIONS_REGEX);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var matches = match[1].substring(0, match[1].length - 1).split(", ");
|
||||
var options = [];
|
||||
angular.forEach(matches, function(match) {
|
||||
options.push(trimQuotes(match));
|
||||
});
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the list of select options for this property
|
||||
*
|
||||
* @return {string[]} null if this property is not selectable; else,
|
||||
* an array of selectable options
|
||||
*/
|
||||
DriverProperty.prototype.getSelectOptions = function() {
|
||||
return this.selectOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Remove leading/trailing double-quotes from a string
|
||||
*
|
||||
* @param {string} str - String to be trimmed
|
||||
* @return {string} trim'd string
|
||||
*/
|
||||
function trimQuotes(str) {
|
||||
return str.charAt(0) === '"'
|
||||
? str.substring(1, str.length - 1) : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the default value of this property
|
||||
*
|
||||
* @return {string} Default value of this property
|
||||
*/
|
||||
DriverProperty.prototype._getDefaultValue = function() {
|
||||
var value;
|
||||
for (var i = 0; i < DEFAULT_REGEX_LIST.length; i++) {
|
||||
var match = this.desc.match(DEFAULT_REGEX_LIST[i]);
|
||||
if (match) {
|
||||
value = trimQuotes(match[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
$log.debug("_getDefaultValue | " + this.desc + " | " + value);
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the input value of this property
|
||||
*
|
||||
* @return {string} the input value of this property
|
||||
*/
|
||||
DriverProperty.prototype.getInputValue = function() {
|
||||
return this.inputValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the default value of this property
|
||||
*
|
||||
* @return {string} the default value of this property
|
||||
*/
|
||||
DriverProperty.prototype.getDefaultValue = function() {
|
||||
return this.defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the description of this property
|
||||
*
|
||||
* @return {string} Description of this property
|
||||
*/
|
||||
DriverProperty.prototype.getDescription = function() {
|
||||
return this.desc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Use the property description to build an expression
|
||||
* that will evaluate to a boolean result indicating whether the
|
||||
* property is active
|
||||
*
|
||||
* @return {array} null if this property is not dependent on any others;
|
||||
* otherwise,
|
||||
* [0] boolean indicating whether if active a value must be
|
||||
* supplied for this property.
|
||||
* [1] an expression that when evaluated will return a boolean
|
||||
* result indicating whether this property is active
|
||||
*/
|
||||
DriverProperty.prototype._analyzeRequiredOnlyDependencies = function() {
|
||||
var re = /(Required|Used) only if ([^ ]+) is set to /g;
|
||||
var match = re.exec(this.desc);
|
||||
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Build logical expression to describe under what conditions this
|
||||
// property is active
|
||||
var expr = new PostfixExpr();
|
||||
var numAdds = 0;
|
||||
|
||||
var i = NOT_INSIDE_MATCH;
|
||||
var j = re.lastIndex;
|
||||
while (j < this.desc.length) {
|
||||
if (i === NOT_INSIDE_MATCH && this.desc.charAt(j) === ".") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.desc.charAt(j) === '"') {
|
||||
if (i === NOT_INSIDE_MATCH) {
|
||||
i = j + 1;
|
||||
} else {
|
||||
expr.addProperty(match[2]);
|
||||
expr.addValue(this.desc.substring(i, j));
|
||||
expr.addOperator(PostfixExpr.op.EQ);
|
||||
numAdds++;
|
||||
if (numAdds > 1) {
|
||||
expr.addOperator(PostfixExpr.op.OR);
|
||||
}
|
||||
i = NOT_INSIDE_MATCH;
|
||||
}
|
||||
}
|
||||
j++;
|
||||
}
|
||||
$log.debug("_analyzeRequiredOnlyDependencies | " +
|
||||
this.desc + " | " +
|
||||
match[2] + ", " +
|
||||
JSON.stringify(expr));
|
||||
return [match[1] === "Required", expr];
|
||||
};
|
||||
|
||||
DriverProperty.prototype._analyzeOneOfDependencies = function() {
|
||||
var match = this.desc.match(ONE_OF_REGEX);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Build logical expression to describe under what conditions this
|
||||
// property is active
|
||||
var expr = new PostfixExpr();
|
||||
|
||||
var parts = match[1].split(", or ");
|
||||
expr.addProperty(parts[1]);
|
||||
expr.addValue(undefined);
|
||||
expr.addOperator(PostfixExpr.op.EQ);
|
||||
|
||||
parts = parts[0].split(", ");
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
expr.addProperty(parts[i]);
|
||||
expr.addValue(undefined);
|
||||
expr.addOperator(PostfixExpr.op.EQ);
|
||||
expr.addOperator(PostfixExpr.op.AND);
|
||||
}
|
||||
$log.debug("_analyzeOneOfDependencies | " +
|
||||
this.desc + " | " +
|
||||
JSON.stringify(match) + ", " +
|
||||
JSON.stringify(expr));
|
||||
return [true, expr];
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get the names of the driver-properties whose values
|
||||
* determine whether this property is active
|
||||
*
|
||||
* @return {object} Object the properties of which are names of
|
||||
* activating driver-properties or null
|
||||
*/
|
||||
DriverProperty.prototype.getActivators = function() {
|
||||
return this.isActiveExpr ? this.isActiveExpr.getProperties() : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* PostFixExpr is a class primarily developed to support the
|
||||
* evaluation of boolean expressions that determine whether a
|
||||
* particular property is active.
|
||||
*
|
||||
* The expression is stored as a postfix sequence of operands and
|
||||
* operators. Operands are currently limited to the literal values
|
||||
* and the values of properties in a specified set. Currently
|
||||
* supported operands are ==, or, and.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function PostfixExpr() {
|
||||
this.elem = [];
|
||||
}
|
||||
|
||||
PostfixExpr.op = {
|
||||
EQ: "==",
|
||||
AND: "and",
|
||||
OR: "or"
|
||||
};
|
||||
|
||||
PostfixExpr.UNDEFINED = undefined;
|
||||
|
||||
PostfixExpr.status = {
|
||||
OK: 0,
|
||||
ERROR: 1,
|
||||
BAD_ARG: 2,
|
||||
UNKNOWN_OP: 3,
|
||||
MALFORMED: 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add a property to the expression
|
||||
*
|
||||
* @param {string} propertyName - Property name
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
PostfixExpr.prototype.addProperty = function(propertyName) {
|
||||
this.elem.push({name: propertyName});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add a value to the expression
|
||||
*
|
||||
* @param {object} value - value
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
PostfixExpr.prototype.addValue = function(value) {
|
||||
this.elem.push({value: value});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add an operator to the expression
|
||||
*
|
||||
* @param {PostfixExpr.op} opId - operator
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
PostfixExpr.prototype.addOperator = function(opId) {
|
||||
this.elem.push({op: opId});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Get a list of property names referenced by this
|
||||
* expression
|
||||
*
|
||||
* @return {object} An object each property of which corresponds to
|
||||
* a property in the expression
|
||||
*/
|
||||
PostfixExpr.prototype.getProperties = function() {
|
||||
var properties = {};
|
||||
angular.forEach(this.elem, function(elem) {
|
||||
if (angular.isDefined(elem.name)) {
|
||||
properties[elem.name] = true;
|
||||
}
|
||||
});
|
||||
return properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Evaluate a boolean binary operation
|
||||
*
|
||||
* @param {array} valStack - Stack of values to operate on
|
||||
* @param {string} opId - operator id
|
||||
*
|
||||
* @return {integer} Return code
|
||||
*/
|
||||
function _evaluateBoolBinaryOp(valStack, opId) {
|
||||
var retCode = PostfixExpr.status.OK;
|
||||
var val1 = valStack.pop();
|
||||
var val2 = valStack.pop();
|
||||
if (typeof val1 === "boolean" &&
|
||||
typeof val2 === "boolean") {
|
||||
switch (opId) {
|
||||
case PostfixExpr.op.AND:
|
||||
valStack.push(val1 && val2);
|
||||
break;
|
||||
case PostfixExpr.op.OR:
|
||||
valStack.push(val1 || val2);
|
||||
break;
|
||||
default:
|
||||
retCode = PostfixExpr.status.UNKNOWN_OP;
|
||||
}
|
||||
} else {
|
||||
retCode = PostfixExpr.status.BAD_ARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Evaluate the experssion using property values from
|
||||
* a specified set
|
||||
*
|
||||
* @param {object} propertySet - Dictionary of DriverProperty instances
|
||||
*
|
||||
* @return {array} Return code and Value of the expression
|
||||
*/
|
||||
PostfixExpr.prototype.evaluate = function(propertySet) {
|
||||
var resultStack = [];
|
||||
for (var i = 0, len = this.elem.length; i < len; i++) {
|
||||
var elem = this.elem[i];
|
||||
if (elem.hasOwnProperty("name")) {
|
||||
resultStack.push(propertySet[elem.name].getInputValue());
|
||||
} else if (elem.hasOwnProperty("value")) {
|
||||
resultStack.push(elem.value);
|
||||
} else if (elem.hasOwnProperty("op")) {
|
||||
if (elem.op === PostfixExpr.op.EQ) {
|
||||
var val1 = resultStack.pop();
|
||||
var val2 = resultStack.pop();
|
||||
resultStack.push(val1 === val2);
|
||||
} else {
|
||||
var ret = _evaluateBoolBinaryOp(resultStack, elem.op);
|
||||
if (ret !== PostfixExpr.status.OK) {
|
||||
return [ret, PostfixExpr.UNDEFINED];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return [PostfixExpr.status.UNKNOWN_ELEMENT, PostfixExpr.UNDEFINED];
|
||||
}
|
||||
}
|
||||
return resultStack.length === 1
|
||||
? [PostfixExpr.status.OK, resultStack.pop()]
|
||||
: [PostfixExpr.status.MALFORMED, PostfixExpr.UNDEFINED];
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Class for representing and manipulating undirected
|
||||
* graphs
|
||||
*
|
||||
* @property {object} vertices - Associative array of vertex objects
|
||||
* indexed by property name
|
||||
* @return {object} Graph
|
||||
*/
|
||||
function Graph() {
|
||||
this.vertices = {};
|
||||
}
|
||||
|
||||
Graph.prototype.getVertex = function(vertexName) {
|
||||
var vertex = null;
|
||||
if (this.vertices.hasOwnProperty(vertexName)) {
|
||||
vertex = this.vertices[vertexName];
|
||||
}
|
||||
return vertex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add a vertex to this graph
|
||||
*
|
||||
* @param {string} name - Vertex name
|
||||
* @param {object} data - Vertex data
|
||||
* @returns {object} - Newly created vertex
|
||||
*/
|
||||
Graph.prototype.addVertex = function(name, data) {
|
||||
var vertex = {name: name, data: data, adjacents: []};
|
||||
this.vertices[name] = vertex;
|
||||
return vertex;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Add an undirected edge between two vertices
|
||||
*
|
||||
* @param {string} vertexName1 - Name of first vertex
|
||||
* @param {string} vertexName2 - Name of second vertex
|
||||
* @returns {void}
|
||||
*/
|
||||
Graph.prototype.addEdge = function(vertexName1, vertexName2) {
|
||||
this.vertices[vertexName1].adjacents.push(vertexName2);
|
||||
this.vertices[vertexName2].adjacents.push(vertexName1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Depth-first-search graph traversal utility function
|
||||
*
|
||||
* @param {object} vertex - Root vertex from which traveral will begin.
|
||||
* It is assumed that this vertex has not alreday been visited as part
|
||||
* of this traversal.
|
||||
* @param {object} visited - Associative array. Each named property
|
||||
* corresponds to a vertex with the same name, and has boolean value
|
||||
* indicating whether the vertex has been alreday visited.
|
||||
* @param {object[]} component - Array of vertices that define a strongly
|
||||
* connected component.
|
||||
* @returns {void}
|
||||
*/
|
||||
Graph.prototype._dfsTraverse = function(vertex, visited, component) {
|
||||
var graph = this;
|
||||
visited[vertex.name] = true;
|
||||
component.push(vertex);
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
angular.forEach(vertex.adjacents, function(vertexName) {
|
||||
if (!visited[vertexName]) {
|
||||
graph._dfsTraverse(graph.vertices[vertexName], visited, component);
|
||||
}
|
||||
});
|
||||
/* eslint-enable no-unused-vars */
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Perform a depth-first-search on a specified graph to
|
||||
* find strongly connected components. A user provided function will
|
||||
* be called to process each component.
|
||||
*
|
||||
* @param {function} componentFunc - Function called on each strongly
|
||||
* connected component. Accepts aruments: array of vertex objects, and
|
||||
* user-provided extra data that can be used in processing the component.
|
||||
* @param {object} extra - Extra data that is passed into the component
|
||||
* processing function.
|
||||
* @returns {void}
|
||||
*/
|
||||
Graph.prototype.dfs = function(componentFunc, extra) {
|
||||
var graph = this;
|
||||
var visited = {};
|
||||
angular.forEach(
|
||||
graph.vertices,
|
||||
function(unused, name) {
|
||||
visited[name] = false;
|
||||
});
|
||||
|
||||
angular.forEach(this.vertices, function(vertex, vertexName) {
|
||||
if (!visited[vertexName]) {
|
||||
var component = [];
|
||||
graph._dfsTraverse(vertex, visited, component);
|
||||
componentFunc(component, extra);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Check whether a group contains required properties
|
||||
*
|
||||
* @param {DriverProperty[]} group - Property group
|
||||
* @return {boolean} Return true if the group contains required
|
||||
* properties, false otherwise
|
||||
*/
|
||||
function driverPropertyGroupHasRequired(group) {
|
||||
var hasRequired = false;
|
||||
for (var i = 0; i < group.length; i++) {
|
||||
if (group[i].required) {
|
||||
hasRequired = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hasRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Convert array of driver property groups to a string
|
||||
*
|
||||
* @param {array[]} groups - Array of driver property groups
|
||||
* @return {string} Output string
|
||||
*/
|
||||
function driverPropertyGroupsToString(groups) {
|
||||
var output = [];
|
||||
angular.forEach(groups, function(group) {
|
||||
var groupStr = [];
|
||||
angular.forEach(group, function(property) {
|
||||
groupStr.push(property.name);
|
||||
});
|
||||
groupStr = groupStr.join(", ");
|
||||
output.push(['[', groupStr, ']'].join(""));
|
||||
});
|
||||
output = output.join(", ");
|
||||
return ['[', output, ']'].join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Comaprison function used to sort driver property groups
|
||||
*
|
||||
* @param {DriverProperty[]} group1 - First group
|
||||
* @param {DriverProperty[]} group2 - Second group
|
||||
* @return {integer} Return:
|
||||
* < 0 if group1 should precede group2 in an ascending ordering
|
||||
* > 0 if group2 should precede group1
|
||||
* 0 if group1 and group2 are considered equal from ordering perpsective
|
||||
*/
|
||||
function compareDriverPropertyGroups(group1, group2) {
|
||||
var group1HasRequired = driverPropertyGroupHasRequired(group1);
|
||||
var group2HasRequired = driverPropertyGroupHasRequired(group2);
|
||||
|
||||
if (group1HasRequired === group2HasRequired) {
|
||||
if (group1.length === group2.length) {
|
||||
return group1[0].name.localeCompare(group2[0].name);
|
||||
} else {
|
||||
return group1.length - group2.length;
|
||||
}
|
||||
} else {
|
||||
return group1HasRequired ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
})();
|
|
@ -1,270 +0,0 @@
|
|||
/**
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
describe(
|
||||
'horizon.dashboard.admin.ironic.base-node.service',
|
||||
function() {
|
||||
var service;
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', jasmine.createSpy());
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
service =
|
||||
$injector.get('horizon.dashboard.admin.ironic.base-node.service');
|
||||
}));
|
||||
|
||||
it('defines the service', function() {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('DriverProperty', function() {
|
||||
it('Base construction', function() {
|
||||
var propertyName = 'propertyName';
|
||||
var description = '';
|
||||
var propertySet = [];
|
||||
var property = new service.DriverProperty(propertyName,
|
||||
description,
|
||||
propertySet);
|
||||
expect(property.name).toBe(propertyName);
|
||||
expect(property.desc).toBe(description);
|
||||
expect(property.propertySet).toBe(propertySet);
|
||||
expect(property.getSelectOptions()).toBe(null);
|
||||
expect(property.required).toBe(false);
|
||||
expect(property.defaultValue).toBe(undefined);
|
||||
expect(property.inputValue).toBe(undefined);
|
||||
expect(property.getInputValue()).toBe(undefined);
|
||||
expect(property.isActive()).toBe(true);
|
||||
});
|
||||
|
||||
it('Required - ends with', function() {
|
||||
var property = new service.DriverProperty('propertyName',
|
||||
' Required.',
|
||||
[]);
|
||||
expect(property.required).toBe(true);
|
||||
});
|
||||
|
||||
it('Not required - missing space', function() {
|
||||
var property = new service.DriverProperty('propertyName',
|
||||
'Required.',
|
||||
[]);
|
||||
expect(property.required).toBe(false);
|
||||
});
|
||||
|
||||
it('Not required - missing period', function() {
|
||||
var property = new service.DriverProperty('propertyName',
|
||||
' Required',
|
||||
[]);
|
||||
expect(property.required).toBe(false);
|
||||
});
|
||||
|
||||
it('Select options', function() {
|
||||
var property = new service.DriverProperty(
|
||||
'propertyName',
|
||||
'One of "foo", bar.',
|
||||
[]);
|
||||
expect(property.getSelectOptions()).toEqual(['foo', 'bar']);
|
||||
});
|
||||
|
||||
it('Select options - No single quotes', function() {
|
||||
var property = new service.DriverProperty(
|
||||
'propertyName',
|
||||
"One of 'foo', bar.",
|
||||
[]);
|
||||
expect(property.getSelectOptions()).toEqual(["'foo'", 'bar']);
|
||||
});
|
||||
|
||||
it('default - is string', function() {
|
||||
var property = new service.DriverProperty(
|
||||
'propertyName',
|
||||
'default is "5.1".',
|
||||
[]);
|
||||
expect(property._getDefaultValue()).toEqual('5.1');
|
||||
});
|
||||
|
||||
it('default - period processing', function() {
|
||||
var property = new service.DriverProperty(
|
||||
'propertyName',
|
||||
'default is 5.1.',
|
||||
[]);
|
||||
expect(property._getDefaultValue()).toEqual('5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('PostfixExpr', function() {
|
||||
it('Base construction', function() {
|
||||
var expr = new service.PostfixExpr();
|
||||
var ret = expr.evaluate({});
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.MALFORMED);
|
||||
expect(ret[1]).toBe(service.PostfixExpr.UNDEFINED);
|
||||
});
|
||||
|
||||
function evalBinary(val1, val2, op) {
|
||||
var propertySet = {};
|
||||
var prop1 = new service.DriverProperty("prop1", "", propertySet);
|
||||
propertySet.prop1 = prop1;
|
||||
var prop2 = new service.DriverProperty("prop2", "", propertySet);
|
||||
propertySet.prop2 = prop2;
|
||||
|
||||
var expr = new service.PostfixExpr();
|
||||
expr.addProperty("prop1");
|
||||
expr.addProperty("prop2");
|
||||
prop1.inputValue = val1;
|
||||
prop2.inputValue = val2;
|
||||
expr.addOperator(op);
|
||||
return expr.evaluate(propertySet);
|
||||
}
|
||||
|
||||
it('T and T', function() {
|
||||
var ret = evalBinary(true, true, service.PostfixExpr.op.AND);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('T and F', function() {
|
||||
var ret = evalBinary(true, false, service.PostfixExpr.op.AND);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('F and T', function() {
|
||||
var ret = evalBinary(false, true, service.PostfixExpr.op.AND);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('F and F', function() {
|
||||
var ret = evalBinary(false, false, service.PostfixExpr.op.AND);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('T or T', function() {
|
||||
var ret = evalBinary(true, true, service.PostfixExpr.op.OR);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('T or F', function() {
|
||||
var ret = evalBinary(true, false, service.PostfixExpr.op.OR);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('F or T', function() {
|
||||
var ret = evalBinary(false, true, service.PostfixExpr.op.OR);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('F or F', function() {
|
||||
var ret = evalBinary(false, false, service.PostfixExpr.op.OR);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('T eq T', function() {
|
||||
var ret = evalBinary(true, true, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('T eq F', function() {
|
||||
var ret = evalBinary(true, false, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('F eq T', function() {
|
||||
var ret = evalBinary(false, true, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('F eq F', function() {
|
||||
var ret = evalBinary(false, false, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('1 eq 1', function() {
|
||||
var ret = evalBinary(1, 1, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(true);
|
||||
});
|
||||
|
||||
it('1 eq 0', function() {
|
||||
var ret = evalBinary(1, 0, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
|
||||
it('"1" eq 1', function() {
|
||||
var ret = evalBinary('1', 1, service.PostfixExpr.op.EQ);
|
||||
expect(ret[0]).toBe(service.PostfixExpr.status.OK);
|
||||
expect(ret[1]).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DriverPropertyGroup', function() {
|
||||
it('driverPropertyGroupHasRequired', function () {
|
||||
var dp1 = new service.DriverProperty("dp-1", " Required.", []);
|
||||
var dp2 = new service.DriverProperty("dp-2", " ", []);
|
||||
|
||||
expect(service.driverPropertyGroupHasRequired).toBeDefined();
|
||||
expect(service.driverPropertyGroupHasRequired([])).toBe(false);
|
||||
expect(service.driverPropertyGroupHasRequired([dp1])).toBe(true);
|
||||
expect(service.driverPropertyGroupHasRequired([dp2])).toBe(false);
|
||||
expect(service.driverPropertyGroupHasRequired([dp1, dp2])).toBe(true);
|
||||
});
|
||||
|
||||
it('driverPropertyGroupsToString', function () {
|
||||
var dp1 = new service.DriverProperty("dp-1", " Required.", []);
|
||||
var dp2 = new service.DriverProperty("dp-2", " ", []);
|
||||
|
||||
expect(service.driverPropertyGroupsToString).toBeDefined();
|
||||
expect(service.driverPropertyGroupsToString([])).toBe("[]");
|
||||
expect(service.driverPropertyGroupsToString([[dp1]]))
|
||||
.toBe("[[dp-1]]");
|
||||
expect(service.driverPropertyGroupsToString([[dp1], [dp2]]))
|
||||
.toBe("[[dp-1], [dp-2]]");
|
||||
});
|
||||
|
||||
it('compareDriverPropertyGroups', function () {
|
||||
var dp1 = new service.DriverProperty("dp-1", " Required.", []);
|
||||
var dp2 = new service.DriverProperty("dp-2", " ", []);
|
||||
|
||||
expect(service.compareDriverPropertyGroups).toBeDefined();
|
||||
expect(service.compareDriverPropertyGroups([dp1], [dp1])).toBe(0);
|
||||
expect(service.compareDriverPropertyGroups([dp1], [dp2])).toBe(-1);
|
||||
expect(service.compareDriverPropertyGroups([dp2], [dp1])).toBe(1);
|
||||
// smaller group precedes larger group
|
||||
expect(service.compareDriverPropertyGroups([dp1], [dp1, dp2]))
|
||||
.toBe(-1);
|
||||
// group order decided on lexographic comparison of names of first
|
||||
// property
|
||||
expect(service.compareDriverPropertyGroups([dp2, dp1], [dp1, dp2]))
|
||||
.toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,228 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Controller used to support operations on an Ironic port
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('BasePortController', BasePortController);
|
||||
|
||||
BasePortController.$inject = [
|
||||
'$uibModalInstance',
|
||||
'horizon.dashboard.admin.ironic.validMacAddressPattern',
|
||||
'horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||
'horizon.dashboard.admin.ironic.form-field.service',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'ctrl',
|
||||
'node'
|
||||
];
|
||||
|
||||
/**
|
||||
* @description Utility class used to manage local-link-connection
|
||||
* form fields.
|
||||
*
|
||||
* @param {string} formFieldService - Provider service for creating
|
||||
* form fields.
|
||||
* @param {string} validMacAddressPattern - Regular expression
|
||||
* pattern used to test for valid mac addresses.
|
||||
* @param {string} validDatapathIdPattern - Regular expression
|
||||
* pattern used to test for valid datapath ids.
|
||||
* @return {void}
|
||||
*/
|
||||
function LocalLinkConnectionMgr(formFieldService,
|
||||
validMacAddressPattern,
|
||||
validDatapathIdPattern) {
|
||||
var mgr = this;
|
||||
|
||||
mgr.port_id = new formFieldService.FormField(
|
||||
{id: 'port_id', title: 'port_id'});
|
||||
|
||||
mgr.switch_id = new formFieldService.FormField(
|
||||
{id: 'switch_id',
|
||||
title: 'switch_id',
|
||||
desc: gettext("MAC address or OpenFlow datapath ID"),
|
||||
pattern: new RegExp(validMacAddressPattern + '|' +
|
||||
validDatapathIdPattern)});
|
||||
|
||||
mgr.switch_info = new formFieldService.FormField(
|
||||
{id: 'switch_info', title: 'switch_info'});
|
||||
|
||||
mgr.fields = {
|
||||
port_id: mgr.port_id,
|
||||
switch_id: mgr.switch_id,
|
||||
switch_info: mgr.switch_info
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the required property of each field based on current values
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
mgr.update = function() {
|
||||
var required = mgr.port_id.hasValue() || mgr.switch_id.hasValue();
|
||||
mgr.port_id.required = required;
|
||||
mgr.switch_id.required = required;
|
||||
};
|
||||
|
||||
// Add form field value change handlers
|
||||
angular.forEach(mgr.fields, function(field) {
|
||||
field.change = mgr.update;
|
||||
});
|
||||
|
||||
/**
|
||||
* Generate an attribute object that conforms to the format
|
||||
* required for port creation using the Ironic client
|
||||
*
|
||||
* @return {object|null} local_link_connection attribute object.
|
||||
* A value of null is returned if the local-link-connection
|
||||
* information is incomplete.
|
||||
*/
|
||||
mgr.toPortAttr = function() {
|
||||
var attr = null;
|
||||
if (mgr.port_id.hasValue() &&
|
||||
mgr.switch_id.hasValue()) {
|
||||
attr = {};
|
||||
attr.port_id = mgr.port_id.value;
|
||||
attr.switch_id = mgr.switch_id.value;
|
||||
|
||||
if (mgr.switch_info.hasValue()) {
|
||||
attr.switch_info = mgr.switch_info.value;
|
||||
}
|
||||
}
|
||||
return attr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Set values of form fields;
|
||||
*
|
||||
* @param {object} values - Dictionary of values indexed by
|
||||
* property-name
|
||||
* @return {void}
|
||||
*/
|
||||
mgr.setValues = function(values) {
|
||||
angular.forEach(mgr.fields, function(field, propertyName) {
|
||||
if (angular.isDefined(values[propertyName])) {
|
||||
field.value = values[propertyName];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Disable the local-link-connection form fields.
|
||||
*
|
||||
* @param {string} reason - Optional reason for disabling fields.
|
||||
* @return {void}
|
||||
*/
|
||||
mgr.disable = function(reason) {
|
||||
angular.forEach(mgr.fields, function(item) {
|
||||
item.disable(reason);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function BasePortController($uibModalInstance,
|
||||
validMacAddressPattern,
|
||||
validDatapathIdPattern,
|
||||
formFieldService,
|
||||
ironic,
|
||||
ctrl,
|
||||
node) {
|
||||
ctrl.port = {
|
||||
extra: {},
|
||||
node_uuid: node.uuid
|
||||
};
|
||||
|
||||
ctrl.address = new formFieldService.FormField({
|
||||
id: "macAddress",
|
||||
title: gettext("MAC address"),
|
||||
desc: gettext("MAC address for this port. Required."),
|
||||
pattern: new RegExp(validMacAddressPattern),
|
||||
value: null,
|
||||
required: true,
|
||||
autoFocus: true
|
||||
});
|
||||
|
||||
ctrl.pxeEnabled = new formFieldService.FormField({
|
||||
type: "radio",
|
||||
id: "pxeEnabled",
|
||||
title: gettext("PXE enabled"),
|
||||
desc: gettext(
|
||||
"Indicates whether this port should be used when PXE booting this node"),
|
||||
options: ['True', 'False'],
|
||||
value: 'True'});
|
||||
|
||||
ctrl.portgroup_uuid = new formFieldService.FormField({
|
||||
type: "select",
|
||||
id: "portgroup-uuid",
|
||||
title: gettext("Portgroup"),
|
||||
desc: gettext("Portgroup that this port belongs to"),
|
||||
portgroups: [],
|
||||
options: "portgroup.uuid as portgroup.name ? portgroup.name : portgroup.uuid for portgroup in field.portgroups", // eslint-disable-line max-len
|
||||
value: null});
|
||||
|
||||
// Object used to manage local-link-connection form fields
|
||||
ctrl.localLinkConnection =
|
||||
new LocalLinkConnectionMgr(formFieldService,
|
||||
validMacAddressPattern,
|
||||
validDatapathIdPattern);
|
||||
|
||||
ironic.getPortgroups(node.uuid).then(function(portgroups) {
|
||||
var field = ctrl.portgroup_uuid;
|
||||
|
||||
if (portgroups.length > 0) {
|
||||
field.portgroups.push({uuid: null, name: gettext("Select a portgroup")});
|
||||
}
|
||||
field.portgroups = field.portgroups.concat(portgroups);
|
||||
|
||||
if (portgroups.length === 0) {
|
||||
field.disable();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Cancel the modal
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.cancel = function() {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a port metadata property
|
||||
*
|
||||
* @param {string} propertyName - Name of the property
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.deleteExtra = function(propertyName) {
|
||||
delete ctrl.port.extra[propertyName];
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the specified port metadata property already exists
|
||||
*
|
||||
* @param {string} propertyName - Name of the metadata property
|
||||
* @return {boolean} True if the property already exists,
|
||||
* otherwise false
|
||||
*/
|
||||
ctrl.checkExtraUnique = function(propertyName) {
|
||||
return !(propertyName in ctrl.port.extra);
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,87 +0,0 @@
|
|||
<div class="modal-header" modal-draggable>
|
||||
<button type="button"
|
||||
class="close"
|
||||
ng-click="$dismiss()"
|
||||
aria-hidden="true"
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true" class="fa fa-times"></span>
|
||||
</button>
|
||||
<h3 class="modal-title">{$ ::ctrl.modalTitle $}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="CreatePortForm" name="CreatePortForm">
|
||||
<form-field field="ctrl.address" form="CreatePortForm"></form-field>
|
||||
<form-field field="ctrl.pxeEnabled" form="CreatePortForm"></form-field>
|
||||
<form-field field="ctrl.portgroup_uuid" form="CreatePortForm"></form-field>
|
||||
</form>
|
||||
|
||||
<form id="LocalLinkConnectionForm"
|
||||
name="LocalLinkConnectionForm"
|
||||
class="well well-sm">
|
||||
<h4 translate>Local link connection</h4>
|
||||
<div class="form-group"
|
||||
ng-repeat="(propertyName, propertyField) in
|
||||
ctrl.localLinkConnection.fields">
|
||||
<form-field field="propertyField" form="LocalLinkConnectionForm"></form-field>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form id="AddExtraForm" name="AddExtraForm" style="margin-bottom:10px;">
|
||||
<label for="extras" class="control-label" translate>Extras</label>
|
||||
<div class="input-group input-group-sm">
|
||||
<span class="input-group-addon"
|
||||
style="width:25%;text-align:right">
|
||||
Add Extra:</span>
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
ng-model="extraName"
|
||||
validate-unique="ctrl.checkExtraUnique"
|
||||
placeholder="{$ ::'Property Name' | translate $}"/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
ng-disabled="!extraName || AddExtraForm.$invalid"
|
||||
ng-click="ctrl.port.extra[extraName] = null;
|
||||
extraName = null">
|
||||
<span class="fa fa-plus"> </span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form class="form-horizontal" id="ExtraForm" name="ExtraForm">
|
||||
<div class="input-group input-group-sm"
|
||||
ng-repeat="(propertyName, propertyValue) in ctrl.port.extra">
|
||||
<span class="input-group-addon"
|
||||
style="width:25%;text-align:right">
|
||||
{$ propertyName $}
|
||||
</span>
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
name="{$ propertyName $}"
|
||||
ng-model="ctrl.port.extra[propertyName]"
|
||||
ng-required="true"/>
|
||||
<div class="input-group-btn">
|
||||
<a class="btn btn-default"
|
||||
ng-click="ctrl.deleteExtra(propertyName)">
|
||||
<span class="fa fa-minus"> </span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!--modal footer-->
|
||||
<div class="modal-footer ng-scope">
|
||||
<button class="btn btn-default" ng-click="ctrl.cancel()">
|
||||
<span class="fa fa-close"></span>
|
||||
<span class="ng-scope" translate>Cancel</span>
|
||||
</button>
|
||||
<button type="submit"
|
||||
ng-disabled="CreatePortForm.$invalid ||
|
||||
LocalLinkConnectionForm.$invalid ||
|
||||
ExtraForm.$invalid"
|
||||
ng-click="ctrl.submit()"
|
||||
class="btn btn-primary">
|
||||
{$ ::ctrl.submitButtonTitle $}
|
||||
</button>
|
||||
</div>
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name horizon.dashboard.admin.ironic:BootDeviceController
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Controller used to prompt the user for information associated with
|
||||
* setting the boot device of a node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('BootDeviceController', BootDeviceController);
|
||||
|
||||
BootDeviceController.$inject = [
|
||||
'$uibModalInstance',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'node'
|
||||
];
|
||||
|
||||
function BootDeviceController($uibModalInstance, ironic, node) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.modalTitle = gettext("Set Boot Device");
|
||||
|
||||
ironic.getSupportedBootDevices(node.uuid).then(
|
||||
function(bootDevices) {
|
||||
ctrl.supportedBootDevices = bootDevices;
|
||||
});
|
||||
|
||||
// Initialize form fields to current values
|
||||
ctrl.bootDevice = null;
|
||||
ctrl.persistent = 'False';
|
||||
ironic.getBootDevice(node.uuid).then(function(device) {
|
||||
ctrl.bootDevice = device.boot_device;
|
||||
ctrl.persistent = device.persistent ? 'True' : 'False';
|
||||
});
|
||||
|
||||
ctrl.cancel = function() {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
ctrl.setSelectedBootDevice = function() {
|
||||
$uibModalInstance.close({device: ctrl.bootDevice,
|
||||
persistent: ctrl.persistent === 'True'});
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.BootDeviceController', function () {
|
||||
var BOOT_DEVICE_CONTROLLER_PROPERTIES = [
|
||||
'bootDevice',
|
||||
'cancel',
|
||||
'modalTitle',
|
||||
'persistent',
|
||||
'setSelectedBootDevice',
|
||||
'supportedBootDevices'
|
||||
];
|
||||
var uibModalInstance, ironicBackendMockService, node;
|
||||
var ctrl = {};
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
uibModalInstance = {
|
||||
close: jasmine.createSpy(),
|
||||
dismiss: jasmine.createSpy()
|
||||
};
|
||||
$provide.value('$uibModalInstance', uibModalInstance);
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
|
||||
var ironicAPI =
|
||||
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||
|
||||
ironicAPI.createNode(
|
||||
{driver: ironicBackendMockService.params.defaultDriver})
|
||||
.then(function(response) {
|
||||
node = response.data;
|
||||
var controller = $injector.get('$controller');
|
||||
ctrl = controller('BootDeviceController', {node: node});
|
||||
});
|
||||
ironicBackendMockService.flush();
|
||||
}));
|
||||
|
||||
it('controller should be defined', function () {
|
||||
expect(ctrl).toBeDefined();
|
||||
expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual(
|
||||
BOOT_DEVICE_CONTROLLER_PROPERTIES.sort());
|
||||
expect(ctrl.supportedBootDevices).toEqual(
|
||||
ironicBackendMockService.getNodeSupportedBootDevices(node.uuid));
|
||||
var bootDevice = ironicBackendMockService.getNodeBootDevice(node.uuid);
|
||||
expect(ctrl.bootDevice).toEqual(bootDevice.boot_device);
|
||||
expect(ctrl.persistent).toEqual(bootDevice.persistent ? 'True' : 'False');
|
||||
});
|
||||
|
||||
it('cancel', function () {
|
||||
ctrl.cancel();
|
||||
expect(uibModalInstance.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('setSelectedBootDevice', function () {
|
||||
ctrl.bootDevice = 'pxe';
|
||||
ctrl.persistent = 'False';
|
||||
ctrl.setSelectedBootDevice();
|
||||
expect(uibModalInstance.close).toHaveBeenCalledWith(
|
||||
{device: ctrl.bootDevice,
|
||||
persistent: ctrl.persistent === 'True'});
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,58 +0,0 @@
|
|||
<div class="modal-header" modal-draggable>
|
||||
<button type="button"
|
||||
class="close"
|
||||
ng-click="$dismiss()"
|
||||
aria-hidden="true"
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true" class="fa fa-times"></span>
|
||||
</button>
|
||||
<h3 class="modal-title">{$ ::ctrl.modalTitle $}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="SetBootDeviceForm" name="SetBootDeviceForm">
|
||||
<!--boot device-->
|
||||
<div class="form-group required">
|
||||
<label for="bootDevice"
|
||||
class="control-label"
|
||||
translate>Boot Device</label>
|
||||
<span class="hz-icon-required fa fa-asterisk"></span>
|
||||
<div>
|
||||
<select id="bootDevice"
|
||||
class="form-control"
|
||||
ng-options="device as device for device in ctrl.supportedBootDevices"
|
||||
ng-model="ctrl.bootDevice">
|
||||
<option value="" disabled selected translate>Select a boot device</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<label for="persistent"
|
||||
class="control-label"
|
||||
translate>Persistent</label>
|
||||
</div>
|
||||
<div class="btn-group" id="persistent">
|
||||
<label class="btn btn-default"
|
||||
ng-model="ctrl.persistent"
|
||||
ng-repeat="opt in ['True', 'False']"
|
||||
uib-btn-radio="opt">{$ opt $}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!--modal footer-->
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default secondary"
|
||||
type="button"
|
||||
ng-click="ctrl.cancel()"
|
||||
translate>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
ng-click="ctrl.setSelectedBootDevice()"
|
||||
ng-disabled="ctrl.bootDevice === null"
|
||||
translate>
|
||||
Set Boot Device
|
||||
</button>
|
||||
</div>
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* @ngdoc service
|
||||
* @name horizon.dashboard.admin.ironic.bootdevice.service
|
||||
* @description Service for setting the boot device of a node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.bootdevice.service',
|
||||
bootDeviceService);
|
||||
|
||||
bootDeviceService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath',
|
||||
'horizon.app.core.openstack-service-api.ironic'
|
||||
];
|
||||
|
||||
function bootDeviceService($uibModal, basePath, ironic) {
|
||||
var service = {
|
||||
setBootDevice: setBootDevice
|
||||
};
|
||||
return service;
|
||||
|
||||
/*
|
||||
* @description Set the boot device of a specified node
|
||||
*
|
||||
* @param {object} node - node object
|
||||
* @return {promise}
|
||||
*/
|
||||
function setBootDevice(node) {
|
||||
var promise;
|
||||
var options = {
|
||||
controller: "BootDeviceController as ctrl",
|
||||
backdrop: 'static',
|
||||
resolve: {
|
||||
node: function() {
|
||||
return node;
|
||||
}
|
||||
},
|
||||
templateUrl: basePath + '/bootdevice/bootdevice.html'
|
||||
};
|
||||
promise = $uibModal.open(options).result.then(
|
||||
function(result) {
|
||||
return ironic.nodeSetBootDevice(node.uuid,
|
||||
result.device,
|
||||
result.persistent);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @description Unit tests for the Ironic-UI boot-device service
|
||||
*/
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.bootdevice.service',
|
||||
function() {
|
||||
var $q,
|
||||
$uibModal,
|
||||
bootDeviceService,
|
||||
ironicAPI,
|
||||
ironicBackendMockService,
|
||||
defaultDriver;
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {
|
||||
open: function() {
|
||||
return $q.when({device: 'pxe',
|
||||
persistent: true});
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service', {
|
||||
add: function() {}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
defaultDriver = ironicBackendMockService.params.defaultDriver;
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
$q = $injector.get('$q');
|
||||
|
||||
$uibModal = $injector.get('$uibModal');
|
||||
|
||||
ironicAPI =
|
||||
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||
|
||||
bootDeviceService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.bootdevice.service');
|
||||
}));
|
||||
|
||||
it('defines the bootDeviceService', function() {
|
||||
expect(bootDeviceService).toBeDefined();
|
||||
expect(bootDeviceService.setBootDevice).toBeDefined();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
ironicBackendMockService.postTest();
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Utility function that creates a node and returns
|
||||
* both it and its boot device
|
||||
*
|
||||
* @return {promise} Containing node and boot_device
|
||||
*/
|
||||
function createNode() {
|
||||
return ironicAPI.createNode({driver: defaultDriver})
|
||||
.then(function(response) {
|
||||
return response.data;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getBootDevice(node.uuid).then(function(device) {
|
||||
return {node: node, boot_device: device};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('setBootDevice', function() {
|
||||
var targetBootDevice = {
|
||||
device: "safe",
|
||||
persistent: false
|
||||
};
|
||||
|
||||
spyOn($uibModal, 'open').and.returnValue(
|
||||
{result: $q.when(targetBootDevice)});
|
||||
|
||||
createNode().then(function(data) {
|
||||
expect(data.node.boot_device).not.toEqual(targetBootDevice.device);
|
||||
bootDeviceService.setBootDevice(data.node)
|
||||
.then(function() {
|
||||
ironicAPI.getBootDevice(data.node.uuid).then(function(device) {
|
||||
expect(device).toEqual(
|
||||
{boot_device: targetBootDevice.device,
|
||||
persistent: targetBootDevice.persistent});
|
||||
});
|
||||
})
|
||||
.catch(fail);
|
||||
});
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('setBootDevice - cancel', function() {
|
||||
spyOn($uibModal, 'open').and.returnValue(
|
||||
{result: $q.reject('cancel')});
|
||||
|
||||
createNode().then(function(data) {
|
||||
bootDeviceService.setBootDevice(data.node)
|
||||
.then(fail)
|
||||
.catch(function() {
|
||||
ironicAPI.getBootDevice(data.node.uuid).then(function(device) {
|
||||
expect(device).toEqual(data.boot_device);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name horizon.dashboard.admin.ironic:CleanNodeController
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Controller used to prompt the user for a list of clean-steps
|
||||
* in JSON format that will be applied a node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('CleanNodeController', CleanNodeController);
|
||||
|
||||
CleanNodeController.$inject = [
|
||||
'$uibModalInstance'
|
||||
];
|
||||
|
||||
function CleanNodeController($uibModalInstance) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.errMsg = '';
|
||||
|
||||
ctrl.cancel = function() {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
ctrl.clean = function(cleanSteps) {
|
||||
try {
|
||||
var steps = JSON.parse(cleanSteps);
|
||||
if (angular.isArray(steps) && steps.length > 0) {
|
||||
var valid = true;
|
||||
angular.forEach(steps, function(step) {
|
||||
if (angular.isUndefined(step.interface) ||
|
||||
angular.isUndefined(step.step)) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
if (valid) {
|
||||
$uibModalInstance.close(steps);
|
||||
} else {
|
||||
ctrl.errMsg = gettext('Each cleaning step must be an object that contains "interface" and "step" properties'); // eslint-disable-line max-len
|
||||
}
|
||||
} else {
|
||||
ctrl.errMsg = gettext('Clean steps should be an non-empty array');
|
||||
}
|
||||
} catch (e) {
|
||||
ctrl.errMsg = gettext('Unable to validate the JSON input');
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,41 +0,0 @@
|
|||
<div class="modal-header" modal-draggable>
|
||||
<h3 class="modal-title" translate>Clean Node</h3>
|
||||
</div>
|
||||
<div class="modal-body clearfix">
|
||||
<div class="content">
|
||||
<div translate class="subtitle">Provide a list of cleaning steps in JSON format</div>
|
||||
<div class="form-group"
|
||||
ng-init="cleanSteps=''">
|
||||
<div class="form-field">
|
||||
<textarea type="text"
|
||||
class="form-control input-sm"
|
||||
ng-model="cleanSteps"
|
||||
ng-change="ctrl.errMsg=''"
|
||||
auto-focus
|
||||
rows="8"
|
||||
required
|
||||
placeholder=""/>
|
||||
</div>
|
||||
</div>
|
||||
<div uib-alert
|
||||
ng-hide="ctrl.errMsg === ''"
|
||||
ng-class="'alert-danger'">
|
||||
{$ ctrl.errMsg $}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default secondary"
|
||||
type="button"
|
||||
ng-click="ctrl.cancel()"
|
||||
translate>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
ng-disabled="cleanSteps === '' || ctrl.errMsg !== ''"
|
||||
ng-click="ctrl.clean(cleanSteps)"
|
||||
translate>
|
||||
Clean node
|
||||
</button>
|
||||
</div>
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray Inc.
|
||||
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* @ngdoc service
|
||||
* @name horizon.dashboard.admin.ironic.maintenance.service
|
||||
* @description Service for putting nodes in, and removing them from
|
||||
* maintenance mode
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.clean-node.service',
|
||||
cleanNodeService);
|
||||
|
||||
cleanNodeService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath',
|
||||
'horizon.app.core.openstack-service-api.ironic'
|
||||
];
|
||||
|
||||
function cleanNodeService($uibModal, basePath, ironic) {
|
||||
var service = {
|
||||
clean: clean
|
||||
};
|
||||
return service;
|
||||
|
||||
/*
|
||||
* @description Initiate manual cleaning of an Ironic node.
|
||||
* The user is prompted for a list of steps that are then
|
||||
* used to clean the node.
|
||||
*
|
||||
* @param {object} node - Node to be cleaned
|
||||
* @return {void}
|
||||
*/
|
||||
function clean(node) {
|
||||
var options = {
|
||||
controller: 'CleanNodeController as ctrl',
|
||||
backdrop: 'static',
|
||||
templateUrl: basePath + '/clean-node/clean-node.html'
|
||||
};
|
||||
$uibModal.open(options).result.then(function(cleanSteps) {
|
||||
return ironic.setNodeProvisionState(node.uuid,
|
||||
'clean',
|
||||
cleanSteps);
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Controller used to create a network port on a specified node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('CreatePortController', CreatePortController);
|
||||
|
||||
CreatePortController.$inject = [
|
||||
'$rootScope',
|
||||
'$controller',
|
||||
'$uibModalInstance',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'node'
|
||||
];
|
||||
|
||||
function CreatePortController($rootScope,
|
||||
$controller,
|
||||
$uibModalInstance,
|
||||
ironic,
|
||||
ironicEvents,
|
||||
node) {
|
||||
var ctrl = this;
|
||||
|
||||
$controller('BasePortController',
|
||||
{ctrl: ctrl,
|
||||
node: node,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Create Port");
|
||||
ctrl.submitButtonTitle = ctrl.modalTitle;
|
||||
|
||||
/**
|
||||
* Create the defined port
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.createPort = function() {
|
||||
var port = angular.copy(ctrl.port);
|
||||
|
||||
port.address = ctrl.address.value;
|
||||
|
||||
var attr = ctrl.localLinkConnection.toPortAttr();
|
||||
if (attr) {
|
||||
port.local_link_connection = attr;
|
||||
}
|
||||
|
||||
if (ctrl.pxeEnabled.value !== 'True') {
|
||||
port.pxe_enabled = ctrl.pxeEnabled.value;
|
||||
}
|
||||
|
||||
if (ctrl.portgroup_uuid.value !== null) {
|
||||
port.portgroup_uuid = ctrl.portgroup_uuid.value;
|
||||
}
|
||||
|
||||
ironic.createPort(port).then(
|
||||
function(createdPort) {
|
||||
$rootScope.$emit(ironicEvents.CREATE_PORT_SUCCESS);
|
||||
$uibModalInstance.close(createdPort);
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.submit = function() {
|
||||
ctrl.createPort();
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.create-port.service',
|
||||
createPortService);
|
||||
|
||||
createPortService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath'
|
||||
];
|
||||
|
||||
function createPortService($uibModal, basePath) {
|
||||
var service = {
|
||||
createPort: createPort
|
||||
};
|
||||
return service;
|
||||
|
||||
/**
|
||||
* @description Launch a modal dialog that will guide the user
|
||||
* in creating a new port
|
||||
*
|
||||
* @param {object} node - Node to which the port will be associated
|
||||
* @return {promise} Object describing the created port
|
||||
*/
|
||||
function createPort(node) {
|
||||
var options = {
|
||||
controller: 'CreatePortController as ctrl',
|
||||
backdrop: 'static',
|
||||
resolve: {
|
||||
node: function() {
|
||||
return node;
|
||||
}
|
||||
},
|
||||
templateUrl: basePath + '/base-port/base-port.html'
|
||||
};
|
||||
return $uibModal.open(options).result;
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Controller used to edit an existing Ironic node
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('EditNodeController', EditNodeController);
|
||||
|
||||
EditNodeController.$inject = [
|
||||
'$rootScope',
|
||||
'$controller',
|
||||
'$uibModalInstance',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'horizon.dashboard.admin.ironic.edit-node.service',
|
||||
'horizon.dashboard.admin.ironic.update-patch.service',
|
||||
'$log',
|
||||
'node'
|
||||
];
|
||||
|
||||
function EditNodeController($rootScope,
|
||||
$controller,
|
||||
$uibModalInstance,
|
||||
toastService,
|
||||
ironic,
|
||||
ironicEvents,
|
||||
editNodeService,
|
||||
updatePatchService,
|
||||
$log,
|
||||
node) {
|
||||
var ctrl = this;
|
||||
|
||||
$controller('BaseNodeController',
|
||||
{ctrl: ctrl,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Edit Node");
|
||||
ctrl.submitButtonTitle = gettext("Update Node");
|
||||
|
||||
ctrl.baseNode = null;
|
||||
|
||||
var instanceInfoId = "instance_info";
|
||||
ctrl.propertyCollections.push(
|
||||
{id: instanceInfoId,
|
||||
formId: "instance_info_form",
|
||||
title: gettext("Instance Info"),
|
||||
addPrompt: gettext("Add Instance Property"),
|
||||
placeholder: gettext("Instance Property Name")});
|
||||
|
||||
ctrl.node[instanceInfoId] = {};
|
||||
|
||||
ctrl.node[instanceInfoId] = {};
|
||||
|
||||
init(node);
|
||||
|
||||
function init(node) {
|
||||
ctrl._loadDrivers().then(function() {
|
||||
_loadNodeData(node.uuid);
|
||||
});
|
||||
ctrl._getImages();
|
||||
}
|
||||
|
||||
function _loadNodeData(nodeId) {
|
||||
ironic.getNode(nodeId).then(function(node) {
|
||||
ctrl.baseNode = node;
|
||||
|
||||
ctrl.node.name = node.name;
|
||||
ctrl.node.resource_class = node.resource_class;
|
||||
ctrl.node.network_interface = node.network_interface;
|
||||
for (var i = 0; i < ctrl.drivers.length; i++) {
|
||||
if (ctrl.drivers[i].name === node.driver) {
|
||||
ctrl.selectedDriver = ctrl.drivers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctrl.loadDriverProperties(node.driver).then(function() {
|
||||
angular.forEach(node.driver_info, function(value, property) {
|
||||
if (angular.isDefined(ctrl.driverProperties[property])) {
|
||||
ctrl.driverProperties[property].inputValue = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ctrl.node.properties = angular.copy(node.properties);
|
||||
ctrl.node.extra = angular.copy(node.extra);
|
||||
ctrl.node.instance_info = angular.copy(node.instance_info);
|
||||
ctrl.node.uuid = node.uuid;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Construct a patch that converts source node into
|
||||
* target node
|
||||
*
|
||||
* @param {object} sourceNode - Source node
|
||||
* @param {object} targetNode - Target node
|
||||
* @return {object[]} Array of patch instructions
|
||||
*/
|
||||
ctrl.buildPatch = function(sourceNode, targetNode) {
|
||||
var patcher = new updatePatchService.UpdatePatch();
|
||||
var PatchItem = function PatchItem(id, path) {
|
||||
this.id = id;
|
||||
this.path = path;
|
||||
};
|
||||
angular.forEach([new PatchItem("name", "/name"),
|
||||
new PatchItem("resource_class", "/resource_class"),
|
||||
new PatchItem("network_interface", "/network_interface"),
|
||||
new PatchItem("driver", "/driver"),
|
||||
new PatchItem("properties", "/properties"),
|
||||
new PatchItem("extra", "/extra"),
|
||||
new PatchItem("driver_info", "/driver_info"),
|
||||
new PatchItem("instance_info", "/instance_info")],
|
||||
function(item) {
|
||||
patcher.buildPatch(sourceNode[item.id],
|
||||
targetNode[item.id],
|
||||
item.path);
|
||||
});
|
||||
|
||||
return patcher.getPatch();
|
||||
};
|
||||
|
||||
ctrl.submit = function() {
|
||||
angular.forEach(ctrl.driverProperties, function(property, name) {
|
||||
$log.debug(name +
|
||||
", required = " + property.isRequired() +
|
||||
", active = " + property.isActive() +
|
||||
", input-value = " + property.getInputValue() +
|
||||
", default-value = " + property.getDefaultValue());
|
||||
if (property.isActive() &&
|
||||
property.getInputValue() &&
|
||||
property.getInputValue() !== property.getDefaultValue()) {
|
||||
$log.debug("Setting driver property " + name + " to " +
|
||||
property.inputValue);
|
||||
ctrl.node.driver_info[name] = property.inputValue;
|
||||
}
|
||||
});
|
||||
|
||||
$log.info("Updating node " + JSON.stringify(ctrl.baseNode));
|
||||
$log.info("to " + JSON.stringify(ctrl.node));
|
||||
|
||||
var patch = ctrl.buildPatch(ctrl.baseNode, ctrl.node);
|
||||
$log.info("patch = " + JSON.stringify(patch.patch));
|
||||
if (patch.status === updatePatchService.UpdatePatch.status.OK) {
|
||||
ironic.updateNode(ctrl.baseNode.uuid, patch.patch).then(function(node) {
|
||||
$rootScope.$emit(ironicEvents.EDIT_NODE_SUCCESS);
|
||||
$uibModalInstance.close(node);
|
||||
});
|
||||
} else {
|
||||
toastService.add('error',
|
||||
gettext('Unable to create node update patch.'));
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.edit-node', function () {
|
||||
var ironicBackendMockService, ctrl, editNode, updatePatchService;
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModalInstance', {});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
|
||||
updatePatchService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.update-patch.service');
|
||||
|
||||
var ironicAPI =
|
||||
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||
ironicAPI.createNode(
|
||||
{driver: ironicBackendMockService.params.defaultDriver})
|
||||
.then(function(response) {
|
||||
editNode = response.data;
|
||||
var controller = $injector.get('$controller');
|
||||
ctrl = controller('EditNodeController', {node: editNode});
|
||||
});
|
||||
ironicBackendMockService.flush();
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
ironicBackendMockService.postTest();
|
||||
});
|
||||
|
||||
it('controller should be defined', function () {
|
||||
expect(ctrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('controller base construction', function () {
|
||||
expect(ctrl.baseNode).toEqual(
|
||||
ironicBackendMockService.getNode(editNode.uuid));
|
||||
expect(ctrl.propertyCollections)
|
||||
.toContain(jasmine.objectContaining({id: "instance_info"}));
|
||||
angular.forEach(ctrl.propertyCollections, function(collection) {
|
||||
expect(Object.getOwnPropertyNames(collection).sort()).toEqual(
|
||||
PROPERTY_COLLECTION_PROPERTIES.sort());
|
||||
});
|
||||
expect(ctrl.node.name).toEqual(editNode.name);
|
||||
expect(ctrl.node.resource_class).toEqual(editNode.resource_class);
|
||||
expect(ctrl.node.network_interface).toEqual(editNode.network_interface);
|
||||
expect(ctrl.node.properties).toEqual(editNode.properties);
|
||||
expect(ctrl.node.extra).toEqual(editNode.extra);
|
||||
expect(ctrl.node.instance_info).toEqual(editNode.instance_info);
|
||||
expect(ctrl.node.uuid).toEqual(editNode.uuid);
|
||||
var properties = angular.copy(BASE_NODE_CONTROLLER_PROPERTIES);
|
||||
properties.push('baseNode',
|
||||
'buildPatch',
|
||||
'selectedDriver',
|
||||
'submit');
|
||||
|
||||
expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual(
|
||||
properties.sort());
|
||||
});
|
||||
|
||||
it('buildPatch', function () {
|
||||
var patch = ctrl.buildPatch(editNode, editNode);
|
||||
expect(patch.patch).toEqual([]);
|
||||
expect(patch.status).toEqual(updatePatchService.UpdatePatch.status.OK);
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.edit-node.service',
|
||||
editNodeService);
|
||||
|
||||
editNodeService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath'
|
||||
];
|
||||
|
||||
function editNodeService($uibModal, basePath) {
|
||||
var service = {
|
||||
editNode: editNode
|
||||
};
|
||||
|
||||
function editNode(node) {
|
||||
var options = {
|
||||
controller: 'EditNodeController as ctrl',
|
||||
backdrop: 'static',
|
||||
resolve: {
|
||||
node: function() {
|
||||
return node;
|
||||
}
|
||||
},
|
||||
templateUrl: basePath + '/base-node/base-node.html'
|
||||
};
|
||||
return $uibModal.open(options).result;
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
})();
|
|
@ -1,133 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG = gettext("This field is disabled because a port cannot have any connectivity attributes (pxe_enabled, local_link_connection, portgroup_id) updated unless its associated node is in an enroll, inspecting, mangeable state; or in maintenance mode."); // eslint-disable-line max-len
|
||||
|
||||
/**
|
||||
* Controller used to edit a specified node port
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('EditPortController', EditPortController);
|
||||
|
||||
EditPortController.$inject = [
|
||||
'$rootScope',
|
||||
'$controller',
|
||||
'$uibModalInstance',
|
||||
'$log',
|
||||
'$q',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'horizon.dashboard.admin.ironic.update-patch.service',
|
||||
'port',
|
||||
'node'
|
||||
];
|
||||
|
||||
function EditPortController($rootScope,
|
||||
$controller,
|
||||
$uibModalInstance,
|
||||
$log,
|
||||
$q,
|
||||
ironic,
|
||||
ironicEvents,
|
||||
updatePatchService,
|
||||
port,
|
||||
node) {
|
||||
var ctrl = this;
|
||||
$controller('BasePortController',
|
||||
{ctrl: ctrl,
|
||||
node: node,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Edit Port");
|
||||
ctrl.submitButtonTitle = gettext("Update Port");
|
||||
|
||||
var cannotEditConnectivityAttr =
|
||||
!(node.maintenance || (node.provision_state === "enroll" ||
|
||||
node.provision_state === "inspecting" ||
|
||||
node.provision_state === "manageable"));
|
||||
|
||||
// Initialize form fields
|
||||
ctrl.address.value = port.address;
|
||||
if ((node.provision_state === "active" || node.instance_uuid) &&
|
||||
!node.maintenance) {
|
||||
ctrl.address.disable();
|
||||
}
|
||||
|
||||
ctrl.pxeEnabled.value = port.pxe_enabled ? 'True' : 'False';
|
||||
|
||||
ctrl.portgroup_uuid.value = port.portgroup_uuid;
|
||||
|
||||
if (cannotEditConnectivityAttr) {
|
||||
angular.forEach(
|
||||
[ctrl.pxeEnabled, ctrl.portgroup_uuid],
|
||||
function(field) {
|
||||
field.disable(UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
||||
});
|
||||
}
|
||||
|
||||
ctrl.localLinkConnection.setValues(
|
||||
port.local_link_connection);
|
||||
|
||||
if (cannotEditConnectivityAttr) {
|
||||
ctrl.localLinkConnection.disable(
|
||||
UNABLE_TO_UPDATE_CONNECTIVITY_ATTR_MSG);
|
||||
}
|
||||
|
||||
ctrl.port.extra = angular.copy(port.extra);
|
||||
|
||||
/**
|
||||
* Apply updates to the port being edited
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ctrl.updatePort = function() {
|
||||
var patcher = new updatePatchService.UpdatePatch();
|
||||
|
||||
$log.info("Updating port " + JSON.stringify(port));
|
||||
|
||||
patcher.buildPatch(port.address, ctrl.address.value, "/address");
|
||||
patcher.buildPatch(port.pxe_enabled ? 'True' : 'False',
|
||||
ctrl.pxeEnabled.value,
|
||||
"/pxe_enabled");
|
||||
patcher.buildPatch(port.local_link_connection,
|
||||
ctrl.localLinkConnection.toPortAttr(),
|
||||
"/local_link_connection");
|
||||
patcher.buildPatch(port.extra, ctrl.port.extra, "/extra");
|
||||
patcher.buildPatch(port.portgroup_uuid,
|
||||
ctrl.portgroup_uuid.value,
|
||||
"/portgroup_uuid");
|
||||
|
||||
var patch = patcher.getPatch();
|
||||
$log.info("patch = " + JSON.stringify(patch.patch));
|
||||
if (patch.status === updatePatchService.UpdatePatch.status.OK) {
|
||||
ironic.updatePort(port.uuid, patch.patch).then(function(port) {
|
||||
$rootScope.$emit(ironicEvents.EDIT_PORT_SUCCESS);
|
||||
$uibModalInstance.close(port);
|
||||
});
|
||||
} else {
|
||||
toastService.add('error',
|
||||
gettext('Unable to create port update patch.'));
|
||||
}
|
||||
};
|
||||
|
||||
ctrl.submit = function() {
|
||||
ctrl.updatePort();
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.edit-port.service',
|
||||
editPortService);
|
||||
|
||||
editPortService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath'
|
||||
];
|
||||
|
||||
function editPortService($uibModal, basePath) {
|
||||
var service = {
|
||||
editPort: editPort
|
||||
};
|
||||
|
||||
/**
|
||||
* @description: Edit a specified port
|
||||
*
|
||||
* @param {object} port - Port to be edited
|
||||
* @param {object} node - Node to which the port is attached
|
||||
* @return {promise} Promise containing the updated port
|
||||
*/
|
||||
function editPort(port, node) {
|
||||
var options = {
|
||||
controller: 'EditPortController as ctrl',
|
||||
backdrop: 'static',
|
||||
resolve: {
|
||||
port: function() {
|
||||
return port;
|
||||
},
|
||||
node: function() {
|
||||
return node;
|
||||
}
|
||||
},
|
||||
templateUrl: basePath + '/base-port/base-port.html'
|
||||
};
|
||||
return $uibModal.open(options).result;
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
})();
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.directive('emptyToPristine', EmptyToPristine);
|
||||
|
||||
function EmptyToPristine() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
link: function(scope, elem, attrs, ctrl) {
|
||||
ctrl.$parsers.push(function(viewValue) {
|
||||
if (viewValue === "") {
|
||||
ctrl.$setPristine();
|
||||
}
|
||||
return viewValue;
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Controller used to enroll a node in the Ironic database
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('EnrollNodeController', EnrollNodeController);
|
||||
|
||||
EnrollNodeController.$inject = [
|
||||
'$rootScope',
|
||||
'$controller',
|
||||
'$uibModalInstance',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'$log'
|
||||
];
|
||||
|
||||
function EnrollNodeController($rootScope,
|
||||
$controller,
|
||||
$uibModalInstance,
|
||||
ironic,
|
||||
ironicEvents,
|
||||
$log) {
|
||||
var ctrl = this;
|
||||
|
||||
$controller('BaseNodeController',
|
||||
{ctrl: ctrl,
|
||||
$uibModalInstance: $uibModalInstance});
|
||||
|
||||
ctrl.modalTitle = gettext("Enroll Node");
|
||||
ctrl.submitButtonTitle = ctrl.modalTitle;
|
||||
|
||||
init();
|
||||
|
||||
function init() {
|
||||
ctrl._loadDrivers();
|
||||
ctrl._getImages();
|
||||
}
|
||||
|
||||
ctrl.submit = function() {
|
||||
$log.debug(">> EnrollNodeController.submit()");
|
||||
angular.forEach(ctrl.driverProperties, function(property, name) {
|
||||
$log.debug(name +
|
||||
", required = " + property.isRequired() +
|
||||
", active = " + property.isActive() +
|
||||
", input-value = " + property.getInputValue() +
|
||||
", default-value = " + property.getDefaultValue());
|
||||
if (property.isActive() &&
|
||||
property.getInputValue() &&
|
||||
property.getInputValue() !== property.getDefaultValue()) {
|
||||
$log.debug("Setting driver property " + name + " to " +
|
||||
property.inputValue);
|
||||
ctrl.node.driver_info[name] = property.inputValue;
|
||||
}
|
||||
});
|
||||
|
||||
ironic.createNode(ctrl.node).then(
|
||||
function(response) {
|
||||
$log.info("create node response = " + JSON.stringify(response));
|
||||
$rootScope.$emit(ironicEvents.ENROLL_NODE_SUCCESS);
|
||||
$uibModalInstance.close(response.data);
|
||||
});
|
||||
$log.debug("<< EnrollNodeController.submit()");
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.enroll-node', function () {
|
||||
var ironicBackendMockService, rootScope, ironicEvents, uibModalInstance;
|
||||
var ctrl = {};
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', {});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
uibModalInstance = {
|
||||
close: jasmine.createSpy()
|
||||
};
|
||||
$provide.value('$uibModalInstance', uibModalInstance);
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
rootScope = $injector.get('$rootScope');
|
||||
ironicEvents = $injector.get('horizon.dashboard.admin.ironic.events');
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
|
||||
var controller = $injector.get('$controller');
|
||||
ctrl = controller('EnrollNodeController');
|
||||
ironicBackendMockService.flush();
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
ironicBackendMockService.postTest();
|
||||
});
|
||||
|
||||
it('controller should be defined', function () {
|
||||
expect(ctrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('base construction', function () {
|
||||
var properties = angular.copy(BASE_NODE_CONTROLLER_PROPERTIES);
|
||||
properties.push('submit');
|
||||
expect(Object.getOwnPropertyNames(ctrl).sort()).toEqual(
|
||||
properties.sort());
|
||||
});
|
||||
|
||||
it('submit - success', function () {
|
||||
spyOn(rootScope, '$emit');
|
||||
var nodeName = "node_" + Date.now();
|
||||
ctrl.node.name = nodeName;
|
||||
ctrl.node.driver = ironicBackendMockService.params.defaultDriver;
|
||||
ctrl.submit();
|
||||
ironicBackendMockService.flush();
|
||||
expect(rootScope.$emit)
|
||||
.toHaveBeenCalledWith(ironicEvents.ENROLL_NODE_SUCCESS);
|
||||
expect(uibModalInstance.close)
|
||||
.toHaveBeenCalledWith(ironicBackendMockService.getNode(nodeName));
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.enroll-node.service',
|
||||
enrollNodeService);
|
||||
|
||||
enrollNodeService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath'
|
||||
];
|
||||
|
||||
function enrollNodeService($uibModal, basePath) {
|
||||
var service = {
|
||||
enrollNode: enrollNode
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Launch a modal dialog that will guide the user
|
||||
* in enrolling a new node
|
||||
*
|
||||
* @return {promise} Object describing the enrolled node
|
||||
*/
|
||||
function enrollNode() {
|
||||
var options = {
|
||||
controller: 'EnrollNodeController as ctrl',
|
||||
backdrop: 'static',
|
||||
templateUrl: basePath + '/base-node/base-node.html'
|
||||
};
|
||||
return $uibModal.open(options).result;
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
})();
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.directive('formField', FormField);
|
||||
|
||||
FormField.$inject = [
|
||||
'$timeout',
|
||||
'$compile',
|
||||
'horizon.dashboard.admin.ironic.basePath'
|
||||
];
|
||||
|
||||
function FormField($timeout, $compile, basePath) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
field: '=',
|
||||
form: '='
|
||||
},
|
||||
templateUrl: basePath + '/form-field.html',
|
||||
link: function(scope, element) {
|
||||
// Process the auto-focus attribute
|
||||
if (scope.field.autoFocus) {
|
||||
// Need to defer processing until the DOM is fully instantiated
|
||||
$timeout(function() {
|
||||
var inputs = element.find('input');
|
||||
if (inputs[0]) {
|
||||
inputs.attr('auto-focus', '');
|
||||
$compile(element.contents())(scope);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,56 +0,0 @@
|
|||
<div class="form-group"
|
||||
ng-class="{'has-error': form[field.id].$invalid &&
|
||||
form[field.id].$dirty}">
|
||||
<label for="{$ field.id $}"
|
||||
class="control-label">{$ field.title $}</label>
|
||||
<span ng-if="field.desc"
|
||||
class="help-icon"
|
||||
data-container="body"
|
||||
data-html="true"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ field.desc $}">
|
||||
<span class="fa fa-question-circle"></span>
|
||||
</span>
|
||||
<span ng-if="field.info"
|
||||
class="help-icon"
|
||||
data-container="body"
|
||||
data-html="true"
|
||||
title=""
|
||||
data-toggle="tooltip"
|
||||
data-original-title="{$ field.info $}">
|
||||
<span class="fa fa-info-circle"></span>
|
||||
</span>
|
||||
<span ng-if="field.required"
|
||||
class="hz-icon-required fa fa-asterisk"></span>
|
||||
<div ng-switch="field.type">
|
||||
<input ng-switch-when="input"
|
||||
type="text"
|
||||
class="form-control"
|
||||
ng-model="field.value"
|
||||
id="{$ field.id $}"
|
||||
name="{$ field.id $}"
|
||||
ng-required="field.required"
|
||||
ng-disabled="field.disabled"
|
||||
ng-pattern="field.pattern"
|
||||
ng-change="{$ field.change $}"
|
||||
placeholder="{$ field.desc $}"/>
|
||||
<div ng-switch-when="radio"
|
||||
class="btn-group"
|
||||
id="{$ field.id $}">
|
||||
<label class="btn btn-default"
|
||||
ng-model="field.value"
|
||||
ng-repeat="opt in field.options"
|
||||
ng-disabled="field.disabled"
|
||||
uib-btn-radio="opt">{$ opt $}</label>
|
||||
</div>
|
||||
<div ng-switch-when="select">
|
||||
<select id="field.id"
|
||||
class="form-control"
|
||||
ng-disabled="field.disabled"
|
||||
ng-options="{$ field.options $}"
|
||||
ng-model="field.value">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.form-field.service',
|
||||
formFieldService);
|
||||
|
||||
function formFieldService() {
|
||||
var service = {
|
||||
FormField: FormField
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Utility class for managing form fields.
|
||||
* Used is association with the form-field directive.
|
||||
*
|
||||
* @param {object} args - Base properties are:
|
||||
* type [string] - Field type. One of: 'input', 'radio', 'select'
|
||||
* id [string] - id/name of the DOM value element
|
||||
* title [string] - Label used to identify the field to the user
|
||||
* options - type == radio [array]:
|
||||
* List of options for a radio field
|
||||
* type == select [string]:
|
||||
* String expression that is passed to ng-options
|
||||
* value - Initial value of the field
|
||||
* required [boolean] - Does the field require a value
|
||||
* desc [string] - Field description
|
||||
* pattern [RegExp] - Regular expression pattern used to match
|
||||
* valid input values
|
||||
* disabled [boolean] - Is the field disabled
|
||||
* info [string] - Additional information about the current state of
|
||||
* the field. It will be displayed in a tooltip
|
||||
* associated with the field.
|
||||
* autoFocus [boolean] - True if the focus should be set to this field. Only
|
||||
* applies to fields of type input.
|
||||
* change [string] - Expression to be evaluated when the value of this
|
||||
* field changes. Only applies to fields of type input.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function FormField(args) {
|
||||
var field = this;
|
||||
field.type = 'input';
|
||||
field.id = undefined;
|
||||
field.title = undefined;
|
||||
field.options = undefined;
|
||||
field.value = undefined;
|
||||
field.required = false;
|
||||
field.desc = undefined;
|
||||
field.pattern = undefined;
|
||||
field.disabled = false;
|
||||
field.info = undefined;
|
||||
field.autoFocus = false;
|
||||
field.change = undefined;
|
||||
|
||||
angular.forEach(args, function(value, arg) {
|
||||
field[arg] = value;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description Test whether the field has a non-empty value.
|
||||
* Note that an empty value can be either '' or undefined in the
|
||||
* case of a required field
|
||||
*
|
||||
* @return {boolean} Return true if the field has a value
|
||||
*/
|
||||
this.hasValue = function() {
|
||||
return angular.isDefined(this.value) && this.value !== '';
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Disable this field.
|
||||
*
|
||||
* @param {string} reason - Optional reason for disabling this field.
|
||||
* @return {void}
|
||||
*/
|
||||
this.disable = function(reason) {
|
||||
this.disabled = true;
|
||||
if (angular.isDefined(reason)) {
|
||||
this.info = reason;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
})();
|
|
@ -1,90 +0,0 @@
|
|||
/**
|
||||
* Copyright 2017 Cray 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @description Unit tests for the form-field service
|
||||
*/
|
||||
|
||||
describe(
|
||||
'horizon.dashboard.admin.ironic.form-field.service',
|
||||
|
||||
function() {
|
||||
var formFieldService;
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
formFieldService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.form-field.service');
|
||||
}));
|
||||
|
||||
it('defines the form-field service', function() {
|
||||
expect(formFieldService).toBeDefined();
|
||||
});
|
||||
|
||||
it('FormField - default construction', function() {
|
||||
var field = new formFieldService.FormField({});
|
||||
|
||||
expect(field.type).toEqual('input');
|
||||
expect(field.id).toBeUndefined();
|
||||
expect(field.title).toBeUndefined();
|
||||
expect(field.options).toBeUndefined();
|
||||
expect(field.value).toBeUndefined();
|
||||
expect(field.required).toBe(false);
|
||||
expect(field.desc).toBeUndefined();
|
||||
expect(field.pattern).toBeUndefined();
|
||||
expect(field.disabled).toBe(false);
|
||||
expect(field.info).toBeUndefined();
|
||||
expect(field.autoFocus).toBe(false);
|
||||
expect(field.change).toBeUndefined();
|
||||
expect(field.hasValue).toBeDefined();
|
||||
expect(field.disable).toBeDefined();
|
||||
});
|
||||
|
||||
it('FormField - local parameters', function() {
|
||||
var title = "title";
|
||||
var field = new formFieldService.FormField({
|
||||
title: title
|
||||
});
|
||||
|
||||
expect(field.title).toBe(title);
|
||||
});
|
||||
|
||||
it('hasValue', function() {
|
||||
var field = new formFieldService.FormField({});
|
||||
expect(field.hasValue()).toBe(false);
|
||||
|
||||
field.value = '';
|
||||
expect(field.hasValue()).toBe(false);
|
||||
|
||||
field.value = null;
|
||||
expect(field.hasValue()).toBe(true);
|
||||
|
||||
field.value = 'True';
|
||||
expect(field.hasValue()).toBe(true);
|
||||
});
|
||||
|
||||
it('disable', function() {
|
||||
var field = new formFieldService.FormField({});
|
||||
expect(field.disabled).toBe(false);
|
||||
field.disable();
|
||||
expect(field.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,718 +0,0 @@
|
|||
/*
|
||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||
* © Copyright 2016 Cray 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.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @description Service that provides a mock for the Ironic backend.
|
||||
*/
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.backend-mock.service',
|
||||
ironicBackendMockService);
|
||||
|
||||
ironicBackendMockService.$inject = [
|
||||
'$httpBackend',
|
||||
'horizon.framework.util.uuid.service',
|
||||
'horizon.dashboard.admin.ironic.validMacAddressPattern'
|
||||
];
|
||||
|
||||
function ironicBackendMockService($httpBackend,
|
||||
uuidService,
|
||||
validMacAddressPattern) {
|
||||
// Default node object.
|
||||
var defaultNode = {
|
||||
chassis_uuid: null,
|
||||
clean_step: {},
|
||||
console_enabled: false,
|
||||
driver: undefined,
|
||||
driver_info: {},
|
||||
driver_internal_info: {},
|
||||
extra: {},
|
||||
inspection_finished_at: null,
|
||||
inspection_started_at: null,
|
||||
instance_info: {},
|
||||
instance_uuid: null,
|
||||
last_error: null,
|
||||
maintenance: false,
|
||||
maintenance_reason: null,
|
||||
name: null,
|
||||
network_interface: "flat",
|
||||
power_state: null,
|
||||
properties: {},
|
||||
provision_state: "enroll",
|
||||
provision_updated_at: null,
|
||||
raid_config: {},
|
||||
reservation: null,
|
||||
resource_class: null,
|
||||
target_power_state: null,
|
||||
target_provision_state: null,
|
||||
target_raid_config: {},
|
||||
updated_at: null,
|
||||
uuid: undefined
|
||||
};
|
||||
|
||||
// Default port object.
|
||||
var defaultPort = {
|
||||
address: undefined,
|
||||
created_at: null,
|
||||
extra: {},
|
||||
internal_info: {},
|
||||
local_link_connection: {},
|
||||
node_uuid: undefined,
|
||||
portgroup_uuid: null,
|
||||
pxe_enabled: true,
|
||||
updated_at: null,
|
||||
uuid: undefined
|
||||
};
|
||||
|
||||
// Default portgroup object.
|
||||
var defaultPortgroup = {
|
||||
address: null,
|
||||
created_at: null,
|
||||
extra: {},
|
||||
internal_info: {},
|
||||
mode: "active-backup",
|
||||
name: null,
|
||||
node_uuid: undefined,
|
||||
ports: [],
|
||||
properties: {},
|
||||
standalone_ports_supported: true,
|
||||
updated_at: null,
|
||||
uuid: undefined
|
||||
};
|
||||
|
||||
// Value of the next available system port
|
||||
var nextAvailableSystemPort = 1024;
|
||||
|
||||
// Additional service parameters
|
||||
var params = {
|
||||
// Console info
|
||||
consoleType: "shellinabox",
|
||||
consoleUrl: "http://localhost:",
|
||||
defaultDriver: "agent_ipmitool",
|
||||
supportedBootDevices: ["pxe", "bios", "safe"]
|
||||
};
|
||||
|
||||
// List of supported drivers
|
||||
var drivers = [{name: params.defaultDriver}];
|
||||
|
||||
// List of images
|
||||
var images = [];
|
||||
|
||||
var service = {
|
||||
params: params,
|
||||
init: init,
|
||||
flush: flush,
|
||||
postTest: postTest,
|
||||
getNode: getNode,
|
||||
getNodeBootDevice: getNodeBootDevice,
|
||||
getNodeSupportedBootDevices: getNodeSupportedBootDevices,
|
||||
nodeGetConsoleUrl: nodeGetConsoleUrl,
|
||||
getDrivers: getDrivers,
|
||||
getImages: getImages,
|
||||
getPort: getPort,
|
||||
getPortgroup: getPortgroup
|
||||
};
|
||||
|
||||
var responseCode = {
|
||||
SUCCESS: 200,
|
||||
EMPTY_RESPONSE: 204,
|
||||
BAD_QUERY: 400,
|
||||
RESOURCE_NOT_FOUND: 404,
|
||||
RESOURCE_CONFLICT: 409
|
||||
};
|
||||
|
||||
// Dictionary of active nodes indexed by node-id (uuid and name)
|
||||
var nodes = {};
|
||||
|
||||
// Dictionary of active ports indexed by port-uuid
|
||||
var ports = {};
|
||||
|
||||
// Dictionary of active portgroups indexed by portgroup-uuid
|
||||
var portgroups = {};
|
||||
|
||||
return service;
|
||||
|
||||
/**
|
||||
* @description Get and reserve the next available system port.
|
||||
*
|
||||
* @return {int} Port number.
|
||||
*/
|
||||
function getNextAvailableSystemPort() {
|
||||
return nextAvailableSystemPort++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a backend managed node.
|
||||
*
|
||||
* @param {object} params - Dictionary of parameters that define
|
||||
* the node to be created.
|
||||
* @return {object|null} Node object, or null if the node could
|
||||
* not be created.
|
||||
*/
|
||||
function createNode(params) {
|
||||
var node = null;
|
||||
|
||||
if (angular.isDefined(params.driver)) {
|
||||
node = angular.copy(defaultNode);
|
||||
angular.forEach(params, function(value, key) {
|
||||
node[key] = value;
|
||||
});
|
||||
|
||||
if (angular.isUndefined(node.uuid)) {
|
||||
node.uuid = uuidService.generate();
|
||||
}
|
||||
|
||||
var backendNode = {
|
||||
base: node,
|
||||
consolePort: getNextAvailableSystemPort(),
|
||||
ports: {}, // Indexed by port-uuid
|
||||
portgroups: {}, // Indexed by portgroup-uuid
|
||||
supportedBootDevices: service.params.supportedBootDevices,
|
||||
bootDevice: {
|
||||
boot_device: service.params.supportedBootDevices[0],
|
||||
persistent: true
|
||||
}
|
||||
};
|
||||
|
||||
nodes[node.uuid] = backendNode;
|
||||
|
||||
if (node.name !== null) {
|
||||
nodes[node.name] = backendNode;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get a specified node.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the requested node.
|
||||
* @return {object|null} Base node object, or null if the node
|
||||
* does not exist.
|
||||
*/
|
||||
function getNode(nodeId) {
|
||||
return angular.isDefined(nodes[nodeId]) ? nodes[nodeId].base : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the boot device of a specified node.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the requested node.
|
||||
* @return {object} Boot device.
|
||||
*/
|
||||
function getNodeBootDevice(nodeId) {
|
||||
return angular.isDefined(nodes[nodeId])
|
||||
? nodes[nodeId].bootDevice : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the list of supported boot devices of
|
||||
* a specified node.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the requested node.
|
||||
* @return {string []} List of supported boot devices.
|
||||
*/
|
||||
function getNodeSupportedBootDevices(nodeId) {
|
||||
return angular.isDefined(nodes[nodeId])
|
||||
? nodes[nodeId].supportedBootDevices : undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* @description Get the console-url for a specified node.
|
||||
*
|
||||
* @param {string} nodeId - Uuid or name of the node.
|
||||
* @return {string|null} Console url if the console is enabled,
|
||||
* null otherwise.
|
||||
*/
|
||||
function nodeGetConsoleUrl(nodeId) {
|
||||
return nodes[nodeId].base.console_enabled
|
||||
? service.params.consoleUrl + nodes[nodeId].consolePort
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Test whether a mac address is being used by an
|
||||
* existing port.
|
||||
*
|
||||
* @param {string} address - Mac address.
|
||||
* @return {boolean} True if the mac address is being used by
|
||||
* another port, otherwise false.
|
||||
*/
|
||||
function macAddressInUse(address) {
|
||||
for (var uuid in ports) {
|
||||
if (ports.hasOwnProperty(uuid) &&
|
||||
angular.isDefined(ports[uuid].address)) {
|
||||
if (ports[uuid].address === address) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a backend managed port.
|
||||
*
|
||||
* @param {object} params - Dictionary of parameters that define
|
||||
* the port to be created.
|
||||
* @return {object|null} Port object, or null if the port could
|
||||
* not be created.
|
||||
*/
|
||||
function createPort(params) {
|
||||
var port = null;
|
||||
var status = responseCode.BAD_QUERY;
|
||||
if (angular.isDefined(params.address) &&
|
||||
angular.isDefined(params.node_uuid) &&
|
||||
params.address.match(validMacAddressPattern) &&
|
||||
angular.isDefined(nodes[params.node_uuid])) {
|
||||
if (macAddressInUse(params.address)) {
|
||||
status = responseCode.RESOURCE_CONFLICT;
|
||||
} else {
|
||||
port = angular.copy(defaultPort);
|
||||
angular.forEach(params, function(value, key) {
|
||||
port[key] = value;
|
||||
});
|
||||
|
||||
if (angular.isUndefined(port.uuid)) {
|
||||
port.uuid = uuidService.generate();
|
||||
}
|
||||
|
||||
ports[port.uuid] = port;
|
||||
|
||||
nodes[port.node_uuid].ports[port.uuid] = port;
|
||||
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return [status, port];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a portgroup.
|
||||
* This function is not yet fully implemented.
|
||||
*
|
||||
* @param {object} params - Dictionary of parameters that define
|
||||
* the portgroup to be created.
|
||||
* @return {object|null} Portgroup object, or null if the port could
|
||||
* not be created.
|
||||
*/
|
||||
function createPortgroup(params) {
|
||||
var portgroup = null;
|
||||
var status = responseCode.BAD_QUERY;
|
||||
if (angular.isDefined(nodes[params.node_uuid])) {
|
||||
if (angular.isDefined(params.name) &&
|
||||
params.name !== null &&
|
||||
angular.isDefined(portgroups[params.name])) {
|
||||
status = responseCode.RESOURCE_CONFLICT;
|
||||
} else {
|
||||
portgroup = angular.copy(defaultPortgroup);
|
||||
angular.forEach(params, function(value, key) {
|
||||
portgroup[key] = value;
|
||||
});
|
||||
|
||||
if (angular.isUndefined(portgroup.uuid)) {
|
||||
portgroup.uuid = uuidService.generate();
|
||||
}
|
||||
|
||||
portgroups[portgroup.uuid] = portgroup;
|
||||
if (portgroup.name !== null) {
|
||||
portgroups[portgroup.name] = portgroup;
|
||||
}
|
||||
|
||||
nodes[portgroup.node_uuid].portgroups[portgroup.uuid] = portgroup;
|
||||
}
|
||||
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
|
||||
return [status, portgroup];
|
||||
}
|
||||
|
||||
/**
|
||||
* description Get a specified port.
|
||||
*
|
||||
* @param {string} portUuid - Uuid of the requested port.
|
||||
* @return {object|null} Port object, or null if the port
|
||||
* does not exist.
|
||||
*/
|
||||
function getPort(portUuid) {
|
||||
return angular.isDefined(ports[portUuid]) ? ports[portUuid] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* description Get a specified portgroup.
|
||||
*
|
||||
* @param {string} portgroupId - Uuid or name of the requested portgroup.
|
||||
* @return {object|null} Portgroup object, or null if the portgroup
|
||||
* does not exist.
|
||||
*/
|
||||
function getPortgroup(portgroupId) {
|
||||
return angular.isDefined(portgroups[portgroupId])
|
||||
? portgroups[portgroupId] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Initialize the Backend-Mock service.
|
||||
* Create the handlers that intercept http requests.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function init() {
|
||||
// Create node
|
||||
$httpBackend.whenPOST(/\/api\/ironic\/nodes\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var node = createNode(JSON.parse(data).node);
|
||||
return [node ? responseCode.SUCCESS : responseCode.BAD_QUERY, node];
|
||||
});
|
||||
|
||||
// Delete node
|
||||
$httpBackend.whenDELETE(/\/api\/ironic\/nodes\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var nodeId = JSON.parse(data).node;
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
if (angular.isDefined(nodes[nodeId])) {
|
||||
var node = nodes[nodeId].base;
|
||||
if (node.name !== null) {
|
||||
delete nodes[node.name];
|
||||
delete nodes[node.uuid];
|
||||
} else {
|
||||
delete nodes[nodeId];
|
||||
}
|
||||
status = responseCode.EMPTY_RESPONSE;
|
||||
}
|
||||
return [status, ""];
|
||||
});
|
||||
|
||||
function _addItem(node, path, value) {
|
||||
var parts = path.substring(1).split("/");
|
||||
var leaf = parts.pop();
|
||||
var obj = node;
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i];
|
||||
if (angular.isUndefined(obj[part])) {
|
||||
obj[part] = {};
|
||||
}
|
||||
obj = obj[part];
|
||||
}
|
||||
obj[leaf] = value;
|
||||
}
|
||||
|
||||
function _removeItem(node, path) {
|
||||
var parts = path.substring(1).split("/");
|
||||
var leaf = parts.pop();
|
||||
var obj = node;
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
obj = obj[parts[i]];
|
||||
}
|
||||
delete obj[leaf];
|
||||
}
|
||||
|
||||
function _replaceItem(node, path, value) {
|
||||
if (path === "/name" &&
|
||||
node.name !== null) {
|
||||
delete nodes[node.name];
|
||||
if (value !== null) {
|
||||
nodes[value] = node;
|
||||
}
|
||||
}
|
||||
|
||||
var parts = path.substring(1).split("/");
|
||||
var leaf = parts.pop();
|
||||
var obj = node;
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
obj = obj[parts[i]];
|
||||
}
|
||||
obj[leaf] = value;
|
||||
}
|
||||
|
||||
// Update node
|
||||
$httpBackend.whenPATCH(/\/api\/ironic\/nodes\/([^\/]+)$/,
|
||||
undefined,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
var node = service.getNode(params.nodeId);
|
||||
if (angular.isDefined(node)) {
|
||||
var patch = JSON.parse(data).patch;
|
||||
angular.forEach(patch, function(operation) {
|
||||
switch (operation.op) {
|
||||
case "add":
|
||||
_addItem(node, operation.path, operation.value);
|
||||
break;
|
||||
case "remove":
|
||||
_removeItem(node, operation.path);
|
||||
break;
|
||||
case "replace":
|
||||
_replaceItem(node, operation.path, operation.value);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
return [status, node];
|
||||
});
|
||||
|
||||
// Get node
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)$/,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
if (angular.isDefined(nodes[params.nodeId])) {
|
||||
return [responseCode.SUCCESS, nodes[params.nodeId].base];
|
||||
} else {
|
||||
return [responseCode.RESOURCE_NOT_FOUND, null];
|
||||
}
|
||||
});
|
||||
|
||||
// Get console
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/(.+)\/states\/console/,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
var node = nodes[params.nodeId];
|
||||
var consoleEnabled = node.base.console_enabled;
|
||||
var consoleInfo = consoleEnabled
|
||||
? {console_type: service.params.consoleType,
|
||||
url: service.params.consoleUrl + node.consolePort}
|
||||
: null;
|
||||
|
||||
var info = {
|
||||
console_enabled: consoleEnabled,
|
||||
console_info: consoleInfo};
|
||||
return [responseCode.SUCCESS, info];
|
||||
});
|
||||
|
||||
// Set console
|
||||
$httpBackend.whenPUT(/\/api\/ironic\/nodes\/(.+)\/states\/console/,
|
||||
undefined,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
data = JSON.parse(data);
|
||||
nodes[params.nodeId].base.console_enabled = data.enabled;
|
||||
return [responseCode.SUCCESS, {}];
|
||||
});
|
||||
|
||||
// Get the ports belonging to a specified node
|
||||
$httpBackend.whenGET(/\/api\/ironic\/ports/)
|
||||
.respond(responseCode.SUCCESS, []);
|
||||
|
||||
// Get boot device
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/boot_device$/,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
if (angular.isDefined(nodes[params.nodeId])) {
|
||||
return [200, nodes[params.nodeId].bootDevice];
|
||||
} else {
|
||||
return [400, null];
|
||||
}
|
||||
});
|
||||
|
||||
// Get supported boot devices
|
||||
$httpBackend.whenGET(
|
||||
/\/api\/ironic\/nodes\/([^\/]+)\/boot_device\/supported$/,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
if (angular.isDefined(nodes[params.nodeId])) {
|
||||
return [200, nodes[params.nodeId].supportedBootDevices];
|
||||
} else {
|
||||
return [400, null];
|
||||
}
|
||||
});
|
||||
|
||||
// Set boot device
|
||||
$httpBackend.whenPUT(/\/api\/ironic\/nodes\/(.+)\/boot_device/,
|
||||
undefined,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
data = JSON.parse(data);
|
||||
var status = 404;
|
||||
if (angular.isDefined(nodes[params.nodeId])) {
|
||||
var node = nodes[params.nodeId];
|
||||
if (node.supportedBootDevices.indexOf(data.boot_device) !== -1) {
|
||||
node.bootDevice.boot_device = data.boot_device;
|
||||
if (angular.isDefined(data.persistent)) {
|
||||
node.bootDevice.persistent = data.persistent;
|
||||
}
|
||||
status = 200;
|
||||
}
|
||||
}
|
||||
return [status, null];
|
||||
});
|
||||
|
||||
// Validate the interfaces associated with a specified node
|
||||
$httpBackend.whenGET(/\/api\/ironic\/nodes\/([^\/]+)\/validate$/,
|
||||
undefined,
|
||||
['nodeId'])
|
||||
.respond(responseCode.SUCCESS, []);
|
||||
|
||||
// Get the currently available drivers
|
||||
$httpBackend.whenGET(/\/api\/ironic\/drivers\/$/)
|
||||
.respond(responseCode.SUCCESS, {drivers: drivers});
|
||||
|
||||
// Get driver properties
|
||||
$httpBackend.whenGET(/\/api\/ironic\/drivers\/([^\/]+)\/properties$/,
|
||||
undefined,
|
||||
['driverName'])
|
||||
.respond(responseCode.SUCCESS, []);
|
||||
|
||||
// Get glance images
|
||||
$httpBackend.whenGET(/\/api\/glance\/images/)
|
||||
.respond(responseCode.SUCCESS, {items: images});
|
||||
|
||||
// Create port
|
||||
$httpBackend.whenPOST(/\/api\/ironic\/ports\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
return createPort(JSON.parse(data).port);
|
||||
});
|
||||
|
||||
// Delete port
|
||||
$httpBackend.whenDELETE(/\/api\/ironic\/ports\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var portUuid = JSON.parse(data).port_uuid;
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
if (angular.isDefined(ports[portUuid])) {
|
||||
delete ports[portUuid];
|
||||
status = responseCode.EMPTY_RESPONSE;
|
||||
}
|
||||
return [status, ""];
|
||||
});
|
||||
|
||||
// Get ports
|
||||
$httpBackend.whenGET(/\/api\/ironic\/ports\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var nodeId = JSON.parse(data).node_id;
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
var ports = [];
|
||||
if (angular.isDefined(nodes[nodeId])) {
|
||||
angular.forEach(nodes[nodeId].ports, function(port) {
|
||||
ports.push(port);
|
||||
});
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
return [status, {ports: ports}];
|
||||
});
|
||||
|
||||
// Create portgroup
|
||||
$httpBackend.whenPOST(/\/api\/ironic\/portgroups\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
return createPortgroup(JSON.parse(data));
|
||||
});
|
||||
|
||||
// Get portgroups. This function is not fully implemented.
|
||||
$httpBackend.whenGET(/\/api\/ironic\/ports\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var nodeId = JSON.parse(data).node_id;
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
var portgroups = [];
|
||||
if (angular.isDefined(nodes[nodeId])) {
|
||||
angular.forEach(nodes[nodeId].portgroups, function(portgroup) {
|
||||
portgroups.push(portgroup);
|
||||
});
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
return [status, {portgroups: portgroups}];
|
||||
});
|
||||
|
||||
// Delete portgroup. This function is not yet implemented.
|
||||
$httpBackend.whenDELETE(/\/api\/ironic\/portgroups\/$/)
|
||||
.respond(function(method, url, data) {
|
||||
var portgroupId = JSON.parse(data).portgroup_id;
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
if (angular.isDefined(portgroups[portgroupId])) {
|
||||
var portgroup = portgroups[portgroupId];
|
||||
if (portgroup.name !== null) {
|
||||
delete portgroups[portgroup.name];
|
||||
delete portgroups[portgroup.uuid];
|
||||
} else {
|
||||
delete portgroups[portgroupId];
|
||||
}
|
||||
status = responseCode.EMPTY_RESPONSE;
|
||||
}
|
||||
return [status, ""];
|
||||
});
|
||||
|
||||
// Get portgroup ports
|
||||
$httpBackend.whenGET(/\/api\/ironic\/portgroups\/([^\/]+)\/ports$/,
|
||||
undefined,
|
||||
['portgroupId'])
|
||||
.respond(function(method, url, data, headers, params) {
|
||||
var ports = [];
|
||||
var status = responseCode.RESOURCE_NOT_FOUND;
|
||||
if (angular.isDefined(portgroups[params.portgroupId])) {
|
||||
var portgroup = portgroups[params.portgroupId];
|
||||
var node = nodes[portgroup.node_uuid];
|
||||
angular.forEach(node.ports, function(port) {
|
||||
if (port.portgroup_uuid === portgroup.uuid) {
|
||||
ports.push(port);
|
||||
}
|
||||
});
|
||||
status = responseCode.SUCCESS;
|
||||
}
|
||||
return [status, {ports: ports}];
|
||||
});
|
||||
} // init()
|
||||
|
||||
/**
|
||||
* @description Get the list of supported drivers
|
||||
*
|
||||
* @return {[]} Array of driver objects
|
||||
*/
|
||||
function getDrivers() {
|
||||
return drivers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the list of images
|
||||
*
|
||||
* @return {[]} Array of image objects
|
||||
*/
|
||||
function getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Flush pending requests
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function flush() {
|
||||
$httpBackend.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Post test verifications.
|
||||
* This function should be called after completion of a unit test.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function postTest() {
|
||||
$httpBackend.verifyNoOutstandingExpectation();
|
||||
$httpBackend.verifyNoOutstandingRequest();
|
||||
}
|
||||
} // ironicBackendMockService()
|
||||
})();
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @ngname horizon.dashboard.admin.ironic
|
||||
*
|
||||
* @description
|
||||
* Provides all of the services and widgets required
|
||||
* to support and display Ironic related content.
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic', [])
|
||||
.config(config);
|
||||
|
||||
config.$inject = ['$provide', '$windowProvider'];
|
||||
|
||||
function config($provide, $windowProvider) {
|
||||
$provide.constant('horizon.dashboard.admin.ironic.validHostNamePattern',
|
||||
'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$'); // eslint-disable-line max-len
|
||||
|
||||
$provide.constant('horizon.dashboard.admin.ironic.validUuidPattern',
|
||||
'^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$'); // eslint-disable-line max-len
|
||||
|
||||
$provide.constant('horizon.dashboard.admin.ironic.validMacAddressPattern',
|
||||
'^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){5}$'); // eslint-disable-line max-len
|
||||
|
||||
$provide.constant('horizon.dashboard.admin.ironic.validDatapathIdPattern',
|
||||
'^[0-9A-Fa-f]{16}$'); // eslint-disable-line max-len
|
||||
|
||||
var path = $windowProvider.$get().STATIC_URL + 'dashboard/admin/ironic/';
|
||||
$provide.constant('horizon.dashboard.admin.ironic.basePath', path);
|
||||
|
||||
var events = {
|
||||
ENROLL_NODE_SUCCESS:'horizon.dashboard.admin.ironic.ENROLL_NODE_SUCCESS',
|
||||
DELETE_NODE_SUCCESS:'horizon.dashboard.admin.ironic.DELETE_NODE_SUCCESS',
|
||||
EDIT_NODE_SUCCESS:'horizon.dashboard.admin.ironic.EDIT_NODE_SUCCESS',
|
||||
CREATE_PORT_SUCCESS:'horizon.dashboard.admin.ironic.CREATE_PORT_SUCCESS',
|
||||
DELETE_PORT_SUCCESS:'horizon.dashboard.admin.ironic.DELETE_PORT_SUCCESS',
|
||||
EDIT_PORT_SUCCESS:'horizon.dashboard.admin.ironic.EDIT_PORT_SUCCESS'
|
||||
};
|
||||
$provide.constant('horizon.dashboard.admin.ironic.events', events);
|
||||
}
|
||||
})();
|
|
@ -1,673 +0,0 @@
|
|||
/*
|
||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||
* © Copyright 2016 Cray Inc.
|
||||
* Copyright 2017 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.openstack-service-api')
|
||||
.factory('horizon.app.core.openstack-service-api.ironic', ironicAPI);
|
||||
|
||||
ironicAPI.$inject = [
|
||||
'$q',
|
||||
'horizon.framework.util.http.service',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.dashboard.admin.ironic.node-error.service'
|
||||
];
|
||||
|
||||
/**
|
||||
* @description Service that provides access to the Ironic client API
|
||||
*
|
||||
* @param {object} $q - Promise provider
|
||||
* @param {object} apiService - HTTP service
|
||||
* @param {object} toastService - User message service
|
||||
* @param {object} nodeErrorService - Node error service
|
||||
* @return {object} Ironic API service
|
||||
*/
|
||||
function ironicAPI($q, apiService, toastService, nodeErrorService) {
|
||||
var service = {
|
||||
createNode: createNode,
|
||||
createPort: createPort,
|
||||
deleteNode: deleteNode,
|
||||
deletePort: deletePort,
|
||||
getDrivers: getDrivers,
|
||||
getDriverProperties: getDriverProperties,
|
||||
getNode: getNode,
|
||||
getNodes: getNodes,
|
||||
getPortsWithNode: getPortsWithNode,
|
||||
getBootDevice: getBootDevice,
|
||||
getSupportedBootDevices: getSupportedBootDevices,
|
||||
nodeGetConsole: nodeGetConsole,
|
||||
nodeSetConsoleMode: nodeSetConsoleMode,
|
||||
nodeSetMaintenance: nodeSetMaintenance,
|
||||
nodeSetBootDevice: nodeSetBootDevice,
|
||||
nodeSetPowerState: nodeSetPowerState,
|
||||
setNodeProvisionState: setNodeProvisionState,
|
||||
updateNode: updateNode,
|
||||
updatePort: updatePort,
|
||||
validateNode: validateNode,
|
||||
createPortgroup: createPortgroup,
|
||||
getPortgroups: getPortgroups,
|
||||
deletePortgroup: deletePortgroup,
|
||||
getPortgroupPorts: getPortgroupPorts
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
/**
|
||||
* @description Retrieve a list of nodes
|
||||
* http://developer.openstack.org/api-ref/baremetal/?
|
||||
* expanded=create-node-detail#list-nodes-detailed
|
||||
*
|
||||
* @return {promise} Node collection in JSON
|
||||
*/
|
||||
function getNodes() {
|
||||
return apiService.get('/api/ironic/nodes/')
|
||||
.then(function(response) {
|
||||
angular.forEach(response.data.nodes, function(node) {
|
||||
nodeErrorService.checkNodeError(node);
|
||||
});
|
||||
return response.data.nodes;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve Ironic nodes. %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve information about the given node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/?
|
||||
* expanded=create-node-detail#list-nodes-detailed
|
||||
*
|
||||
* @param {string} uuid – UUID or logical name of a node.
|
||||
* @return {promise} Node
|
||||
*/
|
||||
function getNode(uuid) {
|
||||
return apiService.get('/api/ironic/nodes/' + uuid)
|
||||
.then(function(response) {
|
||||
nodeErrorService.checkNodeError(response.data);
|
||||
return response.data; // The node
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve the Ironic node: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve the boot device for a node
|
||||
* https://developer.openstack.org/api-ref/baremetal/#get-boot-device
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @return {promise} Dictionary describing the current boot device
|
||||
*/
|
||||
function getBootDevice(nodeId) {
|
||||
return apiService.get('/api/ironic/nodes/' + nodeId + '/boot_device')
|
||||
.then(function(response) {
|
||||
return response.data;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve boot device for Ironic node. %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve the supported boot devices for a node
|
||||
* https://developer.openstack.org/api-ref/baremetal/#get-supported-boot-devices
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @return {promise} List of supported boot devices
|
||||
*/
|
||||
function getSupportedBootDevices(nodeId) {
|
||||
return apiService.get('/api/ironic/nodes/' + nodeId +
|
||||
'/boot_device/supported')
|
||||
.then(function(response) {
|
||||
return response.data; // List of supported boot devices
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext(
|
||||
'Unable to retrieve supported boot devices for Ironic node. %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve a list of ports associated with a node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#list-detailed-ports
|
||||
*
|
||||
* @param {string} uuid – UUID or logical name of a node.
|
||||
* @return {promise} List of ports
|
||||
*/
|
||||
function getPortsWithNode(uuid) {
|
||||
var config = {
|
||||
params : {
|
||||
node_id: uuid
|
||||
}
|
||||
};
|
||||
return apiService.get('/api/ironic/ports/', config)
|
||||
.then(function(response) {
|
||||
// Add id and name properties to support delete operations
|
||||
// using the deleteModalService
|
||||
angular.forEach(response.data.ports, function(port) {
|
||||
port.id = port.uuid;
|
||||
port.name = port.address;
|
||||
});
|
||||
return response.data.ports;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve the Ironic node ports: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the maintenance state of a node
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#set-maintenance-flag
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @param {boolean} mode - True to put the node in maintenance mode,
|
||||
* false to remove it from maintenance mode.
|
||||
* @param {string} reason - Reason for putting the node in maintenance.
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function nodeSetMaintenance(nodeId, mode, reason) {
|
||||
var url = '/api/ironic/nodes/' + nodeId + '/maintenance';
|
||||
var promise = mode
|
||||
? apiService.patch(url,
|
||||
{maint_reason: reason ? reason
|
||||
: gettext("No reason given.")})
|
||||
: apiService.delete(url);
|
||||
|
||||
return promise.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to set Ironic node %s maintenance state: %s'),
|
||||
[nodeId, response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the boot device of a node
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#set-boot-device
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @param {string} bootDevice - Selected boot device.
|
||||
* @param {Boolean} persistent - True or False.
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function nodeSetBootDevice(nodeId, bootDevice, persistent) {
|
||||
return apiService.put('/api/ironic/nodes/' + nodeId + '/boot_device',
|
||||
{boot_device: bootDevice,
|
||||
persistent: persistent})
|
||||
.then(function() {
|
||||
toastService.add('success',
|
||||
gettext('Refresh page to see set boot device'));
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to set boot device: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the power state of the node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#change-node-power-state
|
||||
*
|
||||
* @param {string} uuid – UUID or logical name of a node.
|
||||
* @param {string} state - Target power state ['on', 'off', 'reboot']
|
||||
* @param {boolean} soft - Flag for graceful power 'off' or reboot
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function nodeSetPowerState(uuid, state, soft) {
|
||||
var data = {
|
||||
state: state
|
||||
};
|
||||
if (angular.isDefined(soft)) {
|
||||
data.soft = soft;
|
||||
}
|
||||
return apiService.patch('/api/ironic/nodes/' + uuid + '/states/power',
|
||||
data)
|
||||
.then(function() {
|
||||
toastService.add('success',
|
||||
gettext('Refresh page to see updated power status'));
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to power off the node: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the target provision state of the node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#change-node-provision-state
|
||||
*
|
||||
* @param {string} uuid – UUID of a node.
|
||||
* @param {string} verb – Provisioning verb used to move node to desired
|
||||
* target state
|
||||
* @param {object []} cleanSteps - List of cleaning steps. Only used
|
||||
* when the value of verb is 'clean'
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function setNodeProvisionState(uuid, verb, cleanSteps) {
|
||||
return apiService.put('/api/ironic/nodes/' + uuid + '/states/provision',
|
||||
{verb: verb,
|
||||
clean_steps: cleanSteps})
|
||||
.then(function() {
|
||||
var msg = gettext(
|
||||
'A request has been made to change the provisioning state of node %s');
|
||||
toastService.add('success', interpolate(msg, [uuid], false));
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to set node provision state: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create an Ironic node
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#create-node
|
||||
*
|
||||
* @param {object} params – Object containing parameters that define
|
||||
* the node to be created
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function createNode(params) {
|
||||
var data = {
|
||||
node: params
|
||||
};
|
||||
return apiService.post('/api/ironic/nodes/', data)
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to create node: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Delete the specified node from inventory
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#delete-node
|
||||
*
|
||||
* @param {string} nodeIdent – UUID or logical name of a node.
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function deleteNode(nodeIdent) {
|
||||
var data = {
|
||||
node: nodeIdent
|
||||
};
|
||||
return apiService.delete('/api/ironic/nodes/', data)
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to delete node %s: %s'),
|
||||
[nodeIdent, response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Update the definition of a specified node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#update-node
|
||||
*
|
||||
* @param {string} uuid – UUID of a node.
|
||||
* @param {object[]} patch – Sequence of update operations
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function updateNode(uuid, patch) {
|
||||
var data = {
|
||||
patch: patch
|
||||
};
|
||||
return apiService.patch('/api/ironic/nodes/' + uuid, data)
|
||||
.then(function(response) {
|
||||
var msg = gettext('Successfully updated node %s');
|
||||
toastService.add('success', interpolate(msg, [uuid], false));
|
||||
return response.data; // The updated node
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to update node %s: %s'),
|
||||
[uuid, response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Validate the specified node
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#validate-node
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @return {promise} Promise. success: list of interface validation
|
||||
* records, error: failure response
|
||||
*/
|
||||
function validateNode(nodeId) {
|
||||
return apiService.get('/api/ironic/nodes/' + nodeId + '/validate',
|
||||
{node: nodeId})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to validate node %s: %s'),
|
||||
[nodeId, response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve the list of Ironic drivers
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#list-drivers
|
||||
*
|
||||
* @return {promise} Driver collection in JSON
|
||||
*/
|
||||
function getDrivers() {
|
||||
return apiService.get('/api/ironic/drivers/')
|
||||
.then(function(response) {
|
||||
return response.data.drivers;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve Ironic drivers: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve properities of a specified driver
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#show-driver-properties
|
||||
*
|
||||
* @param {string} driverName - Driver name
|
||||
* @returns {promise} Property list
|
||||
*/
|
||||
function getDriverProperties(driverName) {
|
||||
return apiService.get(
|
||||
'/api/ironic/drivers/' + driverName + '/properties')
|
||||
.then(function(response) {
|
||||
return response.data; // Driver properties
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve driver properties: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a network port
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#create-port
|
||||
*
|
||||
* @param {object} port – Object containing parameters that define
|
||||
* the port to be created
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function createPort(port) {
|
||||
var data = {
|
||||
port: port
|
||||
};
|
||||
return apiService.post('/api/ironic/ports/', data)
|
||||
.then(function(response) {
|
||||
toastService.add('success',
|
||||
gettext('Port successfully created'));
|
||||
return response.data; // The newly created port
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to create port: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Delete a network port
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#delete-port
|
||||
*
|
||||
* @param {string} portUuid – UUID of the port to be deleted
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function deletePort(portUuid) {
|
||||
var data = {
|
||||
port_uuid: portUuid
|
||||
};
|
||||
return apiService.delete('/api/ironic/ports/', data)
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to delete port: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Update the definition of a specified port.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#update-a-port
|
||||
*
|
||||
* @param {string} portUuid – UUID of a port.
|
||||
* @param {object[]} patch – Sequence of update operations
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function updatePort(portUuid, patch) {
|
||||
return apiService.patch('/api/ironic/ports/' + portUuid,
|
||||
{patch: patch})
|
||||
.then(function(response) {
|
||||
var msg = gettext('Successfully updated port %s');
|
||||
toastService.add('success', interpolate(msg, [portUuid], false));
|
||||
return response.data; // The updated port
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to update port %s: %s'),
|
||||
[portUuid, response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Set the console mode of the node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/?
|
||||
* expanded=start-stop-console-detail#start-stop-console
|
||||
*
|
||||
* @param {string} uuid – UUID of a node.
|
||||
* @param {boolean} enabled – true to start the console, false to stop it
|
||||
* @return {promise} Promise
|
||||
*/
|
||||
function nodeSetConsoleMode(uuid, enabled) {
|
||||
return apiService.put('/api/ironic/nodes/' + uuid + '/states/console',
|
||||
{enabled: enabled})
|
||||
.then(function(response) {
|
||||
var msg = gettext('Refresh page to see updated console details');
|
||||
toastService.add('success', interpolate(msg, [uuid], false));
|
||||
return response.data;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = gettext('Unable to set console mode: %s');
|
||||
toastService.add('error', interpolate(msg, [response.data], false));
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
function nodeGetConsole(uuid) {
|
||||
return apiService.get('/api/ironic/nodes/' + uuid + '/states/console')
|
||||
.then(function(response) {
|
||||
return response.data; // Object containing console information
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = gettext('Unable to get console for node %s: %s');
|
||||
toastService.add('error',
|
||||
interpolate(msg, [uuid, response.data], false));
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Retrieve a list of portgroups associated with a node.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#list-detailed-portgroups
|
||||
*
|
||||
* @param {string} nodeId – UUID or logical name of a node.
|
||||
* @return {promise} List of portgroups.
|
||||
*/
|
||||
function getPortgroups(nodeId) {
|
||||
return apiService.get('/api/ironic/portgroups/',
|
||||
{params: {node_id: nodeId}})
|
||||
.then(function(response) {
|
||||
// Add id property to support delete operations
|
||||
// using the deleteModalService
|
||||
angular.forEach(response.data.portgroups, function(portgroup) {
|
||||
portgroup.id = portgroup.uuid;
|
||||
});
|
||||
return response.data.portgroups;
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve Ironic node portgroups: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a protgroup.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#create-portgroup
|
||||
*
|
||||
* @param {object} params – Object containing parameters that define
|
||||
* the portgroup to be created.
|
||||
* @return {promise} Promise containing the portgroup.
|
||||
*/
|
||||
function createPortgroup(params) {
|
||||
return apiService.post('/api/ironic/portgroups/', params)
|
||||
.then(function(response) {
|
||||
toastService.add('success',
|
||||
gettext('Portgroup successfully created'));
|
||||
return response.data; // The newly created portgroup
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to create portgroup: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Delete a portgroup.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#delete-portgroup
|
||||
*
|
||||
* @param {string} portgroupId – UUID or name of the portgroup to be deleted.
|
||||
* @return {promise} Promise.
|
||||
*/
|
||||
function deletePortgroup(portgroupId) {
|
||||
return apiService.delete('/api/ironic/portgroups/',
|
||||
{portgroup_id: portgroupId})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(gettext('Unable to delete portgroup: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Get the ports associated with a specified portgroup.
|
||||
*
|
||||
* http://developer.openstack.org/api-ref/baremetal/#list-ports-by-portgroup
|
||||
*
|
||||
* @param {string} portgroupId – UUID or name of the portgroup.
|
||||
* @return {promise} Promise containing a list of ports.
|
||||
*/
|
||||
function getPortgroupPorts(portgroupId) {
|
||||
return apiService.get(
|
||||
'/api/ironic/portgroups/' + portgroupId + '/ports')
|
||||
.then(function(response) {
|
||||
return response.data.ports; // List of ports
|
||||
})
|
||||
.catch(function(response) {
|
||||
var msg = interpolate(
|
||||
gettext('Unable to retrieve portgroup ports: %s'),
|
||||
[response.data],
|
||||
false);
|
||||
toastService.add('error', msg);
|
||||
return $q.reject(msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
}());
|
|
@ -1,600 +0,0 @@
|
|||
/**
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var IRONIC_API_PROPERTIES = [
|
||||
'createNode',
|
||||
'createPort',
|
||||
'createPortgroup',
|
||||
'deleteNode',
|
||||
'deletePort',
|
||||
'deletePortgroup',
|
||||
'getDrivers',
|
||||
'getDriverProperties',
|
||||
'getNode',
|
||||
'getNodes',
|
||||
'getPortgroupPorts',
|
||||
'getPortgroups',
|
||||
'getPortsWithNode',
|
||||
'getBootDevice',
|
||||
'getSupportedBootDevices',
|
||||
'nodeGetConsole',
|
||||
'nodeSetBootDevice',
|
||||
'nodeSetConsoleMode',
|
||||
'nodeSetPowerState',
|
||||
'nodeSetMaintenance',
|
||||
'setNodeProvisionState',
|
||||
'updateNode',
|
||||
'updatePort',
|
||||
'validateNode'
|
||||
];
|
||||
|
||||
/**
|
||||
* @description Unit tests for the Ironic-UI API service
|
||||
*/
|
||||
|
||||
describe(
|
||||
'horizon.dashboard.admin.ironic.service',
|
||||
|
||||
function() {
|
||||
// Name of default driver used to create nodes.
|
||||
var ironicAPI, ironicBackendMockService, defaultDriver;
|
||||
|
||||
/**
|
||||
* @description Create a node.
|
||||
*
|
||||
* @param {object} params - Dictionary of parameters that define the node.
|
||||
* @return {promise} - Promise containing the newly created node.
|
||||
*/
|
||||
function createNode(params) {
|
||||
return ironicAPI.createNode(params)
|
||||
.then(function(response) {
|
||||
return response.data; // node
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Fail the current test
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function failTest() {
|
||||
fail();
|
||||
}
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module('horizon.framework.util'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service', {
|
||||
add: function() {}
|
||||
});
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.app.core.openstack-service-api'));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicBackendMockService =
|
||||
$injector.get('horizon.dashboard.admin.ironic.backend-mock.service');
|
||||
ironicBackendMockService.init();
|
||||
defaultDriver = ironicBackendMockService.params.defaultDriver;
|
||||
}));
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
ironicAPI =
|
||||
$injector.get('horizon.app.core.openstack-service-api.ironic');
|
||||
}));
|
||||
|
||||
it('defines the ironicAPI', function() {
|
||||
expect(ironicAPI).toBeDefined();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
ironicBackendMockService.postTest();
|
||||
});
|
||||
|
||||
describe('ironicAPI', function() {
|
||||
it('service API', function() {
|
||||
expect(Object.getOwnPropertyNames(ironicAPI).sort())
|
||||
.toEqual(IRONIC_API_PROPERTIES.sort());
|
||||
});
|
||||
|
||||
it('getDrivers', function() {
|
||||
ironicAPI.getDrivers()
|
||||
.then(function(drivers) {
|
||||
expect(drivers.length).toBeGreaterThan(0);
|
||||
angular.forEach(drivers, function(driver) {
|
||||
expect(driver.name).toBeDefined();
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createNode - Minimal input data', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
expect(node.driver).toEqual(defaultDriver);
|
||||
expect(node).toEqual(ironicBackendMockService.getNode(node.uuid));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createNode - Missing input data', function() {
|
||||
createNode({})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getNode', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node1) {
|
||||
ironicAPI.getNode(node1.uuid).then(function(node2) {
|
||||
expect(node2).toEqual(node1);
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deleteNode', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.deleteNode(node.uuid).then(function() {
|
||||
return node;
|
||||
});
|
||||
})
|
||||
.then(function(node) {
|
||||
expect(
|
||||
ironicBackendMockService.getNode(node.uuid)).toBeNull();
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deleteNode - nonexistent node', function() {
|
||||
ironicAPI.deleteNode(0)
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('updateNode - resource_class', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.updateNode(
|
||||
node.uuid,
|
||||
[{op: "replace",
|
||||
path: "/resource_class",
|
||||
value: "some-resource-class"}]).then(
|
||||
function(node) {
|
||||
return node;
|
||||
});
|
||||
})
|
||||
.then(function(node) {
|
||||
expect(node.resource_class).toEqual("some-resource-class");
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeGetConsole - console enabled', function() {
|
||||
createNode({driver: defaultDriver,
|
||||
console_enabled: true})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(true);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.nodeGetConsole(node.uuid).then(
|
||||
function(consoleData) {
|
||||
return {node: node, consoleData: consoleData};
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
expect(data.consoleData.console_enabled).toEqual(true);
|
||||
expect(data.consoleData.console_info.console_type)
|
||||
.toEqual(ironicBackendMockService.params.consoleType);
|
||||
expect(data.consoleData.console_info.url)
|
||||
.toEqual(ironicBackendMockService.nodeGetConsoleUrl(
|
||||
data.node.uuid));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeGetConsole - console not enabled', function() {
|
||||
createNode({driver: defaultDriver,
|
||||
console_enabled: false})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.nodeGetConsole(node.uuid);
|
||||
})
|
||||
.then(function(consoleData) {
|
||||
expect(consoleData).toEqual(
|
||||
{console_enabled: false,
|
||||
console_info: null});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeSetConsoleMode - Toggle console mode', function() {
|
||||
createNode({driver: defaultDriver,
|
||||
console_enabled: false})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
ironicAPI.nodeSetConsoleMode(node.uuid, true);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getNode(node.uuid);
|
||||
})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(true);
|
||||
return node;
|
||||
})
|
||||
// Toggle back
|
||||
.then(function(node) {
|
||||
ironicAPI.nodeSetConsoleMode(node.uuid, false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getNode(node.uuid);
|
||||
})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeSetConsoleMode - Redundant console set', function() {
|
||||
createNode({driver: defaultDriver,
|
||||
console_enabled: false})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
ironicAPI.nodeSetConsoleMode(node.uuid, false);
|
||||
return node;
|
||||
})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getNode(node.uuid);
|
||||
})
|
||||
.then(function(node) {
|
||||
expect(node.console_enabled).toEqual(false);
|
||||
return node;
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getBootDevice', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getBootDevice(node.uuid)
|
||||
.then(function(bootDevice) {
|
||||
return {node: node, bootDevice: bootDevice};
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
expect(data.bootDevice).toEqual(
|
||||
ironicBackendMockService.getNodeBootDevice(data.node.uuid));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getSupportedBootDevices', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getSupportedBootDevices(node.uuid);
|
||||
})
|
||||
.then(function(bootDevices) {
|
||||
expect(bootDevices).toEqual(
|
||||
ironicBackendMockService.params.supportedBootDevices);
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeSetBootDevice', function() {
|
||||
var bootDevice = {
|
||||
boot_device: "bios",
|
||||
persistent: false
|
||||
};
|
||||
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.nodeSetBootDevice(node.uuid,
|
||||
bootDevice.boot_device,
|
||||
bootDevice.persistent)
|
||||
.then(function() {
|
||||
return node;
|
||||
});
|
||||
})
|
||||
.then(function(node) {
|
||||
ironicAPI.getBootDevice(node.uuid).then(function(device) {
|
||||
expect(device).toEqual(bootDevice);
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('nodeSetBootDevice - bad device', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.getBootDevice(node.uuid)
|
||||
.then(function(device) {
|
||||
return {node: node, currentBootDevice: device};
|
||||
});
|
||||
})
|
||||
.then(function(data) {
|
||||
ironicAPI.nodeSetBootDevice(data.node.uuid,
|
||||
"bad-device",
|
||||
false)
|
||||
.then(failTest)
|
||||
.catch(function() {
|
||||
// Ensure the boot device is unchanged
|
||||
ironicAPI.getBootDevice(data.node.uuid)
|
||||
.then(function(device) {
|
||||
expect(device).toEqual(data.currentBootDevice);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPort', function() {
|
||||
var macAddr = '00:00:00:00:00:00';
|
||||
var node;
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(createNode) {
|
||||
node = createNode;
|
||||
return ironicAPI.createPort({address: macAddr,
|
||||
node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(port) {
|
||||
expect(port.address).toBe(macAddr);
|
||||
expect(port.node_uuid).toBe(node.uuid);
|
||||
expect(port)
|
||||
.toEqual(ironicBackendMockService.getPort(port.uuid));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPort - missing input data', function() {
|
||||
ironicAPI.createPort({})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPort - bad input data', function() {
|
||||
ironicAPI.createPort({address: "", node_uuid: ""})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPort - duplicate mac address', function() {
|
||||
var macAddr = '00:00:00:00:00:00';
|
||||
var node;
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(createNode) {
|
||||
node = createNode;
|
||||
return ironicAPI.createPort({address: macAddr,
|
||||
node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(port) {
|
||||
expect(port.address).toBe(macAddr);
|
||||
expect(port.node_uuid).toBe(node.uuid);
|
||||
expect(port)
|
||||
.toEqual(ironicBackendMockService.getPort(port.uuid));
|
||||
|
||||
return ironicAPI.createPort({address: macAddr,
|
||||
node_uuid: node.uuid});
|
||||
})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deletePort', function() {
|
||||
var macAddr = '00:00:00:00:00:00';
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.createPort({address: macAddr,
|
||||
node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(port) {
|
||||
expect(port).toBeDefined();
|
||||
expect(port)
|
||||
.toEqual(ironicBackendMockService.getPort(port.uuid));
|
||||
ironicAPI.deletePort(port.uuid).then(function() {
|
||||
expect(ironicBackendMockService.getPort(port.uuid))
|
||||
.toBeNull();
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deletePort - nonexistent port', function() {
|
||||
ironicAPI.deletePort(0)
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPortgroup', function() {
|
||||
var node;
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(createNode) {
|
||||
node = createNode;
|
||||
return ironicAPI.createPortgroup({node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(portgroup) {
|
||||
expect(portgroup.node_uuid).toBe(node.uuid);
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPortgroup - specify portgroup name', function() {
|
||||
var node;
|
||||
var portgroupName = "test-portgroup";
|
||||
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(createNode) {
|
||||
node = createNode;
|
||||
return ironicAPI.createPortgroup({node_uuid: node.uuid,
|
||||
name: portgroupName});
|
||||
})
|
||||
.then(function(portgroup) {
|
||||
expect(portgroup.node_uuid).toBe(node.uuid);
|
||||
expect(portgroup.name).toBe(portgroupName);
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.name));
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPortgroup - missing input data', function() {
|
||||
ironicAPI.createPortgroup({})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('createPort - bad input data', function() {
|
||||
ironicAPI.createPort({node_uuid: ""})
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deletePortgroup', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.createPortgroup({node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(portgroup) {
|
||||
expect(portgroup).toBeDefined();
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||
ironicAPI.deletePortgroup(portgroup.uuid).then(function() {
|
||||
expect(ironicBackendMockService.getPortgroup(portgroup.uuid))
|
||||
.toBeNull();
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deletePortgroup - by name', function() {
|
||||
var portgroupName = "delete-portgroup";
|
||||
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.createPortgroup({node_uuid: node.uuid,
|
||||
name: portgroupName});
|
||||
})
|
||||
.then(function(portgroup) {
|
||||
expect(portgroup).toBeDefined();
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.name));
|
||||
ironicAPI.deletePortgroup(portgroup.name).then(function() {
|
||||
expect(ironicBackendMockService.getPortgroup(portgroup.name))
|
||||
.toBeNull();
|
||||
expect(ironicBackendMockService.getPortgroup(portgroup.uuid))
|
||||
.toBeNull();
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('deletePortgroup - nonexistent portgroup', function() {
|
||||
ironicAPI.deletePortgroup(0)
|
||||
.then(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
|
||||
it('getPortgroupPorts', function() {
|
||||
createNode({driver: defaultDriver})
|
||||
.then(function(node) {
|
||||
return ironicAPI.createPortgroup({node_uuid: node.uuid});
|
||||
})
|
||||
.then(function(portgroup) {
|
||||
expect(portgroup).toBeDefined();
|
||||
expect(portgroup)
|
||||
.toEqual(ironicBackendMockService.getPortgroup(portgroup.uuid));
|
||||
ironicAPI.getPortgroupPorts(portgroup.uuid).then(function(ports) {
|
||||
expect(ports).toEqual([]);
|
||||
});
|
||||
})
|
||||
.catch(failTest);
|
||||
|
||||
ironicBackendMockService.flush();
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray Inc.
|
||||
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name horizon.dashboard.admin.ironic:MaintenanceController
|
||||
* @ngController
|
||||
*
|
||||
* @description
|
||||
* Controller used to prompt the user for information associated with
|
||||
* putting one or more nodes into maintenance mode
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('MaintenanceController', MaintenanceController);
|
||||
|
||||
MaintenanceController.$inject = [
|
||||
'$uibModalInstance'
|
||||
];
|
||||
|
||||
function MaintenanceController($uibModalInstance) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.cancel = function() {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
ctrl.putInMaintenanceMode = function(maintReason) {
|
||||
$uibModalInstance.close(maintReason);
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,31 +0,0 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title" translate>Put Node(s) Into Maintenance Mode</h3>
|
||||
</div>
|
||||
<div class="modal-body clearfix">
|
||||
<div class="content">
|
||||
<div translate class="subtitle">Provide a reason for why you are putting the selected node(s) into maintenance mode (optional)</div>
|
||||
<div class="form-group">
|
||||
<div class="form-field">
|
||||
<input type="text"
|
||||
class="form-control input-sm"
|
||||
ng-model="maintReason"
|
||||
auto-focus
|
||||
placeholder=""/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default secondary"
|
||||
type="button"
|
||||
ng-click="ctrl.cancel()"
|
||||
translate>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="btn btn-primary"
|
||||
type="button"
|
||||
ng-click="ctrl.putInMaintenanceMode(maintReason)"
|
||||
translate>
|
||||
Put Node(s) Into Maintenance Mode
|
||||
</button>
|
||||
</div>
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray Inc.
|
||||
* Copyright (c) 2016 Hewlett Packard Enterprise Development Company LP
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* @ngdoc service
|
||||
* @name horizon.dashboard.admin.ironic.maintenance.service
|
||||
* @description Service for putting nodes in, and removing them from
|
||||
* maintenance mode
|
||||
*/
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.maintenance.service',
|
||||
maintenanceService);
|
||||
|
||||
maintenanceService.$inject = [
|
||||
'$uibModal',
|
||||
'horizon.dashboard.admin.ironic.basePath',
|
||||
'horizon.dashboard.admin.ironic.actions'
|
||||
];
|
||||
|
||||
function maintenanceService($uibModal, basePath, nodeActions) {
|
||||
var service = {
|
||||
setMaintenance: setMaintenance
|
||||
};
|
||||
return service;
|
||||
|
||||
/*
|
||||
* @description Set the maintenance mode of a specified list of nodes
|
||||
*
|
||||
* If nodes are being put into maintenance mode a modal dialog is used
|
||||
* to prompt the user for a reason.
|
||||
*
|
||||
* @param {object[]} nodes - List of node objects
|
||||
* @param {boolean} mode - Desired maintenance state.
|
||||
* 'true' -> Node is in maintenance mode
|
||||
* 'false' -> Node is not in maintenance mode
|
||||
* @return {promise}
|
||||
*/
|
||||
function setMaintenance(nodes, mode) {
|
||||
var promise;
|
||||
if (mode) {
|
||||
var options = {
|
||||
controller: "MaintenanceController as ctrl",
|
||||
templateUrl: basePath + '/maintenance/maintenance.html'
|
||||
};
|
||||
promise = $uibModal.open(options).result.then(function(reason) {
|
||||
return nodeActions.setMaintenance(nodes, true, reason);
|
||||
});
|
||||
} else {
|
||||
promise = nodeActions.setMaintenance(nodes, false);
|
||||
}
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.directive('modalDraggable', ModalDraggable);
|
||||
|
||||
ModalDraggable.$inject = ['$document', '$log'];
|
||||
|
||||
function ModalDraggable($document, $log) {
|
||||
|
||||
return function (scope, element) {
|
||||
var modalContent = null;
|
||||
while (element) {
|
||||
if (element.hasClass("modal-content")) {
|
||||
modalContent = element;
|
||||
break;
|
||||
}
|
||||
element = element.parent();
|
||||
}
|
||||
|
||||
if (modalContent) {
|
||||
modalContent.draggable({
|
||||
handle: ".modal-header"
|
||||
});
|
||||
} else {
|
||||
$log.error("Unable to find parent dialog");
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -1,244 +0,0 @@
|
|||
/*
|
||||
* © Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function PowerTransition(label, state, soft) {
|
||||
this.label = label;
|
||||
this.state = state;
|
||||
this.soft = soft;
|
||||
}
|
||||
|
||||
var POWER_ON_TRANSITIONS = [
|
||||
new PowerTransition(gettext('Power on'), 'on', false)
|
||||
];
|
||||
|
||||
var POWER_OFF_TRANSITIONS = [
|
||||
new PowerTransition(gettext('Power off'), 'off', false),
|
||||
new PowerTransition(gettext('Soft power off'), 'off', true),
|
||||
new PowerTransition(gettext('Reboot'), 'reboot', false),
|
||||
new PowerTransition(gettext('Soft reboot'), 'reboot', true)
|
||||
];
|
||||
|
||||
var ALL_POWER_TRANSITIONS =
|
||||
POWER_ON_TRANSITIONS.concat(POWER_OFF_TRANSITIONS);
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.factory('horizon.dashboard.admin.ironic.actions', actions);
|
||||
|
||||
actions.$inject = [
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.events',
|
||||
'horizon.framework.widgets.modal.deleteModalService',
|
||||
'horizon.dashboard.admin.ironic.clean-node.service',
|
||||
'$q',
|
||||
'$rootScope'
|
||||
];
|
||||
|
||||
function actions(ironic,
|
||||
ironicEvents,
|
||||
deleteModalService,
|
||||
cleanNodeService,
|
||||
$q,
|
||||
$rootScope) {
|
||||
var service = {
|
||||
deleteNode: deleteNode,
|
||||
deletePort: deletePort,
|
||||
deletePortgroups: deletePortgroups,
|
||||
setPowerState: setPowerState,
|
||||
setMaintenance: setMaintenance,
|
||||
setProvisionState: setProvisionState,
|
||||
getPowerTransitions : getPowerTransitions
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
function deleteNode(nodes) {
|
||||
var context = {
|
||||
labels: {
|
||||
title: ngettext("Delete Node",
|
||||
"Delete Nodes",
|
||||
nodes.length),
|
||||
message: ngettext('Are you sure you want to delete node "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
'Are you sure you want to delete nodes "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
nodes.length),
|
||||
submit: ngettext("Delete Node",
|
||||
"Delete Nodes",
|
||||
nodes.length),
|
||||
success: ngettext('Successfully deleted node "%s"',
|
||||
'Successfully deleted nodes "%s"',
|
||||
nodes.length),
|
||||
error: ngettext('Unable to delete node "%s"',
|
||||
'Unable to delete nodes "%s"',
|
||||
nodes.length)
|
||||
},
|
||||
deleteEntity: ironic.deleteNode,
|
||||
successEvent: ironicEvents.DELETE_NODE_SUCCESS
|
||||
};
|
||||
return deleteModalService.open($rootScope, nodes, context);
|
||||
}
|
||||
|
||||
// power state
|
||||
|
||||
/**
|
||||
* @description Set the power state of a list of nodes
|
||||
*
|
||||
* @param {object[]} nodes - List of node objects
|
||||
* @param {string} state - Target power state
|
||||
* @param {boolean} [soft] - Flag for graceful power 'off' or reboot
|
||||
* @return {promise} promise
|
||||
*/
|
||||
function setPowerState(nodes, state, soft) {
|
||||
var promises = [];
|
||||
angular.forEach(nodes,
|
||||
function(node) {
|
||||
promises.push(
|
||||
ironic.nodeSetPowerState(node.uuid,
|
||||
state,
|
||||
soft)
|
||||
);
|
||||
});
|
||||
return $q.all(promises);
|
||||
}
|
||||
|
||||
// maintenance
|
||||
|
||||
/**
|
||||
* @description Set the maintenance state of a list of nodes
|
||||
*
|
||||
* @param {object[]} nodes - List of node objects
|
||||
* @param {boolean} mode - True if the nodes are to be put in
|
||||
* maintenance mode, otherwise false.
|
||||
* @param {string} [reason] - Optional reason for putting nodes in
|
||||
* maintenance mode.
|
||||
* @return {promise} promise
|
||||
*/
|
||||
function setMaintenance(nodes, mode, reason) {
|
||||
var promises = [];
|
||||
angular.forEach(nodes, function(node) {
|
||||
var promise;
|
||||
if (node.maintenance === mode) {
|
||||
var msg = gettext(
|
||||
"Node %s is already in target maintenance state.");
|
||||
promise = $q.reject(interpolate(msg, [node.uuid], false));
|
||||
} else {
|
||||
promise = ironic.nodeSetMaintenance(node.uuid, mode, reason).then(
|
||||
function (result) {
|
||||
node.maintenance = mode;
|
||||
node.maintenance_reason =
|
||||
mode && angular.isDefined(reason) ? reason : "";
|
||||
return result;
|
||||
}
|
||||
);
|
||||
}
|
||||
promises.push(promise);
|
||||
});
|
||||
return $q.all(promises);
|
||||
}
|
||||
|
||||
/*
|
||||
* @name horizon.dashboard.admin.ironic.actions.setProvisionState
|
||||
* @description Set the provisioning state of a specified node
|
||||
*
|
||||
* @param {object} args - Object with two properties named 'node'
|
||||
* and 'verb'.
|
||||
* node: node object.
|
||||
* verb: string the value of which is the verb used to move
|
||||
* the node to the desired target state for the node.
|
||||
*/
|
||||
function setProvisionState(args) {
|
||||
if (args.verb === 'clean') {
|
||||
cleanNodeService.clean(args.node);
|
||||
} else {
|
||||
ironic.setNodeProvisionState(args.node.uuid, args.verb);
|
||||
}
|
||||
}
|
||||
|
||||
function deletePort(ports) {
|
||||
var context = {
|
||||
labels: {
|
||||
title: ngettext("Delete Port",
|
||||
"Delete Ports",
|
||||
ports.length),
|
||||
message: ngettext('Are you sure you want to delete port "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
'Are you sure you want to delete ports "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
ports.length),
|
||||
submit: ngettext("Delete Port",
|
||||
"Delete Ports",
|
||||
ports.length),
|
||||
success: ngettext('Successfully deleted port "%s"',
|
||||
'Successfully deleted ports "%s"',
|
||||
ports.length),
|
||||
error: ngettext('Unable to delete port "%s"',
|
||||
'Unable to delete ports "%s"',
|
||||
ports.length)
|
||||
},
|
||||
deleteEntity: ironic.deletePort,
|
||||
successEvent: ironicEvents.DELETE_PORT_SUCCESS
|
||||
};
|
||||
return deleteModalService.open($rootScope, ports, context);
|
||||
}
|
||||
|
||||
function deletePortgroups(portgroups) {
|
||||
var context = {
|
||||
labels: {
|
||||
title: ngettext("Delete Portgroup",
|
||||
"Delete Portgroups",
|
||||
portgroups.length),
|
||||
message: ngettext('Are you sure you want to delete portgroup "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
'Are you sure you want to delete portgroups "%s"? ' +
|
||||
'This action cannot be undone.',
|
||||
portgroups.length),
|
||||
submit: ngettext("Delete Portgroup",
|
||||
"Delete Portgroups",
|
||||
portgroups.length),
|
||||
success: ngettext('Successfully deleted portgroup "%s"',
|
||||
'Successfully deleted portgroups "%s"',
|
||||
portgroups.length),
|
||||
error: ngettext('Unable to delete portgroup "%s"',
|
||||
'Unable to delete portgroups "%s"',
|
||||
portgroups.length)
|
||||
},
|
||||
deleteEntity: ironic.deletePortgroup
|
||||
};
|
||||
return deleteModalService.open($rootScope, portgroups, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* @name horizon.dashboard.admin.ironic.actions.getPowerTransitions
|
||||
* @description Get the list of power transitions for a specified
|
||||
* node, or all power transitions if the node is not specified.
|
||||
*
|
||||
* @param {object} node – Node object for which power transitions
|
||||
* are requested. If node is undefined all possible power transitions
|
||||
* are returned.
|
||||
* @return {object[]} - List of PowerTransition objects
|
||||
*/
|
||||
function getPowerTransitions(node) {
|
||||
return angular.isUndefined(node) ? ALL_POWER_TRANSITIONS
|
||||
: node.power_state === 'power on'
|
||||
? POWER_OFF_TRANSITIONS : POWER_ON_TRANSITIONS;
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
|
@ -1,322 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.admin.ironic')
|
||||
.controller('horizon.dashboard.admin.ironic.NodeDetailsController',
|
||||
IronicNodeDetailsController);
|
||||
|
||||
IronicNodeDetailsController.$inject = [
|
||||
'$scope',
|
||||
'$location',
|
||||
'horizon.framework.widgets.toast.service',
|
||||
'horizon.app.core.openstack-service-api.ironic',
|
||||
'horizon.dashboard.admin.ironic.actions',
|
||||
'horizon.dashboard.admin.ironic.basePath',
|
||||
'horizon.dashboard.admin.ironic.edit-node.service',
|
||||
'horizon.dashboard.admin.ironic.create-port.service',
|
||||
'horizon.dashboard.admin.ironic.edit-port.service',
|
||||
'horizon.dashboard.admin.ironic.maintenance.service',
|
||||
'horizon.dashboard.admin.ironic.bootdevice.service',
|
||||
'horizon.dashboard.admin.ironic.node-state-transition.service',
|
||||
'horizon.dashboard.admin.ironic.validUuidPattern'
|
||||
];
|
||||
|
||||
function IronicNodeDetailsController($scope,
|
||||
$location,
|
||||
toastService,
|
||||
ironic,
|
||||
actions,
|
||||
basePath,
|
||||
editNodeService,
|
||||
createPortService,
|
||||
editPortService,
|
||||
maintenanceService,
|
||||
bootDeviceService,
|
||||
nodeStateTransitionService,
|
||||
validUuidPattern) {
|
||||
var ctrl = this;
|
||||
var path = basePath + '/node-details/sections/';
|
||||
|
||||
ctrl.noPortsText = gettext('No network ports have been defined');
|
||||
ctrl.noPortgroupsText = gettext('No portgroups have been defined');
|
||||
|
||||
ctrl.actions = actions;
|
||||
ctrl.maintenanceService = maintenanceService;
|
||||
ctrl.bootDeviceService = bootDeviceService;
|
||||
|
||||
ctrl.sections = [
|
||||
{
|
||||
heading: gettext('Overview'),
|
||||
templateUrl: path + 'overview.html'
|
||||
},
|
||||
{
|
||||
heading: gettext('Configuration'),
|
||||
templateUrl: path + 'configuration.html'
|
||||
}
|
||||
];
|
||||
|
||||
ctrl.portDetailsTemplateUrl = path + "port-details.html";
|
||||
ctrl.portgroupDetailsTemplateUrl = path + "portgroup-details.html";
|
||||
|
||||
ctrl.node = null;
|
||||
ctrl.nodeValidation = [];
|
||||
ctrl.nodeValidationMap = {}; // Indexed by interface
|
||||
ctrl.nodeStateTransitions = [];
|
||||
ctrl.nodePowerTransitions = [];
|
||||
ctrl.ports = [];
|
||||
ctrl.portsSrc = [];
|
||||
ctrl.portgroups = [];
|
||||
ctrl.portgroupsSrc = [];
|
||||
ctrl.basePath = basePath;
|
||||
ctrl.re_uuid = new RegExp(validUuidPattern);
|
||||
ctrl.isUuid = isUuid;
|
||||
ctrl.getVifPortId = getVifPortId;
|
||||
ctrl.editNode = editNode;
|
||||
ctrl.createPort = createPort;
|
||||
ctrl.deletePort = deletePort;
|
||||
ctrl.editPort = editPort;
|
||||
ctrl.refresh = refresh;
|
||||
ctrl.toggleConsoleMode = toggleConsoleMode;
|
||||
ctrl.deletePortgroups = deletePortgroups;
|
||||
|
||||
$scope.emptyObject = function(obj) {
|
||||
return angular.isUndefined(obj) || Object.keys(obj).length === 0;
|
||||
};
|
||||
|
||||
$scope.isDefined = angular.isDefined;
|
||||
|
||||
init();
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.init
|
||||
* @description Initialize the controller instance based on the
|
||||
* current page url.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function init() {
|
||||
// Fetch the Node ID from the URL.
|
||||
var pattern = /(.*\/admin\/ironic\/)(.+)\/(detail)?/;
|
||||
var uuid = $location.absUrl().match(pattern)[2];
|
||||
|
||||
retrieveNode(uuid).then(function () {
|
||||
ctrl.nodeStateTransitions =
|
||||
nodeStateTransitionService.getTransitions(ctrl.node.provision_state);
|
||||
ctrl.nodePowerTransitions = actions.getPowerTransitions(ctrl.node);
|
||||
retrievePorts();
|
||||
retrieveBootDevice();
|
||||
retrievePortgroups();
|
||||
validateNode();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.retrieveNode
|
||||
* @description Retrieve the node instance for a specified node id,
|
||||
* and store it in the controller instance.
|
||||
*
|
||||
* @param {string} uuid – Node name or UUID
|
||||
* @return {promise} promise
|
||||
*/
|
||||
function retrieveNode(uuid) {
|
||||
return ironic.getNode(uuid).then(function (node) {
|
||||
ctrl.node = node;
|
||||
ctrl.node.id = ctrl.node.uuid;
|
||||
ironic.nodeGetConsole(uuid).then(function(consoleData) {
|
||||
ctrl.node.console_enabled = consoleData.console_enabled;
|
||||
ctrl.node.console_info = consoleData.console_info;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.retrievePorts
|
||||
* @description Retrieve the ports associated with the current node,
|
||||
* and store them in the controller instance.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function retrievePorts() {
|
||||
ironic.getPortsWithNode(ctrl.node.uuid).then(function (ports) {
|
||||
ctrl.portsSrc = ports;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.retrieveBootDevice
|
||||
* @description Retrieve the boot device associated with the current node,
|
||||
* and store it in the controller instance.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function retrieveBootDevice() {
|
||||
ironic.getBootDevice(ctrl.node.uuid).then(function (bootDevice) {
|
||||
ctrl.node.bootDevice = bootDevice;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.retrievePortgroups
|
||||
* @description Retrieve the port groups associated with the current node,
|
||||
* and store them in the controller instance.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function retrievePortgroups() {
|
||||
ironic.getPortgroups(ctrl.node.uuid).then(function(portgroups) {
|
||||
ctrl.portgroupsSrc = portgroups;
|
||||
angular.forEach(portgroups, function(portgroup) {
|
||||
portgroup.ports = [];
|
||||
ironic.getPortgroupPorts(portgroup.uuid).then(function(ports) {
|
||||
portgroup.ports = ports;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.validateNode
|
||||
* @description Retrieve the ports associated with the current node,
|
||||
* and store them in the controller instance.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function validateNode() {
|
||||
ironic.validateNode(ctrl.node.uuid).then(function(response) {
|
||||
var nodeValidation = [];
|
||||
ctrl.nodeValidationMap = {};
|
||||
angular.forEach(response.data, function(status) {
|
||||
status.id = status.interface;
|
||||
nodeValidation.push(status);
|
||||
ctrl.nodeValidationMap[status.interface] = status;
|
||||
});
|
||||
ctrl.nodeValidation = nodeValidation;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.isUuid
|
||||
* @description Test whether a string is an OpenStack UUID
|
||||
*
|
||||
* @param {string} str – string
|
||||
* @return {boolean} True if the string is an OpenStack UUID,
|
||||
* otherwise false
|
||||
*/
|
||||
function isUuid(str) {
|
||||
return !!str.match(ctrl.re_uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.getVifPortId
|
||||
* @description Get the vif_port_id property of a specified port
|
||||
*
|
||||
* @param {object} port – instance of port
|
||||
* @return {string} Value of vif_port_id property or
|
||||
* "" if the property does not exist
|
||||
*/
|
||||
function getVifPortId(port) {
|
||||
return angular.isDefined(port.extra) &&
|
||||
angular.isDefined(port.extra.vif_port_id)
|
||||
? port.extra.vif_port_id : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Edit the current node
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function editNode() {
|
||||
editNodeService.editNode(ctrl.node).then(function() {
|
||||
ctrl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.createPort
|
||||
* @description Initiate creation of a newtwork port for the current
|
||||
* node
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function createPort() {
|
||||
createPortService.createPort(ctrl.node).then(function() {
|
||||
ctrl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: Edit a specified port
|
||||
*
|
||||
* @param {port} port - Port to be edited
|
||||
* @return {void}
|
||||
*/
|
||||
function editPort(port) {
|
||||
editPortService.editPort(port, ctrl.node).then(function() {
|
||||
ctrl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.deletePort
|
||||
* @description Delete a list of ports
|
||||
*
|
||||
* @param {port []} ports – ports to be deleted
|
||||
* @return {void}
|
||||
*/
|
||||
function deletePort(ports) {
|
||||
actions.deletePort(ports).then(function() {
|
||||
ctrl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.portgroupDelete
|
||||
* @description Delete a list of portgroups.
|
||||
*
|
||||
* @param {port []} portgroups – portgroups to be deleted.
|
||||
* @return {void}
|
||||
*/
|
||||
function deletePortgroups(portgroups) {
|
||||
actions.deletePortgroups(portgroups).then(function() {
|
||||
ctrl.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.refresh
|
||||
* @description Update node information
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function refresh() {
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @name horizon.dashboard.admin.ironic.NodeDetailsController.toggleConsoleMode
|
||||
* @description Toggle the state of the console for the current node
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
function toggleConsoleMode() {
|
||||
ironic.nodeSetConsoleMode(ctrl.node.uuid, !ctrl.node.console_enabled);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
* Copyright 2015 Hewlett Packard Enterprise Development Company LP
|
||||
* Copyright 2016 Cray 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.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
describe('horizon.dashboard.admin.ironic.node-details', function () {
|
||||
var ctrl, $q, nodeStateTransitionService;
|
||||
var nodeUuid = "0123abcd-0123-4567-abcd-0123456789ab";
|
||||
var nodeName = "herp";
|
||||
var numPorts = 2;
|
||||
var bootDevice = {boot_device: 'pxe', persistent: true};
|
||||
var consoleEnabled = true;
|
||||
var consoleInfo = "console-info";
|
||||
|
||||
function portUuid(nodeUuid, index) {
|
||||
return '' + index + index + nodeUuid.substring(2);
|
||||
}
|
||||
|
||||
function portMacAddr(index) {
|
||||
var mac = '' + index + index;
|
||||
for (var i = 0; i < 5; i++) {
|
||||
mac += ':' + index + index;
|
||||
}
|
||||
return mac;
|
||||
}
|
||||
|
||||
function createPort(nodeUuid, index, extra) {
|
||||
var port = {uuid: portUuid(nodeUuid, index),
|
||||
address: portMacAddr(index)};
|
||||
port.id = port.uuid;
|
||||
port.name = port.address;
|
||||
if (angular.isDefined(extra)) {
|
||||
port.extra = extra;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
function createNode(name, uuid) {
|
||||
return {name: name,
|
||||
uuid: uuid,
|
||||
provision_state: 'enroll'};
|
||||
}
|
||||
|
||||
var ironicAPI = {
|
||||
getNode: function (uuid) {
|
||||
var node = createNode(nodeName, uuid);
|
||||
return $q.when(node);
|
||||
},
|
||||
|
||||
getPortsWithNode: function (uuid) {
|
||||
var ports = [];
|
||||
for (var i = 0; i < numPorts; i++) {
|
||||
ports.push(createPort(uuid, i));
|
||||
}
|
||||
return $q.when(ports);
|
||||
},
|
||||
|
||||
getPortgroups: function() {
|
||||
return $q.when([]);
|
||||
},
|
||||
|
||||
getBootDevice: function () {
|
||||
return $q.when(bootDevice);
|
||||
},
|
||||
|
||||
validateNode: function() {
|
||||
return $q.when({});
|
||||
},
|
||||
|
||||
nodeGetConsole: function() {
|
||||
return $q.when({console_enabled: consoleEnabled,
|
||||
console_info: consoleInfo});
|
||||
}
|
||||
};
|
||||
|
||||
var nodeActions = {
|
||||
getPowerTransitions: function() {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('$uibModal', jasmine.createSpy());
|
||||
}));
|
||||
|
||||
beforeEach(module('horizon.dashboard.admin.ironic'));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.app.core.openstack-service-api.ironic',
|
||||
ironicAPI);
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.framework.widgets.toast.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.dashboard.admin.ironic.edit-node.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('horizon.dashboard.admin.ironic.maintenance.service',
|
||||
{});
|
||||
}));
|
||||
|
||||
beforeEach(inject(function ($injector, _$rootScope_, _$location_) {
|
||||
var scope = _$rootScope_.$new();
|
||||
$q = $injector.get('$q');
|
||||
var controller = $injector.get('$controller');
|
||||
var $location = _$location_;
|
||||
$location.path('/admin/ironic/' + nodeUuid + '/');
|
||||
|
||||
nodeStateTransitionService = $injector.get(
|
||||
'horizon.dashboard.admin.ironic.node-state-transition.service');
|
||||
|
||||
ctrl = controller(
|
||||
'horizon.dashboard.admin.ironic.NodeDetailsController',
|
||||
{$scope: scope,
|
||||
$location: $location,
|
||||
'horizon.dashboard.admin.ironic.edit-port.service': {},
|
||||
'horizon.dashboard.admin.ironic.actions': nodeActions});
|
||||
|
||||
scope.$apply();
|
||||
}));
|
||||
|
||||
it('controller should be defined', function () {
|
||||
expect(ctrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have a basePath', function () {
|
||||
expect(ctrl.basePath).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have a node', function () {
|
||||
expect(ctrl.node).toBeDefined();
|
||||
var node = createNode(nodeName, nodeUuid);
|
||||
node.id = node.uuid;
|
||||
node.bootDevice = bootDevice;
|
||||
node.console_enabled = consoleEnabled;
|
||||
node.console_info = consoleInfo;
|
||||
expect(ctrl.node).toEqual(node);
|
||||
});
|
||||
|
||||
it('should have ports', function () {
|
||||
expect(ctrl.portsSrc).toBeDefined();
|
||||
expect(ctrl.portsSrc.length).toEqual(numPorts);
|
||||
|
||||
var ports = [];
|
||||
for (var i = 0; i < numPorts; i++) {
|
||||
var port = createPort(ctrl.node.uuid, i);
|
||||
port.id = port.uuid;
|
||||
port.name = port.address;
|
||||
ports.push(port);
|
||||
}
|
||||
expect(ctrl.portsSrc).toEqual(ports);
|
||||
});
|
||||
|
||||
it('should have a uuid regular expression pattern', function () {
|
||||
expect(ctrl.re_uuid).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have an isUuid function', function () {
|
||||
expect(ctrl.isUuid).toBeDefined();
|
||||
expect(ctrl.isUuid(ctrl.node.uuid)).toEqual(true);
|
||||
expect(ctrl.isUuid("not a uuid")).toEqual(false);
|
||||
});
|
||||
|
||||
it('should have a getVifPortId function', function () {
|
||||
expect(ctrl.getVifPortId).toBeDefined();
|
||||
expect(ctrl.getVifPortId(createPort(ctrl.node.uuid, 1))).toEqual("");
|
||||
var extra = {vif_port_id: "port_uuid"};
|
||||
expect(ctrl.getVifPortId(createPort(ctrl.node.uuid, 1, extra))).
|
||||
toEqual("port_uuid");
|
||||
});
|
||||
|
||||
it('should have node-state-transitions', function () {
|
||||
expect(ctrl.nodeStateTransitions).toBeDefined();
|
||||
expect(ctrl.nodeStateTransitions).toEqual(
|
||||
nodeStateTransitionService.getTransitions(ctrl.node.provision_state));
|
||||
});
|
||||
|
||||
it('should have node-validation', function () {
|
||||
expect(ctrl.nodeValidation).toBeDefined();
|
||||
expect(ctrl.nodeValidation).toEqual([]);
|
||||
});
|
||||
|
||||
it('should have a boot device', function () {
|
||||
expect(ctrl.node.bootDevice).toBeDefined();
|
||||
expect(ctrl.node.bootDevice).toEqual(bootDevice);
|
||||
});
|
||||
});
|
||||
})();
|
|
@ -1,77 +0,0 @@
|
|||
<div class="detail-page"
|
||||
ng-controller="horizon.dashboard.admin.ironic.NodeDetailsController as ctrl">
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-default btn-sm"
|
||||
style="margin-right:10px;"
|
||||
ng-click="ctrl.refresh()">
|
||||
<span translate>Refresh</span>
|
||||
</button>
|
||||
<action-list uib-dropdown>
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="ctrl.editNode">
|
||||
{$ ::'Edit' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<li role="presentation"
|
||||
ng-repeat="transition in ctrl.nodePowerTransitions">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.actions.setPowerState(
|
||||
[ctrl.node],
|
||||
transition.state,
|
||||
transition.soft);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.maintenanceService.setMaintenance(
|
||||
[ctrl.node],
|
||||
!ctrl.node.maintenance);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ ctrl.node.maintenance ?
|
||||
"Maintenance off" : "Maintenance on" | translate $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.bootDeviceService.setBootDevice(ctrl.node);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ "Set boot device" | translate $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation"
|
||||
ng-repeat="transition in ctrl.nodeStateTransitions">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.actions.setProvisionState({
|
||||
node: ctrl.node,
|
||||
verb: transition.verb});
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span>{$ transition.label $}</span>
|
||||
</a>
|
||||
</li>
|
||||
<action button-type="menu-item"
|
||||
disabled="isDefined(ctrl.nodeValidationMap.console)
|
||||
&& ctrl.nodeValidationMap.console.result===false"
|
||||
callback="ctrl.toggleConsoleMode">
|
||||
{$ ctrl.node.console_enabled ? 'Disable console' : 'Enable console' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
</div>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<uib-tabset>
|
||||
<uib-tab ng-repeat="section in ctrl.sections"
|
||||
heading="{$ section.heading $}">
|
||||
<ng-include src="section.templateUrl"></ng-include>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
</div>
|
|
@ -1,418 +0,0 @@
|
|||
<div class="row">
|
||||
|
||||
<!-- General -->
|
||||
<div class="col-md-12 status detail">
|
||||
<h4 translate>General</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Node ID</dt>
|
||||
<dd>{$ ctrl.node.uuid | noValue $}</dd>
|
||||
<dt translate>Chassis ID</dt>
|
||||
<dd>{$ ctrl.node.chassis_uuid | noValue $}</dd>
|
||||
<dt translate>Resource Class</dt>
|
||||
<dd>{$ ctrl.node.resource_class | noValue $}</dd>
|
||||
<dt translate>Created At</dt>
|
||||
<dd>{$ ctrl.node.created_at | date:'medium' | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Ports -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Ports</h4>
|
||||
<hr class="header_rule">
|
||||
<table hz-table ng-cloak
|
||||
st-table="ctrl.ports"
|
||||
st-safe-src="ctrl.portsSrc"
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="100" class="action-col">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="ctrl.createPort">
|
||||
{$ ::'Create port' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<action button-type="menu-item"
|
||||
callback="ctrl.deletePort"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
<span class="fa fa-trash"></span>
|
||||
{$ ::'Delete ports' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="multi_select_column">
|
||||
<input type="checkbox"
|
||||
hz-select-all="ctrl.ports"/>
|
||||
</th>
|
||||
<th> </th>
|
||||
<th translate class="rsp-p1" style="white-space:nowrap">
|
||||
MAC Address
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||
PXE Enabled
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||
Portgroup
|
||||
</th>
|
||||
<th translate class="actions_column">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat-start="port in ctrl.ports">
|
||||
<td class="multi_select_column">
|
||||
<input type="checkbox"
|
||||
hz-select="port"
|
||||
ng-model="tCtrl.selections[port.id].checked"/>
|
||||
<td>
|
||||
<i class="fa fa-chevron-right"
|
||||
hz-expand-detail="fa-chevron-right fa-chevron-down"
|
||||
duration="200"
|
||||
item="port"></i>
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ port.address $}
|
||||
</td>
|
||||
<td>
|
||||
{$ port.pxe_enabled $}
|
||||
<td>
|
||||
{$ port.portgroup_uuid | noValue $}
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="ctrl.editPort"
|
||||
item="port">
|
||||
{$ ::'Edit port' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<li role="presentation">
|
||||
<a role="menuitem"
|
||||
ng-click="ctrl.deletePort([port]);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span class="fa fa-trash"></span>
|
||||
<span>{$ :: 'Delete port' | translate $}</span>
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
<td class="detail" colspan="100">
|
||||
<hz-detail-row
|
||||
template-url="ctrl.portDetailsTemplateUrl">
|
||||
</hz-detail-row>
|
||||
</td>
|
||||
</tr>
|
||||
<tr hz-no-items
|
||||
items="ctrl.ports"
|
||||
message="ctrl.noPortsText">
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Portgroups -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Portgroups</h4>
|
||||
<hr class="header_rule">
|
||||
<table hz-table ng-cloak
|
||||
st-table="ctrl.portgroups"
|
||||
st-safe-src="ctrl.portgroupsSrc"
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="100" class="action-col">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback="">
|
||||
{$ ::'Create portgroup' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<action button-type="menu-item"
|
||||
callback="ctrl.deletePortgroups"
|
||||
item="tCtrl.selected"
|
||||
disabled="tCtrl.selected.length === 0">
|
||||
<span class="fa fa-trash"></span>
|
||||
{$ ::'Delete portgroups' | translate $}
|
||||
</action>
|
||||
</menu>
|
||||
</action-list>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="multi_select_column">
|
||||
<input type="checkbox"
|
||||
hz-select-all="ctrl.portgroups"/>
|
||||
</th>
|
||||
<th> </th>
|
||||
<th translate class="rsp-p1" style="width:100%;">
|
||||
UUID
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="white-space:nowrap;">
|
||||
MAC Address
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||
Name
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="width:white-space:nowrap;">
|
||||
Ports
|
||||
</th>
|
||||
<th translate class="actions_column">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat-start="portgroup in ctrl.portgroups">
|
||||
<td class="multi_select_column">
|
||||
<input type="checkbox"
|
||||
hz-select="portgroup"
|
||||
ng-model="tCtrl.selections[portgroup.id].checked"/>
|
||||
<td>
|
||||
<i class="fa fa-chevron-right"
|
||||
hz-expand-detail="fa-chevron-right fa-chevron-down"
|
||||
duration="200"
|
||||
item="portgroup"></i>
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.uuid $}
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.address | noValue $}
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.name | noValue $}
|
||||
</td>
|
||||
<td class="rsp-p1">
|
||||
{$ portgroup.ports.length | noValue $}
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list uib-dropdown class="pull-right">
|
||||
<action button-type="split-button"
|
||||
action-classes="'btn btn-default btn-sm'"
|
||||
callback=""
|
||||
item="port">
|
||||
{$ ::'Edit portgroup' | translate $}
|
||||
</action>
|
||||
<menu>
|
||||
<li role="presentation"
|
||||
ng-class="{disabled: portgroup.ports.length > 0}">
|
||||
<a role="menuitem"
|
||||
ng-click="portgroup.ports.length > 0 ||
|
||||
ctrl.deletePortgroups([portgroup]);
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault()">
|
||||
<span class="fa fa-trash"></span>
|
||||
<span>{$ :: 'Delete portgroup' | translate $}</span>
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
<td class="detail" colspan="100">
|
||||
<hz-detail-row
|
||||
template-url="ctrl.portgroupDetailsTemplateUrl">
|
||||
</hz-detail-row>
|
||||
</td>
|
||||
</tr>
|
||||
<tr hz-no-items
|
||||
items="ctrl.portgroups"
|
||||
message="ctrl.noPortgroupsText">
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Driver Info -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Driver Info</h4>
|
||||
<hr class="header_rule">
|
||||
<div ng-switch="ctrl.node.driver">
|
||||
<dl ng-switch-when="pxe_ssh" class="dl-horizontal">
|
||||
<dt translate>Driver</dt>
|
||||
<dd>{$ ctrl.node.driver | noValue $}</dd>
|
||||
<dt translate>SSH Address</dt>
|
||||
<dd>{$ ctrl.node.driver_info.ssh_address | noValue $}</dd>
|
||||
<dt translate>SSH Port</dt>
|
||||
<dd>{$ ctrl.node.driver_info.ssh_port | noValue $}</dd>
|
||||
<dt translate>SSH Username</dt>
|
||||
<dd>{$ ctrl.node.driver_info.ssh_username | noValue $}</dd>
|
||||
<dt ng-if="ssh_key_filename = ctrl.node.driver_info.ssh_key_filename"
|
||||
translate>SSH Key File</dt>
|
||||
<dd ng-if="ssh_key_filename">
|
||||
{$ ssh_key_filename | noValue $}
|
||||
</dd>
|
||||
<dt ng-if="ssh_password = ctrl.node.driver_info.ssh_password"
|
||||
translate>SSH Password</dt>
|
||||
<dd ng-if="ssh_password">
|
||||
{$ ssh_password | noValue $}
|
||||
</dd>
|
||||
<dt ng-if="ssh_key_contents = ctrl.node.driver_info.ssh_key_contents"
|
||||
translate>SSH Key Contents</dt>
|
||||
<dd ng-if="ssh_key_contents">
|
||||
{$ ssh_key_contents | noValue $}
|
||||
</dd>
|
||||
<dt translate>SSH terminal port</dt>
|
||||
<dd>{$ ctrl.node.driver_info.ssh_terminal_port | noValue $}</dd>
|
||||
<dt translate>Virtualization Software</dt>
|
||||
<dd>{$ ctrl.node.driver_info.ssh_virt_type | noValue $}</dd>
|
||||
<dt translate>Deploy Kernel</dt>
|
||||
<dd>
|
||||
<a ng-if="deploy_kernel_is_uuid = angular.isDefined(ctrl.node.driver_info.deploy_kernel) ? ctrl.isUuid(ctrl.node.driver_info.deploy_kernel) : false"
|
||||
href="/dashboard/admin/images/{$ ctrl.node.driver_info.deploy_kernel $}/detail">
|
||||
{$ ctrl.node.driver_info.deploy_kernel | noValue $}
|
||||
</a>
|
||||
<span ng-if="!deploy_kernel_is_uuid">
|
||||
{$ ctrl.node.driver_info.deploy_kernel | noValue $}
|
||||
</span>
|
||||
</dd>
|
||||
<dt translate>Deploy Ramdisk</dt>
|
||||
<dd>
|
||||
<a ng-if="deploy_ramdisk_is_uuid = angular.isDefined(ctrl.node.driver_info.deploy_ramdisk) ? ctrl.isUuid(ctrl.node.driver_info.deploy_ramdisk) : false"
|
||||
href="/dashboard/admin/images/{$ ctrl.node.driver_info.deploy_ramdisk $}/detail">
|
||||
{$ ctrl.node.driver_info.deploy_ramdisk | noValue $}
|
||||
</a>
|
||||
<span ng-if="!deploy_ramdisk_is_uuid">
|
||||
{$ ctrl.node.driver_info.deploy_ramdisk | noValue $}
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl ng-switch-default class="dl-horizontal">
|
||||
<dt ng-repeat-start="(id, value) in ctrl.node.driver_info">{$ id $}</dt>
|
||||
<dd ng-repeat-end>{$ value $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Validation -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Driver Validation</h4>
|
||||
<hr class="header_rule">
|
||||
|
||||
<table hz-table ng-cloak
|
||||
st-table="nodeValidation"
|
||||
st-safe-src="ctrl.nodeValidation"
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th translate class="rsp-p1" style="white-space:nowrap">
|
||||
Interface
|
||||
</th>
|
||||
<th translate class="rsp-p1">
|
||||
Valid
|
||||
</th>
|
||||
<th translate class="rsp-p2" style="width:100%;">
|
||||
Reason
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="item in nodeValidation">
|
||||
<td class="rsp-p1">
|
||||
{$ item.id $}
|
||||
</td>
|
||||
<td class="rsp-p1" ng-switch="item.result">
|
||||
<span ng-switch-when="true" class="fa fa-check text-success"></span>
|
||||
<span ng-switch-when="false" class="fa fa-close text-danger"></span>
|
||||
<span ng-switch-default class="fa fa-minus"></span>
|
||||
</td>
|
||||
<td class="rsp-p2">
|
||||
{$ item.reason $}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Properties -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Properties</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt ng-repeat-start="(propertyName, propertyValue) in ctrl.node.properties">
|
||||
{$ propertyName $}</dt>
|
||||
<dd ng-repeat-end>{$ propertyValue | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Instance Info -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Instance Info</h4>
|
||||
<hr class="header_rule"/>
|
||||
<div ng-switch="ctrl.node.driver">
|
||||
<dl ng-switch-when="pxe_ssh" class="dl-horizontal">
|
||||
<dt translate>Instance Name</dt>
|
||||
<dd>{$ ctrl.node.instance_info.display_name | noValue $}</dd>
|
||||
<dt translate>Ramdisk</dt>
|
||||
<dd>
|
||||
<a href="/dashboard/admin/images/{$ ctrl.node.instance_info.ramdisk $}/detail">
|
||||
{$ ctrl.node.instance_info.ramdisk | noValue $}
|
||||
</a>
|
||||
</dd>
|
||||
<dt translate>Kernel</dt>
|
||||
<dd>
|
||||
<a href="/dashboard/admin/images/{$ ctrl.node.instance_info.kernel $}/detail">
|
||||
{$ ctrl.node.instance_info.kernel | noValue $}
|
||||
</a>
|
||||
</dd>
|
||||
<dt translate>Image Source</dt>
|
||||
<dd>
|
||||
<a href="/dashboard/admin/images/{$ ctrl.node.instance_info.image_source $}/detail">
|
||||
{$ ctrl.node.instance_info.image_source | noValue $}
|
||||
</a>
|
||||
</dd>
|
||||
<dt translate>Root GB</dt>
|
||||
<dd>{$ ctrl.node.instance_info.root_gb | noValue $}</dd>
|
||||
</dl>
|
||||
<dl ng-switch-default class="dl-horizontal">
|
||||
<dt ng-repeat-start="(id, value) in ctrl.node.instance_info">{$ id $}</dt>
|
||||
<dd ng-repeat-end>{$ value | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Extra -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Extra</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt ng-repeat-start="(propertyName, propertyValue) in ctrl.node.extra">
|
||||
{$ propertyName $}</dt>
|
||||
<dd ng-repeat-end>{$ propertyValue | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Boot Device -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Boot Device</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Device</dt>
|
||||
<dd>{$ ctrl.node.bootDevice.boot_device | noValue $}</dd>
|
||||
<dt translate>Persistent</dt>
|
||||
<dd>{$ ctrl.node.bootDevice.persistent | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
|
@ -1,64 +0,0 @@
|
|||
<div class="row">
|
||||
|
||||
<!-- General -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>General</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Name</dt>
|
||||
<dd>{$ ctrl.node.name | noValue $}</dd>
|
||||
<dt translate>Network Interface</dt>
|
||||
<dd>{$ ctrl.node.network_interface $}</dd>
|
||||
<dt translate>Maintenance</dt>
|
||||
<dd>{$ ctrl.node.maintenance | yesno $}</dd>
|
||||
<dt translate>Maintenance Reason</dt>
|
||||
<dd>{$ ctrl.node.maintenance_reason | noValue $}</dd>
|
||||
<dt translate>Inspection Started At</dt>
|
||||
<dd>{$ ctrl.node.inspection_started_at | date: 'medium' | noValue $}</dd>
|
||||
<dt translate>Inspection Finished At</dt>
|
||||
<dd>{$ ctrl.node.inspection_finished_at | date: 'medium' | noValue $}</dd>
|
||||
<dt translate>Reservation</dt>
|
||||
<dd>{$ ctrl.node.reservation | noValue $}</dd>
|
||||
<dt translate>Console Enabled</dt>
|
||||
<dd>{$ ctrl.node.console_enabled | yesno $}</dd>
|
||||
<dt translate>Console Info.</dt>
|
||||
<dd ng-if="ctrl.node.console_info.type==='shellinabox'">
|
||||
<a href="{$ ctrl.node.console_info.url $}"
|
||||
title="{$ 'Click link to view console' | translate $}">
|
||||
shellinabox
|
||||
</a>
|
||||
</dd>
|
||||
<dd ng-if="ctrl.node.console_info.type!=='shellinabox'">
|
||||
{$ ctrl.node.console_info | noValue $}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Provisioning Status -->
|
||||
<div class="col-md-6 status detail">
|
||||
<h4 translate>Provisioning Status</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt translate>Instance ID</dt>
|
||||
<dd>
|
||||
<a href="/admin/instances/{$ ctrl.node.instance_uuid $}/detail">
|
||||
{$ ctrl.node.instance_uuid | noValue $}
|
||||
</a>
|
||||
</dd>
|
||||
<dt translate>Power State</dt>
|
||||
<dd ng-class="{'running': ctrl.node.target_power_state}">{$ ctrl.node.power_state | noValue $}</dd>
|
||||
<dt translate>Target Power State</dt>
|
||||
<dd>{$ ctrl.node.target_power_state | noValue $}</dd>
|
||||
<dt translate>Provision State</dt>
|
||||
<dd>{$ ctrl.node.provision_state | noValue $}</dd>
|
||||
<dt translate>Target Provision State</dt>
|
||||
<dd>{$ ctrl.node.target_provision_state | noValue $}</dd>
|
||||
<dt translate>Clean Step</dt>
|
||||
<dd>{$ ctrl.node.clean_step $}</dd>
|
||||
<dt translate>Last Error</dt>
|
||||
<dd>{$ ctrl.node.last_error | noValue $}</dd>
|
||||
<dt translate>Updated At</dt>
|
||||
<dd>{$ ctrl.node.updated_at | date: 'medium' | noValue $}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue