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:
Ghanshyam Mann 2024-05-10 14:29:10 -07:00
parent b9648e6ce2
commit 0830f74f9b
315 changed files with 8 additions and 25258 deletions

View File

@ -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

38
.gitignore vendored
View File

@ -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

View File

@ -1 +0,0 @@
1

View File

@ -1 +0,0 @@
0

View File

@ -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
View File

@ -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.

View File

@ -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.

View File

@ -1,7 +0,0 @@
ANGULAR_FEATURES.update({
'profiles_panel': True,
'nodes_panel': True,
'clusters_panel': True,
'policies_panel': True,
'receivers_panel': True
})

View File

@ -1,2 +0,0 @@
[python: **.py]
[django: **/templates/**.html]

View File

@ -1,2 +0,0 @@
[javascript: **.js]
[angular: **/static/**.html]

View File

@ -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]

View File

@ -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

View File

@ -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 = ''

View File

@ -1,4 +0,0 @@
.. include:: ../../../README.rst
:start-after: inclusion-start-marker-configuration
:end-before: inclusion-end-marker-configuration

View File

@ -1,8 +0,0 @@
Source Code Reference
---------------------
.. toctree::
:maxdepth: 1
:glob:
api/*

View File

@ -1,4 +0,0 @@
.. include:: ../../../README.rst
:start-after: inclusion-start-marker-develop
:end-before: inclusion-end-marker-develop

View File

@ -1,4 +0,0 @@
.. include:: ../../../README.rst
:start-after: inclusion-start-marker-hosts
:end-before: inclusion-end-marker-hosts

View File

@ -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

View File

@ -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

View File

@ -1,4 +0,0 @@
.. include:: ../../../README.rst
:start-after: inclusion-start-marker-install
:end-before: inclusion-end-marker-install

View File

@ -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)

View File

@ -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": {}
}

View File

@ -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.

View File

@ -1,5 +0,0 @@
---
fixes:
- |
[`bug 1805740 <https://bugs.launchpad.net/senlin/+bug/1805740>`_]
Fixed senlin dashboard to work with latest openstacksdk version.

View File

@ -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.

View File

@ -1,3 +0,0 @@
---
fixes:
- Fixed link to cluster in receiver table.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -1,3 +0,0 @@
---
fixes:
- Fixed installation documentation when using devstack environment.

View File

@ -1,3 +0,0 @@
---
fixes:
- Fixed the display of long names which could break the table layout.

View File

@ -1,3 +0,0 @@
---
fixes:
- Fixed node detail page view.

View File

@ -1,3 +0,0 @@
---
features:
- Paginated list for node objects.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -1,6 +0,0 @@
===========================
2023.1 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.1

View File

@ -1,6 +0,0 @@
===========================
2023.2 Series Release Notes
===========================
.. release-notes::
:branch: stable/2023.2

View File

@ -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 = ''

View File

@ -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

View File

@ -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."

View File

@ -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."

View File

@ -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"

View File

@ -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."

View File

@ -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` イベ"
"ントを発火するようにしました。"

View File

@ -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."

View File

@ -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发布说明"

View File

@ -1,6 +0,0 @@
===================================
Newton Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/newton

View File

@ -1,6 +0,0 @@
===================================
Ocata Series Release Notes
===================================
.. release-notes::
:branch: origin/stable/ocata

View File

@ -1,6 +0,0 @@
===================================
Pike Series Release Notes
===================================
.. release-notes::
:branch: stable/pike

View File

@ -1,6 +0,0 @@
===========================
Queens Series Release Notes
===========================
.. release-notes::
:branch: stable/queens

View File

@ -1,6 +0,0 @@
===================================
Rocky Series Release Notes
===================================
.. release-notes::
:branch: stable/rocky

View File

@ -1,6 +0,0 @@
==========================
Stein Series Release Notes
==========================
.. release-notes::
:branch: stable/stein

View File

@ -1,6 +0,0 @@
==========================
Train Series Release Notes
==========================
.. release-notes::
:branch: stable/train

View File

@ -1,5 +0,0 @@
==============================
Current Series Release Notes
==============================
.. release-notes::

View File

@ -1,6 +0,0 @@
===========================
Ussuri Series Release Notes
===========================
.. release-notes::
:branch: stable/ussuri

View File

@ -1,6 +0,0 @@
=============================
Victoria Series Release Notes
=============================
.. release-notes::
:branch: stable/victoria

View File

@ -1,6 +0,0 @@
============================
Wallaby Series Release Notes
============================
.. release-notes::
:branch: stable/wallaby

View File

@ -1,6 +0,0 @@
=========================
Xena Series Release Notes
=========================
.. release-notes::
:branch: stable/xena

View File

@ -1,6 +0,0 @@
=========================
Yoga Series Release Notes
=========================
.. release-notes::
:branch: unmaintained/yoga

View File

@ -1,6 +0,0 @@
========================
Zed Series Release Notes
========================
.. release-notes::
:branch: stable/zed

View File

@ -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

View File

@ -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

View File

@ -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())

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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,)

View File

@ -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)

View File

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

View File

@ -1,5 +0,0 @@
<div class="row-fluid">
<div class="span12">
{{ table.render }}
</div>
</div>

View File

@ -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>

View File

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

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Cluster" %}{% endblock %}
{% block main %}
{% include 'cluster/clusters/_create.html' %}
{% endblock %}

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Clusters" %}{% endblock %}
{% block main %}
{{ table.render }}
{% endblock %}

View File

@ -1,7 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Manage Policies" %}{% endblock %}
{% block main %}
{% include 'cluster/clusters/_manage_policies.html' %}
{% endblock %}

View File

@ -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', {})])

View File

@ -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'),
]

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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)

View File

@ -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,)

View File

@ -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)

View File

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

View File

@ -1,5 +0,0 @@
<div class="row-fluid">
<div class="span12">
{{ table.render }}
</div>
</div>

View File

@ -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