Retire Senlin: remove repo content
Senlin project is retiring - https://review.opendev.org/c/openstack/governance/+/919347 this commit remove the content of this project repo Depends-On: https://review.opendev.org/c/openstack/project-config/+/919348/ Change-Id: I6ad02f6ad71a1f6f363a42a8ec1475768e0f8b7a
This commit is contained in:
parent
b9648e6ce2
commit
0830f74f9b
54
.eslintrc
54
.eslintrc
|
@ -1,54 +0,0 @@
|
|||
# Set up globals
|
||||
globals:
|
||||
angular: false
|
||||
|
||||
extends: openstack
|
||||
|
||||
# Most environment options are not explicitly enabled or disabled, only
|
||||
# included here for completeness' sake. They are commented out, because the
|
||||
# global updates.py script would otherwise override them during a global
|
||||
# requirements synchronization.
|
||||
#
|
||||
# Individual projects should choose which platforms they deploy to.
|
||||
|
||||
env:
|
||||
# browser global variables.
|
||||
browser: true
|
||||
|
||||
# Adds all of the Jasmine testing global variables for version 1.3 and 2.0.
|
||||
jasmine: true
|
||||
|
||||
# Enable eslint-plugin-angular
|
||||
plugins:
|
||||
- angular
|
||||
|
||||
# Below we adjust rules specific to horizon's usage of openstack's linting
|
||||
# rules, and its own plugin inclusions.
|
||||
rules:
|
||||
#############################################################################
|
||||
# Disabled Rules from eslint-config-openstack
|
||||
#############################################################################
|
||||
valid-jsdoc: [1, {
|
||||
requireParamDescription: false
|
||||
}]
|
||||
brace-style: 1
|
||||
no-extra-parens: 1
|
||||
consistent-return: 1
|
||||
callback-return: 1
|
||||
guard-for-in: 1
|
||||
block-scoped-var: 1
|
||||
semi-spacing: 1
|
||||
no-redeclare: 1
|
||||
no-new: 1
|
||||
|
||||
#############################################################################
|
||||
# Angular Plugin Customization
|
||||
#############################################################################
|
||||
|
||||
angular/controller-as-vm:
|
||||
- 1
|
||||
- "ctrl"
|
||||
|
||||
# Remove after migrating to angular 1.4 or later.
|
||||
angular/no-cookiestore:
|
||||
- 1
|
|
@ -1,38 +0,0 @@
|
|||
*.egg*
|
||||
*.lock
|
||||
*.py[cod]
|
||||
*.sw[op]
|
||||
*.sqlite3
|
||||
*nose_results.html
|
||||
|
||||
.DS_Store
|
||||
.DS_STORE
|
||||
.coverage*
|
||||
.environment_version
|
||||
.idea
|
||||
.noseids
|
||||
.project
|
||||
.pydevproject
|
||||
.selenium_log
|
||||
.tox
|
||||
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
build
|
||||
cover
|
||||
coverage.xml
|
||||
dist
|
||||
doc/build/
|
||||
doc/source/contributor/api/
|
||||
horizon.egg-info
|
||||
node_modules
|
||||
nosetests.xml
|
||||
npm-debug.log
|
||||
openstack_dashboard/dummydb.sqlite
|
||||
pep8.txt
|
||||
pylint.txt
|
||||
releasenotes/build
|
||||
reports
|
||||
senlin_dashboard/test/.secret_key_store
|
||||
tags
|
||||
|
|
@ -1 +0,0 @@
|
|||
1
|
|
@ -1 +0,0 @@
|
|||
0
|
|
@ -1,8 +0,0 @@
|
|||
- project:
|
||||
templates:
|
||||
- check-requirements
|
||||
- horizon-non-primary-django-jobs
|
||||
- horizon-nodejs-jobs
|
||||
- openstack-python3-jobs-horizon
|
||||
- publish-openstack-docs-pti
|
||||
- release-notes-jobs-python3
|
175
LICENSE
175
LICENSE
|
@ -1,175 +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.
|
138
README.rst
138
README.rst
|
@ -1,132 +1,10 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
This project is no longer maintained.
|
||||
|
||||
.. image:: https://governance.openstack.org/tc/badges/senlin-dashboard.svg
|
||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
================
|
||||
Senlin Dashboard
|
||||
================
|
||||
|
||||
Senlin Management Dashboard
|
||||
|
||||
.. inclusion-start-marker-hosts
|
||||
|
||||
Project Hosting
|
||||
---------------
|
||||
|
||||
- Documentation: https://docs.openstack.org/senlin-dashboard/latest/
|
||||
- Release notes: https://docs.openstack.org/releasenotes/senlin-dashboard/
|
||||
- Blueprints: https://blueprints.launchpad.net/senlin-dashboard
|
||||
- Bugs: https://bugs.launchpad.net/senlin-dashboard
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Use ``[senlin-dashboard]`` prefix in subjects with for faster responses
|
||||
|
||||
- http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss
|
||||
|
||||
Code Hosting
|
||||
------------
|
||||
|
||||
- https://opendev.org/openstack/senlin-dashboard
|
||||
|
||||
Code Review
|
||||
-----------
|
||||
|
||||
- https://review.opendev.org/#/q/status:open+project:openstack/senlin-dashboard,n,z
|
||||
|
||||
.. inclusion-end-marker-hosts
|
||||
|
||||
.. inclusion-start-marker-install
|
||||
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
Before install the Senlin Dashboard, setup the Horizon.
|
||||
To setup the Horizon, see
|
||||
`Installation Guide
|
||||
<https://docs.openstack.org/horizon/latest/install/index.html>`__
|
||||
in the Horizon documentation.
|
||||
|
||||
1. Clone the Senlin Dashboard repository::
|
||||
|
||||
$ git clone https://opendev.org/openstack/senlin-dashboard
|
||||
|
||||
2. Copy the ``_50_senlin.py`` file from ``senlin_dashboard/enabled/_50_senlin.py``
|
||||
file to ``horizon/openstack_dashboard/local/enabled`` directory. Example,
|
||||
set as if being executed from the root of the senlin-dashboard repository::
|
||||
|
||||
cp ./senlin_dashboard/enabled/_50_senlin.py ../horizon/openstack_dashboard/local/enabled
|
||||
|
||||
3. Change into the senlin-dashboard repository and package the plugin::
|
||||
|
||||
pip install -r requirements.txt -e .
|
||||
|
||||
This will build and install the senlin-dashboard plugin into the active virtual
|
||||
environment associated with your horizon installation. The plugin is installed
|
||||
in "editable" mode as a link back to your senlin-dashboard plugin directory.
|
||||
|
||||
.. inclusion-end-marker-install
|
||||
|
||||
.. inclusion-start-marker-develop
|
||||
|
||||
Devstack Installation
|
||||
---------------------
|
||||
|
||||
1. Download DevStack::
|
||||
|
||||
$ git clone https://opendev.org/openstack/devstack
|
||||
$ cd devstack
|
||||
|
||||
2. Add following repo as external repositories into your ``local.conf`` file::
|
||||
|
||||
[[local|localrc]]
|
||||
#Enable senlin
|
||||
enable_plugin senlin https://opendev.org/openstack/senlin
|
||||
#Enable senlin-dashboard
|
||||
enable_plugin senlin-dashboard https://opendev.org/openstack/senlin-dashboard
|
||||
|
||||
Please see the link: https://docs.openstack.org/senlin/latest/install/index.html
|
||||
for more detail about setting Senlin Server.
|
||||
|
||||
3. Run ``stack.sh``::
|
||||
|
||||
$ ./stack.sh
|
||||
|
||||
Unit Test
|
||||
---------
|
||||
|
||||
The unit tests can be executed directly from within this Senlin Dashboard plugin
|
||||
project directory by using::
|
||||
|
||||
tox
|
||||
|
||||
.. inclusion-end-marker-develop
|
||||
|
||||
.. inclusion-start-marker-configuration
|
||||
|
||||
=============
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Switch to Angularized panels
|
||||
----------------------------
|
||||
|
||||
The panels are ongoing to migrate to AngularJS based. If you would try them,
|
||||
please copy ``_59_toggle_angular_senlin_dashboard.py.example`` to
|
||||
``horizon/openstack_dashboard/local_settings.d/_59_toggle_angular_senlin_dashboard.py``
|
||||
and restart Horizon.
|
||||
|
||||
For more information on configuration, see
|
||||
`Configuration Guide
|
||||
<https://docs.openstack.org/horizon/latest/configuration/index.html>`__
|
||||
in the Horizon documentation.
|
||||
|
||||
.. inclusion-end-marker-configuration
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For any further questions, please email
|
||||
openstack-discuss@lists.openstack.org or join #openstack-dev on
|
||||
OFTC.
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
ANGULAR_FEATURES.update({
|
||||
'profiles_panel': True,
|
||||
'nodes_panel': True,
|
||||
'clusters_panel': True,
|
||||
'policies_panel': True,
|
||||
'receivers_panel': True
|
||||
})
|
|
@ -1,2 +0,0 @@
|
|||
[python: **.py]
|
||||
[django: **/templates/**.html]
|
|
@ -1,2 +0,0 @@
|
|||
[javascript: **.js]
|
||||
[angular: **/static/**.html]
|
|
@ -1,4 +0,0 @@
|
|||
# This is a cross-platform list tracking distribution packages needed for install and tests;
|
||||
# see https://docs.openstack.org/infra/bindep/ for additional information.
|
||||
|
||||
libfontconfig1 [nodejs platform:dpkg]
|
|
@ -1,13 +0,0 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
# Order matters to the pip dependency resolver, so sorting this file
|
||||
# changes how packages are installed. New dependencies should be
|
||||
# added in alphabetical order, however, some dependencies may need to
|
||||
# be installed in a specific order.
|
||||
#
|
||||
# Requirements for docs
|
||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
||||
reno>=3.1.0 # Apache-2.0
|
||||
sphinxcontrib-apidoc>=0.2.0 # BSD
|
||||
sphinx>=2.0.0,!=2.1.0 # BSD
|
|
@ -1,90 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
|
||||
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
# This is required for ReadTheDocs.org, but isn't a bad idea anyway.
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
|
||||
'senlin_dashboard.test.settings')
|
||||
django.setup()
|
||||
|
||||
# -- 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.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinxcontrib.apidoc',
|
||||
'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 = 'senlin-dashboard'
|
||||
copyright = '2015, OpenStack Foundation'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['**/#*', '**~', '**/#*#']
|
||||
|
||||
# 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 = 'native'
|
||||
|
||||
# sphinxcontrib-apidoc
|
||||
apidoc_module_dir = '../../senlin_dashboard'
|
||||
apidoc_output_dir = 'contributor/api'
|
||||
apidoc_excluded_paths = [
|
||||
'test',
|
||||
]
|
||||
|
||||
# -- 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_static_path = []
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
openstackdocs_repo_name = 'openstack/senlin-dashboard'
|
||||
openstackdocs_auto_name = False
|
||||
openstackdocs_bug_project = 'senlin-dashboard'
|
||||
openstackdocs_bug_tag = ''
|
|
@ -1,4 +0,0 @@
|
|||
.. include:: ../../../README.rst
|
||||
:start-after: inclusion-start-marker-configuration
|
||||
:end-before: inclusion-end-marker-configuration
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
Source Code Reference
|
||||
---------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
api/*
|
|
@ -1,4 +0,0 @@
|
|||
.. include:: ../../../README.rst
|
||||
:start-after: inclusion-start-marker-develop
|
||||
:end-before: inclusion-end-marker-develop
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
.. include:: ../../../README.rst
|
||||
:start-after: inclusion-start-marker-hosts
|
||||
:end-before: inclusion-end-marker-hosts
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
================================
|
||||
Contributing to Senlin Dashboard
|
||||
================================
|
||||
|
||||
If you're interested in contributing to the Senlin Dashboard project,
|
||||
the following will help get you started.
|
||||
|
||||
Contributor License Agreement
|
||||
-----------------------------
|
||||
|
||||
.. index::
|
||||
single: license; agreement
|
||||
|
||||
In order to contribute to the Senlin Dashboard project, you need to have
|
||||
signed OpenStack's contributor's agreement.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* https://docs.openstack.org/infra/manual/developers.html
|
||||
* https://wiki.openstack.org/wiki/How_To_Contribute#Contributors_License_Agreement
|
||||
|
||||
LaunchPad Project
|
||||
-----------------
|
||||
|
||||
Most of the tools used for OpenStack depend on a launchpad.net ID for
|
||||
authentication.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* https://launchpad.net
|
||||
* https://launchpad.net/senlin-dashboard
|
||||
|
||||
.. include:: hosts.rst
|
||||
|
||||
.. include:: develop.rst
|
||||
|
||||
.. include:: api.rst
|
|
@ -1,22 +0,0 @@
|
|||
|
||||
Welcome to Senlin Dashboard's documentation!
|
||||
============================================
|
||||
|
||||
User Documentation
|
||||
------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
configuration/index
|
||||
Release Notes <https://docs.openstack.org/releasenotes/senlin-dashboard>
|
||||
|
||||
Contributor Guide
|
||||
-----------------
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 2
|
||||
|
||||
contributor/index
|
|
@ -1,4 +0,0 @@
|
|||
.. include:: ../../../README.rst
|
||||
:start-after: inclusion-start-marker-install
|
||||
:end-before: inclusion-end-marker-install
|
||||
|
23
manage.py
23
manage.py
|
@ -1,23 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
||||
"senlin_dashboard.test.settings")
|
||||
execute_from_command_line(sys.argv)
|
27
package.json
27
package.json
|
@ -1,27 +0,0 @@
|
|||
{
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"name": "horizon",
|
||||
"description": "OpenStack Senlin Dashboard",
|
||||
"repository": "none",
|
||||
"license": "Apache 2.0",
|
||||
"devDependencies": {
|
||||
"eslint": "3.19.x",
|
||||
"eslint-config-openstack": "^4.0.1",
|
||||
"eslint-plugin-angular": "3.1.x",
|
||||
"jasmine-core": "2.8.x",
|
||||
"karma": "1.7.x",
|
||||
"karma-firefox-launcher": "2.1.0",
|
||||
"karma-cli": "1.0.x",
|
||||
"karma-coverage": "1.1.x",
|
||||
"karma-jasmine": "1.1.x",
|
||||
"karma-ng-html2js-preprocessor": "1.0.x",
|
||||
"karma-threshold-reporter": "0.1.x"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "if [ ! -d .tox ] || [ ! -d .tox/karma ]; then tox -ekarma --notest; fi",
|
||||
"test": "karma start senlin_dashboard/karma.conf.js --single-run",
|
||||
"lint": "eslint --no-color senlin_dashboard/static"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
prelude: >
|
||||
AngularJS-based panels are implemented. These new
|
||||
panels have most of functions in exist Django-based
|
||||
panels. Users can switch to AngularJS-based panels by
|
||||
editing settings in
|
||||
``_59_toggle_angular_senlin_dashboard.py`` .
|
||||
features:
|
||||
- >
|
||||
Five panels, profiles, nodes, clusters, policies and
|
||||
receivers, are implemented as AngularJS-based. These
|
||||
panels uses recent Horizon framework features, e.g.
|
||||
angular-json-schema, common "views" for AngularJS-based
|
||||
plugin, initAction instead initScope, and so on. Also
|
||||
these source codes are tested with Jasmine and Eslint
|
||||
to ensure its quality.
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_]
|
||||
Fixed senlin dashboard to work with latest openstacksdk version.
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_]
|
||||
Fixed profile create to pass in the correct encoding type during form
|
||||
submission.
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed link to cluster in receiver table.
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/senlin-dashboard/+spec/add-cluster-resize-action>`_]
|
||||
Resize action for cluster is added. This action is added
|
||||
as row action for each cluster in Clusters table view.
|
||||
Although, this action is only for Angularized clusters panel.
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
prelude: >
|
||||
Angular-based panels are as default.
|
||||
upgrade:
|
||||
- |
|
||||
Angular-based panels are as default. So ``_59_toggle_angular_senlin_dashboard.py``
|
||||
in ``openstack_dashboard/local/local_settings.d/`` is not needed
|
||||
for enabling Angular-based panels. Conversely to use Django-based
|
||||
panels, operators need to use ``_59_toggle_angular_senlin_dashboard.py``
|
||||
and set ``False`` for each panels, e.g ``'profiles_panel': False,``.
|
||||
deprecations:
|
||||
- |
|
||||
Angular-based panels are made as default. New features will not be
|
||||
added into Django-based panels anymore. And Django-based panels
|
||||
will be removed after S cycle or later.
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of senlin-dashboard
|
||||
to support python 2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by senlin-dashboard is Python 3.6.
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed installation documentation when using devstack environment.
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed the display of long names which could break the table layout.
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed node detail page view.
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- Paginated list for node objects.
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
upgrade:
|
||||
- >
|
||||
Use only tox for test and remove run_tests.sh that is
|
||||
no longer used.
|
||||
- >
|
||||
Support python 3.5.
|
||||
- >
|
||||
To remove "project/ngdetails/" hard coded in the path
|
||||
of the details view, use "horizon.app.core.detailRoute".
|
||||
- >
|
||||
Switch theme for documentation from oslosphinx to
|
||||
openstackdocstheme.
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- >
|
||||
Added region support.
|
||||
- >
|
||||
The load-edit directive is used. The Spec field on Profile creation
|
||||
dialog and Policy creation dialog use load-edit directive newly
|
||||
added into Horizon.
|
||||
- >
|
||||
Switched to OSC module. Previously the client module for senlin API
|
||||
have been used from senlin command module. Now the senlin command
|
||||
module has retired and the client module for OpenStack Client is
|
||||
used to call senlin API.
|
||||
- >
|
||||
Added the region support for Keystone V3.
|
||||
fixes:
|
||||
- >
|
||||
[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_]
|
||||
Fixed showing node list in Nodes tab of a cluster failed for
|
||||
Dango-based panel.
|
||||
- >
|
||||
[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_]
|
||||
Fixed issue the deleted item is selected again with batch delete.
|
||||
Items recently deleted with batch action had been shown in deletion
|
||||
confirmation dialog when execute the batch delete action again.
|
||||
And this had caused the conflict error due to trying to delete
|
||||
unexisting item.
|
||||
- >
|
||||
[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_]
|
||||
Fixed being able not to recover a cluster in warning status.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- >
|
||||
Update action for receiver is added. This action is added
|
||||
as row action for each receiver in Receivers table view.
|
||||
Although, this action is only for Angularized receivers panel.
|
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
fixes:
|
||||
- >
|
||||
[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_]
|
||||
Fixed issue the deleted item is selected again with batch delete.
|
||||
Item selections on table view for batch actions are not cleared
|
||||
after execution of actions. To ensure to clear item selections,
|
||||
`hzTable:clearSelection` event is emitted.
|
||||
- >
|
||||
[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_]
|
||||
Reproduced navigations when refreshing details view. Previously the fix
|
||||
for [`bug/1681627 <https://bugs.launchpad.net/horizon/+bug/1681627>`_]
|
||||
allowed us to reload or directly open Angular-based detail page (ngdetail),
|
||||
but the navigation menu was not reproduced correctly.
|
||||
- >
|
||||
Fixed `type` for profile. Senlin API uses `type_name` for type, but
|
||||
dashboard did not process `type_name`. So this issue caused error to
|
||||
handle Profile object.
|
||||
- >
|
||||
[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_]
|
||||
`region_name` was not passed on when using the senlin-dashboard.
|
||||
This makes the client always fallback on the first region. This issue was
|
||||
fixed.
|
||||
- >
|
||||
[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_]
|
||||
Horizon by default sets the Project dashboard as default for non-admin
|
||||
users. The _50_senlin.py file that comes with the Senlin dashboard also
|
||||
has 'DEFAULT = True' set. Because 'cluster' comes before 'project'
|
||||
alphabetically, this defaults all non-admin users to getting the cluster
|
||||
dashboard on login. This issue was fixed.
|
|
@ -1,7 +0,0 @@
|
|||
---
|
||||
features:
|
||||
- >
|
||||
Scale-in and Scale-out actions for cluster added.
|
||||
These actions are added as row action for each cluster
|
||||
in Clusters table view. Although, this action is only
|
||||
for Angularized clusters panel.
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
upgrade:
|
||||
- >
|
||||
Switched nodejs4-jobs to nodejs10.
|
||||
- >
|
||||
Dropped the py35 testing.
|
||||
- >
|
||||
Switched to the new canonical constraints URL on master.
|
||||
- >
|
||||
Added Python3 Train unit tests.
|
|
@ -1,6 +0,0 @@
|
|||
===========================
|
||||
2023.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.1
|
|
@ -1,6 +0,0 @@
|
|||
===========================
|
||||
2023.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.2
|
|
@ -1,288 +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.
|
||||
|
||||
# Senlin Release Notes documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Nov 3 17:40:50 2015.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
import subprocess
|
||||
import warnings
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'reno.sphinxext',
|
||||
'openstackdocstheme'
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'Senlin Dashboard Release Notes'
|
||||
copyright = '2015, Senlin Developers'
|
||||
|
||||
# Release notes are version independent.
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = ''
|
||||
# The short X.Y version.
|
||||
version = ''
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
# add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
# add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'native'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = ["."]
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local",
|
||||
"-n1"]
|
||||
try:
|
||||
html_last_updated_fmt = subprocess.check_output(git_cmd).decode('utf-8')
|
||||
except Exception:
|
||||
warnings.warn('Cannot get last updated time from git repository. '
|
||||
'Not setting "html_last_updated_fmt".')
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'SenlinDashboardReleaseNotesdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'SenlinDashboardReleaseNotes.tex',
|
||||
'Senlin Dashboard Release Notes Documentation',
|
||||
'Senlin Developers', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'senlindashboardreleasenotes',
|
||||
'Senlin Dashboard Release Notes Documentation',
|
||||
['Senlin Developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'SenlinDashboardReleaseNotes',
|
||||
'Senlin Dashboard Release Notes Documentation',
|
||||
'Senlin Developers', 'SenlinDashboardReleaseNotes',
|
||||
'Dashboard for Senlin.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
||||
|
||||
# -- Options for openstackdocstheme -------------------------------------------
|
||||
openstackdocs_repo_name = 'openstack/senlin-dashboard'
|
||||
openstackdocs_auto_name = False
|
||||
openstackdocs_bug_project = 'senlin-dashboard'
|
||||
openstackdocs_bug_tag = ''
|
|
@ -1,23 +0,0 @@
|
|||
===============================
|
||||
Senlin Dashboard Release Notes
|
||||
===============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
unreleased
|
||||
2023.2
|
||||
2023.1
|
||||
zed
|
||||
yoga
|
||||
xena
|
||||
wallaby
|
||||
victoria
|
||||
ussuri
|
||||
train
|
||||
stein
|
||||
rocky
|
||||
queens
|
||||
pike
|
||||
ocata
|
||||
newton
|
|
@ -1,100 +0,0 @@
|
|||
# Robert Simai <robert.simai@suse.com>, 2018. #zanata
|
||||
# Andreas Jaeger <jaegerandi@gmail.com>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: senlin-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-09-26 05:23+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-09-28 07:53+0000\n"
|
||||
"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "0.10.0"
|
||||
msgstr "0.10.0"
|
||||
|
||||
msgid "0.11.0"
|
||||
msgstr "0.11.0"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "0.6.0"
|
||||
msgstr "0.6.0"
|
||||
|
||||
msgid "0.7.0"
|
||||
msgstr "0.7.0"
|
||||
|
||||
msgid "0.9.0"
|
||||
msgstr "0.9.0"
|
||||
|
||||
msgid "Added region support."
|
||||
msgstr "Regionenunterstützung hinzugefügt."
|
||||
|
||||
msgid "Added the region support for Keystone V3."
|
||||
msgstr "Regionenunterstützung für Keystone V3 hinzugefügt."
|
||||
|
||||
msgid "Angular-based panels are as default."
|
||||
msgstr "Angular-basierte Paneele sind der Standard."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Fehlerkorrekturen"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Aktuelle Serie Releasenotes"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Ablaufwarnungen"
|
||||
|
||||
msgid "Fixed node detail page view."
|
||||
msgstr "Knotendetails Seitenansicht gefixt."
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Neue Funktionen"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton Serie Releasenotes"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata Serie Releasenotes"
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike Serie Releasenotes"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "Einleitung"
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Queens Serie Releasenotes"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Rocky Serie Releasenotes"
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Senlin Dashboard Releasenotes"
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Stein Serie Releasenotes"
|
||||
|
||||
msgid "Support python 3.5."
|
||||
msgstr "Unterstützung für Python 3.5."
|
||||
|
||||
msgid "Switch theme for documentation from oslosphinx to openstackdocstheme."
|
||||
msgstr ""
|
||||
"Motiv der Dokumentation von oslosphinx nach openstackdocstheme gewechselt."
|
||||
|
||||
msgid "Train Series Release Notes"
|
||||
msgstr "Train Serie Releasenotes"
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "Aktualisierungsnotizen"
|
||||
|
||||
msgid "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
msgstr ""
|
||||
"Benutze nur tox für tests und entferne run_tests.sh welches nicht mehr "
|
||||
"benutzt wird."
|
|
@ -1,369 +0,0 @@
|
|||
# Andi Chandler <andi@gowling.com>, 2017. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2018. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2019. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2020. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2022. #zanata
|
||||
# Andi Chandler <andi@gowling.com>, 2023. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Senlin Dashboard Release Notes\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-06 05:41+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2023-09-21 12:34+0000\n"
|
||||
"Last-Translator: Andi Chandler <andi@gowling.com>\n"
|
||||
"Language-Team: English (United Kingdom)\n"
|
||||
"Language: en_GB\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "0.10.0"
|
||||
msgstr "0.10.0"
|
||||
|
||||
msgid "0.11.0"
|
||||
msgstr "0.11.0"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "0.6.0"
|
||||
msgstr "0.6.0"
|
||||
|
||||
msgid "0.7.0"
|
||||
msgstr "0.7.0"
|
||||
|
||||
msgid "0.9.0"
|
||||
msgstr "0.9.0"
|
||||
|
||||
msgid "1.0.0"
|
||||
msgstr "1.0.0"
|
||||
|
||||
msgid "1.0.0.0rc1"
|
||||
msgstr "1.0.0.0rc1"
|
||||
|
||||
msgid "2023.1 Series Release Notes"
|
||||
msgstr "2023.1 Series Release Notes"
|
||||
|
||||
msgid "2023.2 Series Release Notes"
|
||||
msgstr "2023.2 Series Release Notes"
|
||||
|
||||
msgid "Added Python3 Train unit tests."
|
||||
msgstr "Added Python3 Train unit tests."
|
||||
|
||||
msgid "Added region support."
|
||||
msgstr "Added region support."
|
||||
|
||||
msgid "Added the region support for Keystone V3."
|
||||
msgstr "Added the region support for Keystone V3."
|
||||
|
||||
msgid "Angular-based panels are as default."
|
||||
msgstr "Angular-based panels are as default."
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are as default. So "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` in ``openstack_dashboard/local/"
|
||||
"local_settings.d/`` is not needed for enabling Angular-based panels. "
|
||||
"Conversely to use Django-based panels, operators need to use "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` and set ``False`` for each "
|
||||
"panels, e.g ``'profiles_panel': False,``."
|
||||
msgstr ""
|
||||
"Angular-based panels are as default. So "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` in ``openstack_dashboard/local/"
|
||||
"local_settings.d/`` is not needed for enabling Angular-based panels. "
|
||||
"Conversely to use Django-based panels, operators need to use "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` and set ``False`` for each "
|
||||
"panels, e.g ``'profiles_panel': False,``."
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are made as default. New features will not be added "
|
||||
"into Django-based panels anymore. And Django-based panels will be removed "
|
||||
"after S cycle or later."
|
||||
msgstr ""
|
||||
"Angular-based panels are made as default. New features will not be added "
|
||||
"into Django-based panels any more. And Django-based panels will be removed "
|
||||
"after S cycle or later."
|
||||
|
||||
msgid ""
|
||||
"AngularJS-based panels are implemented. These new panels have most of "
|
||||
"functions in exist Django-based panels. Users can switch to AngularJS-based "
|
||||
"panels by editing settings in ``_59_toggle_angular_senlin_dashboard.py`` ."
|
||||
msgstr ""
|
||||
"AngularJS-based panels are implemented. These new panels have most of "
|
||||
"functions in exist Django-based panels. Users can switch to AngularJS-based "
|
||||
"panels by editing settings in ``_59_toggle_angular_senlin_dashboard.py`` ."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Bug Fixes"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Current Series Release Notes"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Deprecation Notes"
|
||||
|
||||
msgid "Dropped the py35 testing."
|
||||
msgstr "Dropped the py35 testing."
|
||||
|
||||
msgid ""
|
||||
"Five panels, profiles, nodes, clusters, policies and receivers, are "
|
||||
"implemented as AngularJS-based. These panels uses recent Horizon framework "
|
||||
"features, e.g. angular-json-schema, common \"views\" for AngularJS-based "
|
||||
"plugin, initAction instead initScope, and so on. Also these source codes are "
|
||||
"tested with Jasmine and Eslint to ensure its quality."
|
||||
msgstr ""
|
||||
"Five panels, profiles, nodes, clusters, policies and receivers, are "
|
||||
"implemented as AngularJS-based. These panels uses recent Horizon framework "
|
||||
"features, e.g. angular-json-schema, common \"views\" for AngularJS-based "
|
||||
"plugin, initAction instead initScope, and so on. Also these source codes are "
|
||||
"tested with Jasmine and Eslint to ensure its quality."
|
||||
|
||||
msgid ""
|
||||
"Fixed `type` for profile. Senlin API uses `type_name` for type, but "
|
||||
"dashboard did not process `type_name`. So this issue caused error to handle "
|
||||
"Profile object."
|
||||
msgstr ""
|
||||
"Fixed `type` for profile. Senlin API uses `type_name` for type, but "
|
||||
"dashboard did not process `type_name`. So this issue caused error to handle "
|
||||
"Profile object."
|
||||
|
||||
msgid "Fixed installation documentation when using devstack environment."
|
||||
msgstr "Fixed installation documentation when using devstack environment."
|
||||
|
||||
msgid "Fixed link to cluster in receiver table."
|
||||
msgstr "Fixed link to cluster in receiver table."
|
||||
|
||||
msgid "Fixed node detail page view."
|
||||
msgstr "Fixed node detail page view."
|
||||
|
||||
msgid "Fixed the display of long names which could break the table layout."
|
||||
msgstr "Fixed the display of long names which could break the table layout."
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "New Features"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton Series Release Notes"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata Series Release Notes"
|
||||
|
||||
msgid "Paginated list for node objects."
|
||||
msgstr "Paginated list for node objects."
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike Series Release Notes"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "Prelude"
|
||||
|
||||
msgid ""
|
||||
"Python 2.7 support has been dropped. Last release of senlin-dashboard to "
|
||||
"support python 2.7 is OpenStack Train. The minimum version of Python now "
|
||||
"supported by senlin-dashboard is Python 3.6."
|
||||
msgstr ""
|
||||
"Python 2.7 support has been dropped. Last release of Senlin-dashboard to "
|
||||
"support Python 2.7 is OpenStack Train. The minimum version of Python now "
|
||||
"supported by Senlin-dashboard is Python 3.6."
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Queens Series Release Notes"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Rocky Series Release Notes"
|
||||
|
||||
msgid ""
|
||||
"Scale-in and Scale-out actions for cluster added. These actions are added as "
|
||||
"row action for each cluster in Clusters table view. Although, this action is "
|
||||
"only for Angularized clusters panel."
|
||||
msgstr ""
|
||||
"Scale-in and Scale-out actions for cluster added. These actions are added as "
|
||||
"row action for each cluster in Clusters table view. Although, this action is "
|
||||
"only for Angularised clusters panel."
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Senlin Dashboard Release Notes"
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Stein Series Release Notes"
|
||||
|
||||
msgid "Support python 3.5."
|
||||
msgstr "Support python 3.5."
|
||||
|
||||
msgid "Switch theme for documentation from oslosphinx to openstackdocstheme."
|
||||
msgstr "Switch theme for documentation from oslosphinx to openstackdocstheme."
|
||||
|
||||
msgid "Switched nodejs4-jobs to nodejs10."
|
||||
msgstr "Switched nodejs4-jobs to nodejs10."
|
||||
|
||||
msgid ""
|
||||
"Switched to OSC module. Previously the client module for senlin API have "
|
||||
"been used from senlin command module. Now the senlin command module has "
|
||||
"retired and the client module for OpenStack Client is used to call senlin "
|
||||
"API."
|
||||
msgstr ""
|
||||
"Switched to OSC module. Previously the client module for Senlin API have "
|
||||
"been used from Senlin command module. Now the Senlin command module has "
|
||||
"retired and the client module for OpenStack Client is used to call Senlin "
|
||||
"API."
|
||||
|
||||
msgid "Switched to the new canonical constraints URL on master."
|
||||
msgstr "Switched to the new canonical constraints URL on master."
|
||||
|
||||
msgid ""
|
||||
"The load-edit directive is used. The Spec field on Profile creation dialog "
|
||||
"and Policy creation dialog use load-edit directive newly added into Horizon."
|
||||
msgstr ""
|
||||
"The load-edit directive is used. The Spec field on Profile creation dialogue "
|
||||
"and Policy creation dialogue use load-edit directive newly added into "
|
||||
"Horizon."
|
||||
|
||||
msgid ""
|
||||
"To remove \"project/ngdetails/\" hard coded in the path of the details view, "
|
||||
"use \"horizon.app.core.detailRoute\"."
|
||||
msgstr ""
|
||||
"To remove \"project/ngdetails/\" hard coded in the path of the details view, "
|
||||
"use \"horizon.app.core.detailRoute\"."
|
||||
|
||||
msgid "Train Series Release Notes"
|
||||
msgstr "Train Series Release Notes"
|
||||
|
||||
msgid ""
|
||||
"Update action for receiver is added. This action is added as row action for "
|
||||
"each receiver in Receivers table view. Although, this action is only for "
|
||||
"Angularized receivers panel."
|
||||
msgstr ""
|
||||
"Update action for receiver is added. This action is added as row action for "
|
||||
"each receiver in Receivers table view. Although, this action is only for "
|
||||
"Angularised receivers panel."
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "Upgrade Notes"
|
||||
|
||||
msgid "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
msgstr "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
|
||||
msgid "Ussuri Series Release Notes"
|
||||
msgstr "Ussuri Series Release Notes"
|
||||
|
||||
msgid "Victoria Series Release Notes"
|
||||
msgstr "Victoria Series Release Notes"
|
||||
|
||||
msgid "Wallaby Series Release Notes"
|
||||
msgstr "Wallaby Series Release Notes"
|
||||
|
||||
msgid "Xena Series Release Notes"
|
||||
msgstr "Xena Series Release Notes"
|
||||
|
||||
msgid "Yoga Series Release Notes"
|
||||
msgstr "Yoga Series Release Notes"
|
||||
|
||||
msgid "Zed Series Release Notes"
|
||||
msgstr "Zed Series Release Notes"
|
||||
|
||||
msgid ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] Resize action for "
|
||||
"cluster is added. This action is added as row action for each cluster in "
|
||||
"Clusters table view. Although, this action is only for Angularized clusters "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] Resize action for "
|
||||
"cluster is added. This action is added as row action for each cluster in "
|
||||
"Clusters table view. Although, this action is only for Angularised clusters "
|
||||
"panel."
|
||||
|
||||
msgid ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] Fixed "
|
||||
"senlin dashboard to work with latest openstacksdk version."
|
||||
msgstr ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] Fixed "
|
||||
"Senlin dashboard to work with latest openstacksdk version."
|
||||
|
||||
msgid ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] Fixed "
|
||||
"profile create to pass in the correct encoding type during form submission."
|
||||
msgstr ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] Fixed "
|
||||
"profile create to pass in the correct encoding type during form submission."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Fixed showing node list in Nodes tab of a cluster failed for Dango-based "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Fixed showing node list in Nodes tab of a cluster failed for Dango-based "
|
||||
"panel."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"Fixed being able not to recover a cluster in warning status."
|
||||
msgstr ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"Fixed being able not to recover a cluster in warning status."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Items "
|
||||
"recently deleted with batch action had been shown in deletion confirmation "
|
||||
"dialog when execute the batch delete action again. And this had caused the "
|
||||
"conflict error due to trying to delete unexisting item."
|
||||
msgstr ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"Fixed issue where the deleted item is selected again with batch delete. "
|
||||
"Items recently deleted with batch action had been shown in deletion "
|
||||
"confirmation dialogue when execute the batch delete action again. This had "
|
||||
"caused the conflict error due to trying to delete a non-existent item."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"Reproduced navigations when refreshing details view. Previously the fix for "
|
||||
"[`bug/1681627 <https://bugs.launchpad.net/horizon/+bug/1681627>`_] allowed "
|
||||
"us to reload or directly open Angular-based detail page (ngdetail), but the "
|
||||
"navigation menu was not reproduced correctly."
|
||||
msgstr ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"Reproduced navigations when refreshing details view. Previously the fix for "
|
||||
"[`bug/1681627 <https://bugs.launchpad.net/horizon/+bug/1681627>`_] allowed "
|
||||
"us to reload or directly open Angular-based detail page (ngdetail), but the "
|
||||
"navigation menu was not reproduced correctly."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"Horizon by default sets the Project dashboard as default for non-admin "
|
||||
"users. The _50_senlin.py file that comes with the Senlin dashboard also has "
|
||||
"'DEFAULT = True' set. Because 'cluster' comes before 'project' "
|
||||
"alphabetically, this defaults all non-admin users to getting the cluster "
|
||||
"dashboard on login. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"Horizon by default sets the Project dashboard as default for non-admin "
|
||||
"users. The _50_senlin.py file that comes with the Senlin dashboard also has "
|
||||
"'DEFAULT = True' set. Because 'cluster' comes before 'project' "
|
||||
"alphabetically, this defaults all non-admin users to getting the cluster "
|
||||
"dashboard on login. This issue was fixed."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` was not passed on when using the senlin-dashboard. This makes "
|
||||
"the client always fallback on the first region. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` was not passed on when using the Senlin-Dashboard. This makes "
|
||||
"the client always fallback on the first region. This issue was fixed."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Item "
|
||||
"selections on table view for batch actions are not cleared after execution "
|
||||
"of actions. To ensure to clear item selections, `hzTable:clearSelection` "
|
||||
"event is emitted."
|
||||
msgstr ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Item "
|
||||
"selections on table view for batch actions are not cleared after execution "
|
||||
"of actions. To ensure to clear item selections, `hzTable:clearSelection` "
|
||||
"event is emitted."
|
|
@ -1,30 +0,0 @@
|
|||
# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: senlin-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-21 00:07+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-22 06:38+0000\n"
|
||||
"Last-Translator: Gérald LONLAS <g.lonlas@gmail.com>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Corrections de bugs"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Note de la release actuelle"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Nouvelles fonctionnalités"
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Note de release pour le Tableau de bord Senlin"
|
|
@ -1,328 +0,0 @@
|
|||
# suhartono <cloudsuhartono@gmail.com>, 2018. #zanata
|
||||
# suhartono <cloudsuhartono@gmail.com>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: senlin-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-09-26 05:23+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-09-23 05:13+0000\n"
|
||||
"Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "0.10.0"
|
||||
msgstr "0.10.0"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "0.6.0"
|
||||
msgstr "0.6.0"
|
||||
|
||||
msgid "0.7.0"
|
||||
msgstr "0.7.0"
|
||||
|
||||
msgid "0.9.0"
|
||||
msgstr "0.9.0"
|
||||
|
||||
msgid "Added Python3 Train unit tests."
|
||||
msgstr "Menambahkan tes unit Python3 Train."
|
||||
|
||||
msgid "Added region support."
|
||||
msgstr "Ditambahkan dukungan wilayah."
|
||||
|
||||
msgid "Added the region support for Keystone V3."
|
||||
msgstr "Menambahkan dukungan wilayah untuk Keystone V3."
|
||||
|
||||
msgid "Angular-based panels are as default."
|
||||
msgstr "Panel berbasis Angular adalah sebagai default."
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are as default. So "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` in ``openstack_dashboard/local/"
|
||||
"local_settings.d/`` is not needed for enabling Angular-based panels. "
|
||||
"Conversely to use Django-based panels, operators need to use "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` and set ``False`` for each "
|
||||
"panels, e.g ``'profiles_panel': False,``."
|
||||
msgstr ""
|
||||
"Panel berbasis Angular adalah sebagai default. Jadi "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` di ``openstack_dashboard/local/"
|
||||
"local_settings.d/`` tidak diperlukan untuk mengaktifkan panel berbasis "
|
||||
"Angular. Sebaliknya untuk menggunakan panel berbasis Django, operator harus "
|
||||
"menggunakan ``_59_toggle_angular_senlin_dashboard.py`` dan mengatur "
|
||||
"``False`` untuk setiap panel, misalnya ``'profiles_panel': False,``."
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are made as default. New features will not be added "
|
||||
"into Django-based panels anymore. And Django-based panels will be removed "
|
||||
"after S cycle or later."
|
||||
msgstr ""
|
||||
"Panel berbasis Angular dibuat sebagai default. Fitur-fitur baru tidak akan "
|
||||
"ditambahkan ke panel berbasis Django lagi. Dan panel berbasis Django akan "
|
||||
"dihapus setelah siklus S atau berikutnya."
|
||||
|
||||
msgid ""
|
||||
"AngularJS-based panels are implemented. These new panels have most of "
|
||||
"functions in exist Django-based panels. Users can switch to AngularJS-based "
|
||||
"panels by editing settings in ``_59_toggle_angular_senlin_dashboard.py`` ."
|
||||
msgstr ""
|
||||
"Panel berbasis AngularJS diimplementasikan. Panel-panel baru ini memiliki "
|
||||
"sebagian besar fungsi dalam panel-panel berbasis Django. Pengguna dapat "
|
||||
"beralih ke panel berbasis AngularJS dengan mengedit pengaturan di "
|
||||
"``_59_toggle_angular_senlin_dashboard.py``."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Perbaikan Bug"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Saat Ini"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Catatan Deprekasi"
|
||||
|
||||
msgid "Dropped the py35 testing."
|
||||
msgstr "Menjatuhkan pengujian py35."
|
||||
|
||||
msgid ""
|
||||
"Five panels, profiles, nodes, clusters, policies and receivers, are "
|
||||
"implemented as AngularJS-based. These panels uses recent Horizon framework "
|
||||
"features, e.g. angular-json-schema, common \"views\" for AngularJS-based "
|
||||
"plugin, initAction instead initScope, and so on. Also these source codes are "
|
||||
"tested with Jasmine and Eslint to ensure its quality."
|
||||
msgstr ""
|
||||
"Lima panel, profil, simpul, klaster, kebijakan, dan penerima, "
|
||||
"diimplementasikan sebagai berbasis AngularJS. Panel ini menggunakan fitur "
|
||||
"kerangka Horizon terkini, mis. angular-json-schema, common \"views\" untuk "
|
||||
"plugin berbasis AngularJS, initAction sebagai gantinya initScope, dan "
|
||||
"seterusnya. Juga kode sumber ini diuji dengan Jasmine dan Eslint untuk "
|
||||
"memastikan kualitasnya."
|
||||
|
||||
msgid ""
|
||||
"Fixed `type` for profile. Senlin API uses `type_name` for type, but "
|
||||
"dashboard did not process `type_name`. So this issue caused error to handle "
|
||||
"Profile object."
|
||||
msgstr ""
|
||||
"Memperbaiki `type` untuk profil. Senlin API menggunakan `type_name` untuk "
|
||||
"tipe, tetapi dasbor tidak memproses `type_name`. Jadi masalah ini "
|
||||
"menyebabkan kesalahan untuk menangani objek Profil."
|
||||
|
||||
msgid "Fixed installation documentation when using devstack environment."
|
||||
msgstr ""
|
||||
"Memperbaiki dokumentasi instalasi saat menggunakan lingkungan devstack."
|
||||
|
||||
msgid "Fixed link to cluster in receiver table."
|
||||
msgstr "Memperbaiki tautan ke kluster di table penerima."
|
||||
|
||||
msgid "Fixed node detail page view."
|
||||
msgstr "Memperbaiki tampilan halaman detail node."
|
||||
|
||||
msgid "Fixed the display of long names which could break the table layout."
|
||||
msgstr "Memperbaiki tampilan nama panjang yang bisa merusak tata letak tabel."
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Fitur baru"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Newton"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Ocata"
|
||||
|
||||
msgid "Paginated list for node objects."
|
||||
msgstr "Daftar halaman nomor untuk objek node."
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Pike"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "Prelude"
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Queens"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Rocky"
|
||||
|
||||
msgid ""
|
||||
"Scale-in and Scale-out actions for cluster added. These actions are added as "
|
||||
"row action for each cluster in Clusters table view. Although, this action is "
|
||||
"only for Angularized clusters panel."
|
||||
msgstr ""
|
||||
"Aksi Scale-in dan Scale-out untuk kluster ditambahkan. Aksi ini ditambahkan "
|
||||
"sebagai aksi baris untuk setiap cluster dalam tampilan tabel Cluster. "
|
||||
"Meskipun, tindakan ini hanya untuk panel klaster Angularized."
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Catatan Rilis Dashboard Senlin"
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Stein"
|
||||
|
||||
msgid "Support python 3.5."
|
||||
msgstr "Mendukung python 3.5."
|
||||
|
||||
msgid "Switch theme for documentation from oslosphinx to openstackdocstheme."
|
||||
msgstr "Ganti tema untuk dokumentasi dari oslosphinx ke openstackdocstheme."
|
||||
|
||||
msgid "Switched nodejs4-jobs to nodejs10."
|
||||
msgstr "Pindah nodejs4-jobs ke nodejs10."
|
||||
|
||||
msgid ""
|
||||
"Switched to OSC module. Previously the client module for senlin API have "
|
||||
"been used from senlin command module. Now the senlin command module has "
|
||||
"retired and the client module for OpenStack Client is used to call senlin "
|
||||
"API."
|
||||
msgstr ""
|
||||
"Beralih ke modul OSC. Sebelumnya modul klien untuk senlin API telah "
|
||||
"digunakan dari modul perintah senlin. Sekarang modul perintah senlin telah "
|
||||
"pensiun dan modul klien untuk OpenStack Client digunakan untuk memanggil "
|
||||
"Senlin API."
|
||||
|
||||
msgid "Switched to the new canonical constraints URL on master."
|
||||
msgstr "Beralih ke URL batasan canonical baru pada master."
|
||||
|
||||
msgid ""
|
||||
"The load-edit directive is used. The Spec field on Profile creation dialog "
|
||||
"and Policy creation dialog use load-edit directive newly added into Horizon."
|
||||
msgstr ""
|
||||
"Direktif load-edit digunakan. Spec field pada dialog pembuatan Profil dan "
|
||||
"dialog pembuatan Policy menggunakan direktif load-edit yang baru ditambahkan "
|
||||
"ke Horizon."
|
||||
|
||||
msgid ""
|
||||
"To remove \"project/ngdetails/\" hard coded in the path of the details view, "
|
||||
"use \"horizon.app.core.detailRoute\"."
|
||||
msgstr ""
|
||||
"Untuk menghapus \"project/ngdetails/\" hard coded di path tampilan detail, "
|
||||
"gunakan \"horizon.app.core.detailRoute\"."
|
||||
|
||||
msgid ""
|
||||
"Update action for receiver is added. This action is added as row action for "
|
||||
"each receiver in Receivers table view. Although, this action is only for "
|
||||
"Angularized receivers panel."
|
||||
msgstr ""
|
||||
"Perbarui aksi untuk penerima ditambahkan. Aksi ini ditambahkan sebagai aksi "
|
||||
"baris untuk setiap penerima dalam tampilan tabel Penerima. Meskipun, aksi "
|
||||
"ini hanya untuk panel receiver Angularized."
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "Catatan Upgrade"
|
||||
|
||||
msgid "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
msgstr ""
|
||||
"Gunakan hanya tox untuk menguji dan hapus run_tests.sh yang tidak lagi "
|
||||
"digunakan."
|
||||
|
||||
msgid ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] Resize action for "
|
||||
"cluster is added. This action is added as row action for each cluster in "
|
||||
"Clusters table view. Although, this action is only for Angularized clusters "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] Tindakan resize untuk "
|
||||
"cluster ditambahkan. Tindakan ini ditambahkan sebagai tindakan baris (row "
|
||||
"action) untuk setiap cluster di tampilan tabel Cluster. Meskipun, tindakan "
|
||||
"ini hanya untuk panel cluster Angularized."
|
||||
|
||||
msgid ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] Fixed "
|
||||
"senlin dashboard to work with latest openstacksdk version."
|
||||
msgstr ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] "
|
||||
"Memperbaiki senlin dashboard untuk bekerja dengan versi openstacksdk terbaru."
|
||||
|
||||
msgid ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] Fixed "
|
||||
"profile create to pass in the correct encoding type during form submission."
|
||||
msgstr ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] Profil "
|
||||
"tetap dibuat untuk meneruskan dalam jenis penyandian yang benar selama "
|
||||
"pengiriman formulir."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Fixed showing node list in Nodes tab of a cluster failed for Dango-based "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Memperbaiki menampilkan daftar node di tab Node dari gugus gagal (cluster "
|
||||
"failed) untuk panel berbasis Django."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"Fixed being able not to recover a cluster in warning status."
|
||||
msgstr ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"Memperbaiki agar tidak dapat memulihkan kluster dalam status peringatan."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Items "
|
||||
"recently deleted with batch action had been shown in deletion confirmation "
|
||||
"dialog when execute the batch delete action again. And this had caused the "
|
||||
"conflict error due to trying to delete unexisting item."
|
||||
msgstr ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"Memperbaiki masalah item yang dihapus dipilih lagi dengan penghapusan batch. "
|
||||
"Item yang baru ini dihapus dengan aksi batch telah ditunjukkan dalam dialog "
|
||||
"konfirmasi penghapusan ketika menjalankan tindakan hapus batch lagi. Dan ini "
|
||||
"telah menyebabkan kesalahan konflik karena mencoba untuk menghapus item yang "
|
||||
"tidak ada."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"Reproduced navigations when refreshing details view. Previously the fix for "
|
||||
"[`bug/1681627 <https://bugs.launchpad.net/horizon/+bug/1681627>`_] allowed "
|
||||
"us to reload or directly open Angular-based detail page (ngdetail), but the "
|
||||
"navigation menu was not reproduced correctly."
|
||||
msgstr ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"Navigasi yang direproduksi saat menyegarkan tampilan detail. Sebelumnya "
|
||||
"perbaikan untuk [`bug/1681627 <https://bugs.launchpad.net/horizon/"
|
||||
"+bug/1681627>`_] memungkinkan kami memuat ulang (reload) atau langsung "
|
||||
"membuka Angular-based detail page (ngdetail), tetapi menu navigasi tidak "
|
||||
"direproduksi dengan benar."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"Horizon by default sets the Project dashboard as default for non-admin "
|
||||
"users. The _50_senlin.py file that comes with the Senlin dashboard also has "
|
||||
"'DEFAULT = True' set. Because 'cluster' comes before 'project' "
|
||||
"alphabetically, this defaults all non-admin users to getting the cluster "
|
||||
"dashboard on login. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"Horizon secara default menetapkan Project dashboard sebagai default untuk "
|
||||
"pengguna non-admin. File _50_senlin.py yang datang dengan dasbor Senlin juga "
|
||||
"memiliki set 'DEFAULT = True'. Karena 'cluster' hadir sebelum 'project' "
|
||||
"secara alfabet, ini menetapkan default semua pengguna non-admin untuk "
|
||||
"mendapatkan dashboard cluster saat login. Masalah ini telah diperbaiki."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` was not passed on when using the senlin-dashboard. This makes "
|
||||
"the client always fallback on the first region. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` tidak diteruskan saat menggunakan senlin-dashboard. Ini "
|
||||
"membuat klien selalu mundur di wilayah pertama. Masalah ini telah diperbaiki."
|
||||
|
||||
msgid ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Item "
|
||||
"selections on table view for batch actions are not cleared after execution "
|
||||
"of actions. To ensure to clear item selections, `hzTable:clearSelection` "
|
||||
"event is emitted."
|
||||
msgstr ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"Memperbaiki masalah item yang dihapus dipilih lagi dengan penghapusan batch. "
|
||||
"Pilihan item pada tampilan tabel untuk tindakan batch tidak dihapus setelah "
|
||||
"eksekusi tindakan. Untuk memastikan untuk menghapus pilihan item, event "
|
||||
"`hzTable:clearSelection` dipancarkan."
|
|
@ -1,314 +0,0 @@
|
|||
# Shu Muto <shu.mutow@gmail.com>, 2016. #zanata
|
||||
# Shu Muto <shu.mutow@gmail.com>, 2017. #zanata
|
||||
# Shu Muto <shu.mutow@gmail.com>, 2018. #zanata
|
||||
# Shu Muto <shu.mutow@gmail.com>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: senlin-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-03-19 01:50+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-03-27 02:11+0000\n"
|
||||
"Last-Translator: Shu Muto <shu.mutow@gmail.com>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "0.10.0"
|
||||
msgstr "0.10.0"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "0.6.0"
|
||||
msgstr "0.6.0"
|
||||
|
||||
msgid "0.7.0"
|
||||
msgstr "0.7.0"
|
||||
|
||||
msgid "0.9.0"
|
||||
msgstr "0.9.0"
|
||||
|
||||
msgid "Added region support."
|
||||
msgstr "リージョンサポートの追加。"
|
||||
|
||||
msgid "Added the region support for Keystone V3."
|
||||
msgstr "Keystone V3 のリージョンサポートを追加しました。"
|
||||
|
||||
msgid "Angular-based panels are as default."
|
||||
msgstr "Angular ベースのパネルがデフォルトになりました。"
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are as default. So "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` in ``openstack_dashboard/local/"
|
||||
"local_settings.d/`` is not needed for enabling Angular-based panels. "
|
||||
"Conversely to use Django-based panels, operators need to use "
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` and set ``False`` for each "
|
||||
"panels, e.g ``'profiles_panel': False,``."
|
||||
msgstr ""
|
||||
"Angular ベースのパネルがデフォルトになりました。``openstack_dashboard/local/"
|
||||
"local_settings.d/`` の ``_59_toggle_angular_senlin_dashboard.py`` は、"
|
||||
"Angular ベースのパネルを有効にするのに不要になりました。逆に、Django ベースの"
|
||||
"パネルを使用するには、オペレーターは ``_59_toggle_angular_senlin_dashboard."
|
||||
"py`` を使用する必要があり、それぞれのパネルに ``False`` を設定する必要があり"
|
||||
"ます。例)``'profiles_panel': False,``"
|
||||
|
||||
msgid ""
|
||||
"Angular-based panels are made as default. New features will not be added "
|
||||
"into Django-based panels anymore. And Django-based panels will be removed "
|
||||
"after S cycle or later."
|
||||
msgstr ""
|
||||
"Angular ベースのパネルがデフォルトになりました。以降、新しい機能は Django "
|
||||
"ベースのパネルには追加されません。また、Django ベースのパネルは S サイクル以"
|
||||
"降に削除される予定です。"
|
||||
|
||||
msgid ""
|
||||
"AngularJS-based panels are implemented. These new panels have most of "
|
||||
"functions in exist Django-based panels. Users can switch to AngularJS-based "
|
||||
"panels by editing settings in ``_59_toggle_angular_senlin_dashboard.py`` ."
|
||||
msgstr ""
|
||||
"AngularJS ベースのパネルが実装されました。これらの新しいパネルには、既存の "
|
||||
"Django ベースのパネルのほとんどの機能があります。"
|
||||
"``_59_toggle_angular_senlin_dashboard.py`` の設定を編集することによって、 "
|
||||
"AngularJS ベースのパネルに切り替えられます。"
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "バグ修正"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "開発中バージョンのリリースノート"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "廃止予定の機能"
|
||||
|
||||
msgid ""
|
||||
"Five panels, profiles, nodes, clusters, policies and receivers, are "
|
||||
"implemented as AngularJS-based. These panels uses recent Horizon framework "
|
||||
"features, e.g. angular-json-schema, common \"views\" for AngularJS-based "
|
||||
"plugin, initAction instead initScope, and so on. Also these source codes are "
|
||||
"tested with Jasmine and Eslint to ensure its quality."
|
||||
msgstr ""
|
||||
"5つのパネル、プロファイル、ノード、クラスター、ポリシー、およびレシーバー、"
|
||||
"は、 AngularJS ベースで実装されました。これらのパネルは、最新の Horizon フ"
|
||||
"レームワークの機能、例えば、 angular-json-schema 、AngularJS ベースのプラグイ"
|
||||
"ン向けの \"views\" モジュール、 initScope の代わりに initAction 、など、を使"
|
||||
"用しています。また、これらのソースコードは、 Jasmine や Eslint でテストされ、"
|
||||
"品質を確保しています。"
|
||||
|
||||
msgid ""
|
||||
"Fixed `type` for profile. Senlin API uses `type_name` for type, but "
|
||||
"dashboard did not process `type_name`. So this issue caused error to handle "
|
||||
"Profile object."
|
||||
msgstr ""
|
||||
"プロファイルの `type` を修正しました。 Senlin API は type に `type_name` を使"
|
||||
"用しますが、ダッシュボードは `type_name` を処理していませんでした。この問題に"
|
||||
"より、 Profile オブジェクトを処理する際にエラーが発生していました。"
|
||||
|
||||
msgid "Fixed installation documentation when using devstack environment."
|
||||
msgstr "DevStack 環境を使用するときのインストールドキュメントを修正しました。"
|
||||
|
||||
msgid "Fixed link to cluster in receiver table."
|
||||
msgstr "レシーバーテーブルのクラスターへのリンクを修正しました。"
|
||||
|
||||
msgid "Fixed node detail page view."
|
||||
msgstr "ノードの詳細ページのビューを修正しました。"
|
||||
|
||||
msgid "Fixed the display of long names which could break the table layout."
|
||||
msgstr "テーブルレイアウトを崩す可能性がある、長い名前の表示を修正しました。"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "新機能"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton バージョンのリリースノート"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata バージョンのリリースノート"
|
||||
|
||||
msgid "Paginated list for node objects."
|
||||
msgstr "ノードオブジェクトのページ送り付きリスト。"
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike バージョンのリリースノート"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "紹介"
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Queens バージョンのリリースノート"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Rocky バージョンのリリースノート"
|
||||
|
||||
msgid ""
|
||||
"Scale-in and Scale-out actions for cluster added. These actions are added as "
|
||||
"row action for each cluster in Clusters table view. Although, this action is "
|
||||
"only for Angularized clusters panel."
|
||||
msgstr ""
|
||||
"クラスターのスケールインとスケールアウトアクションを追加しました。これらのア"
|
||||
"クションは、クラスターのテーブルビューの各クラスターに行アクションとして追加"
|
||||
"されます。ただし、このアクションは Angular 化したクラスターパネルのみに実装さ"
|
||||
"れています。"
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Senlin Dashboard リリースノート"
|
||||
|
||||
msgid "Support python 3.5."
|
||||
msgstr "Python 3.5 をサポートしました。"
|
||||
|
||||
msgid "Switch theme for documentation from oslosphinx to openstackdocstheme."
|
||||
msgstr ""
|
||||
"ドキュメントサイトのテーマを oslosphinx から openstackdocstheme に変更しまし"
|
||||
"た。"
|
||||
|
||||
msgid ""
|
||||
"Switched to OSC module. Previously the client module for senlin API have "
|
||||
"been used from senlin command module. Now the senlin command module has "
|
||||
"retired and the client module for OpenStack Client is used to call senlin "
|
||||
"API."
|
||||
msgstr ""
|
||||
"OSC モジュールに切り替えました。以前は senlin コマンドモジュールから Senlin "
|
||||
"API 向けのクライアントモジュールが使われていました。現在 senlin コマンドモ"
|
||||
"ジュールは廃止され、senlin API の呼び出しには、 OpenStack クライアント向けの"
|
||||
"クライアントモジュールが使用されています。"
|
||||
|
||||
msgid ""
|
||||
"The load-edit directive is used. The Spec field on Profile creation dialog "
|
||||
"and Policy creation dialog use load-edit directive newly added into Horizon."
|
||||
msgstr ""
|
||||
"load-edit ディレクティブを使用しました。プロファイル作成ダイアログとポリシー"
|
||||
"作成ダイアログのスペックフィールドで、Horizon に新しく追加された load-edit "
|
||||
"ディレクティブを使用しています。"
|
||||
|
||||
msgid ""
|
||||
"To remove \"project/ngdetails/\" hard coded in the path of the details view, "
|
||||
"use \"horizon.app.core.detailRoute\"."
|
||||
msgstr ""
|
||||
"詳細ビューのパスにハードコードされていた「project/ngdetails」を削除するため"
|
||||
"に、「horizon.app.core.detailRoute」を使用しました。"
|
||||
|
||||
msgid ""
|
||||
"Update action for receiver is added. This action is added as row action for "
|
||||
"each receiver in Receivers table view. Although, this action is only for "
|
||||
"Angularized receivers panel."
|
||||
msgstr ""
|
||||
"レシーバーの更新アクションを追加しました。これらのアクションは、レシーバーの"
|
||||
"テーブルビューの各レシーバーに行アクションとして追加されます。ただし、このア"
|
||||
"クションは Angular 化したレシーバーパネルのみに実装されています。"
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "アップグレード時の注意"
|
||||
|
||||
msgid "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
msgstr ""
|
||||
"テストには tox のみを使用し、使用されなくなった run_test.sh を削除しました。"
|
||||
|
||||
msgid ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] Resize action for "
|
||||
"cluster is added. This action is added as row action for each cluster in "
|
||||
"Clusters table view. Although, this action is only for Angularized clusters "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`blueprint add-cluster-resize-action <https://blueprints.launchpad.net/"
|
||||
"senlin-dashboard/+spec/add-cluster-resize-action>`_] クラスターのリサイズアク"
|
||||
"ションを追加しました。これらのアクションは、クラスターのテーブルビューの各ク"
|
||||
"ラスターに行アクションとして追加されます。ただし、このアクションは Angular 化"
|
||||
"したクラスターパネルのみに実装されています。"
|
||||
|
||||
msgid ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] Fixed "
|
||||
"senlin dashboard to work with latest openstacksdk version."
|
||||
msgstr ""
|
||||
"[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_] 最新の "
|
||||
"openstacksdk バージョンで動作するように修正しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] Fixed "
|
||||
"profile create to pass in the correct encoding type during form submission."
|
||||
msgstr ""
|
||||
"[`bug 1817803 <https://bugs.launchpad.net/senlin/+bug/1817803>`_] 送信時に正"
|
||||
"しいエンコードで渡されるようにプロファイル作成を修正しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Fixed showing node list in Nodes tab of a cluster failed for Dango-based "
|
||||
"panel."
|
||||
msgstr ""
|
||||
"[`bug/1733833 <https://bugs.launchpad.net/senlin-dashboard/+bug/1733833>`_] "
|
||||
"Django ベースのパネルで、失敗したクラスターのノードタブにノード一覧が表示され"
|
||||
"るように修正しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"Fixed being able not to recover a cluster in warning status."
|
||||
msgstr ""
|
||||
"[`bug/1742091 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742091>`_] "
|
||||
"警告状態にあるクラスターの復旧ができない問題を修正しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Items "
|
||||
"recently deleted with batch action had been shown in deletion confirmation "
|
||||
"dialog when execute the batch delete action again. And this had caused the "
|
||||
"conflict error due to trying to delete unexisting item."
|
||||
msgstr ""
|
||||
"[`bug/1742599 <https://bugs.launchpad.net/senlin-dashboard/+bug/1742599>`_] "
|
||||
"一括削除において、削除されたアイテムが再度選択される問題を解決しました。一括"
|
||||
"削除で直前に削除されたアイテムが、一括削除実行時の削除確認ダイアログで再度表"
|
||||
"示されていました。また、この問題は存在しないアイテムを削除しようとして、コン"
|
||||
"フリクトエラーを引き起こしていました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"Reproduced navigations when refreshing details view. Previously the fix for "
|
||||
"[`bug/1681627 <https://bugs.launchpad.net/horizon/+bug/1681627>`_] allowed "
|
||||
"us to reload or directly open Angular-based detail page (ngdetail), but the "
|
||||
"navigation menu was not reproduced correctly."
|
||||
msgstr ""
|
||||
"[`bug/1746706 <https://bugs.launchpad.net/senlin-dashboard/+bug/1746706>`_] "
|
||||
"詳細画面の再描画でナビゲーションを再生します。以前、[`bug/1681627 <https://"
|
||||
"bugs.launchpad.net/horizon/+bug/1681627>`_] の修正でAngular ベースの詳細画面 "
|
||||
"(ngdetail) をリロード、あるいは直接開くようにしましたが、ナビゲーションメ"
|
||||
"ニューが正しく再生されませんでした。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"Horizon by default sets the Project dashboard as default for non-admin "
|
||||
"users. The _50_senlin.py file that comes with the Senlin dashboard also has "
|
||||
"'DEFAULT = True' set. Because 'cluster' comes before 'project' "
|
||||
"alphabetically, this defaults all non-admin users to getting the cluster "
|
||||
"dashboard on login. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754183 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754183>`_] "
|
||||
"デフォルトでは Horizon は、非管理者ユーザー向けにプロジェクトダッシュボードを"
|
||||
"デフォルトに設定しています。Senlin Dashboard により導入される _50_senlin.py "
|
||||
"ファイルも 'DEFAULT = True' を設定します。アルファベット順で「プロジェクト」"
|
||||
"よりも「クラスター」が前に来るので、すべての非管理者ユーザーに対してログイン"
|
||||
"によりクラスターダッシュボードがデフォルトになってしまいます。この問題を修正"
|
||||
"しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` was not passed on when using the senlin-dashboard. This makes "
|
||||
"the client always fallback on the first region. This issue was fixed."
|
||||
msgstr ""
|
||||
"[`bug/1754416 <https://bugs.launchpad.net/senlin-dashboard/+bug/1754416>`_] "
|
||||
"`region_name` が渡されませんでした。これにより、クライアントは常に最初のリー"
|
||||
"ジョンにフォールバックしていました。この問題を修正しました。"
|
||||
|
||||
msgid ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"Fixed issue the deleted item is selected again with batch delete. Item "
|
||||
"selections on table view for batch actions are not cleared after execution "
|
||||
"of actions. To ensure to clear item selections, `hzTable:clearSelection` "
|
||||
"event is emitted."
|
||||
msgstr ""
|
||||
"[`bug/1777545 <https://bugs.launchpad.net/senlin-dashboard/+bug/1777545>`_] "
|
||||
"一括削除で、削除したアイテムが再度選択されてしまう問題を修正しました。テーブ"
|
||||
"ル画面の一括アクションにおけるアイテム選択が、アクション実行後にクリアされま"
|
||||
"せんでした。アイテム選択を確実に実行するために、`hzTable:clearSelection` イベ"
|
||||
"ントを発火するようにしました。"
|
|
@ -1,79 +0,0 @@
|
|||
# Rodrigo Loures <rmoraesloures@gmail.com>, 2018. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: senlin-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-11-16 00:25+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2018-12-11 09:51+0000\n"
|
||||
"Last-Translator: Rodrigo Loures <rmoraesloures@gmail.com>\n"
|
||||
"Language-Team: Portuguese (Brazil)\n"
|
||||
"Language: pt_BR\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "0.6.0"
|
||||
msgstr "0.6.0"
|
||||
|
||||
msgid "0.7.0"
|
||||
msgstr "0.7.0"
|
||||
|
||||
msgid "0.9.0"
|
||||
msgstr "0.9.0"
|
||||
|
||||
msgid "Added region support."
|
||||
msgstr "Adicionado suporte regional."
|
||||
|
||||
msgid "Added the region support for Keystone V3."
|
||||
msgstr "Adicionado suporte regional para Keystone V3."
|
||||
|
||||
msgid "Angular-based panels are as default."
|
||||
msgstr "Painéis baseados em angular são como padrão."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Correção de erros"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Atual - Série de Notas de Versão"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Notas de obsolência"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Novos Recursos"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton - Série de Notas de Versão"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata - Série de Notas de Versão"
|
||||
|
||||
msgid "Paginated list for node objects."
|
||||
msgstr "Lista paginada para nó de objetos."
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike - Série de Notas de Versão"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "Prelúdio"
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Notas de Versão da Série Queens"
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Senlin Dashboard - Notas de Versão"
|
||||
|
||||
msgid "Support python 3.5."
|
||||
msgstr "Suporta Python 3.5"
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "Notas de Atualização"
|
||||
|
||||
msgid "Use only tox for test and remove run_tests.sh that is no longer used."
|
||||
msgstr ""
|
||||
"Utilize apenas tox para teste e remova run_tests.sh que não é mais utilizado."
|
|
@ -1,33 +0,0 @@
|
|||
# zzxwill <zzxwill@gmail.com>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Senlin Dashboard Release Notes\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-02-26 11:55+0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-09-13 01:59+0000\n"
|
||||
"Last-Translator: zzxwill <zzxwill@gmail.com>\n"
|
||||
"Language-Team: Chinese (China)\n"
|
||||
"Language: zh_CN\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "0.4.0"
|
||||
msgstr "0.4.0"
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Bug修复"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "当前版本发布说明"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "新特性"
|
||||
|
||||
msgid "Paginated list for node objects."
|
||||
msgstr "节点对象的可分页列表。"
|
||||
|
||||
msgid "Senlin Dashboard Release Notes"
|
||||
msgstr "Senlin Dashboard发布说明"
|
|
@ -1,6 +0,0 @@
|
|||
===================================
|
||||
Newton Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/newton
|
|
@ -1,6 +0,0 @@
|
|||
===================================
|
||||
Ocata Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/ocata
|
|
@ -1,6 +0,0 @@
|
|||
===================================
|
||||
Pike Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/pike
|
|
@ -1,6 +0,0 @@
|
|||
===========================
|
||||
Queens Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/queens
|
|
@ -1,6 +0,0 @@
|
|||
===================================
|
||||
Rocky Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/rocky
|
|
@ -1,6 +0,0 @@
|
|||
==========================
|
||||
Stein Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/stein
|
|
@ -1,6 +0,0 @@
|
|||
==========================
|
||||
Train Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/train
|
|
@ -1,5 +0,0 @@
|
|||
==============================
|
||||
Current Series Release Notes
|
||||
==============================
|
||||
|
||||
.. release-notes::
|
|
@ -1,6 +0,0 @@
|
|||
===========================
|
||||
Ussuri Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/ussuri
|
|
@ -1,6 +0,0 @@
|
|||
=============================
|
||||
Victoria Series Release Notes
|
||||
=============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/victoria
|
|
@ -1,6 +0,0 @@
|
|||
============================
|
||||
Wallaby Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/wallaby
|
|
@ -1,6 +0,0 @@
|
|||
=========================
|
||||
Xena Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/xena
|
|
@ -1,6 +0,0 @@
|
|||
=========================
|
||||
Yoga Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: unmaintained/yoga
|
|
@ -1,6 +0,0 @@
|
|||
========================
|
||||
Zed Series Release Notes
|
||||
========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/zed
|
|
@ -1,10 +0,0 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
PyYAML>=3.12 # MIT
|
||||
|
||||
python-senlinclient>=1.9.0 # Apache-2.0
|
||||
|
||||
horizon>=17.1.0 # Apache-2.0
|
|
@ -1,18 +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.
|
||||
|
||||
"""This package holds the REST API that supports the Senlin dashboard
|
||||
Javascript code.
|
||||
"""
|
||||
|
||||
# import REST API modules here
|
||||
from senlin_dashboard.api.rest import senlin # noqa: F401
|
|
@ -1,559 +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.
|
||||
"""API for the senlin service."""
|
||||
|
||||
from django.views import generic
|
||||
|
||||
from openstack_dashboard.api.rest import urls
|
||||
from openstack_dashboard.api.rest import utils as rest_utils
|
||||
from senlin_dashboard.api import senlin
|
||||
from senlin_dashboard.api import utils as api_utils
|
||||
from senlin_dashboard.cluster.nodes import forms as node_forms
|
||||
from senlin_dashboard.cluster.policies import forms as policy_forms
|
||||
from senlin_dashboard.cluster.profiles import forms
|
||||
from senlin_dashboard.cluster.receivers import forms as receiver_forms
|
||||
|
||||
CLIENT_KEYWORDS = {'marker', 'sort_dir', 'sort_key', 'paginate'}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Receivers(generic.View):
|
||||
"""API for Senlin receiver."""
|
||||
|
||||
url_regex = r'senlin/receivers/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of receivers."""
|
||||
|
||||
filters, kwargs = rest_utils.parse_filters_kwargs(request,
|
||||
CLIENT_KEYWORDS)
|
||||
|
||||
receivers, has_more_data, has_prev_data = senlin.receiver_list(
|
||||
request, filters=filters, **kwargs)
|
||||
|
||||
receivers_dict = []
|
||||
for r in receivers:
|
||||
r = r.to_dict()
|
||||
r["params"] = api_utils.convert_to_yaml(r["params"])
|
||||
r["channel"] = api_utils.convert_to_yaml(r["channel"])
|
||||
receivers_dict.append(r)
|
||||
|
||||
return {
|
||||
'items': receivers_dict,
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Receiver.
|
||||
|
||||
Returns the new Receiver object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = receiver_forms._populate_receiver_params(
|
||||
request_param.get("name"),
|
||||
request_param.get("type"),
|
||||
request_param.get("cluster_id"),
|
||||
request_param.get("action"),
|
||||
request_param.get("params"))
|
||||
new_receiver = senlin.receiver_create(request, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/receivers/%s' % new_receiver.id,
|
||||
new_receiver.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Receiver(generic.View):
|
||||
"""API for Senlin receiver."""
|
||||
|
||||
url_regex = r'senlin/receivers/(?P<receiver_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, receiver_id):
|
||||
"""Get a single receiver's details with the receiver id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param receiver_id: the id of the receiver
|
||||
|
||||
The result is a receiver object.
|
||||
"""
|
||||
receiver = senlin.receiver_get(request, receiver_id).to_dict()
|
||||
receiver["params"] = api_utils.convert_to_yaml(receiver["params"])
|
||||
receiver["channel"] = api_utils.convert_to_yaml(receiver["channel"])
|
||||
return receiver
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, receiver_id):
|
||||
"""Update a Profile.
|
||||
|
||||
Returns the Profile object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = receiver_forms._populate_receiver_params(
|
||||
request_param.get("name"),
|
||||
None,
|
||||
None,
|
||||
request_param.get("action"),
|
||||
request_param.get("params"))
|
||||
del params['type']
|
||||
del params['cluster_id']
|
||||
updated_receiver = senlin.receiver_update(
|
||||
request, receiver_id, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/receivers/%s' % updated_receiver.id,
|
||||
updated_receiver.to_dict())
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, receiver_id):
|
||||
"""Delete a specific receiver
|
||||
|
||||
DELETE http://localhost/api/senlin/receivers/cc758c90-3d98-4ea1-af44-aab405c9c915 # noqa
|
||||
"""
|
||||
senlin.receiver_delete(request, receiver_id)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Profiles(generic.View):
|
||||
"""API for Senlin profile."""
|
||||
|
||||
url_regex = r'senlin/profiles/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of profiles."""
|
||||
|
||||
filters, kwargs = rest_utils.parse_filters_kwargs(request,
|
||||
CLIENT_KEYWORDS)
|
||||
|
||||
profiles, has_more_data, has_prev_data = senlin.profile_list(
|
||||
request, filters=filters, **kwargs)
|
||||
|
||||
return {
|
||||
'items': [p.to_dict() for p in profiles],
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Profile.
|
||||
|
||||
Returns the new Profile object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = forms._populate_profile_params(request_param.get("name"),
|
||||
request_param.get("spec"),
|
||||
request_param.get("metadata"))
|
||||
new_profile = senlin.profile_create(request, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/profiles/%s' % new_profile.id,
|
||||
new_profile.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Profile(generic.View):
|
||||
"""API for Senlin profile."""
|
||||
|
||||
url_regex = r'senlin/profiles/(?P<profile_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, profile_id):
|
||||
"""Get a single profile's details with the profile id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param profile_id: the id of the profile
|
||||
|
||||
The result is a profile object.
|
||||
"""
|
||||
profile = senlin.profile_get(request, profile_id).to_dict()
|
||||
profile["spec"] = api_utils.convert_to_yaml(profile["spec"])
|
||||
profile["metadata"] = api_utils.convert_to_yaml(profile["metadata"])
|
||||
return profile
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, profile_id):
|
||||
"""Update a Profile.
|
||||
|
||||
Returns the Profile object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = forms._populate_profile_params(request_param.get("name"),
|
||||
None,
|
||||
request_param.get("metadata"))
|
||||
del params['spec']
|
||||
updated_profile = senlin.profile_update(
|
||||
request, profile_id, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/profiles/%s' % updated_profile.id,
|
||||
updated_profile.to_dict())
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, profile_id):
|
||||
"""Delete a specific profile
|
||||
|
||||
DELETE http://localhost/api/senlin/profiles/cc758c90-3d98-4ea1-af44-aab405c9c915 # noqa
|
||||
"""
|
||||
senlin.profile_delete(request, profile_id)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Nodes(generic.View):
|
||||
"""API for Senlin node."""
|
||||
|
||||
url_regex = r'senlin/nodes/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of nodes."""
|
||||
|
||||
filters, kwargs = rest_utils.parse_filters_kwargs(request,
|
||||
CLIENT_KEYWORDS)
|
||||
|
||||
nodes, has_more_data, has_prev_data = senlin.node_list(
|
||||
request, filters=filters, **kwargs)
|
||||
|
||||
return {
|
||||
'items': [n.to_dict() for n in nodes],
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Node.
|
||||
|
||||
Returns the new Node object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = node_forms._populate_node_params(
|
||||
request_param.get("name"),
|
||||
request_param.get("profile_id"),
|
||||
request_param.get("cluster_id"),
|
||||
request_param.get("role"),
|
||||
request_param.get("metadata"))
|
||||
new_node = senlin.node_create(request, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/nodes/%s' % new_node.id,
|
||||
new_node.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Node(generic.View):
|
||||
"""API for Senlin node."""
|
||||
|
||||
url_regex = r'senlin/nodes/(?P<node_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, node_id):
|
||||
"""Get a single node's details with the receiver id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param node_id: the id of the node
|
||||
|
||||
The result is a node object.
|
||||
"""
|
||||
node = senlin.node_get(request, node_id).to_dict()
|
||||
node["metadata"] = api_utils.convert_to_yaml(node["metadata"])
|
||||
return node
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, node_id):
|
||||
"""Delete a specific node
|
||||
|
||||
DELETE http://localhost/api/senlin/nodes/cc758c90-3d98-4ea1-af44-aab405c9c915 # noqa
|
||||
"""
|
||||
senlin.node_delete(request, node_id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, node_id):
|
||||
"""Update a Node.
|
||||
|
||||
Returns the Node object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = node_forms._populate_node_params(
|
||||
request_param.get("name"),
|
||||
request_param.get("profile_id"),
|
||||
None,
|
||||
request_param.get("role"),
|
||||
request_param.get("metadata"))
|
||||
params.pop('cluster_id')
|
||||
updated_node = senlin.node_update(
|
||||
request, node_id, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/nodes/%s' % updated_node.id,
|
||||
updated_node.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Events(generic.View):
|
||||
"""API for Senlin events."""
|
||||
|
||||
url_regex = r'senlin/events/(?P<obj_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, obj_id):
|
||||
"""Get a list of events."""
|
||||
|
||||
events, has_more_data, has_prev_data = senlin.event_list(
|
||||
request, filters={"obj_id": obj_id}, paginate=False)
|
||||
|
||||
return {
|
||||
'items': [e.to_dict() for e in events],
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
|
||||
@urls.register
|
||||
class Clusters(generic.View):
|
||||
"""API for Senlin cluster."""
|
||||
|
||||
url_regex = r'senlin/clusters/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of clusters."""
|
||||
|
||||
filters, kwargs = rest_utils.parse_filters_kwargs(request,
|
||||
CLIENT_KEYWORDS)
|
||||
|
||||
clusters, has_more_data, has_prev_data = senlin.cluster_list(
|
||||
request, filters=filters, **kwargs)
|
||||
|
||||
return {
|
||||
'items': [c.to_dict() for c in clusters],
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Cluster.
|
||||
|
||||
Returns the new Cluster object on success.
|
||||
"""
|
||||
params = request.DATA
|
||||
params["metadata"] = api_utils.load_yaml(params.get("metadata"))
|
||||
cluster = senlin.cluster_create(request, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/clusters/%s' % cluster.id, cluster.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Cluster(generic.View):
|
||||
"""API for Senlin cluster."""
|
||||
|
||||
url_regex = r'senlin/clusters/(?P<cluster_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, cluster_id):
|
||||
"""Get a single cluster's details with the cluster id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param cluster_id: the id of the cluster
|
||||
|
||||
The result is a cluster object.
|
||||
"""
|
||||
cluster = senlin.cluster_get(request, cluster_id).to_dict()
|
||||
cluster["metadata"] = api_utils.convert_to_yaml(cluster["metadata"])
|
||||
return cluster
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, cluster_id):
|
||||
"""Delete a specific cluster
|
||||
|
||||
DELETE http://localhost/api/senlin/clusters/cc758c90-3d98-4ea1-af44-aab405c9c915 # noqa
|
||||
"""
|
||||
senlin.cluster_delete(request, cluster_id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, cluster_id):
|
||||
"""Update a Cluster.
|
||||
|
||||
Returns the Cluster object on success.
|
||||
"""
|
||||
params = request.DATA
|
||||
params["metadata"] = api_utils.load_yaml(params.get("metadata"))
|
||||
updated_cluster = senlin.cluster_update(
|
||||
request, cluster_id, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/clusters/%s' % updated_cluster.id,
|
||||
updated_cluster.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class ClusterActions(generic.View):
|
||||
"""API for Senlin cluster."""
|
||||
|
||||
url_regex = r'senlin/clusters/(?P<cluster_id>[^/]+)/(?P<action>[^/]+)$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, cluster_id, action):
|
||||
if action == "policy":
|
||||
"""Get policies of a single cluster with the cluster id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param cluster_id: the id of the cluster
|
||||
|
||||
The result is a cluster object.
|
||||
"""
|
||||
policies = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
return {
|
||||
'items': [p.to_dict() for p in policies],
|
||||
}
|
||||
elif action == "":
|
||||
return None
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, cluster_id, action):
|
||||
if action == "policy":
|
||||
"""Update policies for the cluster."""
|
||||
params = request.DATA
|
||||
|
||||
new_attach_ids = params["ids"]
|
||||
old_attached = senlin.cluster_policy_list(request, cluster_id, {})
|
||||
|
||||
# Extract policies should be detached and execute
|
||||
for policy in old_attached:
|
||||
should_detach = True
|
||||
for new_id in new_attach_ids:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_detach = False
|
||||
break
|
||||
if should_detach:
|
||||
# If policy is not exist in new policies,
|
||||
# it should be removed
|
||||
senlin.cluster_detach_policy(
|
||||
request, cluster_id, policy.policy_id)
|
||||
|
||||
# Extract policies should be attached and execute
|
||||
for new_id in new_attach_ids:
|
||||
should_attach = True
|
||||
for policy in old_attached:
|
||||
if new_id == policy.policy_id:
|
||||
# This policy is already attached.
|
||||
should_attach = False
|
||||
break
|
||||
if should_attach:
|
||||
# If policy is not exist in old policies,
|
||||
# it should be added
|
||||
senlin.cluster_attach_policy(request, cluster_id,
|
||||
new_id, {})
|
||||
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/clusters/%s/policy' % cluster_id)
|
||||
elif action == "scale-in":
|
||||
count = request.DATA.get("count") or None
|
||||
return senlin.cluster_scale_in(request, cluster_id, count)
|
||||
elif action == "scale-out":
|
||||
count = request.DATA.get("count") or None
|
||||
return senlin.cluster_scale_out(request, cluster_id, count)
|
||||
elif action == "resize":
|
||||
params = request.DATA
|
||||
return senlin.cluster_resize(request, cluster_id, **params)
|
||||
|
||||
|
||||
@urls.register
|
||||
class Policies(generic.View):
|
||||
"""API for Senlin policies."""
|
||||
|
||||
url_regex = r'senlin/policies/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request):
|
||||
"""Get a list of policies."""
|
||||
|
||||
filters, kwargs = rest_utils.parse_filters_kwargs(request,
|
||||
CLIENT_KEYWORDS)
|
||||
policies, has_more_data, has_prev_data = senlin.policy_list(
|
||||
request, filters=filters, **kwargs)
|
||||
|
||||
return {
|
||||
'items': [p.to_dict() for p in policies],
|
||||
'has_more_data': has_more_data,
|
||||
'has_prev_data': has_prev_data,
|
||||
}
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def post(self, request):
|
||||
"""Create a new Policy.
|
||||
|
||||
Returns the new Policy object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = policy_forms._populate_policy_params(
|
||||
request_param.get("name"),
|
||||
request_param.get("spec"),
|
||||
request_param.get("cooldown"),
|
||||
request_param.get("level"))
|
||||
new_policy = senlin.policy_create(request, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/policies/%s' % new_policy.id,
|
||||
new_policy.to_dict())
|
||||
|
||||
|
||||
@urls.register
|
||||
class Policy(generic.View):
|
||||
"""API for Senlin policy."""
|
||||
|
||||
url_regex = r'senlin/policies/(?P<policy_id>[^/]+)/$'
|
||||
|
||||
@rest_utils.ajax()
|
||||
def get(self, request, policy_id):
|
||||
"""Get a single policy's details with the policy id.
|
||||
|
||||
The following get parameters may be passed in the GET
|
||||
|
||||
:param policy_id: the id of the policy
|
||||
|
||||
The result is a policy object.
|
||||
"""
|
||||
policy = senlin.policy_get(request, policy_id).to_dict()
|
||||
policy["spec"] = api_utils.convert_to_yaml(policy["spec"])
|
||||
return policy
|
||||
|
||||
@rest_utils.ajax()
|
||||
def delete(self, request, policy_id):
|
||||
"""Delete a specific policy
|
||||
|
||||
DELETE http://localhost/api/senlin/policies/cc758c90-3d98-4ea1-af44-aab405c9c915 # noqa
|
||||
"""
|
||||
senlin.policy_delete(request, policy_id)
|
||||
|
||||
@rest_utils.ajax(data_required=True)
|
||||
def put(self, request, policy_id):
|
||||
"""Update a Policy.
|
||||
|
||||
Returns the Policy object on success.
|
||||
"""
|
||||
request_param = request.DATA
|
||||
params = policy_forms._populate_policy_params(
|
||||
request_param.get("name"),
|
||||
None, None, None)
|
||||
params.pop('spec')
|
||||
params.pop('cooldown')
|
||||
params.pop('level')
|
||||
updated_policy = senlin.policy_update(
|
||||
request, policy_id, **params)
|
||||
return rest_utils.CreatedResponse(
|
||||
'/api/senlin/policies/%s' % updated_policy.id,
|
||||
updated_policy.to_dict())
|
|
@ -1,506 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from horizon.utils import functions as utils
|
||||
from horizon.utils import memoized
|
||||
|
||||
from keystoneauth1.identity import generic
|
||||
from keystoneauth1 import session as ks_session
|
||||
from openstack_dashboard.api import base
|
||||
from senlin_dashboard.api import utils as api_utils
|
||||
from senlinclient.v1 import client as senlin_client
|
||||
|
||||
USER_AGENT = 'python-senlinclient'
|
||||
|
||||
|
||||
class Cluster(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'name', 'status', 'created_at', 'updated_at',
|
||||
'profile_name', 'profile_id', 'status_reason',
|
||||
'max_size', 'min_size', 'desired_capacity', 'timeout',
|
||||
'metadata']
|
||||
|
||||
|
||||
class ClusterPolicy(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'policy_name', 'policy_type', 'enabled',
|
||||
'cluster_id', 'policy_id']
|
||||
|
||||
|
||||
class Profile(base.APIResourceWrapper):
|
||||
# NOTE(Kenji-i): Generally, openstack.cluster.v1.profile.Profile
|
||||
# class has 'type'. However, it doesn't provide that attribute at
|
||||
# least in Oct 2016. Instead of this, it provides 'type_name'
|
||||
# attribute.
|
||||
_attrs = ['id', 'name', 'type', 'type_name', 'created_at',
|
||||
'updated_at', 'metadata', 'spec']
|
||||
|
||||
# Attribute mapping. All of original codes are using 'type' and
|
||||
# it's correct. Thus, this is to avoid changing these codes.
|
||||
_attrs_map = {'type': 'type_name', 'type_name': 'type'}
|
||||
|
||||
def __getattr__(self, attr):
|
||||
try:
|
||||
return super(Profile, self).__getattribute__(attr)
|
||||
except Exception:
|
||||
return super(Profile, self).__getattribute__(self._attrs_map[attr])
|
||||
|
||||
|
||||
class ProfileType(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'name']
|
||||
|
||||
|
||||
class Policy(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'name', 'type', 'spec', 'level', 'cooldown',
|
||||
'created_at', 'updated_at']
|
||||
|
||||
|
||||
class Node(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'name', 'status', 'created_at', 'updated_at',
|
||||
'profile_name', 'status_reason', 'physical_id', 'role',
|
||||
'profile_id', 'profile_url', 'cluster_id', 'metadata']
|
||||
|
||||
|
||||
class Event(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'obj_id', 'obj_name', 'generated_at', 'status',
|
||||
'status_reason', 'action']
|
||||
|
||||
|
||||
class Receiver(base.APIResourceWrapper):
|
||||
_attrs = ['id', 'name', 'type', 'cluster_id', 'action', 'created_at',
|
||||
'updated_at', 'params', 'channel']
|
||||
|
||||
|
||||
@memoized.memoized
|
||||
def senlinclient(request):
|
||||
auth = generic.Token(
|
||||
auth_url=getattr(settings, 'OPENSTACK_KEYSTONE_URL'),
|
||||
token=request.user.token.id,
|
||||
project_id=request.user.tenant_id
|
||||
)
|
||||
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
|
||||
session = ks_session.Session(auth=auth,
|
||||
verify=cacert if cacert else not insecure,
|
||||
user_agent=USER_AGENT)
|
||||
return senlin_client.Client(session=session,
|
||||
region_name=request.user.services_region)
|
||||
|
||||
|
||||
def _populate_request_size_and_page_size(request, paginate=False):
|
||||
limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
page_size = utils.get_page_size(request)
|
||||
|
||||
if paginate:
|
||||
request_size = page_size + 1
|
||||
else:
|
||||
request_size = limit
|
||||
|
||||
return page_size, request_size
|
||||
|
||||
|
||||
def cluster_list(request, sort_dir='desc', sort_key='created_at',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
filters=None):
|
||||
"""Returns all clusters."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
if reversed_order:
|
||||
sort_dir = 'desc' if sort_dir == 'asc' else 'asc'
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
clusters_iter = senlinclient(request).clusters(**params)
|
||||
|
||||
if paginate:
|
||||
clusters, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
clusters_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
clusters = list(clusters_iter)
|
||||
|
||||
return [Cluster(c) for c in clusters], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def cluster_create(request, **params):
|
||||
"""Create a cluster."""
|
||||
cluster = senlinclient(request).create_cluster(**params)
|
||||
return Cluster(cluster)
|
||||
|
||||
|
||||
def cluster_update(request, cluster_id, **params):
|
||||
"""Update a cluster"""
|
||||
cluster = senlinclient(request).get_cluster(cluster_id)
|
||||
updated = senlinclient(request).update_cluster(cluster, **params)
|
||||
return Cluster(updated)
|
||||
|
||||
|
||||
def cluster_check(request, cluster, params=None):
|
||||
"""Check a Cluster's Health Status."""
|
||||
if not params:
|
||||
params = {}
|
||||
senlinclient(request).check_cluster(cluster, **params)
|
||||
|
||||
|
||||
def cluster_recover(request, cluster, params=None):
|
||||
"""Recover a Cluster to a Healthy Status"""
|
||||
if not params:
|
||||
params = {}
|
||||
senlinclient(request).recover_cluster(cluster, **params)
|
||||
|
||||
|
||||
def cluster_scale_in(request, cluster, count=None):
|
||||
"""Scale in a Cluster"""
|
||||
senlinclient(request).cluster_scale_in(cluster, count)
|
||||
|
||||
|
||||
def cluster_scale_out(request, cluster, count=None):
|
||||
"""Scale out a Cluster"""
|
||||
senlinclient(request).cluster_scale_out(cluster, count)
|
||||
|
||||
|
||||
def cluster_resize(request, cluster, **params):
|
||||
"""Resize a Cluster"""
|
||||
senlinclient(request).cluster_resize(cluster, **params)
|
||||
|
||||
|
||||
def cluster_delete(request, cluster):
|
||||
"""Delete cluster."""
|
||||
senlinclient(request).delete_cluster(cluster)
|
||||
|
||||
|
||||
def cluster_get(request, cluster):
|
||||
"""Returns cluster."""
|
||||
cluster = senlinclient(request).get_cluster(cluster)
|
||||
return Cluster(cluster)
|
||||
|
||||
|
||||
def cluster_attach_policy(request, cluster, policy, params):
|
||||
"""Attach policy to a specific cluster"""
|
||||
return senlinclient(request).cluster_attach_policy(
|
||||
cluster, policy, **params)
|
||||
|
||||
|
||||
def cluster_detach_policy(request, cluster, policy):
|
||||
"""Detach policy from cluster."""
|
||||
senlinclient(request).cluster_detach_policy(
|
||||
cluster, policy)
|
||||
|
||||
|
||||
def cluster_policy_list(request, cluster, params):
|
||||
"""List policies from cluster."""
|
||||
policies = senlinclient(request).cluster_policies(
|
||||
cluster, **params)
|
||||
return [ClusterPolicy(p) for p in policies]
|
||||
|
||||
|
||||
def profile_list(request, sort_dir='desc', sort_key='created_at',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
filters=None):
|
||||
"""Returns all profiles."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
profiles_iter = senlinclient(request).profiles(**params)
|
||||
|
||||
if paginate:
|
||||
profiles, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
profiles_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
profiles = list(profiles_iter)
|
||||
|
||||
return [Profile(p) for p in profiles], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def profile_get(request, profile):
|
||||
"""Returns a profile."""
|
||||
profile = senlinclient(request).get_profile(profile)
|
||||
return Profile(profile)
|
||||
|
||||
|
||||
def profile_create(request, **params):
|
||||
"""Create a profile."""
|
||||
profile = senlinclient(request).create_profile(**params)
|
||||
return Profile(profile)
|
||||
|
||||
|
||||
def profile_update(request, profile_id, **params):
|
||||
"""Update a profile."""
|
||||
profile = senlinclient(request).get_profile(profile_id)
|
||||
updated_profile = senlinclient(request).update_profile(profile, **params)
|
||||
return Profile(updated_profile)
|
||||
|
||||
|
||||
def profile_delete(request, profile):
|
||||
"""Delete a profile."""
|
||||
senlinclient(request).delete_profile(profile)
|
||||
|
||||
|
||||
def policy_list(request, sort_dir='desc', sort_key='created_at',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
filters=None):
|
||||
"""Returns all policies."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
if reversed_order:
|
||||
sort_dir = 'desc' if sort_dir == 'asc' else 'asc'
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
policies_iter = senlinclient(request).policies(**params)
|
||||
|
||||
if paginate:
|
||||
policies, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
policies_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
policies = list(policies_iter)
|
||||
|
||||
return [Policy(p) for p in policies], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def policy_create(request, **params):
|
||||
"""Create a policy."""
|
||||
policy = senlinclient(request).create_policy(**params)
|
||||
return Policy(policy)
|
||||
|
||||
|
||||
def policy_update(request, policy, **params):
|
||||
"""Update a policy."""
|
||||
policy = senlinclient(request).get_policy(policy)
|
||||
updated = senlinclient(request).update_policy(policy, **params)
|
||||
return Policy(updated)
|
||||
|
||||
|
||||
def policy_delete(request, policy):
|
||||
"""Delete a policy."""
|
||||
senlinclient(request).delete_policy(policy)
|
||||
|
||||
|
||||
def policy_get(request, policy):
|
||||
"""Returns a policy."""
|
||||
policy = senlinclient(request).get_policy(policy)
|
||||
return Policy(policy)
|
||||
|
||||
|
||||
def node_list(request, sort_dir='desc', sort_key='name',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
cluster_id=None, filters=None):
|
||||
"""Returns all nodes."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
# NOTE(Liuqing): workaround for bug: 1594352
|
||||
# https://bugs.launchpad.net/senlin/+bug/1594352
|
||||
# Sometimes we failed to create node and the `created_at` attribution
|
||||
# node object will be None. The api node_list will failed if we
|
||||
# use `created_at` as the `sort_key`.
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
if reversed_order:
|
||||
sort_dir = 'desc' if sort_dir == 'asc' else 'asc'
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker,
|
||||
'cluster_id': cluster_id}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
nodes_iter = senlinclient(request).nodes(**params)
|
||||
|
||||
if paginate:
|
||||
nodes, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
nodes_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
nodes = list(nodes_iter)
|
||||
|
||||
return [Node(n) for n in nodes], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def node_create(request, **params):
|
||||
"""Create a node."""
|
||||
node = senlinclient(request).create_node(**params)
|
||||
return Node(node)
|
||||
|
||||
|
||||
def node_check(request, node, params=None):
|
||||
"""Check a node's health status."""
|
||||
if not params:
|
||||
params = {}
|
||||
senlinclient(request).check_node(node, **params)
|
||||
|
||||
|
||||
def node_recover(request, node, params=None):
|
||||
"""Recover a Node to Healthy Status"""
|
||||
if not params:
|
||||
params = {}
|
||||
senlinclient(request).recover_node(node, **params)
|
||||
|
||||
|
||||
def node_delete(request, node):
|
||||
"""Delete a node."""
|
||||
senlinclient(request).delete_node(node)
|
||||
|
||||
|
||||
def node_get(request, node):
|
||||
"""Returns a node."""
|
||||
node = senlinclient(request).get_node(node)
|
||||
return Node(node)
|
||||
|
||||
|
||||
def node_update(request, node_id, **params):
|
||||
"""Update a node"""
|
||||
node = senlinclient(request).get_node(node_id)
|
||||
updated = senlinclient(request).update_node(node, **params)
|
||||
return Node(updated)
|
||||
|
||||
|
||||
def event_list(request, sort_dir='desc', sort_key='timestamp',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
filters=None):
|
||||
"""Returns all events."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
if reversed_order:
|
||||
sort_dir = 'desc' if sort_dir == 'asc' else 'asc'
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
events_iter = senlinclient(request).events(**params)
|
||||
|
||||
if paginate:
|
||||
events, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
events_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
events = list(events_iter)
|
||||
|
||||
return [Event(e) for e in events], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def receiver_list(request, sort_dir='desc', sort_key='created_at',
|
||||
marker=None, paginate=False, reversed_order=False,
|
||||
filters=None):
|
||||
"""Returns all receivers."""
|
||||
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
page_size, request_size = _populate_request_size_and_page_size(
|
||||
request, paginate)
|
||||
|
||||
if not filters:
|
||||
filters = {}
|
||||
|
||||
if reversed_order:
|
||||
sort_dir = 'desc' if sort_dir == 'asc' else 'asc'
|
||||
|
||||
params = {
|
||||
'sort': '%s:%s' % (sort_key, sort_dir),
|
||||
'limit': request_size,
|
||||
'marker': marker}
|
||||
|
||||
params.update(filters)
|
||||
|
||||
receivers_iter = senlinclient(request).receivers(**params)
|
||||
|
||||
if paginate:
|
||||
receivers, has_more_data, has_prev_data = api_utils.update_pagination(
|
||||
receivers_iter, request_size, page_size, marker, reversed_order)
|
||||
else:
|
||||
receivers = list(receivers_iter)
|
||||
|
||||
return [Receiver(r) for r in receivers], has_more_data, has_prev_data
|
||||
|
||||
|
||||
def receiver_create(request, **params):
|
||||
"""Create a receiver"""
|
||||
receiver = senlinclient(request).create_receiver(**params)
|
||||
return Receiver(receiver)
|
||||
|
||||
|
||||
def receiver_delete(request, receiver):
|
||||
"""Delete a receiver."""
|
||||
senlinclient(request).delete_receiver(receiver)
|
||||
|
||||
|
||||
def receiver_update(request, receiver_id, **params):
|
||||
"""Update a receiver"""
|
||||
receiver = senlinclient(request).get_receiver(receiver_id)
|
||||
updated = senlinclient(request).update_receiver(receiver, **params)
|
||||
return Receiver(updated)
|
||||
|
||||
|
||||
def receiver_get(request, receiver):
|
||||
"""Returns a receiver."""
|
||||
receiver = senlinclient(request).get_receiver(receiver)
|
||||
return Receiver(receiver)
|
|
@ -1,64 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import itertools
|
||||
import yaml
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def update_pagination(entities, request_size, page_size, marker,
|
||||
reversed_order):
|
||||
has_prev_data = False
|
||||
has_more_data = False
|
||||
|
||||
entities = list(itertools.islice(entities, request_size))
|
||||
# first and middle page condition
|
||||
if len(entities) > page_size:
|
||||
entities.pop(-1)
|
||||
has_more_data = True
|
||||
# middle page condition
|
||||
if marker is not None:
|
||||
has_prev_data = True
|
||||
# first page condition when reached via prev back
|
||||
elif reversed_order and marker is not None:
|
||||
has_more_data = True
|
||||
# last page condition
|
||||
elif marker is not None:
|
||||
has_prev_data = True
|
||||
|
||||
# restore the original ordering here
|
||||
if reversed_order:
|
||||
entities.reverse()
|
||||
|
||||
return entities, has_more_data, has_prev_data
|
||||
|
||||
|
||||
def convert_to_yaml(data, default_flow_style=False):
|
||||
if not data:
|
||||
return ''
|
||||
try:
|
||||
return yaml.safe_dump(data, default_flow_style=default_flow_style)
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
|
||||
def load_yaml(data):
|
||||
if not data:
|
||||
loaded_data = {}
|
||||
else:
|
||||
try:
|
||||
loaded_data = yaml.safe_load(data)
|
||||
except Exception as ex:
|
||||
raise Exception(_('The specified input is not a valid '
|
||||
'YAML format: %s') % ex)
|
||||
return loaded_data
|
|
@ -1,131 +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 yaml
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from horizon.utils.memoized import memoized # noqa: F401
|
||||
|
||||
from senlin_dashboard.api import senlin
|
||||
|
||||
|
||||
INDEX_URL = "horizon:cluster:clusters:index"
|
||||
|
||||
|
||||
class CreateForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length=255, label=_("Cluster Name"))
|
||||
profile_id = forms.ThemableChoiceField(label=_("Profile"))
|
||||
min_size = forms.IntegerField(
|
||||
label=_("Min Size"),
|
||||
required=False,
|
||||
initial=0,
|
||||
help_text=_("Min size of the cluster. Default to 0."))
|
||||
max_size = forms.IntegerField(
|
||||
label=_("Max Size"),
|
||||
required=False,
|
||||
initial=-1,
|
||||
help_text=_("Max size of the cluster. Default to -1, "
|
||||
"means unlimited."))
|
||||
desired_capacity = forms.IntegerField(
|
||||
label=_("Desired Capacity"),
|
||||
initial=0,
|
||||
help_text=_("Desired capacity of the cluster. Default to 0."))
|
||||
# Hide the parent field
|
||||
parent = forms.ThemableChoiceField(
|
||||
label=_("Parent Cluster"),
|
||||
required=False,
|
||||
widget=forms.HiddenInput())
|
||||
timeout = forms.IntegerField(
|
||||
label=_("Timeout"),
|
||||
required=False,
|
||||
help_text=_("Cluster creation timeout in seconds."))
|
||||
metadata = forms.CharField(
|
||||
max_length=255,
|
||||
label=_("Metadata"),
|
||||
required=False,
|
||||
help_text=_("YAML formated metadata"),
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateForm, self).__init__(request, *args, **kwargs)
|
||||
profiles = senlin.profile_list(request)[0]
|
||||
self.fields['profile_id'].choices = (
|
||||
[("", _("Select Profile"))] + [(profile.id, profile.name)
|
||||
for profile in profiles])
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
# As we hide the parent field, use None here
|
||||
data['parent'] = None
|
||||
|
||||
if not data['metadata']:
|
||||
metadata = {}
|
||||
else:
|
||||
try:
|
||||
metadata = yaml.safe_load(data['metadata'])
|
||||
except Exception as ex:
|
||||
raise Exception(_('The specified metadata is not a valid '
|
||||
'YAML format: %s') % ex)
|
||||
data['metadata'] = metadata
|
||||
|
||||
cluster = senlin.cluster_create(request, **data)
|
||||
msg = _('Creating cluster "%s" successfully') % data['name']
|
||||
messages.success(request, msg)
|
||||
return cluster
|
||||
except Exception:
|
||||
redirect = reverse(INDEX_URL)
|
||||
exceptions.handle(request,
|
||||
_("Unable to create cluster."),
|
||||
redirect=redirect)
|
||||
|
||||
|
||||
class ManagePoliciesForm(forms.SelfHandlingForm):
|
||||
cluster_id = forms.CharField(widget=forms.HiddenInput())
|
||||
policies = forms.ThemableChoiceField(label=_("Policies"))
|
||||
enabled = forms.BooleanField(
|
||||
label=_("Enabled"),
|
||||
initial=True,
|
||||
required=False,
|
||||
help_text=_("Whether the policy should be enabled once attached. "
|
||||
"Default to enabled."))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(ManagePoliciesForm, self).__init__(request, *args, **kwargs)
|
||||
cluster_policies = senlin.cluster_policy_list(
|
||||
self.request, kwargs['initial']['cluster_id'], {})
|
||||
cluster_policies_ids = [policy.id for policy in cluster_policies]
|
||||
policies = senlin.policy_list(self.request)[0]
|
||||
available_policies = [(policy.id, policy.name) for policy in policies
|
||||
if policy.id not in cluster_policies_ids]
|
||||
self.fields['policies'].choices = (
|
||||
[("", _("Select Policy"))] + available_policies)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
params = {"enabled": data.pop('enabled')}
|
||||
attach = senlin.cluster_attach_policy(
|
||||
request, data["cluster_id"], data['policies'], params)
|
||||
msg = _('Attaching policy %(policy)s to cluster '
|
||||
'%(cluster)s.') % {"policy": data['policies'],
|
||||
"cluster": data['cluster_id']}
|
||||
messages.success(request, msg)
|
||||
return attach
|
||||
except Exception:
|
||||
redirect = reverse(INDEX_URL)
|
||||
exceptions.handle(request,
|
||||
_("Unable to attach policy."),
|
||||
redirect=redirect)
|
|
@ -1,25 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from senlin_dashboard.cluster import dashboard
|
||||
|
||||
|
||||
class Clusters(horizon.Panel):
|
||||
name = _("Clusters")
|
||||
slug = 'clusters'
|
||||
|
||||
|
||||
dashboard.Cluster.register(Clusters)
|
|
@ -1,273 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.template import defaultfilters
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
from django.utils.translation import pgettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon.utils import filters
|
||||
|
||||
from senlin_dashboard import api
|
||||
from senlin_dashboard import exceptions
|
||||
|
||||
|
||||
class CreateCluster(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Cluster")
|
||||
url = "horizon:cluster:clusters:create"
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
icon = "plus"
|
||||
ajax = True
|
||||
|
||||
|
||||
class ManagePolicies(tables.LinkAction):
|
||||
name = "manage_policies"
|
||||
verbose_name = _("Manage Policies")
|
||||
url = "horizon:cluster:clusters:manage_policies"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
|
||||
class CheckCluster(tables.BatchAction):
|
||||
name = "check"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Check Cluster",
|
||||
u"Check Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Checked Cluster",
|
||||
u"Checked Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.senlin.cluster_check(request, obj_id)
|
||||
|
||||
|
||||
class RecoverCluster(tables.BatchAction):
|
||||
name = "recover"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Recover Cluster",
|
||||
u"Recover Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Recovered Cluster",
|
||||
u"Recovered Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.senlin.cluster_recover(request, obj_id)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
if datum:
|
||||
return datum.status == "ERROR" or datum.status == "WARNING"
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def get_profile_link(cluster):
|
||||
return reverse_lazy('horizon:cluster:profiles:detail',
|
||||
args=[cluster.profile_id])
|
||||
|
||||
|
||||
def get_updated_time(object):
|
||||
return object.updated_at or None
|
||||
|
||||
|
||||
class DeleteCluster(tables.DeleteAction):
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Delete Cluster",
|
||||
u"Delete Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Scheduled deletion of Cluster",
|
||||
u"Scheduled deletion of Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
api.senlin.cluster_delete(request, obj_id)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
if datum:
|
||||
return datum.status != "DELETING"
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, cluster_id):
|
||||
try:
|
||||
cluster = api.senlin.cluster_get(request, cluster_id)
|
||||
return cluster
|
||||
except exceptions.ResourceNotFound:
|
||||
raise exceptions.NOT_FOUND
|
||||
|
||||
|
||||
class ClusterFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = (
|
||||
("name", _("Cluster Name ="), True),
|
||||
("status", _("Status ="), True),
|
||||
("profile_name", _("Profile Name ="), True),
|
||||
)
|
||||
|
||||
|
||||
class ClustersTable(tables.DataTable):
|
||||
STATUS_CHOICES = (
|
||||
("INIT", None),
|
||||
("ACTIVE", True),
|
||||
("ERROR", False),
|
||||
("CRITICAL", False),
|
||||
("WARNING", False),
|
||||
("CREATING", None),
|
||||
("UPDATING", None),
|
||||
("DELETING", None),
|
||||
("RESIZING", None),
|
||||
("CHECKING", None),
|
||||
("RECOVERING", None),
|
||||
)
|
||||
|
||||
STATUS_DISPLAY_CHOICES = (
|
||||
("INIT", pgettext_lazy("Current status of a Cluster", u"INIT")),
|
||||
("ACTIVE", pgettext_lazy("Current status of a Cluster", u"ACTIVE")),
|
||||
("ERROR", pgettext_lazy("Current status of a Cluster", u"ERROR")),
|
||||
("CRITICAL", pgettext_lazy("Current status of a Cluster",
|
||||
u"CRITICAL")),
|
||||
("WARNING", pgettext_lazy("Current status of a Cluster", u"WARNING")),
|
||||
("CREATING", pgettext_lazy("Current status of a Cluster",
|
||||
u"CREATING")),
|
||||
("UPDATING", pgettext_lazy("Current status of a Cluster",
|
||||
u"UPDATING")),
|
||||
("DELETING", pgettext_lazy("Current status of a Cluster",
|
||||
u"DELETING")),
|
||||
("RESIZING", pgettext_lazy("Current status of a Cluster",
|
||||
u"RESIZING")),
|
||||
("CHECKING", pgettext_lazy("Current status of a Cluster",
|
||||
u"CHECKING")),
|
||||
("RECOVERING", pgettext_lazy("Current status of a Cluster",
|
||||
u"RECOVERING")),
|
||||
)
|
||||
|
||||
name = tables.WrappingColumn(
|
||||
"name",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:cluster:clusters:detail")
|
||||
status = tables.Column("status",
|
||||
verbose_name=_("Status"),
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES,
|
||||
display_choices=STATUS_DISPLAY_CHOICES)
|
||||
status_reason = tables.Column("status_reason",
|
||||
verbose_name=_("Status Reason"))
|
||||
profile_name = tables.Column("profile_name",
|
||||
link=get_profile_link,
|
||||
verbose_name=_("Profile Name"))
|
||||
created = tables.Column(
|
||||
"created_at",
|
||||
verbose_name=_("Created"),
|
||||
filters=(filters.parse_isotime,)
|
||||
)
|
||||
updated = tables.Column(
|
||||
get_updated_time,
|
||||
verbose_name=_("Updated"),
|
||||
filters=(filters.parse_isotime,)
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
name = "clusters"
|
||||
row_class = UpdateRow
|
||||
verbose_name = _("Clusters")
|
||||
status_columns = ["status"]
|
||||
table_actions_menu = (CheckCluster, RecoverCluster)
|
||||
table_actions = (ClusterFilterAction,
|
||||
CreateCluster,
|
||||
DeleteCluster,)
|
||||
row_actions = (ManagePolicies,
|
||||
CheckCluster,
|
||||
RecoverCluster,
|
||||
DeleteCluster,)
|
||||
|
||||
|
||||
class DetachPolicy(tables.BatchAction):
|
||||
name = "detach"
|
||||
classes = ('btn-danger', 'btn-detach')
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Detach Policy",
|
||||
u"Detach Policies",
|
||||
count
|
||||
)
|
||||
|
||||
# This action is asynchronous.
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Detaching Policy",
|
||||
u"Detaching Policies",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
policy_obj = self.table.get_object_by_id(obj_id)
|
||||
api.senlin.cluster_detach_policy(request,
|
||||
policy_obj.cluster_id,
|
||||
obj_id)
|
||||
|
||||
def get_success_url(self, request):
|
||||
return reverse('horizon:cluster:clusters:index')
|
||||
|
||||
|
||||
class AttachedPoliciesTable(tables.DataTable):
|
||||
policy_name = tables.Column("policy_name", verbose_name=_("Name"))
|
||||
policy_type = tables.Column("policy_type", verbose_name=_("Type"))
|
||||
enabled = tables.Column(
|
||||
"enabled",
|
||||
verbose_name=_("Enabled"),
|
||||
filters=(defaultfilters.yesno, defaultfilters.capfirst))
|
||||
|
||||
class Meta(object):
|
||||
name = "attached_policies"
|
||||
hidden_title = False
|
||||
verbose_name = _("Attached Policies")
|
||||
table_actions = (DetachPolicy,)
|
||||
row_actions = (DetachPolicy,)
|
|
@ -1,87 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from senlin_dashboard.api import senlin
|
||||
from senlin_dashboard.cluster.nodes import event_tables
|
||||
from senlin_dashboard.cluster.nodes import tables as node_table
|
||||
from senlin_dashboard.cluster.nodes import tabs as node_tab
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = ("cluster/clusters/_detail_overview.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"cluster": self.tab_group.kwargs['cluster']}
|
||||
|
||||
|
||||
class EventTab(node_tab.EventTab):
|
||||
|
||||
def get_event_data(self):
|
||||
|
||||
prev_marker = self.request.GET.get(
|
||||
event_tables.EventsTable._meta.prev_pagination_param, None)
|
||||
|
||||
if prev_marker is not None:
|
||||
marker = prev_marker
|
||||
else:
|
||||
marker = self.request.GET.get(
|
||||
event_tables.EventsTable._meta.pagination_param, None)
|
||||
reversed_order = prev_marker is not None
|
||||
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
try:
|
||||
filters = {"obj_id": cluster_id}
|
||||
events, self._more, self._prev = senlin.event_list(
|
||||
self.request,
|
||||
marker=marker,
|
||||
paginate=True,
|
||||
reversed_order=reversed_order,
|
||||
filters=filters)
|
||||
except Exception:
|
||||
self._prev = self._more = False
|
||||
events = []
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve node event list.'))
|
||||
return events
|
||||
|
||||
|
||||
class NodesTab(tabs.TableTab):
|
||||
name = _("Nodes")
|
||||
slug = "nodes"
|
||||
table_classes = (node_table.NodesTable,)
|
||||
template_name = "cluster/clusters/_detail_nodes.html"
|
||||
preload = False
|
||||
|
||||
def get_nodes_data(self):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
try:
|
||||
cluster_nodes, self._more, self._prev = senlin.node_list(
|
||||
self.request,
|
||||
cluster_id=cluster_id)
|
||||
except Exception:
|
||||
cluster_nodes = []
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve nodes from cluster.'))
|
||||
return cluster_nodes
|
||||
|
||||
|
||||
class ClusterDetailTabs(tabs.TabGroup):
|
||||
slug = "cluster_details"
|
||||
sticky = True
|
||||
tabs = (OverviewTab, EventTab, NodesTab)
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Cluster is the collection of cloud objects, e.g. Nova servers, Heat stacks, Cinder volumes, etc." %}</p>
|
||||
{% endblock %}
|
|
@ -1,5 +0,0 @@
|
|||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
|
@ -1,50 +0,0 @@
|
|||
{% load i18n nbsp %}
|
||||
|
||||
<div class="detail">
|
||||
<h4>{% trans "Information" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ cluster.name }}</dd>
|
||||
<dt>{% trans "Profile" %}</dt>
|
||||
<dd><a href="{{ cluster.profile_url }}">{{ cluster.profile_name }}</a></dd>
|
||||
{% if cluster.policies %}
|
||||
<dt>{% trans "Policies" %}</dt>
|
||||
{% for policy in cluster.policies %}
|
||||
<dd>
|
||||
<a href="{% url 'horizon:cluster:policies:detail' policy.policy_id %}">{{ policy.policy_name }}</a>
|
||||
</dd>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ cluster.id }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ cluster.status }}</dd>
|
||||
{% if cluster.status_reason %}
|
||||
<dt>{% trans "Status Reason" %}</dt>
|
||||
<dd>{{ cluster.status_reason }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Max Size" %}</dt>
|
||||
<dd>{{ cluster.max_size }}</dd>
|
||||
<dt>{% trans "Min Size" %}</dt>
|
||||
<dd>{{ cluster.min_size }}</dd>
|
||||
<dt>{% trans "Desired Capacity" %}</dt>
|
||||
<dd>{{ cluster.desired_capacity }}</dd>
|
||||
<dt>{% trans "Timeout" %}</dt>
|
||||
<dd>{{ cluster.timeout }}</dd>
|
||||
<dt>{% trans "Created" context "Created time" %}</dt>
|
||||
<dd>{{ cluster.created_at|parse_isotime }}</dd>
|
||||
{% if cluster.updated_at %}
|
||||
<dt>{% trans "Updated" context "Updated time" %}</dt>
|
||||
<dd>{{ cluster.updated_at|parse_isotime }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
{% if cluster.metadat.vars %}
|
||||
<h4>{% trans "Metadata" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dd>{{ cluster.metadata|force_escape|nbsp|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
</div>
|
|
@ -1,9 +0,0 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body %}
|
||||
<h3>{% trans "Available Policies" %}</h3>
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Cluster" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'cluster/clusters/_create.html' %}
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Clusters" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{{ table.render }}
|
||||
{% endblock %}
|
|
@ -1,7 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Manage Policies" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'cluster/clusters/_manage_policies.html' %}
|
||||
{% endblock %}
|
|
@ -1,175 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from senlin_dashboard import api
|
||||
from senlin_dashboard.test import helpers as test
|
||||
|
||||
CLUSTER_INDEX_URL = reverse('horizon:cluster:clusters:index')
|
||||
CLUSTER_CREATE_URL = reverse('horizon:cluster:clusters:create')
|
||||
CLUSTER_DETAIL_URL = reverse(
|
||||
'horizon:cluster:clusters:detail',
|
||||
args=[u'123456'])
|
||||
CLUSTER_MANAGE_POLICIES_URL = reverse(
|
||||
'horizon:cluster:clusters:manage_policies',
|
||||
args=[u'123456'])
|
||||
|
||||
|
||||
class ClustersTest(test.TestCase):
|
||||
|
||||
@test.create_mocks({api.senlin: ('cluster_list',)})
|
||||
def test_index(self):
|
||||
clusters = self.clusters.list()
|
||||
self.mock_cluster_list.return_value = \
|
||||
(clusters, False, False)
|
||||
|
||||
res = self.client.get(CLUSTER_INDEX_URL)
|
||||
self.assertContains(res, '<h1>Clusters</h1>')
|
||||
self.assertTemplateUsed(res, 'cluster/clusters/index.html')
|
||||
self.assertEqual(1, len(clusters))
|
||||
self.mock_cluster_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), filters={}, marker=None,
|
||||
paginate=True, reversed_order=False)
|
||||
|
||||
@test.create_mocks({api.senlin: ('cluster_list',)})
|
||||
def test_index_cluster_list_exception(self):
|
||||
self.mock_cluster_list.side_effect = \
|
||||
self.exceptions.senlin
|
||||
|
||||
res = self.client.get(CLUSTER_INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'cluster/clusters/index.html')
|
||||
self.assertEqual(0, len(res.context['clusters_table'].data))
|
||||
self.assertMessageCount(res, error=1)
|
||||
self.mock_cluster_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), filters={}, marker=None,
|
||||
paginate=True, reversed_order=False)
|
||||
|
||||
@test.create_mocks({api.senlin: ('cluster_list',)})
|
||||
def test_index_no_cluster(self):
|
||||
self.mock_cluster_list.return_value = \
|
||||
([], False, False)
|
||||
|
||||
res = self.client.get(CLUSTER_INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'cluster/clusters/index.html')
|
||||
self.assertContains(res, 'No items to display')
|
||||
self.assertEqual(0, len(res.context['clusters_table'].data))
|
||||
self.mock_cluster_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), filters={}, marker=None,
|
||||
paginate=True, reversed_order=False)
|
||||
|
||||
@test.create_mocks({api.senlin: ('cluster_create',
|
||||
'profile_list',)})
|
||||
def test_create_node(self):
|
||||
profiles = self.profiles.list()
|
||||
|
||||
formdata = {
|
||||
'name': 'test-cluster',
|
||||
'profile_id': '123456',
|
||||
'min_size': 0,
|
||||
'max_size': -1,
|
||||
'desired_capacity': 1,
|
||||
'parent': '',
|
||||
'timeout': 200,
|
||||
'metadata': ''
|
||||
}
|
||||
|
||||
opts = formdata
|
||||
|
||||
self.mock_profile_list.return_value = \
|
||||
(profiles, False, False)
|
||||
self.mock_cluster_create.return_value = opts
|
||||
|
||||
res = self.client.post(CLUSTER_CREATE_URL, formdata)
|
||||
self.assertNoFormErrors(res)
|
||||
|
||||
@test.create_mocks({api.senlin: ('cluster_get',
|
||||
'cluster_policy_list')})
|
||||
def test_cluster_detail(self):
|
||||
policies = self.policies.list()
|
||||
cluster = self.clusters.list()[0]
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
self.mock_cluster_policy_list.return_value = policies
|
||||
|
||||
res = self.client.get(CLUSTER_DETAIL_URL)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
|
||||
self.assertContains(res, 'test-cluster')
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456')
|
||||
self.mock_cluster_policy_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456', {})
|
||||
|
||||
@test.create_mocks({api.senlin: ('event_list',
|
||||
'cluster_get',
|
||||
'cluster_policy_list')})
|
||||
def test_cluster_event(self):
|
||||
cluster = self.clusters.list()[0]
|
||||
policies = self.policies.list()
|
||||
events = self.events.list()
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
self.mock_event_list.return_value = events
|
||||
self.mock_cluster_policy_list.return_value = policies
|
||||
|
||||
res = self.client.get(
|
||||
CLUSTER_DETAIL_URL + '?tab=cluster_details__event')
|
||||
self.assertTemplateUsed(res, 'cluster/nodes/_detail_event.html')
|
||||
self.assertContains(res, '123456')
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456')
|
||||
self.mock_event_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), filters={'obj_id': u'123456'},
|
||||
marker=None, paginate=True, reversed_order=False)
|
||||
self.mock_cluster_policy_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456', {})
|
||||
|
||||
@test.create_mocks({api.senlin: ('node_list',
|
||||
'cluster_get',
|
||||
'cluster_policy_list')})
|
||||
def test_cluster_nodes(self):
|
||||
policies = self.policies.list()
|
||||
cluster = self.clusters.list()[0]
|
||||
nodes = self.nodes.list()
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
self.mock_node_list.return_value = nodes
|
||||
self.mock_cluster_policy_list.return_value = policies
|
||||
|
||||
res = self.client.get(
|
||||
CLUSTER_DETAIL_URL + '?tab=cluster_details__nodes')
|
||||
self.assertTemplateUsed(res, 'cluster/clusters/_detail_nodes.html')
|
||||
self.assertContains(res, '123456')
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456')
|
||||
self.mock_node_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), cluster_id=u'123456')
|
||||
self.mock_cluster_policy_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), u'123456', {})
|
||||
|
||||
@test.create_mocks({api.senlin: ('policy_list',
|
||||
'cluster_policy_list')})
|
||||
def test_cluster_mamage_policies_index(self):
|
||||
policies = self.policies.list()
|
||||
cluster_policies = policies[:1]
|
||||
self.mock_policy_list.return_value = \
|
||||
[policies, False, False]
|
||||
self.mock_cluster_policy_list.side_effect = \
|
||||
[cluster_policies, cluster_policies]
|
||||
|
||||
res = self.client.get(CLUSTER_MANAGE_POLICIES_URL)
|
||||
self.assertTemplateUsed(res, 'cluster/clusters/manage_policies.html')
|
||||
self.assertContains(res, 'test-policy02')
|
||||
self.mock_policy_list.assert_called_once_with(
|
||||
test.IsHttpRequest())
|
||||
self.mock_cluster_policy_list.assert_has_calls([
|
||||
mock.call(test.IsHttpRequest(), u'123456', {}),
|
||||
mock.call(test.IsHttpRequest(), u'123456', {})])
|
|
@ -1,37 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import re_path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon.browsers.views import AngularIndexView
|
||||
|
||||
from senlin_dashboard.cluster.clusters import views as legacyViews
|
||||
|
||||
|
||||
if settings.ANGULAR_FEATURES.get('clusters_panel', True):
|
||||
title = _("Clusters")
|
||||
urlpatterns = [
|
||||
re_path('', AngularIndexView.as_view(title=title), name='index'),
|
||||
]
|
||||
else:
|
||||
urlpatterns = [
|
||||
re_path(r'^$', legacyViews.IndexView.as_view(), name='index'),
|
||||
re_path(r'^create/$', legacyViews.CreateView.as_view(),
|
||||
name='create'),
|
||||
re_path(r'^(?P<cluster_id>[^/]+)/$',
|
||||
legacyViews.DetailView.as_view(), name='detail'),
|
||||
re_path(r'^(?P<cluster_id>[^/]+)/manage_policies/$',
|
||||
legacyViews.ManagePoliciesView.as_view(),
|
||||
name='manage_policies'),
|
||||
]
|
|
@ -1,160 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.urls import reverse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from senlin_dashboard.api import senlin
|
||||
from senlin_dashboard.cluster.clusters import forms as clusters_forms
|
||||
from senlin_dashboard.cluster.clusters.tables import AttachedPoliciesTable
|
||||
from senlin_dashboard.cluster.clusters.tables import ClustersTable
|
||||
from senlin_dashboard.cluster.clusters import tabs as clusters_tabs
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = ClustersTable
|
||||
template_name = 'cluster/clusters/index.html'
|
||||
page_title = _("Clusters")
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return getattr(self, "_prev", False)
|
||||
|
||||
def has_more_data(self, table):
|
||||
return getattr(self, "_more", False)
|
||||
|
||||
def get_data(self):
|
||||
filters = self.get_filters()
|
||||
|
||||
prev_marker = self.request.GET.get(
|
||||
ClustersTable._meta.prev_pagination_param, None)
|
||||
|
||||
if prev_marker is not None:
|
||||
marker = prev_marker
|
||||
else:
|
||||
marker = self.request.GET.get(
|
||||
ClustersTable._meta.pagination_param, None)
|
||||
reversed_order = prev_marker is not None
|
||||
try:
|
||||
clusters, self._more, self._prev = senlin.cluster_list(
|
||||
self.request,
|
||||
marker=marker,
|
||||
paginate=True,
|
||||
reversed_order=reversed_order,
|
||||
filters=filters)
|
||||
except Exception:
|
||||
self._prev = self._more = False
|
||||
clusters = []
|
||||
msg = _('Unable to retrieve clusters.')
|
||||
exceptions.handle(self.request, msg)
|
||||
return clusters
|
||||
|
||||
|
||||
class CreateView(forms.ModalFormView):
|
||||
template_name = 'cluster/clusters/create.html'
|
||||
form_class = clusters_forms.CreateForm
|
||||
submit_url = reverse_lazy("horizon:cluster:clusters:create")
|
||||
success_url = reverse_lazy(clusters_forms.INDEX_URL)
|
||||
|
||||
text = _("Create Cluster")
|
||||
modal_header = text
|
||||
submit_label = text
|
||||
page_title = text
|
||||
|
||||
|
||||
class DetailView(tabs.TabView):
|
||||
tab_group_class = clusters_tabs.ClusterDetailTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
page_title = "{{ cluster.name }}"
|
||||
profile_url = 'horizon:cluster:profiles:detail'
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
# Get cluster information
|
||||
cluster_id = self.kwargs["cluster_id"]
|
||||
cluster = senlin.cluster_get(self.request, cluster_id)
|
||||
cluster.profile_url = reverse_lazy(self.profile_url,
|
||||
args=[cluster.profile_id])
|
||||
except Exception:
|
||||
msg = _("Unable to retrieve cluster.")
|
||||
url = reverse_lazy(clusters_forms.INDEX_URL)
|
||||
exceptions.handle(self.request, msg, redirect=url)
|
||||
return cluster
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
table = ClustersTable(self.request)
|
||||
cluster = self.get_object()
|
||||
policies = senlin.cluster_policy_list(
|
||||
self.request, self.kwargs['cluster_id'], {})
|
||||
cluster.policies = policies
|
||||
context["actions"] = table.render_row_actions(cluster)
|
||||
context["cluster"] = cluster
|
||||
return context
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
cluster = self.get_object()
|
||||
return self.tab_group_class(request, cluster=cluster, **kwargs)
|
||||
|
||||
|
||||
class ManagePoliciesView(tables.DataTableView, forms.ModalFormView):
|
||||
table_class = AttachedPoliciesTable
|
||||
form_class = clusters_forms.ManagePoliciesForm
|
||||
template_name = 'cluster/clusters/manage_policies.html'
|
||||
submit_url = "horizon:cluster:clusters:manage_policies"
|
||||
success_url = reverse_lazy("horizon:cluster:clusters:index")
|
||||
|
||||
text = _("Manage Policies")
|
||||
modal_header = text
|
||||
submit_label = text
|
||||
page_title = text
|
||||
|
||||
def get_data(self):
|
||||
policies = senlin.cluster_policy_list(
|
||||
self.request, self.kwargs['cluster_id'], {})
|
||||
return policies
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ManagePoliciesView, self).get_context_data(**kwargs)
|
||||
args = (self.kwargs['cluster_id'],)
|
||||
context['submit_url'] = reverse(self.submit_url, args=args)
|
||||
context['form'] = self.get_form()
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
return {'cluster_id': self.kwargs['cluster_id']}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
# Table action handling
|
||||
handled = self.construct_tables()
|
||||
if handled:
|
||||
return handled
|
||||
return self.render_to_response(self.get_context_data(**kwargs))
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_form(self, **kwargs):
|
||||
form_class = kwargs.get('form_class', self.get_form_class())
|
||||
return super(ManagePoliciesView, self).get_form(form_class)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.get(request, *args, **kwargs)
|
|
@ -1,29 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Cluster(horizon.Dashboard):
|
||||
name = _("Cluster")
|
||||
slug = "cluster"
|
||||
panels = ('profiles',
|
||||
'nodes',
|
||||
'clusters',
|
||||
'policies',
|
||||
'receivers')
|
||||
default_panel = 'clusters'
|
||||
|
||||
|
||||
horizon.register(Cluster)
|
|
@ -1,62 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import pgettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon.utils import filters
|
||||
|
||||
|
||||
class EventsTable(tables.DataTable):
|
||||
STATUS_CHOICES = (
|
||||
("INIT", None),
|
||||
("ACTIVE", True),
|
||||
("ERROR", False),
|
||||
("DELETED", False),
|
||||
("WARNING", None),
|
||||
("CREATING", None),
|
||||
("UPDATING", None),
|
||||
("DELETING", None),
|
||||
)
|
||||
|
||||
STATUS_DISPLAY_CHOICES = (
|
||||
("INIT", pgettext_lazy("Current status of the event", u"INIT")),
|
||||
("ACTIVE", pgettext_lazy("Current status of the event", u"ACTIVE")),
|
||||
("ERROR", pgettext_lazy("Current status of the event", u"ERROR")),
|
||||
("DELETED", pgettext_lazy("Current status of the event", u"DELETED")),
|
||||
("WARNING", pgettext_lazy("Current status of the event", u"WARNING")),
|
||||
("CREATING",
|
||||
pgettext_lazy("Current status of the event", u"CREATING")),
|
||||
("UPDATING",
|
||||
pgettext_lazy("Current status of the event", u"UPDATING")),
|
||||
("DELETING",
|
||||
pgettext_lazy("Current status of the event", u"DELETING")),
|
||||
)
|
||||
|
||||
obj_id = tables.Column("obj_id", verbose_name=_("Object ID"))
|
||||
obj_name = tables.Column("obj_name", verbose_name=_("Object Name"))
|
||||
status = tables.Column("status",
|
||||
verbose_name=_("Status"),
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES,
|
||||
display_choices=STATUS_DISPLAY_CHOICES)
|
||||
status_reason = tables.Column("status_reason",
|
||||
verbose_name=_("Status Reason"))
|
||||
action = tables.Column("action", verbose_name=_("Action"))
|
||||
generated_at = tables.Column("generated_at",
|
||||
verbose_name=_("Generated At"),
|
||||
filters=(filters.parse_isotime,))
|
||||
|
||||
class Meta(object):
|
||||
name = "event"
|
||||
verbose_name = _("Event")
|
|
@ -1,140 +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 yaml
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from horizon.utils.memoized import memoized # noqa: F401
|
||||
|
||||
from senlin_dashboard.api import senlin
|
||||
|
||||
|
||||
def _populate_node_params(name, profile_id, cluster_id, role, metadata):
|
||||
if not metadata:
|
||||
metadata_dict = {}
|
||||
else:
|
||||
try:
|
||||
metadata_dict = yaml.safe_load(metadata)
|
||||
except Exception as ex:
|
||||
raise Exception(_('The specified metadata is not a valid '
|
||||
'YAML: %s') % ex)
|
||||
params = {"name": name,
|
||||
"profile_id": profile_id,
|
||||
"cluster_id": cluster_id,
|
||||
"role": role,
|
||||
"metadata": metadata_dict}
|
||||
|
||||
return params
|
||||
|
||||
|
||||
class CreateForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length=255, label=_("Node Name"))
|
||||
profile_id = forms.ThemableChoiceField(
|
||||
label=_("Profile"),
|
||||
help_text=_("Profile used for this node."))
|
||||
cluster_id = forms.ThemableChoiceField(
|
||||
label=_("Cluster"),
|
||||
required=False,
|
||||
help_text=_("Cluster for this node."))
|
||||
role = forms.CharField(
|
||||
max_length=255,
|
||||
label=_("Role"),
|
||||
required=False,
|
||||
help_text=_("Role for this node in the specific cluster."))
|
||||
metadata = forms.CharField(
|
||||
label=_("Metadata"),
|
||||
required=False,
|
||||
help_text=_("YAML formatted metadata."),
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateForm, self).__init__(request, *args, **kwargs)
|
||||
profiles = senlin.profile_list(request)[0]
|
||||
self.fields['profile_id'].choices = (
|
||||
[("", _("Select Profile"))] + [(profile.id, profile.name)
|
||||
for profile in profiles])
|
||||
|
||||
clusters = senlin.cluster_list(request)[0]
|
||||
self.fields['cluster_id'].choices = (
|
||||
[("", _("Select Cluster"))] + [(cluster.id, cluster.name)
|
||||
for cluster in clusters])
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
params = _populate_node_params(data['name'],
|
||||
data['profile_id'],
|
||||
data['cluster_id'],
|
||||
data['role'],
|
||||
data['metadata'])
|
||||
|
||||
node = senlin.node_create(request, **params)
|
||||
msg = _('Creating node "%s" successfully') % data['name']
|
||||
messages.info(request, msg)
|
||||
return node
|
||||
except Exception:
|
||||
redirect = reverse("horizon:cluster:nodes:index")
|
||||
exceptions.handle(request,
|
||||
_("Unable to create node."),
|
||||
redirect=redirect)
|
||||
|
||||
|
||||
class UpdateNodeForm(forms.SelfHandlingForm):
|
||||
node_id = forms.CharField(widget=forms.HiddenInput())
|
||||
name = forms.CharField(max_length=255, label=_("Node Name"))
|
||||
profile_id = forms.ThemableChoiceField(
|
||||
label=_("Profile"),
|
||||
help_text=_("Profile used for this node."))
|
||||
role = forms.CharField(
|
||||
max_length=255,
|
||||
label=_("Role"),
|
||||
required=False,
|
||||
help_text=_("Role for this node in the specific cluster."))
|
||||
metadata = forms.CharField(
|
||||
label=_("Metadata"),
|
||||
required=False,
|
||||
help_text=_("YAML formatted metadata."),
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(UpdateNodeForm, self).__init__(request, *args, **kwargs)
|
||||
profiles = senlin.profile_list(request)[0]
|
||||
self.fields['profile_id'].choices = (
|
||||
[("", _("Select Profile"))] + [(profile.id, profile.name)
|
||||
for profile in profiles])
|
||||
|
||||
def handle(self, request, data):
|
||||
params = _populate_node_params(data['name'],
|
||||
data['profile_id'],
|
||||
None,
|
||||
data['role'],
|
||||
data['metadata'])
|
||||
|
||||
del params['cluster_id']
|
||||
try:
|
||||
node = senlin.node_update(request, data.get('node_id'), **params)
|
||||
messages.success(
|
||||
request,
|
||||
_('Your node %s update request'
|
||||
' has been accepted for processing.') %
|
||||
data['name'])
|
||||
return node
|
||||
except Exception:
|
||||
redirect = reverse("horizon:cluster:nodes:index")
|
||||
exceptions.handle(request,
|
||||
_("Unable to update node."),
|
||||
redirect=redirect)
|
||||
return False
|
|
@ -1,25 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from senlin_dashboard.cluster import dashboard
|
||||
|
||||
|
||||
class Nodes(horizon.Panel):
|
||||
name = _("Nodes")
|
||||
slug = 'nodes'
|
||||
|
||||
|
||||
dashboard.Cluster.register(Nodes)
|
|
@ -1,232 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
from django.utils.translation import pgettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon.utils import filters
|
||||
|
||||
from senlin_dashboard import api
|
||||
from senlin_dashboard import exceptions
|
||||
|
||||
|
||||
class CreateNode(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Node")
|
||||
url = "horizon:cluster:nodes:create"
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
icon = "plus"
|
||||
ajax = True
|
||||
|
||||
|
||||
class DeleteNode(tables.DeleteAction):
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Delete Node",
|
||||
u"Delete Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Scheduled deletion of Node",
|
||||
u"Scheduled deletion of Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
api.senlin.node_delete(request, obj_id)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
if datum:
|
||||
return datum.status != "DELETING"
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class UpdateNode(tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Update Node")
|
||||
url = "horizon:cluster:nodes:update"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
|
||||
|
||||
class RecoverNode(tables.BatchAction):
|
||||
name = "recover"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Recover Node",
|
||||
u"Recover Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Recovered Node",
|
||||
u"Recovered Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.senlin.node_recover(request, obj_id)
|
||||
|
||||
def allowed(self, request, datum):
|
||||
if datum:
|
||||
return datum.status == "ERROR"
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
class CheckNode(tables.BatchAction):
|
||||
name = "check"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Check Node",
|
||||
u"Check Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Checked Node",
|
||||
u"Checked Nodes",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
api.senlin.node_check(request, obj_id)
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, node_id):
|
||||
try:
|
||||
node = api.senlin.node_get(request, node_id)
|
||||
return node
|
||||
except exceptions.ResourceNotFound:
|
||||
raise exceptions.NOT_FOUND
|
||||
|
||||
|
||||
def get_profile_link(node):
|
||||
return reverse_lazy('horizon:cluster:profiles:detail',
|
||||
args=[node.profile_id])
|
||||
|
||||
|
||||
def get_physical_link(node):
|
||||
if node.physical_id:
|
||||
return reverse_lazy('horizon:project:instances:detail',
|
||||
args=[node.physical_id])
|
||||
|
||||
|
||||
def get_cluster_link(node):
|
||||
if node.cluster_id:
|
||||
return reverse_lazy('horizon:cluster:clusters:detail',
|
||||
args=[node.cluster_id])
|
||||
|
||||
|
||||
def get_updated_time(object):
|
||||
return object.updated_at or None
|
||||
|
||||
|
||||
class NodeFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = (
|
||||
("name", _("Node Name ="), True),
|
||||
("status", _("Status ="), True),
|
||||
("profile_name", _("Profile Name ="), True),
|
||||
("cluster_id", _("Cluster ID ="), True),
|
||||
)
|
||||
|
||||
|
||||
class NodesTable(tables.DataTable):
|
||||
STATUS_CHOICES = (
|
||||
("INIT", None),
|
||||
("ACTIVE", True),
|
||||
("ERROR", False),
|
||||
("WARNING", None),
|
||||
("CREATING", None),
|
||||
("UPDATING", None),
|
||||
("DELETING", None),
|
||||
("RECOVERING", None),
|
||||
)
|
||||
|
||||
STATUS_DISPLAY_CHOICES = (
|
||||
("INIT", pgettext_lazy("Current status of a Node", u"INIT")),
|
||||
("ACTIVE", pgettext_lazy("Current status of a Node", u"ACTIVE")),
|
||||
("ERROR", pgettext_lazy("Current status of a Node", u"ERROR")),
|
||||
("WARNING", pgettext_lazy("Current status of a Node", u"WARNING")),
|
||||
("CREATING", pgettext_lazy("Current status of a Node", u"CREATING")),
|
||||
("UPDATING", pgettext_lazy("Current status of a Node", u"UPDATING")),
|
||||
("DELETING", pgettext_lazy("Current status of a Node", u"DELETING")),
|
||||
("RECOVERING", pgettext_lazy("Current status of a Node",
|
||||
u"RECOVERING")),
|
||||
)
|
||||
|
||||
name = tables.WrappingColumn(
|
||||
"name",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:cluster:nodes:detail")
|
||||
profile_name = tables.Column("profile_name",
|
||||
link=get_profile_link,
|
||||
verbose_name=_("Profile Name"))
|
||||
physical_id = tables.Column("physical_id",
|
||||
link=get_physical_link,
|
||||
verbose_name=_("Physical ID"))
|
||||
role = tables.Column("role", verbose_name=_("Role"))
|
||||
cluster_id = tables.Column("cluster_id",
|
||||
link=get_cluster_link,
|
||||
verbose_name=_("Cluster ID"))
|
||||
status = tables.Column("status",
|
||||
verbose_name=_("Status"),
|
||||
status=True,
|
||||
status_choices=STATUS_CHOICES,
|
||||
display_choices=STATUS_DISPLAY_CHOICES)
|
||||
status_reason = tables.Column("status_reason",
|
||||
verbose_name=_("Status Reason"))
|
||||
created = tables.Column(
|
||||
"created_at",
|
||||
verbose_name=_("Created"),
|
||||
filters=(filters.parse_isotime,)
|
||||
)
|
||||
updated = tables.Column(
|
||||
get_updated_time,
|
||||
verbose_name=_("Updated"),
|
||||
filters=(filters.parse_isotime,)
|
||||
)
|
||||
|
||||
class Meta(object):
|
||||
name = "nodes"
|
||||
row_class = UpdateRow
|
||||
verbose_name = _("Nodes")
|
||||
status_columns = ["status"]
|
||||
table_actions_menu = (CheckNode, RecoverNode)
|
||||
table_actions = (NodeFilterAction,
|
||||
CreateNode,
|
||||
DeleteNode,)
|
||||
row_actions = (UpdateNode,
|
||||
CheckNode,
|
||||
RecoverNode,
|
||||
DeleteNode,)
|
|
@ -1,76 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from senlin_dashboard.api import senlin
|
||||
from senlin_dashboard.cluster.nodes import event_tables
|
||||
|
||||
|
||||
class OverviewTab(tabs.Tab):
|
||||
name = _("Overview")
|
||||
slug = "overview"
|
||||
template_name = ("cluster/nodes/_detail_overview.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
return {"node": self.tab_group.kwargs['node']}
|
||||
|
||||
|
||||
class EventTab(tabs.TableTab):
|
||||
name = _("Event")
|
||||
slug = "event"
|
||||
table_classes = (event_tables.EventsTable,)
|
||||
template_name = "cluster/nodes/_detail_event.html"
|
||||
preload = False
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return getattr(self, "_prev", False)
|
||||
|
||||
def has_more_data(self, table):
|
||||
return getattr(self, "_more", False)
|
||||
|
||||
def get_event_data(self):
|
||||
|
||||
prev_marker = self.request.GET.get(
|
||||
event_tables.EventsTable._meta.prev_pagination_param, None)
|
||||
|
||||
if prev_marker is not None:
|
||||
marker = prev_marker
|
||||
else:
|
||||
marker = self.request.GET.get(
|
||||
event_tables.EventsTable._meta.pagination_param, None)
|
||||
reversed_order = prev_marker is not None
|
||||
|
||||
node_id = self.tab_group.kwargs['node_id']
|
||||
try:
|
||||
filters = {"obj_id": node_id}
|
||||
events, self._more, self._prev = senlin.event_list(
|
||||
self.request,
|
||||
marker=marker,
|
||||
paginate=True,
|
||||
reversed_order=reversed_order,
|
||||
filters=filters)
|
||||
except Exception:
|
||||
self._prev = self._more = False
|
||||
events = []
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve node event list.'))
|
||||
return events
|
||||
|
||||
|
||||
class NodeDetailTabs(tabs.TabGroup):
|
||||
slug = "node_details"
|
||||
sticky = True
|
||||
tabs = (OverviewTab, EventTab)
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Nodes are the physical objects, which can belong to any cluster of the same profile type." %}</p>
|
||||
{% endblock %}
|
|
@ -1,5 +0,0 @@
|
|||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
|
@ -1,44 +0,0 @@
|
|||
{% load i18n nbsp %}
|
||||
|
||||
<div class="detail">
|
||||
<h4>{% trans "Information" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ node.name }}</dd>
|
||||
<dt>{% trans "Profile Name" %}</dt>
|
||||
<dd><a href="{{ node.profile_url }}">{{ node.profile_name }}</a></dd>
|
||||
{% if node.cluster_id %}
|
||||
<dt>{% trans "Cluster ID" %}</dt>
|
||||
<dd><a href="{{ node.cluster_url }}">{{ node.cluster_id }}</a></dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ node.id }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ node.status }}</dd>
|
||||
{% if node.status_reason %}
|
||||
<dt>{% trans "Status Reason" %}</dt>
|
||||
<dd>{{ node.status_reason }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Physical ID" %}</dt>
|
||||
<dd>{{ node.physical_id }}</dd>
|
||||
{% if node.role %}
|
||||
<dt>{% trans "Role" %}</dt>
|
||||
<dd>{{ node.role }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Created" context "Created time" %}</dt>
|
||||
<dd>{{ node.created_at|parse_isotime }}</dd>
|
||||
{% if node.updated_at %}
|
||||
<dt>{% trans "Updated" context "Updated time" %}</dt>
|
||||
<dd>{{ node.updated_at|parse_isotime }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
|
||||
{% if node.metadata.vars %}
|
||||
<h4>{% trans "Metadata" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl class="dl-horizontal">
|
||||
<dd>{{ node.metadata|force_escape|nbsp|linebreaksbr }}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue