Retire Sahara: remove repo content
Sahara project is retiring - https://review.opendev.org/c/openstack/governance/ /919374 this commit remove the content of this project repo Depends-On: https://review.opendev.org/c/openstack/project-config/ /919376 Change-Id: I4eb4556e448b4eacdbf1b5cc4164bcf6a4ccbed0
This commit is contained in:
parent
744efb4fc5
commit
af37b9efbf
13
.coveragerc
13
.coveragerc
@ -1,13 0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = sahara_dashboard
|
||||
omit =
|
||||
.tox/*
|
||||
sahara_dashboard/test/*
|
||||
|
||||
[paths]
|
||||
source = sahara_dashboard
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
||||
precision = 3
|
39
.gitignore
vendored
39
.gitignore
vendored
@ -1,39 0,0 @@
|
||||
*.py[co]
|
||||
*.egg
|
||||
*.egg-info
|
||||
*.lock
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
pip-log.txt
|
||||
.tox
|
||||
*.mo
|
||||
.mr.developer.cfg
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.venv
|
||||
.idea
|
||||
out
|
||||
target
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
doc/html
|
||||
doc/source/apidoc
|
||||
doc/source/api
|
||||
doc/build
|
||||
*.db
|
||||
.coverage
|
||||
nosetests.xml
|
||||
pylint-report.txt
|
||||
ChangeLog
|
||||
cscope.out
|
||||
horizon
|
||||
sahara_dashboard/test/.secret_key_store
|
||||
sahara_dashboard/test/configs/config.conf
|
||||
AUTHORS
|
53
.zuul.yaml
53
.zuul.yaml
@ -1,53 0,0 @@
|
||||
- project:
|
||||
templates:
|
||||
- check-requirements
|
||||
- horizon-non-primary-django-jobs
|
||||
- openstack-python3-jobs-horizon
|
||||
- release-notes-jobs-python3
|
||||
experimental:
|
||||
jobs:
|
||||
- sahara-dashboard-integration
|
||||
|
||||
- job:
|
||||
name: sahara-dashboard-tox-base
|
||||
parent: openstack-tox
|
||||
required-projects:
|
||||
- openstack/sahara-dashboard
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/.*$
|
||||
- ^releasenotes/.*$
|
||||
- ^sahara_dashboard/locale/.*$
|
||||
|
||||
- job:
|
||||
name: sahara-dashboard-integration
|
||||
parent: devstack
|
||||
required-projects:
|
||||
- openstack/heat
|
||||
- openstack/horizon
|
||||
- openstack/sahara
|
||||
- openstack/sahara-plugin-ambari
|
||||
- openstack/sahara-plugin-cdh
|
||||
- openstack/sahara-plugin-mapr
|
||||
- openstack/sahara-plugin-spark
|
||||
- openstack/sahara-plugin-storm
|
||||
- openstack/sahara-plugin-vanilla
|
||||
- openstack/sahara-dashboard
|
||||
roles:
|
||||
- zuul: openstack/horizon
|
||||
vars:
|
||||
devstack_plugins:
|
||||
sahara: 'https://opendev.org/openstack/sahara'
|
||||
sahara-dashboard: 'https://opendev.org/openstack/sahara-dashboard'
|
||||
heat: 'https://opendev.org/openstack/heat'
|
||||
devstack_services:
|
||||
horizon: true
|
||||
tls-proxy: false
|
||||
devstack_localrc:
|
||||
USE_PYTHON3: True
|
||||
pre-run: playbooks/sahara-dashboard-integration/pre.yaml
|
||||
run: playbooks/sahara-dashboard-integration/run.yaml
|
||||
irrelevant-files:
|
||||
- ^.*\.rst$
|
||||
- ^doc/.*$
|
||||
- ^releasenotes/.*$
|
@ -1,19 0,0 @@
|
||||
The source repository for this project can be found at:
|
||||
|
||||
https://opendev.org/openstack/sahara-dashboard
|
||||
|
||||
Pull requests submitted through GitHub are not monitored.
|
||||
|
||||
To start contributing to OpenStack, follow the steps in the contribution guide
|
||||
to set up and use Gerrit:
|
||||
|
||||
https://docs.openstack.org/contributors/code-and-documentation/quick-start.html
|
||||
|
||||
Bugs should be filed on Storyboard:
|
||||
|
||||
https://storyboard.openstack.org/#!/project/openstack/sahara-dashboard
|
||||
|
||||
For more specific information about contributing to this repository, see the
|
||||
sahara contributor guide:
|
||||
|
||||
https://docs.openstack.org/sahara/latest/contributor/contributing.html
|
12
HACKING.rst
12
HACKING.rst
@ -1,12 0,0 @@
|
||||
Sahara Style Commandments
|
||||
==========================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
https://docs.openstack.org/hacking/latest/
|
||||
- Step 2: Read on
|
||||
|
||||
Sahara Specific Commandments
|
||||
-----------------------------
|
||||
|
||||
None so far
|
||||
|
176
LICENSE
176
LICENSE
@ -1,176 0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
@ -1,4 0,0 @@
|
||||
recursive-include sahara_dashboard *.html *.scss *.js
|
||||
|
||||
include AUTHORS
|
||||
include ChangeLog
|
64
README.rst
64
README.rst
@ -1,58 1,10 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
This project is no longer maintained.
|
||||
|
||||
.. image:: https://governance.openstack.org/tc/badges/sahara-dashboard.svg
|
||||
:target: https://governance.openstack.org/tc/reference/tags/index.html
|
||||
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".
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
OpenStack Dashboard plugin for Sahara project
|
||||
=============================================
|
||||
|
||||
How to use with Horizon on server:
|
||||
----------------------------------
|
||||
|
||||
Use pip to install the package on the server running Horizon. Then either copy
|
||||
or link the files in sahara_dashboard/enabled to
|
||||
openstack_dashboard/local/enabled. This step will cause the Horizon service to
|
||||
pick up the Sahara plugin when it starts.
|
||||
|
||||
How to use with devstack:
|
||||
-------------------------
|
||||
|
||||
Add the following to your devstack ``local.conf`` file::
|
||||
|
||||
enable_plugin sahara-dashboard https://opendev.org/openstack/sahara-dashboard
|
||||
|
||||
|
||||
To run unit tests:
|
||||
------------------
|
||||
|
||||
./run_tests.sh
|
||||
|
||||
NOTE:
|
||||
=====
|
||||
|
||||
As of the Mitaka release, the dashboard for sahara is now maintained
|
||||
outside of the horizon codebase, in the repository.
|
||||
|
||||
Links:
|
||||
------
|
||||
|
||||
Sahara project: https://opendev.org/openstack/sahara
|
||||
|
||||
Storyboard project: https://storyboard.openstack.org/#!/project/936
|
||||
|
||||
Sahara docs site: https://docs.openstack.org/sahara/latest/
|
||||
|
||||
Quickstart guide: https://docs.openstack.org/sahara/latest/user/quickstart.html
|
||||
|
||||
How to participate: https://docs.openstack.org/sahara/latest/contributor/how-to-participate.html
|
||||
|
||||
Release notes: https://docs.openstack.org/releasenotes/sahara-dashboard/
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
|
||||
For any further questions, please email
|
||||
[email protected] or join #openstack-dev on
|
||||
OFTC.
|
||||
|
@ -1,2 0,0 @@
|
||||
[python: **.py]
|
||||
[django: **/templates/**.html]
|
@ -1,2 0,0 @@
|
||||
[javascript: **.js]
|
||||
[angular: **/static/**.html]
|
41
bindep.txt
41
bindep.txt
@ -1,41 0,0 @@
|
||||
# This is a cross-platform list tracking distribution packages needed by tests;
|
||||
# see https://docs.openstack.org/infra/bindep/ for additional information.
|
||||
|
||||
build-essential [platform:dpkg]
|
||||
gawk
|
||||
# gettext and graphviz are needed by doc builds only. For transition,
|
||||
# have them in both doc and test.
|
||||
gettext [doc test]
|
||||
graphviz [doc test]
|
||||
language-pack-en [platform:ubuntu]
|
||||
libevent-dev [platform:dpkg]
|
||||
libevent-devel [platform:rpm]
|
||||
libffi-dev [platform:dpkg]
|
||||
libffi-devel [platform:rpm]
|
||||
libjpeg-dev [platform:dpkg]
|
||||
libjpeg-turbo-devel [platform:rpm]
|
||||
libsasl2-dev [platform:dpkg]
|
||||
libselinux-python [platform:rpm]
|
||||
libsqlite3-dev [platform:dpkg]
|
||||
libuuid-devel [platform:rpm]
|
||||
locales [platform:debian]
|
||||
pkg-config [platform:dpkg]
|
||||
pkgconfig [platform:rpm]
|
||||
python3-all-dev [platform:ubuntu !platform:ubuntu-precise]
|
||||
python3-dev [platform:dpkg]
|
||||
python3-devel [platform:fedora]
|
||||
python3.4 [platform:ubuntu-trusty]
|
||||
python34-devel [platform:centos]
|
||||
python3.5 [platform:ubuntu-xenial]
|
||||
uuid-dev [platform:dpkg]
|
||||
zlib-devel [platform:rpm]
|
||||
zlib1g-dev [platform:dpkg]
|
||||
|
||||
# integration tests
|
||||
firefox [integrationtests]
|
||||
dbus [integrationtests platform:redhat]
|
||||
dbus-1 [integrationtests platform:suse]
|
||||
xvfb [integrationtests platform:dpkg]
|
||||
# already part of xorg-x11-server on openSUSE
|
||||
xorg-x11-server-Xvfb [integrationtests platform:redhat]
|
||||
libav-tools [integrationtests platform:dpkg]
|
@ -1,55 0,0 @@
|
||||
# plugin.sh - DevStack plugin.sh dispatch script sahara-dashboard
|
||||
|
||||
SAHARA_DASH_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd)
|
||||
|
||||
function install_sahara_dashboard {
|
||||
setup_develop ${SAHARA_DASH_DIR}
|
||||
}
|
||||
|
||||
function configure_sahara_dashboard {
|
||||
cp -a ${SAHARA_DASH_DIR}/sahara_dashboard/enabled/* ${DEST}/horizon/openstack_dashboard/local/enabled/
|
||||
cp -a ${SAHARA_DASH_DIR}/sahara_dashboard/local_settings.d/* ${DEST}/horizon/openstack_dashboard/local/local_settings.d/
|
||||
# NOTE: If locale directory does not exist, compilemessages will fail,
|
||||
# so check for an existence of locale directory is required.
|
||||
if [ -d ${SAHARA_DASH_DIR}/sahara_dashboard/locale ]; then
|
||||
(cd ${SAHARA_DASH_DIR}/sahara_dashboard; DJANGO_SETTINGS_MODULE=openstack_dashboard.settings $PYTHON ../manage.py compilemessages)
|
||||
fi
|
||||
}
|
||||
|
||||
# check for service enabled
|
||||
if is_service_enabled sahara-dashboard; then
|
||||
|
||||
if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
|
||||
# Set up system services
|
||||
# no-op
|
||||
:
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
# Perform installation of service source
|
||||
echo_summary "Installing Sahara Dashboard"
|
||||
install_sahara_dashboard
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
# Configure after the other layer 1 and 2 services have been configured
|
||||
echo_summary "Configuring Sahara Dashboard"
|
||||
configure_sahara_dashboard
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
# Initialize and start the app-catalog-ui service
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "unstack" ]]; then
|
||||
# Shut down app-catalog-ui services
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "clean" ]]; then
|
||||
# Remove state and transient data
|
||||
# Remember clean.sh first calls unstack.sh
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
fi
|
@ -1,2 0,0 @@
|
||||
# settings file for sahara-dashboard plugin
|
||||
enable_service sahara-dashboard
|
@ -1,7 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.
|
||||
|
||||
sphinx>=2.0.0,!=2.1.0 # BSD
|
||||
openstackdocstheme>=2.2.1 # Apache-2.0
|
||||
reno>=3.1.0 # Apache-2.0
|
23
manage.py
23
manage.py
@ -1,23 0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE",
|
||||
"sahara_dashboard.test.settings")
|
||||
execute_from_command_line(sys.argv)
|
@ -1,8 0,0 @@
|
||||
---
|
||||
- hosts: controller
|
||||
roles:
|
||||
- role: bindep
|
||||
bindep_profile: integrationtests
|
||||
become: true
|
||||
- setup-selenium-tests
|
||||
- setup-sahara-ui-integration
|
@ -1,13 0,0 @@
|
||||
---
|
||||
- hosts: all
|
||||
strategy: linear
|
||||
roles:
|
||||
- orchestrate-devstack
|
||||
|
||||
- hosts: controller
|
||||
roles:
|
||||
- post-devstack-sahara-ui-integration
|
||||
- role: tox
|
||||
tox_environment:
|
||||
AVCONV_INSTALLED: 1
|
||||
tox_envlist: py27integration
|
@ -1,6 0,0 @@
|
||||
---
|
||||
features:
|
||||
- There is now support for APIv2 in sahara-dashboard. Enable it by setting
|
||||
the "data-processing" API version to "2" in the `OPENSTACK_API_VERSIONS`
|
||||
dictionary found in Horizon's local_settings.py. Some Sahara features are
|
||||
only exposed through APIv2.
|
@ -1,3 0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fixed issue with retrieving details for public clusters.
|
@ -1,3 0,0 @@
|
||||
---
|
||||
features:
|
||||
- Added integration with Designate for hostname resolution.
|
@ -1,7 0,0 @@
|
||||
---
|
||||
security:
|
||||
- |
|
||||
Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which
|
||||
prevent DOS on data received via GET and POST methods. Default values
|
||||
is set to 2000 instead of upstream default of 1000 to keep the security
|
||||
feature and to not cause issue with the Sahara Dashboard forms.
|
@ -1,6 0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 2.7 support has been dropped. Last release of sahara-dashboard
|
||||
to support python 2.7 is OpenStack Train. The minimum version of Python now
|
||||
supported by sahara-dashboard is Python 3.6.
|
@ -1,5 0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Python 3.6 & 3.7 support has been dropped. The minimum version of Python now
|
||||
supported is Python 3.8.
|
@ -1,5 0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Increasing DATA_UPLOAD_MAX_NUMBER_FIELDS django configuration to allow creation
|
||||
of CDH node group templates.
|
@ -1,12 0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support for the new boot from volume options introduced in the
|
||||
Stein release of Sahara is added.
|
||||
other:
|
||||
- |
|
||||
Coinciding with the official stability of Sahara APIv2, operators
|
||||
should feel especially encouraged to set "data-processing" API
|
||||
version to "2" in the `OPENSTACK_API_VERSIONS` dictionary found in
|
||||
Horizon's local_settings.py. As previously stated, some Sahara
|
||||
features are only exposed through APIv2.
|
@ -1,9 0,0 @@
|
||||
---
|
||||
prelude: >
|
||||
The config parameter 'SAHARA_FLOATING_IP_DISABLED' replaces
|
||||
'SAHARA_AUTO_IP_ALLOCATION_ENABLED'.
|
||||
deprecations:
|
||||
- |
|
||||
The 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' config parameter in Sahara
|
||||
was deprecated and replaced by 'SAHARA_FLOATING_IP_DISABLED'
|
||||
which has a less confusing name.
|
@ -1,3 0,0 @@
|
||||
---
|
||||
other:
|
||||
- Start using reno for release notes management.
|
@ -1,6 0,0 @@
|
||||
===========================
|
||||
2023.1 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.1
|
@ -1,6 0,0 @@
|
||||
===========================
|
||||
2023.2 Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/2023.2
|
@ -1,239 0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# -- 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 = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# 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.
|
||||
copyright = '2013, Sahara Developers'
|
||||
|
||||
# openstackdocstheme options
|
||||
openstackdocs_repo_name = 'openstack/sahara-dashboard'
|
||||
openstackdocs_use_storyboard = True
|
||||
|
||||
# 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 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 = 'SaharaDashboardReleaseNotesdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
# 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', 'SaharaDashboardReleaseNotes.tex',
|
||||
'Sahara Dashboard Release Notes Documentation',
|
||||
'Sahara 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', 'saharadashboardreleasenotes',
|
||||
'Sahara Dashboard Release Notes Documentation',
|
||||
['Sahara 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', 'SaharaDashboardReleaseNotes',
|
||||
'Sahara Dashboard Release Notes Documentation',
|
||||
'Sahara Developers', 'SaharaDashboardReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
@ -1,24 0,0 @@
|
||||
===============================
|
||||
Sahara Dashboard Release Notes
|
||||
===============================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
unreleased
|
||||
2023.2
|
||||
2023.1
|
||||
zed
|
||||
yoga
|
||||
xena
|
||||
wallaby
|
||||
victoria
|
||||
ussuri
|
||||
train
|
||||
stein
|
||||
rocky
|
||||
queens
|
||||
pike
|
||||
ocata
|
||||
newton
|
||||
mitaka
|
@ -1,138 0,0 @@
|
||||
# Robert Simai <[email protected]>, 2016. #zanata
|
||||
# Robert Simai <[email protected]>, 2017. #zanata
|
||||
# Robert Simai <[email protected]>, 2018. #zanata
|
||||
# Andreas Jaeger <[email protected]>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-26 16:02 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:55 0000\n"
|
||||
"Last-Translator: Andreas Jaeger <[email protected]>\n"
|
||||
"Language-Team: German\n"
|
||||
"Language: de\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
msgid "10.0.0"
|
||||
msgstr "10.0.0"
|
||||
|
||||
msgid "4.1.2"
|
||||
msgstr "4.1.2"
|
||||
|
||||
msgid "5.0.0"
|
||||
msgstr "5.0.0"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3"
|
||||
|
||||
msgid "7.0.2"
|
||||
msgstr "7.0.2"
|
||||
|
||||
msgid "8.0.0"
|
||||
msgstr "8.0.0"
|
||||
|
||||
msgid "9.0.0"
|
||||
msgstr "9.0.0"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "Integration mit Designate zur Auflösung von Hostnamen hinzugefügt."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Fehlerkorrekturen"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Aktuelle Serie Releasenotes"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Ablaufwarnungen"
|
||||
|
||||
msgid ""
|
||||
"Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which "
|
||||
"prevent DOS on data received via GET and POST methods. Default values is set "
|
||||
"to 2000 instead of upstream default of 1000 to keep the security feature and "
|
||||
"to not cause issue with the Sahara Dashboard forms."
|
||||
msgstr ""
|
||||
"Django 1.10 führt eine neue Variable ein: DATA_UPLOAD_MAX_NUMBER_FIELDS, "
|
||||
"welche DOS bei durch GET und POST Methoden empfangene Daten verhindert. Der "
|
||||
"Standardwert ist 2000, anstelle des Upstream-Standardwertes 1000, um das "
|
||||
"Sicherheitsmerkmal zu behalten ohne Schwierigkeiten mit den Sahara Dashboard "
|
||||
"Formularen hervorzurufen."
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr "Problem beim abrufen von Details von öffentlichen Clustern beseitigt."
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Mitaka Series Releasenotes"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Neue Funktionen"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton Series Releasenotes"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata Serie Releasenotes"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "Andere Notizen"
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike Serie Release Notes"
|
||||
|
||||
msgid "Prelude"
|
||||
msgstr "Einleitung"
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Queens Serie Release Notes"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Rocky Serie Releasenotes"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara Dashboard Releasenotes"
|
||||
|
||||
msgid "Security Issues"
|
||||
msgstr "Sicherheitsrelevante Probleme"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "Reno wird für die Verwaltung der Releasenotes verwendet."
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Stein Serie Releasenotes"
|
||||
|
||||
msgid ""
|
||||
"The 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' config parameter in Sahara was "
|
||||
"deprecated and replaced by 'SAHARA_FLOATING_IP_DISABLED' which has a less "
|
||||
"confusing name."
|
||||
msgstr ""
|
||||
"Der 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' Konfigurationsparameter in Sahara "
|
||||
"wurde abgelöst und durch den weniger verwirrenden Namen "
|
||||
"'SAHARA_FLOATING_IP_DISABLED' ersetzt."
|
||||
|
||||
msgid ""
|
||||
"The config parameter 'SAHARA_FLOATING_IP_DISABLED' replaces "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
msgstr ""
|
||||
"Der Konfigurationsparameter 'SAHARA_FLOATING_IP_DISABLED' ersetzt "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
|
||||
msgid ""
|
||||
"There is now support for APIv2 in sahara-dashboard. Enable it by setting the "
|
||||
"\"data-processing\" API version to \"2\" in the `OPENSTACK_API_VERSIONS` "
|
||||
"dictionary found in Horizon's local_settings.py. Some Sahara features are "
|
||||
"only exposed through APIv2."
|
||||
msgstr ""
|
||||
"Es gibt jetzt Unterstützung für APIv2 in sahara-dashboard. Aktivieren Sie "
|
||||
"dies durch setzen von \"data-processing\" API Version auf \"2\" im "
|
||||
"`OPENSTACK_API_VERSIONS` Wörterbuch in der Horizon-Datei local_settings.py. "
|
||||
"Einige Sahara-Features können nur mittels der APIv2 verwendet werden."
|
||||
|
||||
msgid "Train Series Release Notes"
|
||||
msgstr "Train Serie Releasenotes"
|
@ -1,222 0,0 @@
|
||||
# Andi Chandler <[email protected]>, 2016. #zanata
|
||||
# Andi Chandler <[email protected]>, 2017. #zanata
|
||||
# Andi Chandler <[email protected]>, 2018. #zanata
|
||||
# Andi Chandler <[email protected]>, 2019. #zanata
|
||||
# Andi Chandler <[email protected]>, 2020. #zanata
|
||||
# Andi Chandler <[email protected]>, 2022. #zanata
|
||||
# Andi Chandler <[email protected]>, 2023. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-30 23:36 0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2023-01-26 10:29 0000\n"
|
||||
"Last-Translator: Andi Chandler <[email protected]>\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 "10.0.0"
|
||||
msgstr "10.0.0"
|
||||
|
||||
msgid "12.0.0"
|
||||
msgstr "12.0.0"
|
||||
|
||||
msgid "12.0.0.0rc1"
|
||||
msgstr "12.0.0.0rc1"
|
||||
|
||||
msgid "17.0.0"
|
||||
msgstr "17.0.0"
|
||||
|
||||
msgid "4.1.2"
|
||||
msgstr "4.1.2"
|
||||
|
||||
msgid "5.0.0"
|
||||
msgstr "5.0.0"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3"
|
||||
|
||||
msgid "7.0.2"
|
||||
msgstr "7.0.2"
|
||||
|
||||
msgid "8.0.0"
|
||||
msgstr "8.0.0"
|
||||
|
||||
msgid "8.0.2"
|
||||
msgstr "8.0.2"
|
||||
|
||||
msgid "9.0.0"
|
||||
msgstr "9.0.0"
|
||||
|
||||
msgid "9.0.0.0b3"
|
||||
msgstr "9.0.0.0b3"
|
||||
|
||||
msgid "9.0.2"
|
||||
msgstr "9.0.2"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "Added integration with Designate for hostname resolution."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Bug Fixes"
|
||||
|
||||
msgid ""
|
||||
"Coinciding with the official stability of Sahara APIv2, operators should "
|
||||
"feel especially encouraged to set \"data-processing\" API version to \"2\" "
|
||||
"in the `OPENSTACK_API_VERSIONS` dictionary found in Horizon's local_settings."
|
||||
"py. As previously stated, some Sahara features are only exposed through "
|
||||
"APIv2."
|
||||
msgstr ""
|
||||
"Coinciding with the official stability of Sahara APIv2, operators should "
|
||||
"feel especially encouraged to set \"data-processing\" API version to \"2\" "
|
||||
"in the `OPENSTACK_API_VERSIONS` dictionary found in Horizon's local_settings."
|
||||
"py. As previously stated, some Sahara features are only exposed through "
|
||||
"APIv2."
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Current Series Release Notes"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Deprecation Notes"
|
||||
|
||||
msgid ""
|
||||
"Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which "
|
||||
"prevent DOS on data received via GET and POST methods. Default values is set "
|
||||
"to 2000 instead of upstream default of 1000 to keep the security feature and "
|
||||
"to not cause issue with the Sahara Dashboard forms."
|
||||
msgstr ""
|
||||
"Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which "
|
||||
"prevent DOS on data received via GET and POST methods. Default values is set "
|
||||
"to 2000 instead of upstream default of 1000 to keep the security feature and "
|
||||
"to not cause issue with the Sahara Dashboard forms."
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr "Fixed issue with retrieving details for public clusters."
|
||||
|
||||
msgid ""
|
||||
"Increasing DATA_UPLOAD_MAX_NUMBER_FIELDS django configuration to allow "
|
||||
"creation of CDH node group templates."
|
||||
msgstr ""
|
||||
"Increasing DATA_UPLOAD_MAX_NUMBER_FIELDS django configuration to allow "
|
||||
"creation of CDH node group templates."
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Mitaka Series Release Notes"
|
||||
|
||||
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 "Other Notes"
|
||||
msgstr "Other Notes"
|
||||
|
||||
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 sahara-dashboard to "
|
||||
"support python 2.7 is OpenStack Train. The minimum version of Python now "
|
||||
"supported by sahara-dashboard is Python 3.6."
|
||||
msgstr ""
|
||||
"Python 2.7 support has been dropped. Last release of Sahara-dashboard to "
|
||||
"support Python 2.7 is OpenStack Train. The minimum version of Python now "
|
||||
"supported by Sahara-dashboard is Python 3.6."
|
||||
|
||||
msgid ""
|
||||
"Python 3.6 & 3.7 support has been dropped. The minimum version of Python now "
|
||||
"supported is Python 3.8."
|
||||
msgstr ""
|
||||
"Python 3.6 & 3.7 support has been dropped. The minimum version of Python now "
|
||||
"supported is Python 3.8."
|
||||
|
||||
msgid "Queens Series Release Notes"
|
||||
msgstr "Queens Series Release Notes"
|
||||
|
||||
msgid "Rocky Series Release Notes"
|
||||
msgstr "Rocky Series Release Notes"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara Dashboard Release Notes"
|
||||
|
||||
msgid "Security Issues"
|
||||
msgstr "Security Issues"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "Start using reno for release notes management."
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Stein Series Release Notes"
|
||||
|
||||
msgid ""
|
||||
"Support for the new boot from volume options introduced in the Stein release "
|
||||
"of Sahara is added."
|
||||
msgstr ""
|
||||
"Support for the new boot from volume options introduced in the Stein release "
|
||||
"of Sahara is added."
|
||||
|
||||
msgid ""
|
||||
"The 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' config parameter in Sahara was "
|
||||
"deprecated and replaced by 'SAHARA_FLOATING_IP_DISABLED' which has a less "
|
||||
"confusing name."
|
||||
msgstr ""
|
||||
"The 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' config parameter in Sahara was "
|
||||
"deprecated and replaced by 'SAHARA_FLOATING_IP_DISABLED' which has a less "
|
||||
"confusing name."
|
||||
|
||||
msgid ""
|
||||
"The config parameter 'SAHARA_FLOATING_IP_DISABLED' replaces "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
msgstr ""
|
||||
"The config parameter 'SAHARA_FLOATING_IP_DISABLED' replaces "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
|
||||
msgid ""
|
||||
"There is now support for APIv2 in sahara-dashboard. Enable it by setting the "
|
||||
"\"data-processing\" API version to \"2\" in the `OPENSTACK_API_VERSIONS` "
|
||||
"dictionary found in Horizon's local_settings.py. Some Sahara features are "
|
||||
"only exposed through APIv2."
|
||||
msgstr ""
|
||||
"There is now support for APIv2 in Sahara-Dashboard. Enable it by setting the "
|
||||
"\"data-processing\" API version to \"2\" in the `OPENSTACK_API_VERSIONS` "
|
||||
"dictionary found in Horizon's local_settings.py. Some Sahara features are "
|
||||
"only exposed through APIv2."
|
||||
|
||||
msgid "Train Series Release Notes"
|
||||
msgstr "Train Series Release Notes"
|
||||
|
||||
msgid "Upgrade Notes"
|
||||
msgstr "Upgrade Notes"
|
||||
|
||||
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"
|
@ -1,55 0,0 @@
|
||||
# Gérald LONLAS <[email protected]>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-26 16:02 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:21 0000\n"
|
||||
"Last-Translator: Gérald LONLAS <[email protected]>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
|
||||
msgid "5.0.0"
|
||||
msgstr "5.0.0"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "Ajout l'intégration de la résolution du hostname avec Designate."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Corrections de bugs"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Note de la release actuelle"
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr ""
|
||||
"Corrige un bug durant la récupération des détails des Clusters publics."
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Note de release pour Mitaka"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "Nouvelles fonctionnalités"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Note de release pour Newton"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "Autres notes"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Note de release pour le Tableau de bord Sahara"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "Commence à utiliser reno pour la gestion des notes de release"
|
@ -1,161 0,0 @@
|
||||
# suhartono <[email protected]>, 2016. #zanata
|
||||
# suhartono <[email protected]>, 2017. #zanata
|
||||
# suhartono <[email protected]>, 2018. #zanata
|
||||
# suhartono <[email protected]>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-26 16:02 0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-07-17 04:38 0000\n"
|
||||
"Last-Translator: suhartono <[email protected]>\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"Language: id\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "10.0.0"
|
||||
msgstr "10.0.0"
|
||||
|
||||
msgid "4.1.2"
|
||||
msgstr "4.1.2"
|
||||
|
||||
msgid "5.0.0"
|
||||
msgstr "5.0.0"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3"
|
||||
|
||||
msgid "7.0.2"
|
||||
msgstr "7.0.2"
|
||||
|
||||
msgid "8.0.0"
|
||||
msgstr "8.0.0"
|
||||
|
||||
msgid "9.0.0"
|
||||
msgstr "9.0.0"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "Ditambahkan integrasi dengan Designate untuk resolusi hostname"
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "Bug Fixes (perbaikan kerusakan)"
|
||||
|
||||
msgid ""
|
||||
"Coinciding with the official stability of Sahara APIv2, operators should "
|
||||
"feel especially encouraged to set \"data-processing\" API version to \"2\" "
|
||||
"in the `OPENSTACK_API_VERSIONS` dictionary found in Horizon's local_settings."
|
||||
"py. As previously stated, some Sahara features are only exposed through "
|
||||
"APIv2."
|
||||
msgstr ""
|
||||
"Bertepatan dengan stabilitas resmi Sahara APIv2, operator harus merasa "
|
||||
"sangat dianjurkan untuk mengatur versi API \"data-processing\" menjadi "
|
||||
"\"2\" dalam kamus `OPENSTACK_API_VERSIONS` yang ditemukan di kamus Horizon "
|
||||
"local_settings.py. Seperti yang dinyatakan sebelumnya, beberapa fitur Sahara "
|
||||
"hanya diekspos melalui APIv2."
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "Current Series Release Notes (catatan rilis Curent Series)"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "Catatan Deprecation"
|
||||
|
||||
msgid ""
|
||||
"Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which "
|
||||
"prevent DOS on data received via GET and POST methods. Default values is set "
|
||||
"to 2000 instead of upstream default of 1000 to keep the security feature and "
|
||||
"to not cause issue with the Sahara Dashboard forms."
|
||||
msgstr ""
|
||||
"Django 1.10 memperkenalkan sebuah var baru: DATA_UPLOAD_MAX_NUMBER_FIELDS "
|
||||
"yang mencegah DOS pada data yang diterima melalui metode GET dan POST. Nilai "
|
||||
"default diatur ke 2000, bukan default hulu 1000 untuk menjaga fitur keamanan "
|
||||
"dan tidak menimbulkan masalah dengan Dashboard Sahara form."
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr "Isu tetap mengenai pengambilan rincian untuk kelompok masyarakat."
|
||||
|
||||
msgid ""
|
||||
"Increasing DATA_UPLOAD_MAX_NUMBER_FIELDS django configuration to allow "
|
||||
"creation of CDH node group templates."
|
||||
msgstr ""
|
||||
"Meningkatkan konfigurasi django DATA_UPLOAD_MAX_NUMBER_FIELDS untuk "
|
||||
"memungkinkan pembuatan templat grup node CDH."
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Mitaka Series Release Notes (Catatan rilis Mitaka Series)"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "New Features (fitur baru)"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton Series Release Notes (catatan rilis Newton Series"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Catatan rilis seri Ocata"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "Other Notes (catatan lain)"
|
||||
|
||||
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 "Rocky Series Release Notes"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara Dashboard Release Notes (catatan rilis Sahara Dashboard)"
|
||||
|
||||
msgid "Security Issues"
|
||||
msgstr "Security Issues"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "Mulai menggunakan reno untuk manajemen catatan rilis."
|
||||
|
||||
msgid "Stein Series Release Notes"
|
||||
msgstr "Catatan Rilis Seri Stein"
|
||||
|
||||
msgid ""
|
||||
"Support for the new boot from volume options introduced in the Stein release "
|
||||
"of Sahara is added."
|
||||
msgstr ""
|
||||
"Dukungan untuk boot baru dari opsi volume yang diperkenalkan dalam rilis "
|
||||
"Sahara dari Stein ditambahkan."
|
||||
|
||||
msgid ""
|
||||
"The 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' config parameter in Sahara was "
|
||||
"deprecated and replaced by 'SAHARA_FLOATING_IP_DISABLED' which has a less "
|
||||
"confusing name."
|
||||
msgstr ""
|
||||
"Parameter konfigurasi 'SAHARA_AUTO_IP_ALLOCATION_ENABLED' di Sahara sudah "
|
||||
"tidak berlaku lagi dan diganti dengan 'SAHARA_FLOATING_IP_DISABLED' yang "
|
||||
"memiliki nama yang tidak membingungkan."
|
||||
|
||||
msgid ""
|
||||
"The config parameter 'SAHARA_FLOATING_IP_DISABLED' replaces "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
msgstr ""
|
||||
"Parameter konfigurasi 'SAHARA_FLOATING_IP_DISABLED' menggantikan "
|
||||
"'SAHARA_AUTO_IP_ALLOCATION_ENABLED'."
|
||||
|
||||
msgid ""
|
||||
"There is now support for APIv2 in sahara-dashboard. Enable it by setting the "
|
||||
"\"data-processing\" API version to \"2\" in the `OPENSTACK_API_VERSIONS` "
|
||||
"dictionary found in Horizon's local_settings.py. Some Sahara features are "
|
||||
"only exposed through APIv2."
|
||||
msgstr ""
|
||||
"Sekarang ada dukungan untuk APIv2 di sahara-dashboard. Aktifkan dengan "
|
||||
"menyetel versi API \"data-processing\" ke \"2\" dalam kamus "
|
||||
"`OPENSTACK_API_VERSIONS` yang ditemukan di local_settings.py Horizon. "
|
||||
"Beberapa fitur Sahara hanya diekspos melalui APIv2."
|
@ -1,27 0,0 @@
|
||||
# Shu Muto <[email protected]>, 2016. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Sahara Dashboard Release Notes\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-02-09 18:15 0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2016-06-16 05:52 0000\n"
|
||||
"Last-Translator: Shu Muto <[email protected]>\n"
|
||||
"Language-Team: Japanese\n"
|
||||
"Language: ja\n"
|
||||
"X-Generator: Zanata 3.9.6\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "開発中バージョンのリリースノート"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "その他の注意点"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara Dashboard リリースノート"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "リリースノートの管理に reno を使い始めました。"
|
@ -1,105 0,0 @@
|
||||
# Ian Y. Choi <[email protected]>, 2016. #zanata
|
||||
# Ian Y. Choi <[email protected]>, 2017. #zanata
|
||||
# Sungjin Kang <[email protected]>, 2017. #zanata
|
||||
# minwook-shin <[email protected]>, 2017. #zanata
|
||||
# Hongjae Kim <[email protected]>, 2019. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-26 16:02 0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2019-11-02 09:05 0000\n"
|
||||
"Last-Translator: Hongjae Kim <[email protected]>\n"
|
||||
"Language-Team: Korean (South Korea)\n"
|
||||
"Language: ko_KR\n"
|
||||
"X-Generator: Zanata 4.3.3\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
|
||||
msgid "5.0.0"
|
||||
msgstr "5.0.0"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "호스트 이름 변환을 위해 Designate와의 통합을 추가하였습니다."
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "버그 수정"
|
||||
|
||||
msgid ""
|
||||
"Coinciding with the official stability of Sahara APIv2, operators should "
|
||||
"feel especially encouraged to set \"data-processing\" API version to \"2\" "
|
||||
"in the `OPENSTACK_API_VERSIONS` dictionary found in Horizon's local_settings."
|
||||
"py. As previously stated, some Sahara features are only exposed through "
|
||||
"APIv2."
|
||||
msgstr ""
|
||||
"사하라 APIv2의 공식 안정성과 일치하여 운영자들은 특히 Horizon의 "
|
||||
"local_settings.py.에 수록된 \"OPENSTACK_API_VERSIONS\" 사전에서 \"데이터 처리"
|
||||
"\" API 버전을 \"2\"로 설정하도록 권장해야 한다. 앞서 언급한 바와 같이 일부 사"
|
||||
"하라 기능은 APIv2를 통해서만 노출된다."
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "현재 시리즈에 대한 릴리즈 노트"
|
||||
|
||||
msgid "Deprecation Notes"
|
||||
msgstr "감가 상각"
|
||||
|
||||
msgid ""
|
||||
"Django 1.10 introduced a new var : DATA_UPLOAD_MAX_NUMBER_FIELDS which "
|
||||
"prevent DOS on data received via GET and POST methods. Default values is set "
|
||||
"to 2000 instead of upstream default of 1000 to keep the security feature and "
|
||||
"to not cause issue with the Sahara Dashboard forms."
|
||||
msgstr ""
|
||||
"Django 1.10에서 새로운 var: DATA_UPLOAD_를 도입함GET 및 POST 방법을 통해 수신"
|
||||
"된 데이터에 대한 DOS를 방지하는 MAX_NUMBER_FIELDS. 보안 기능을 유지하고 사하"
|
||||
"라 대시보드 양식에 문제를 일으키지 않으려면 기본값을 업스트림 기본값 1000이 "
|
||||
"아닌 2000으로 설정하십시오."
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr ""
|
||||
"공용 클러스터에 대한 세부 사항을 가져오는 데 존재하던 이슈를 수정하였습니다."
|
||||
|
||||
msgid ""
|
||||
"Increasing DATA_UPLOAD_MAX_NUMBER_FIELDS django configuration to allow "
|
||||
"creation of CDH node group templates."
|
||||
msgstr ""
|
||||
"DATA_UPLOAD_ 증가CDH 노드 그룹 템플릿을 만들 수 있도록 MAX_NUMBER_FIELDS "
|
||||
"django 구성."
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Mitaka 시리즈에 대한 릴리즈 노트"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "새로운 기능"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton 시리즈에 대한 릴리즈 노트"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata 시리즈에 대한 릴리즈 노트"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "기타 기능"
|
||||
|
||||
msgid "Pike Series Release Notes"
|
||||
msgstr "Pike 시리즈에 대한 릴리즈 노트"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara 대시보드 릴리즈 노트"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "릴리즈 노트 관리를 위해 reno 사용을 시작합니다."
|
||||
|
||||
msgid ""
|
||||
"Support for the new boot from volume options introduced in the Stein release "
|
||||
"of Sahara is added."
|
||||
msgstr ""
|
||||
"Sahara의 Stein 릴리스에 도입된 볼륨 옵션에서 새로운 부팅에 대한 지원이 추가되"
|
||||
"었다."
|
@ -1,58 0,0 @@
|
||||
# sunanchen <[email protected]>, 2016. #zanata
|
||||
# Bin <[email protected]>, 2017. #zanata
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sahara-dashboard\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-10-26 16:02 0000\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"PO-Revision-Date: 2017-07-24 03:49 0000\n"
|
||||
"Last-Translator: Bin <[email protected]>\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 "5.0.0"
|
||||
msgstr "5.0.0版本"
|
||||
|
||||
msgid "5.0.0.0b2"
|
||||
msgstr "5.0.0.0b2版本"
|
||||
|
||||
msgid "5.0.0.0b3"
|
||||
msgstr "5.0.0.0b3版本"
|
||||
|
||||
msgid "Added integration with Designate for hostname resolution."
|
||||
msgstr "给Designate增加域名以便于主机名解析"
|
||||
|
||||
msgid "Bug Fixes"
|
||||
msgstr "修复漏洞"
|
||||
|
||||
msgid "Current Series Release Notes"
|
||||
msgstr "当前版本发布说明"
|
||||
|
||||
msgid "Fixed issue with retrieving details for public clusters."
|
||||
msgstr "修复了公共集群的回收问题"
|
||||
|
||||
msgid "Mitaka Series Release Notes"
|
||||
msgstr "Mitaka版本发布说明"
|
||||
|
||||
msgid "New Features"
|
||||
msgstr "新特征"
|
||||
|
||||
msgid "Newton Series Release Notes"
|
||||
msgstr "Newton版本发布说明"
|
||||
|
||||
msgid "Ocata Series Release Notes"
|
||||
msgstr "Ocata版本发布说明"
|
||||
|
||||
msgid "Other Notes"
|
||||
msgstr "其他注意事项"
|
||||
|
||||
msgid "Sahara Dashboard Release Notes"
|
||||
msgstr "Sahara Dashboard发布说明"
|
||||
|
||||
msgid "Start using reno for release notes management."
|
||||
msgstr "开始使用reno来管理版本发布"
|
@ -1,6 0,0 @@
|
||||
============================
|
||||
Mitaka Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/mitaka
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Newton Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/newton
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Ocata Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: origin/stable/ocata
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Pike Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/pike
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Queens Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/queens
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Rocky Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/rocky
|
@ -1,6 0,0 @@
|
||||
===================================
|
||||
Stein Series Release Notes
|
||||
===================================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/stein
|
@ -1,6 0,0 @@
|
||||
==========================
|
||||
Train Series Release Notes
|
||||
==========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/train
|
@ -1,5 0,0 @@
|
||||
==============================
|
||||
Current Series Release Notes
|
||||
==============================
|
||||
|
||||
.. release-notes::
|
@ -1,6 0,0 @@
|
||||
===========================
|
||||
Ussuri Series Release Notes
|
||||
===========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/ussuri
|
@ -1,6 0,0 @@
|
||||
=============================
|
||||
Victoria Series Release Notes
|
||||
=============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/victoria
|
@ -1,6 0,0 @@
|
||||
============================
|
||||
Wallaby Series Release Notes
|
||||
============================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/wallaby
|
@ -1,6 0,0 @@
|
||||
=========================
|
||||
Xena Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/xena
|
@ -1,6 0,0 @@
|
||||
=========================
|
||||
Yoga Series Release Notes
|
||||
=========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/yoga
|
@ -1,6 0,0 @@
|
||||
========================
|
||||
Zed Series Release Notes
|
||||
========================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/zed
|
@ -1,21 0,0 @@
|
||||
# Requirements lower bounds listed here are our best effort to keep them up to
|
||||
# date but we do not test them so no guarantee of having them all correct. If
|
||||
# you find any incorrect lower bounds, let us know or propose a fix.
|
||||
|
||||
# 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
|
||||
keystoneauth1>=3.8.0 # Apache-2.0
|
||||
oslo.log>=3.36.0 # Apache-2.0
|
||||
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
|
||||
python-designateclient>=2.7.0 # Apache-2.0
|
||||
python-keystoneclient>=3.22.0 # Apache-2.0
|
||||
python-manilaclient>=1.16.0 # Apache-2.0
|
||||
python-neutronclient>=6.7.0 # Apache-2.0
|
||||
python-novaclient>=9.1.0 # Apache-2.0
|
||||
python-saharaclient>=2.2.0 # Apache-2.0
|
||||
pytz>=2013.6 # MIT
|
||||
|
||||
horizon>=17.1.0 # Apache-2.0
|
@ -1,4 0,0 @@
|
||||
---
|
||||
devstack_base_dir: "/opt/stack"
|
||||
sahara_cloud_admin: "devstack-admin"
|
||||
sahara_cloud_demo: "devstack"
|
@ -1,7 0,0 @@
|
||||
{
|
||||
"plugin_labels": {
|
||||
"hidden": {
|
||||
"status": false
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 0,0 @@
|
||||
---
|
||||
- name: copy the updated config snipped which enables the fake plugin
|
||||
copy:
|
||||
src: fake_config.json
|
||||
dest: /tmp/sahara_fake_config.json
|
||||
|
||||
- name: change the config of the fake plugin
|
||||
shell: |
|
||||
openstack --os-cloud {{ sahara_cloud_admin }} --os-project-name demo \
|
||||
dataprocessing plugin update fake /tmp/sahara_fake_config.json
|
@ -1,5 0,0 @@
|
||||
---
|
||||
devstack_base_dir: "/opt/stack"
|
||||
sahara_cloud_image: "https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img"
|
||||
# TODO: if the following option is changed, it should also be specified in tempest.conf
|
||||
sahara_cloud_image_remote_path: "/tmp/xenial-server-cloudimg-amd64-disk1.img"
|
@ -1,5 0,0 @@
|
||||
[image]
|
||||
panel_type=legacy
|
||||
|
||||
[flavors]
|
||||
panel_type=legacy
|
@ -1,21 0,0 @@
|
||||
---
|
||||
- name: download the ubuntu image file
|
||||
get_url:
|
||||
url: "{{ sahara_cloud_image }}"
|
||||
dest: "{{ sahara_cloud_image_remote_path }}"
|
||||
|
||||
# enable legacy panels (two files in local/local_settings.d and a new setting file
|
||||
# for the integration tests)
|
||||
- name: setup the old behavior of tables for testing purposes
|
||||
copy:
|
||||
src: "{{ devstack_base_dir }}/horizon/openstack_dashboard/local/local_settings.d/{{ item }}.example"
|
||||
dest: "{{ devstack_base_dir }}/horizon/openstack_dashboard/local/local_settings.d/{{ item }}"
|
||||
remote_src: yes
|
||||
with_items:
|
||||
- '_20_integration_tests_scaffolds.py'
|
||||
- '_2010_integration_tests_deprecated.py'
|
||||
|
||||
- name: setup the old behavior of panels for testing purposes
|
||||
copy:
|
||||
src: legacy_panels.conf
|
||||
dest: "{{ devstack_base_dir }}/horizon/openstack_dashboard/test/integration_tests/local-horizon.conf"
|
@ -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.
|
||||
|
||||
from sahara_dashboard.api import sahara
|
||||
|
||||
__all__ = [
|
||||
"sahara"
|
||||
]
|
@ -1,34 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from designateclient.v2 import client as designate
|
||||
from keystoneauth1.identity import generic
|
||||
from keystoneauth1 import session as keystone_session
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
|
||||
def client(request):
|
||||
auth_url = base.url_for(request, 'identity')
|
||||
token_kwargs = dict(
|
||||
auth_url=auth_url,
|
||||
token=request.user.token.id,
|
||||
tenant_id=request.user.project_id,
|
||||
tenant_name=request.user.project_name,
|
||||
)
|
||||
auth = generic.Token(**token_kwargs)
|
||||
session = keystone_session.Session(auth=auth)
|
||||
return designate.Client(session=session)
|
||||
|
||||
|
||||
def get_domain_names(request):
|
||||
return client(request).zones.list()
|
@ -1,62 0,0 @@
|
||||
# Copyright 2012 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# Copyright 2012 Nebula, Inc.
|
||||
# Copyright (c) 2012 X.commerce, a business unit of eBay Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from django.conf import settings
|
||||
from manilaclient.v1 import client as manila_client
|
||||
from oslo_log import log as logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# API static values
|
||||
SHARE_STATE_AVAILABLE = "available"
|
||||
DEFAULT_QUOTA_NAME = 'default'
|
||||
|
||||
|
||||
def manilaclient(request):
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
try:
|
||||
manila_url = base.url_for(request, 'share')
|
||||
except exceptions.ServiceCatalogException:
|
||||
LOG.debug('no share service configured.')
|
||||
return None
|
||||
c = manila_client.Client(request.user.username,
|
||||
input_auth_token=request.user.token.id,
|
||||
project_id=request.user.tenant_id,
|
||||
service_catalog_url=manila_url,
|
||||
insecure=insecure,
|
||||
cacert=cacert,
|
||||
http_log_debug=settings.DEBUG)
|
||||
c.client.auth_token = request.user.token.id
|
||||
c.client.management_url = manila_url
|
||||
return c
|
||||
|
||||
|
||||
def share_list(request, search_opts=None):
|
||||
return manilaclient(request).shares.list(search_opts=search_opts)
|
||||
|
||||
|
||||
def share_get(request, share_id):
|
||||
return manilaclient(request).shares.get(share_id)
|
@ -1,778 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 keystoneauth1 import identity
|
||||
from keystoneauth1 import session
|
||||
from saharaclient.api.base import APIException
|
||||
from saharaclient.api.base import Page
|
||||
from saharaclient import client as api_client
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon.utils import functions
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
from sahara_dashboard import utils as u
|
||||
|
||||
|
||||
# "type" of Sahara service registered in keystone
|
||||
SAHARA_SERVICE = 'data-processing'
|
||||
|
||||
try:
|
||||
SAHARA_FLOATING_IP_DISABLED = getattr(
|
||||
settings,
|
||||
'SAHARA_FLOATING_IP_DISABLED')
|
||||
except AttributeError:
|
||||
SAHARA_FLOATING_IP_DISABLED = getattr(
|
||||
settings,
|
||||
'SAHARA_AUTO_IP_ALLOCATION_ENABLED',
|
||||
False)
|
||||
|
||||
SAHARA_VERIFICATION_DISABLED = getattr(
|
||||
settings,
|
||||
'SAHARA_VERIFICATION_DISABLED',
|
||||
False)
|
||||
|
||||
VERSIONS = base.APIVersionManager(
|
||||
SAHARA_SERVICE,
|
||||
preferred_version=getattr(settings,
|
||||
'OPENSTACK_API_VERSIONS',
|
||||
{}).get(SAHARA_SERVICE, 1.1))
|
||||
VERSIONS.load_supported_version(1.1, {"client": api_client,
|
||||
"version": 1.1})
|
||||
VERSIONS.load_supported_version(2, {"client": api_client,
|
||||
"version": 2})
|
||||
|
||||
SAHARA_PAGE_SIZE = 15
|
||||
|
||||
|
||||
def get_page_size(request=None):
|
||||
if request:
|
||||
return functions.get_page_size(request)
|
||||
else:
|
||||
return SAHARA_PAGE_SIZE
|
||||
|
||||
|
||||
def _get_marker(request):
|
||||
return request.GET["marker"] if 'marker' in request.GET else None
|
||||
|
||||
|
||||
def _update_pagination_params(marker, limit, request=None):
|
||||
marker = _get_marker(request) if marker is None else marker
|
||||
limit = get_page_size(request) if limit is None else limit
|
||||
return marker, limit
|
||||
|
||||
|
||||
def safe_call(func, *args, **kwargs):
|
||||
"""Call a function ignoring Not Found error
|
||||
|
||||
This method is supposed to be used only for safe retrieving Sahara
|
||||
objects. If the object is no longer available, then None should be
|
||||
returned.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except APIException as e:
|
||||
if e.error_code == 404:
|
||||
return None # Not found. Exiting with None
|
||||
raise # Other errors are not expected here
|
||||
|
||||
|
||||
@memoized
|
||||
def client(request):
|
||||
insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
|
||||
cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
|
||||
endpoint_type = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL')
|
||||
auth = identity.Token(auth_url=request.user.endpoint,
|
||||
token=request.user.token.id,
|
||||
project_id=request.user.project_id)
|
||||
verify = False
|
||||
if cacert:
|
||||
verify = cacert
|
||||
elif not insecure:
|
||||
verify = True
|
||||
sess = session.Session(auth=auth, verify=verify)
|
||||
return api_client.Client(VERSIONS.get_active_version()["version"],
|
||||
service_type=SAHARA_SERVICE,
|
||||
session=sess,
|
||||
endpoint_type=endpoint_type)
|
||||
|
||||
|
||||
def prepare_acl_update_dict(is_public=None, is_protected=None):
|
||||
data = dict(is_public=is_public, is_protected=is_protected)
|
||||
result = {}
|
||||
for key in data:
|
||||
if data[key] is not None:
|
||||
result[key] = data[key]
|
||||
return result
|
||||
|
||||
|
||||
def image_list(request, search_opts=None):
|
||||
return client(request).images.list(search_opts=search_opts)
|
||||
|
||||
|
||||
def image_get(request, image_id):
|
||||
return client(request).images.get(id=image_id)
|
||||
|
||||
|
||||
def image_unregister(request, image_id):
|
||||
client(request).images.unregister_image(image_id=image_id)
|
||||
|
||||
|
||||
def image_update(request, image_id, user_name, desc):
|
||||
client(request).images.update_image(image_id=image_id,
|
||||
user_name=user_name,
|
||||
desc=desc)
|
||||
|
||||
|
||||
def image_tags_update(request, image_id, image_tags):
|
||||
client(request).images.update_tags(image_id=image_id,
|
||||
new_tags=image_tags)
|
||||
|
||||
|
||||
def plugin_list(request, search_opts=None):
|
||||
return client(request).plugins.list(search_opts=search_opts)
|
||||
|
||||
|
||||
def plugin_get(request, plugin_name):
|
||||
return client(request).plugins.get(plugin_name=plugin_name)
|
||||
|
||||
|
||||
def plugin_get_version_details(request, plugin_name, hadoop_version):
|
||||
return client(request).plugins.get_version_details(
|
||||
plugin_name, hadoop_version)
|
||||
|
||||
|
||||
def nodegroup_template_create(request, name, plugin_name, hadoop_version,
|
||||
flavor_id, description=None,
|
||||
volumes_per_node=None, volumes_size=None,
|
||||
node_processes=None, node_configs=None,
|
||||
floating_ip_pool=None, security_groups=None,
|
||||
auto_security_group=False,
|
||||
availability_zone=False,
|
||||
volumes_availability_zone=False,
|
||||
volume_type=None,
|
||||
image_id=None,
|
||||
is_proxy_gateway=False,
|
||||
volume_local_to_instance=False,
|
||||
use_autoconfig=None,
|
||||
shares=None,
|
||||
is_public=None,
|
||||
is_protected=None,
|
||||
volume_mount_prefix=None,
|
||||
boot_from_volume=None,
|
||||
boot_volume_type=None,
|
||||
boot_volume_availability_zone=None,
|
||||
boot_volume_local_to_instance=None):
|
||||
|
||||
payload = dict(
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
flavor_id=flavor_id,
|
||||
description=description,
|
||||
volumes_per_node=volumes_per_node,
|
||||
volumes_size=volumes_size,
|
||||
node_processes=node_processes,
|
||||
node_configs=node_configs,
|
||||
floating_ip_pool=floating_ip_pool,
|
||||
security_groups=security_groups,
|
||||
auto_security_group=auto_security_group,
|
||||
availability_zone=availability_zone,
|
||||
volumes_availability_zone=volumes_availability_zone,
|
||||
volume_type=volume_type,
|
||||
image_id=image_id,
|
||||
is_proxy_gateway=is_proxy_gateway,
|
||||
volume_local_to_instance=volume_local_to_instance,
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
volume_mount_prefix=volume_mount_prefix)
|
||||
|
||||
if VERSIONS.active == '2':
|
||||
payload['plugin_version'] = hadoop_version
|
||||
payload['boot_from_volume'] = boot_from_volume
|
||||
payload['boot_volume_type'] = boot_volume_type
|
||||
payload['boot_volume_availability_zone'] = (
|
||||
boot_volume_availability_zone
|
||||
)
|
||||
payload['boot_volume_local_to_instance'] = (
|
||||
boot_volume_local_to_instance
|
||||
)
|
||||
else:
|
||||
payload['hadoop_version'] = hadoop_version
|
||||
|
||||
return client(request).node_group_templates.create(**payload)
|
||||
|
||||
|
||||
def nodegroup_template_list(request, search_opts=None,
|
||||
marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).node_group_templates.list(
|
||||
search_opts=search_opts, limit=limit, marker=marker)
|
||||
|
||||
|
||||
def nodegroup_template_get(request, ngt_id):
|
||||
return client(request).node_group_templates.get(ng_template_id=ngt_id)
|
||||
|
||||
|
||||
def nodegroup_template_find(request, **kwargs):
|
||||
if "hadoop_version" in kwargs and VERSIONS.active == '2':
|
||||
kwargs["plugin_version"] = kwargs.pop("hadoop_version")
|
||||
return client(request).node_group_templates.find(**kwargs)
|
||||
|
||||
|
||||
def nodegroup_template_delete(request, ngt_id):
|
||||
client(request).node_group_templates.delete(ng_template_id=ngt_id)
|
||||
|
||||
|
||||
def nodegroup_template_update(request, ngt_id, name, plugin_name,
|
||||
hadoop_version, flavor_id,
|
||||
description=None, volumes_per_node=None,
|
||||
volumes_size=None, node_processes=None,
|
||||
node_configs=None, floating_ip_pool=None,
|
||||
security_groups=None, auto_security_group=False,
|
||||
availability_zone=None,
|
||||
volumes_availability_zone=None,
|
||||
volume_type=None,
|
||||
is_proxy_gateway=False,
|
||||
volume_local_to_instance=False,
|
||||
use_autoconfig=None,
|
||||
shares=None,
|
||||
is_protected=None,
|
||||
is_public=None,
|
||||
image_id=None,
|
||||
boot_from_volume=None,
|
||||
boot_volume_type=None,
|
||||
boot_volume_availability_zone=None,
|
||||
boot_volume_local_to_instance=None):
|
||||
|
||||
payload = dict(
|
||||
ng_template_id=ngt_id,
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
flavor_id=flavor_id,
|
||||
description=description,
|
||||
volumes_per_node=volumes_per_node,
|
||||
volumes_size=volumes_size,
|
||||
node_processes=node_processes,
|
||||
node_configs=node_configs,
|
||||
floating_ip_pool=floating_ip_pool,
|
||||
security_groups=security_groups,
|
||||
auto_security_group=auto_security_group,
|
||||
availability_zone=availability_zone,
|
||||
volumes_availability_zone=volumes_availability_zone,
|
||||
volume_type=volume_type,
|
||||
is_proxy_gateway=is_proxy_gateway,
|
||||
volume_local_to_instance=volume_local_to_instance,
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
image_id=image_id)
|
||||
|
||||
if VERSIONS.active == '2':
|
||||
payload['plugin_version'] = hadoop_version
|
||||
payload['boot_from_volume'] = boot_from_volume
|
||||
payload['boot_volume_type'] = boot_volume_type
|
||||
payload['boot_volume_availability_zone'] = (
|
||||
boot_volume_availability_zone
|
||||
)
|
||||
payload['boot_volume_local_to_instance'] = (
|
||||
boot_volume_local_to_instance
|
||||
)
|
||||
|
||||
else:
|
||||
payload['hadoop_version'] = hadoop_version
|
||||
|
||||
return client(request).node_group_templates.update(**payload)
|
||||
|
||||
|
||||
def nodegroup_update_acl_rules(request, nid,
|
||||
is_public=None, is_protected=None):
|
||||
return client(request).node_group_templates.update(
|
||||
nid, **prepare_acl_update_dict(is_public, is_protected))
|
||||
|
||||
|
||||
def nodegroup_template_export(request, object_id):
|
||||
return client(request).node_group_templates.export(object_id)
|
||||
|
||||
|
||||
def cluster_template_create(request, name, plugin_name, hadoop_version,
|
||||
description=None, cluster_configs=None,
|
||||
node_groups=None, anti_affinity=None,
|
||||
net_id=None, use_autoconfig=None, shares=None,
|
||||
is_public=None, is_protected=None,
|
||||
domain_name=None):
|
||||
payload = dict(
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
description=description,
|
||||
cluster_configs=cluster_configs,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
domain_name=domain_name
|
||||
)
|
||||
if VERSIONS.active == '2':
|
||||
payload['plugin_version'] = hadoop_version
|
||||
else:
|
||||
payload['hadoop_version'] = hadoop_version
|
||||
return client(request).cluster_templates.create(**payload)
|
||||
|
||||
|
||||
def cluster_template_list(request, search_opts=None, marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).cluster_templates.list(
|
||||
search_opts=search_opts,
|
||||
limit=limit,
|
||||
marker=marker)
|
||||
|
||||
|
||||
def cluster_template_get(request, ct_id):
|
||||
return client(request).cluster_templates.get(cluster_template_id=ct_id)
|
||||
|
||||
|
||||
def cluster_template_delete(request, ct_id):
|
||||
client(request).cluster_templates.delete(cluster_template_id=ct_id)
|
||||
|
||||
|
||||
def cluster_template_update(request, ct_id, name, plugin_name,
|
||||
hadoop_version, description=None,
|
||||
cluster_configs=None, node_groups=None,
|
||||
anti_affinity=None, net_id=None,
|
||||
use_autoconfig=None, shares=None,
|
||||
is_public=None, is_protected=None,
|
||||
domain_name=None):
|
||||
try:
|
||||
payload = dict(
|
||||
cluster_template_id=ct_id,
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
description=description,
|
||||
cluster_configs=cluster_configs,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
use_autoconfig=use_autoconfig,
|
||||
shares=shares,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
domain_name=domain_name
|
||||
)
|
||||
if VERSIONS.active == '2':
|
||||
payload['plugin_version'] = hadoop_version
|
||||
else:
|
||||
payload['hadoop_version'] = hadoop_version
|
||||
template = client(request).cluster_templates.update(**payload)
|
||||
|
||||
except APIException as e:
|
||||
raise exceptions.Conflict(e)
|
||||
return template
|
||||
|
||||
|
||||
def cluster_template_update_acl_rules(request, ct_id,
|
||||
is_public=None, is_protected=None):
|
||||
return client(request).cluster_templates.update(
|
||||
ct_id, **prepare_acl_update_dict(is_public, is_protected))
|
||||
|
||||
|
||||
def cluster_template_export(request, object_id):
|
||||
return client(request).cluster_templates.export(object_id)
|
||||
|
||||
|
||||
def cluster_create(request, name, plugin_name, hadoop_version,
|
||||
cluster_template_id=None, default_image_id=None,
|
||||
is_transient=None, description=None, cluster_configs=None,
|
||||
node_groups=None, user_keypair_id=None, anti_affinity=None,
|
||||
net_id=None, count=None, use_autoconfig=None,
|
||||
is_public=None, is_protected=None):
|
||||
payload = dict(
|
||||
name=name,
|
||||
plugin_name=plugin_name,
|
||||
cluster_template_id=cluster_template_id,
|
||||
default_image_id=default_image_id,
|
||||
is_transient=is_transient,
|
||||
description=description,
|
||||
cluster_configs=cluster_configs,
|
||||
node_groups=node_groups,
|
||||
user_keypair_id=user_keypair_id,
|
||||
anti_affinity=anti_affinity,
|
||||
net_id=net_id,
|
||||
count=count,
|
||||
use_autoconfig=use_autoconfig,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected)
|
||||
if VERSIONS.active == '2':
|
||||
payload['plugin_version'] = hadoop_version
|
||||
else:
|
||||
payload['hadoop_version'] = hadoop_version
|
||||
return client(request).clusters.create(**payload)
|
||||
|
||||
|
||||
def cluster_scale(request, cluster_id, scale_object):
|
||||
return client(request).clusters.scale(
|
||||
cluster_id=cluster_id,
|
||||
scale_object=scale_object)
|
||||
|
||||
|
||||
def cluster_list(request, search_opts=None, marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).clusters.list(
|
||||
search_opts=search_opts, limit=limit, marker=marker)
|
||||
|
||||
|
||||
def _cluster_list(request):
|
||||
return client(request).clusters.list()
|
||||
|
||||
|
||||
def cluster_get(request, cluster_id, show_progress=False):
|
||||
return client(request).clusters.get(
|
||||
cluster_id=cluster_id,
|
||||
show_progress=show_progress)
|
||||
|
||||
|
||||
def cluster_delete(request, cluster_id):
|
||||
client(request).clusters.delete(cluster_id=cluster_id)
|
||||
|
||||
|
||||
def cluster_force_delete(request, cluster_id):
|
||||
client(request).clusters.force_delete(cluster_id=cluster_id)
|
||||
|
||||
|
||||
def cluster_update(request, cluster_id, name=None, description=None,
|
||||
is_public=None, is_protected=None, shares=None):
|
||||
return client(request).clusters.update(cluster_id,
|
||||
name=name,
|
||||
description=description,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
shares=shares)
|
||||
|
||||
|
||||
def cluster_update_shares(request, cl_id, shares):
|
||||
return client(request).clusters.update(cl_id, shares)
|
||||
|
||||
|
||||
def cluster_update_acl_rules(request, cl_id, is_public=None,
|
||||
is_protected=None):
|
||||
return client(request).clusters.update(
|
||||
cl_id, **prepare_acl_update_dict(is_public, is_protected))
|
||||
|
||||
|
||||
def data_source_create(request, name, description, ds_type, url,
|
||||
credential_user=None, credential_pass=None,
|
||||
is_public=None, is_protected=None,
|
||||
s3_credentials=None):
|
||||
return client(request).data_sources.create(
|
||||
name=name,
|
||||
description=description,
|
||||
data_source_type=ds_type,
|
||||
url=url,
|
||||
credential_user=credential_user or None,
|
||||
credential_pass=credential_pass or None,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
s3_credentials=s3_credentials)
|
||||
|
||||
|
||||
def data_source_list(request, search_opts=None, limit=None, marker=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).data_sources.list(
|
||||
search_opts=search_opts,
|
||||
limit=limit,
|
||||
marker=marker)
|
||||
|
||||
|
||||
def data_source_get(request, ds_id):
|
||||
return client(request).data_sources.get(data_source_id=ds_id)
|
||||
|
||||
|
||||
def data_source_delete(request, ds_id):
|
||||
client(request).data_sources.delete(data_source_id=ds_id)
|
||||
|
||||
|
||||
def data_source_update(request, ds_id, data):
|
||||
return client(request).data_sources.update(ds_id, data)
|
||||
|
||||
|
||||
def job_binary_create(request, name, url, description, extra,
|
||||
is_public=None, is_protected=None):
|
||||
return client(request).job_binaries.create(
|
||||
name=name,
|
||||
url=url,
|
||||
description=description,
|
||||
extra=extra,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
)
|
||||
|
||||
|
||||
def job_binary_list(request, search_opts=None, marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).job_binaries.list(
|
||||
search_opts=search_opts,
|
||||
limit=limit,
|
||||
marker=marker)
|
||||
|
||||
|
||||
def job_binary_get(request, jb_id):
|
||||
return client(request).job_binaries.get(job_binary_id=jb_id)
|
||||
|
||||
|
||||
def job_binary_delete(request, jb_id):
|
||||
client(request).job_binaries.delete(job_binary_id=jb_id)
|
||||
|
||||
|
||||
def job_binary_get_file(request, jb_id):
|
||||
return client(request).job_binaries.get_file(job_binary_id=jb_id)
|
||||
|
||||
|
||||
def job_binary_update(request, jb_id, data):
|
||||
return client(request).job_binaries.update(jb_id, data)
|
||||
|
||||
|
||||
def job_binary_internal_create(request, name, data):
|
||||
return client(request).job_binary_internals.create(
|
||||
name=name,
|
||||
data=data)
|
||||
|
||||
|
||||
def job_binary_internal_list(request, search_opts=None,
|
||||
marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
return client(request).job_binary_internals.list(
|
||||
search_opts=search_opts,
|
||||
limit=limit,
|
||||
marker=marker)
|
||||
|
||||
|
||||
def job_binary_internal_get(request, jbi_id):
|
||||
# The argument name looks wrong. This should be changed in the sahara
|
||||
# client first and then updated here
|
||||
return client(request).job_binary_internals.get(job_binary_id=jbi_id)
|
||||
|
||||
|
||||
def job_binary_internal_delete(request, jbi_id):
|
||||
# The argument name looks wrong. This should be changed in the sahara
|
||||
# client first and then updated here
|
||||
client(request).job_binary_internals.delete(job_binary_id=jbi_id)
|
||||
|
||||
|
||||
def job_create(request, name, j_type, mains, libs, description, interface,
|
||||
is_public=None, is_protected=None):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).create(
|
||||
name=name,
|
||||
type=j_type,
|
||||
mains=mains,
|
||||
libs=libs,
|
||||
description=description,
|
||||
interface=interface,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected)
|
||||
|
||||
|
||||
def job_update(request, job_id, is_public=None, is_protected=None):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).update(
|
||||
job_id=job_id, **prepare_acl_update_dict(is_public, is_protected))
|
||||
|
||||
|
||||
def job_list(request, search_opts=None, marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).list(
|
||||
search_opts=search_opts,
|
||||
limit=limit,
|
||||
marker=marker)
|
||||
|
||||
|
||||
def _job_list(request):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).list()
|
||||
|
||||
|
||||
def job_get(request, job_id):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).get(job_id=job_id)
|
||||
|
||||
|
||||
def job_delete(request, job_id):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
getattr(sahara, manager).delete(job_id=job_id)
|
||||
|
||||
|
||||
def job_get_configs(request, job_type):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'job_templates'
|
||||
else:
|
||||
manager = 'jobs'
|
||||
return getattr(sahara, manager).get_configs(job_type=job_type)
|
||||
|
||||
|
||||
def job_execution_create(request, job_id, cluster_id,
|
||||
input_id, output_id, configs,
|
||||
interface, is_public=None, is_protected=None):
|
||||
if input_id in [None, "", "None"]:
|
||||
input_id = None
|
||||
if output_id in [None, "", "None"]:
|
||||
output_id = None
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'jobs'
|
||||
else:
|
||||
manager = 'job_executions'
|
||||
return getattr(sahara, manager).create(
|
||||
job_id,
|
||||
cluster_id=cluster_id,
|
||||
input_id=input_id,
|
||||
output_id=output_id,
|
||||
configs=configs,
|
||||
interface=interface,
|
||||
is_public=is_public,
|
||||
is_protected=is_protected,
|
||||
)
|
||||
|
||||
|
||||
def job_execution_update(request, jbe_id, is_public=None, is_protected=None):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'jobs'
|
||||
else:
|
||||
manager = 'job_executions'
|
||||
return getattr(sahara, manager).update(jbe_id,
|
||||
**prepare_acl_update_dict(
|
||||
is_public, is_protected))
|
||||
|
||||
|
||||
def _resolve_job_execution_names(job_execution, cluster=None,
|
||||
job=None):
|
||||
|
||||
job_execution.cluster_name = None
|
||||
if cluster:
|
||||
job_execution.cluster_name = cluster.name
|
||||
|
||||
job_execution.job_name = None
|
||||
if job:
|
||||
job_execution.job_name = job.name
|
||||
|
||||
return job_execution
|
||||
|
||||
|
||||
def job_execution_list(request, search_opts=None, marker=None, limit=None):
|
||||
marker, limit = _update_pagination_params(marker, limit, request)
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'jobs'
|
||||
else:
|
||||
manager = 'job_executions'
|
||||
job_execution_list = getattr(sahara, manager).list(
|
||||
search_opts=search_opts, limit=limit,
|
||||
marker=marker)
|
||||
|
||||
new_request = u.delete_pagination_params_from_request(
|
||||
request, save_limit=False)
|
||||
|
||||
job_dict = {j.id: j for j in _job_list(new_request)}
|
||||
cluster_dict = {c.id: c for c in _cluster_list(new_request)}
|
||||
|
||||
def _find_jt_id(jex_obj):
|
||||
try:
|
||||
return jex_obj.job_template_id # typical APIv2
|
||||
except AttributeError:
|
||||
return jex_obj.job_id # APIv1.1, older APIv2
|
||||
|
||||
resolved_job_execution_list = [
|
||||
_resolve_job_execution_names(
|
||||
job_execution,
|
||||
cluster_dict.get(job_execution.cluster_id),
|
||||
job_dict.get(_find_jt_id(job_execution)))
|
||||
for job_execution in job_execution_list
|
||||
]
|
||||
|
||||
return Page(resolved_job_execution_list, job_execution_list.prev,
|
||||
job_execution_list.next, job_execution_list.limit)
|
||||
|
||||
|
||||
def job_execution_get(request, jex_id):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
je_manager = 'jobs'
|
||||
jt_manager = 'job_templates'
|
||||
else:
|
||||
je_manager = 'job_executions'
|
||||
jt_manager = 'jobs'
|
||||
|
||||
jex = getattr(sahara, je_manager).get(obj_id=jex_id)
|
||||
cluster = safe_call(client(request).clusters.get, jex.cluster_id)
|
||||
try:
|
||||
jt_id = jex.job_template_id # typical APIv2
|
||||
except AttributeError:
|
||||
jt_id = jex.job_id # APIv1.1, older APIv2
|
||||
job = safe_call(getattr(sahara, jt_manager).get, jt_id)
|
||||
|
||||
return _resolve_job_execution_names(jex, cluster, job)
|
||||
|
||||
|
||||
def job_execution_delete(request, jex_id):
|
||||
sahara = client(request)
|
||||
if VERSIONS.active == '2':
|
||||
manager = 'jobs'
|
||||
else:
|
||||
manager = 'job_executions'
|
||||
getattr(sahara, manager).delete(obj_id=jex_id)
|
||||
|
||||
|
||||
def job_types_list(request):
|
||||
return client(request).job_types.list()
|
||||
|
||||
|
||||
def verification_update(request, cluster_id, status):
|
||||
return client(request).clusters.verification_update(cluster_id, status)
|
||||
|
||||
|
||||
def plugin_update(request, plugin_name, values):
|
||||
return client(request).plugins.update(plugin_name, values)
|
@ -1,220 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 oslo_serialization import jsonutils as json
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing. \
|
||||
utils.workflow_helpers as whelpers
|
||||
from sahara_dashboard import utils
|
||||
|
||||
BASE_IMAGE_URL = "horizon:project:data_processing.clusters:register"
|
||||
|
||||
|
||||
class ImportClusterTemplateFileForm(forms.SelfHandlingForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Import Cluster Template")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.next_view = kwargs.pop("next_view")
|
||||
super(ImportClusterTemplateFileForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
|
||||
template_upload = forms.FileField(
|
||||
label=_("Template File"),
|
||||
required=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
kwargs = {"template_upload": data["template_upload"]}
|
||||
request.method = "GET"
|
||||
return self.next_view.as_view()(request, **kwargs)
|
||||
|
||||
|
||||
class ImportClusterTemplateNameForm(forms.SelfHandlingForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Import Cluster Template")
|
||||
|
||||
template_upload = forms.CharField(
|
||||
widget=forms.widgets.HiddenInput)
|
||||
|
||||
name = forms.CharField(label=_("Name"),
|
||||
required=False,
|
||||
help_text=_("Name must be provided "
|
||||
"either here or in the template. If "
|
||||
"provided in both places, this one "
|
||||
"will be used."))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
request = args[0]
|
||||
template_string = ""
|
||||
self.next_view = kwargs.pop("next_view")
|
||||
|
||||
if "template_upload" in kwargs:
|
||||
template_upload = kwargs.pop("template_upload")
|
||||
super(ImportClusterTemplateNameForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
|
||||
template_string = template_upload.read()
|
||||
self.fields["template_upload"].initial = template_string
|
||||
|
||||
else:
|
||||
super(ImportClusterTemplateNameForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
except (ValueError, KeyError):
|
||||
raise exceptions.BadRequest(_("Could not parse template"))
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
|
||||
def handle(self, request, data):
|
||||
template = data["template_upload"]
|
||||
if data["name"]:
|
||||
template = json.loads(template)
|
||||
template["cluster_template"]["name"] = data["name"]
|
||||
template = json.dumps(template)
|
||||
kwargs = {"template_upload": template}
|
||||
request.method = "GET"
|
||||
return self.next_view.as_view()(request, **kwargs)
|
||||
|
||||
|
||||
class ImportClusterTemplateNodegroupsForm(forms.SelfHandlingForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Import Cluster Template")
|
||||
|
||||
template_upload = forms.CharField(
|
||||
widget=forms.widgets.HiddenInput)
|
||||
hidden_nodegroups_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_nodegroups_field"}))
|
||||
forms_ids = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
request = args[0]
|
||||
template_string = ""
|
||||
|
||||
if "template_upload" in kwargs:
|
||||
template_string = kwargs.pop("template_upload")
|
||||
super(ImportClusterTemplateNodegroupsForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
|
||||
self.fields["template_upload"].initial = template_string
|
||||
|
||||
else:
|
||||
super(ImportClusterTemplateNodegroupsForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
template_string = self.data["template_upload"]
|
||||
|
||||
template_json = json.loads(template_string)
|
||||
template_json = template_json["cluster_template"]
|
||||
|
||||
req = request.GET.copy()
|
||||
req.update(request.POST)
|
||||
|
||||
plugin = template_json["plugin_name"]
|
||||
version = (template_json.get("hadoop_version", None) or
|
||||
template_json["plugin_version"])
|
||||
|
||||
if not plugin or not version:
|
||||
self.templates = saharaclient.nodegroup_template_find(request)
|
||||
else:
|
||||
self.templates = saharaclient.nodegroup_template_find(
|
||||
request, plugin_name=plugin, hadoop_version=version)
|
||||
|
||||
deletable = req.get("deletable", dict())
|
||||
|
||||
if "forms_ids" in req:
|
||||
self.groups = []
|
||||
for id in json.loads(req["forms_ids"]):
|
||||
group_name = "group_name_" str(id)
|
||||
template_id = "template_id_" str(id)
|
||||
count = "count_" str(id)
|
||||
serialized = "serialized_" str(id)
|
||||
self.groups.append({"name": req[group_name],
|
||||
"template_id": req[template_id],
|
||||
"count": req[count],
|
||||
"id": id,
|
||||
"deletable": deletable.get(
|
||||
req[group_name], "true"),
|
||||
"serialized": req[serialized]})
|
||||
|
||||
whelpers.build_node_group_fields(self,
|
||||
group_name,
|
||||
template_id,
|
||||
count,
|
||||
serialized)
|
||||
|
||||
except (ValueError, KeyError):
|
||||
raise exceptions.BadRequest(_("Could not parse template"))
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
template = data["template_upload"]
|
||||
template = json.loads(template)
|
||||
template = template["cluster_template"]
|
||||
|
||||
if "name" not in template.keys():
|
||||
return False
|
||||
|
||||
if "neutron_management_network" in template:
|
||||
template["net_id"] = (
|
||||
template.pop("neutron_management_network"))
|
||||
|
||||
# default_image_id is not supported by the client now
|
||||
if "default_image_id" in template:
|
||||
template.pop("default_image_id")
|
||||
|
||||
node_groups = []
|
||||
ids = json.loads(data['forms_ids'])
|
||||
for id in ids:
|
||||
name = data['group_name_' str(id)]
|
||||
template_id = data['template_id_' str(id)]
|
||||
count = data['count_' str(id)]
|
||||
|
||||
raw_ng = data.get("serialized_" str(id))
|
||||
|
||||
if raw_ng and raw_ng != 'null':
|
||||
ng = json.loads(utils.deserialize(str(raw_ng)))
|
||||
else:
|
||||
ng = dict()
|
||||
ng["name"] = name
|
||||
ng["count"] = count
|
||||
if template_id and template_id != u'None':
|
||||
ng["node_group_template_id"] = template_id
|
||||
node_groups.append(ng)
|
||||
|
||||
template["node_groups"] = node_groups
|
||||
|
||||
saharaclient.cluster_template_create(request, **template)
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception as e:
|
||||
if isinstance(e, TypeError):
|
||||
raise exceptions.BadRequest(
|
||||
_("Template JSON contained invalid key"))
|
||||
else:
|
||||
raise exceptions.BadRequest(_("Could not parse template"))
|
@ -1,208 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django import http as http_response
|
||||
from django.template import defaultfilters as filters
|
||||
from django import urls
|
||||
from django.utils import http
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon.tabs import base as tabs_base
|
||||
from oslo_serialization import jsonutils as json
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content.data_processing \
|
||||
import tables as sahara_table
|
||||
from sahara_dashboard.content.data_processing.utils \
|
||||
import acl as acl_utils
|
||||
|
||||
|
||||
class ClusterTemplatesFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = (('name', _("Name"), True),
|
||||
('plugin_name', _("Plugin"), True),
|
||||
('hadoop_version', _("Version"), True),
|
||||
('description', _("Description")))
|
||||
|
||||
|
||||
class CreateCluster(tables.LinkAction):
|
||||
name = "create cluster"
|
||||
verbose_name = _("Launch Cluster")
|
||||
url = "horizon:project:data_processing.clusters:configure-cluster"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
def get_link_url(self, datum):
|
||||
base_url = urls.reverse(self.url)
|
||||
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
version_attr = "plugin_version"
|
||||
else:
|
||||
version_attr = "hadoop_version"
|
||||
params = http.urlencode({"hadoop_version":
|
||||
getattr(datum, version_attr),
|
||||
"plugin_name": datum.plugin_name,
|
||||
"cluster_template_id": datum.id})
|
||||
return "?".join([base_url, params])
|
||||
|
||||
|
||||
class CopyTemplate(tables.LinkAction):
|
||||
name = "copy"
|
||||
verbose_name = _("Copy Template")
|
||||
url = "horizon:project:data_processing.clusters:ct-copy"
|
||||
classes = ("ajax-modal", )
|
||||
|
||||
|
||||
class ExportTemplate(tables.Action):
|
||||
name = "export"
|
||||
verbose_name = _("Export Template")
|
||||
classes = ("ajax-modal", )
|
||||
|
||||
def single(self, data_table, request, object_id):
|
||||
content = json.dumps(saharaclient.cluster_template_export(
|
||||
request, object_id)._info)
|
||||
response = http_response.HttpResponse(
|
||||
content, content_type="application/json")
|
||||
filename = '%s-cluster-template.json' % object_id
|
||||
disposition = 'attachment; filename="%s"' % filename
|
||||
response['Content-Disposition'] = disposition.encode('utf-8')
|
||||
response['Content-Length'] = str(len(response.content))
|
||||
return response
|
||||
|
||||
|
||||
class EditTemplate(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit Template")
|
||||
url = "horizon:project:data_processing.clusters:ct-edit"
|
||||
classes = ("ajax-modal", )
|
||||
|
||||
|
||||
class DeleteTemplate(tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Delete Template",
|
||||
u"Delete Templates",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Deleted Template",
|
||||
u"Deleted Templates",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, template_id):
|
||||
saharaclient.cluster_template_delete(request, template_id)
|
||||
|
||||
|
||||
class CreateClusterTemplate(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Template")
|
||||
url = ("horizon:project:data_processing.clusters:"
|
||||
"create-cluster-template")
|
||||
classes = ("ajax-modal", "create-clustertemplate-btn")
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class ImportClusterTemplate(tables.LinkAction):
|
||||
name = "import"
|
||||
verbose_name = _("Import Template")
|
||||
url = ("horizon:project:data_processing.clusters:"
|
||||
"import-cluster-template-file")
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class ConfigureClusterTemplate(tables.LinkAction):
|
||||
name = "configure"
|
||||
verbose_name = _("Configure Cluster Template")
|
||||
url = ("horizon:project:data_processing.clusters:"
|
||||
"configure-cluster-template")
|
||||
classes = ("ajax-modal", "configure-clustertemplate-btn")
|
||||
icon = "plus"
|
||||
attrs = {"style": "display: none"}
|
||||
|
||||
|
||||
def render_node_groups(cluster_template):
|
||||
node_groups = [node_group['name'] ': ' str(node_group['count'])
|
||||
for node_group in cluster_template.node_groups]
|
||||
return node_groups
|
||||
|
||||
|
||||
class MakePublic(acl_utils.MakePublic):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_template_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakePrivate(acl_utils.MakePrivate):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_template_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakeProtected(acl_utils.MakeProtected):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_template_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakeUnProtected(acl_utils.MakeUnProtected):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_template_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class ClusterTemplatesTable(sahara_table.SaharaPaginateTabbedTable):
|
||||
tab_name = 'cluster_tabs%sclusters_templates_tab' % tabs_base.SEPARATOR
|
||||
|
||||
name = tables.Column("name",
|
||||
verbose_name=_("Name"),
|
||||
link=("horizon:project:data_processing."
|
||||
"clusters:ct-details"))
|
||||
plugin_name = tables.Column("plugin_name",
|
||||
verbose_name=_("Plugin"))
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
version_attr = "plugin_version"
|
||||
else:
|
||||
version_attr = "hadoop_version"
|
||||
hadoop_version = tables.Column(version_attr,
|
||||
verbose_name=_("Version"))
|
||||
node_groups = tables.Column(render_node_groups,
|
||||
verbose_name=_("Node Groups"),
|
||||
wrap_list=True,
|
||||
filters=(filters.unordered_list,))
|
||||
description = tables.Column("description",
|
||||
verbose_name=_("Description"))
|
||||
|
||||
class Meta(object):
|
||||
name = "cluster_templates"
|
||||
verbose_name = _("Cluster Templates")
|
||||
table_actions = (CreateClusterTemplate,
|
||||
ImportClusterTemplate,
|
||||
ConfigureClusterTemplate,
|
||||
DeleteTemplate,
|
||||
ClusterTemplatesFilterAction,)
|
||||
table_actions_menu = (MakePublic, MakePrivate,
|
||||
MakeProtected, MakeUnProtected)
|
||||
row_actions = (CreateCluster,
|
||||
EditTemplate,
|
||||
CopyTemplate,
|
||||
ExportTemplate,
|
||||
DeleteTemplate, MakePublic, MakePrivate,
|
||||
MakeProtected, MakeUnProtected)
|
@ -1,118 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 oslo_log import log as logging
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from openstack_dashboard.api import nova
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content. \
|
||||
data_processing.utils import workflow_helpers as helpers
|
||||
from sahara_dashboard.content.data_processing.clusters.cluster_templates \
|
||||
import tables as cluster_template_tables
|
||||
from sahara_dashboard.content.data_processing \
|
||||
import tabs as sahara_tabs
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClusterTemplatesTab(sahara_tabs.SaharaTableTab):
|
||||
table_classes = (cluster_template_tables.ClusterTemplatesTable, )
|
||||
name = _("Cluster Templates")
|
||||
slug = "clusters_templates_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_cluster_templates_data(self):
|
||||
try:
|
||||
table = self._tables['cluster_templates']
|
||||
search_opts = {}
|
||||
filter = self.get_server_filter_info(table.request, table)
|
||||
if filter['value'] and filter['field']:
|
||||
search_opts = {filter['field']: filter['value']}
|
||||
cluster_templates = saharaclient.cluster_template_list(
|
||||
self.request, search_opts)
|
||||
except Exception:
|
||||
cluster_templates = []
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch cluster template list"))
|
||||
return cluster_templates
|
||||
|
||||
|
||||
class GeneralTab(tabs.Tab):
|
||||
name = _("General Info")
|
||||
slug = "cluster_template_details_tab"
|
||||
template_name = "cluster_templates/_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
template_id = self.tab_group.kwargs['template_id']
|
||||
try:
|
||||
template = saharaclient.cluster_template_get(request, template_id)
|
||||
except Exception as e:
|
||||
template = {}
|
||||
LOG.error("Unable to fetch cluster template details: %s" % str(e))
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
template.hadoop_version = template.plugin_version
|
||||
return {"template": template}
|
||||
|
||||
|
||||
class ClusterTemplateConfigsDetails(tabs.Tab):
|
||||
name = _("Configuration Details")
|
||||
slug = "cluster_template_configs_details_tab"
|
||||
template_name = (
|
||||
"cluster_templates/_cluster_template_configs_details.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
template_id = self.tab_group.kwargs['template_id']
|
||||
try:
|
||||
template = saharaclient.cluster_template_get(request, template_id)
|
||||
except Exception as e:
|
||||
template = {}
|
||||
LOG.error("Unable to fetch cluster template details: %s" % str(e))
|
||||
return {"template": template}
|
||||
|
||||
|
||||
class NodeGroupsTab(tabs.Tab):
|
||||
name = _("Node Groups")
|
||||
slug = "cluster_template_nodegroups_tab"
|
||||
template_name = "cluster_templates/_nodegroups_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
template_id = self.tab_group.kwargs['template_id']
|
||||
try:
|
||||
template = saharaclient.cluster_template_get(request, template_id)
|
||||
for ng in template.node_groups:
|
||||
if not ng["flavor_id"]:
|
||||
continue
|
||||
ng["flavor_name"] = (
|
||||
nova.flavor_get(request, ng["flavor_id"]).name)
|
||||
ng["node_group_template"] = saharaclient.safe_call(
|
||||
saharaclient.nodegroup_template_get,
|
||||
request, ng.get("node_group_template_id", None))
|
||||
ng["security_groups_full"] = helpers.get_security_groups(
|
||||
request, ng.get("security_groups"))
|
||||
except Exception:
|
||||
template = {}
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch node group details."))
|
||||
return {"template": template}
|
||||
|
||||
|
||||
class ClusterTemplateDetailsTabs(tabs.TabGroup):
|
||||
slug = "cluster_template_details"
|
||||
tabs = (GeneralTab, ClusterTemplateConfigsDetails, NodeGroupsTab, )
|
||||
sticky = True
|
@ -1,159 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 copy
|
||||
|
||||
from django.urls import reverse
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from openstack_dashboard import api as dash_api
|
||||
|
||||
from sahara_dashboard import api
|
||||
from sahara_dashboard.test import helpers as test
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:project:data_processing.clusters:'
|
||||
'cluster-templates-tab')
|
||||
DETAILS_URL = reverse(
|
||||
'horizon:project:data_processing.clusters:ct-details', args=['id'])
|
||||
|
||||
|
||||
class DataProcessingClusterTemplateTests(test.TestCase):
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_list',
|
||||
'image_list',
|
||||
'cluster_list',
|
||||
'nodegroup_template_list')})
|
||||
def test_index(self):
|
||||
self.mock_cluster_template_list.return_value = \
|
||||
self.cluster_templates.list()
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.mock_cluster_template_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), {})
|
||||
self.assertTemplateUsed(res, 'clusters/index.html')
|
||||
self.assertContains(res, 'Cluster Templates')
|
||||
self.assertContains(res, 'Name')
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_get',
|
||||
'nodegroup_template_get'),
|
||||
dash_api.nova: ('flavor_get',)})
|
||||
def test_details(self):
|
||||
flavor = self.flavors.first()
|
||||
ct = self.cluster_templates.first()
|
||||
self.mock_flavor_get.return_value = flavor
|
||||
self.mock_cluster_template_get.return_value = ct
|
||||
res = self.client.get(DETAILS_URL)
|
||||
self.assertTemplateUsed(res, 'horizon/common/_detail.html')
|
||||
|
||||
@test.create_mocks({api.sahara: ('client',
|
||||
'cluster_template_get',
|
||||
'plugin_get_version_details',
|
||||
'nodegroup_template_find')})
|
||||
def test_copy(self):
|
||||
ct = self.cluster_templates.first()
|
||||
ngts = self.nodegroup_templates.list()
|
||||
configs = self.plugins_configs.first()
|
||||
self.mock_cluster_template_get.return_value = ct
|
||||
self.mock_plugin_get_version_details.return_value = configs
|
||||
self.mock_nodegroup_template_find.return_value = ngts
|
||||
|
||||
url = reverse('horizon:project:data_processing.clusters:ct-copy',
|
||||
args=[ct.id])
|
||||
res = self.client.get(url)
|
||||
self.mock_cluster_template_get.assert_called_once_with(
|
||||
test.IsHttpRequest(), ct.id)
|
||||
workflow = res.context['workflow']
|
||||
step = workflow.get_step("generalconfigaction")
|
||||
self.assertEqual(step.action['cluster_template_name'].field.initial,
|
||||
ct.name "-copy")
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_list',
|
||||
'cluster_template_delete')})
|
||||
def test_delete(self):
|
||||
ct = self.cluster_templates.first()
|
||||
self.mock_cluster_template_list.return_value = \
|
||||
self.cluster_templates.list()
|
||||
self.mock_cluster_template_delete.return_value = None
|
||||
|
||||
form_data = {'action': 'cluster_templates__delete__%s' % ct.id}
|
||||
res = self.client.post(INDEX_URL, form_data)
|
||||
|
||||
self.mock_cluster_template_list.assert_called_once_with(
|
||||
test.IsHttpRequest(), {})
|
||||
self.mock_cluster_template_delete.assert_called_once_with(
|
||||
test.IsHttpRequest(), ct.id)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
@test.create_mocks({api.sahara: ('client',
|
||||
'cluster_template_get',
|
||||
'cluster_template_update',
|
||||
'plugin_get_version_details',
|
||||
'nodegroup_template_find')})
|
||||
def test_update(self):
|
||||
ct = self.cluster_templates.first()
|
||||
ngts = self.nodegroup_templates.list()
|
||||
configs = self.plugins_configs.first()
|
||||
new_name = "UpdatedName"
|
||||
new_ct = copy.copy(ct)
|
||||
new_ct.name = new_name
|
||||
self.mock_cluster_template_get.return_value = ct
|
||||
self.mock_plugin_get_version_details.return_value = configs
|
||||
self.mock_nodegroup_template_find.return_value = ngts
|
||||
self.mock_cluster_template_update.return_value = new_ct
|
||||
|
||||
url = reverse('horizon:project:data_processing.clusters:ct-edit',
|
||||
args=[ct.id])
|
||||
|
||||
def serialize(obj):
|
||||
return utils.serialize(jsonutils.dump_as_bytes(obj))
|
||||
|
||||
res = self.client.post(
|
||||
url,
|
||||
{'ct_id': ct.id,
|
||||
'cluster_template_name': new_name,
|
||||
'plugin_name': ct.plugin_name,
|
||||
'hadoop_version': ct.hadoop_version,
|
||||
'description': ct.description,
|
||||
'hidden_configure_field': "",
|
||||
'template_id_0': ct.node_groups[0]['node_group_template_id'],
|
||||
'group_name_0': ct.node_groups[0]['name'],
|
||||
'count_0': 1,
|
||||
'serialized_0': serialize(ct.node_groups[0]),
|
||||
'template_id_1': ct.node_groups[1]['node_group_template_id'],
|
||||
'group_name_1': ct.node_groups[1]['name'],
|
||||
'count_1': 2,
|
||||
'serialized_1': serialize(ct.node_groups[1]),
|
||||
'forms_ids': "[0,1]",
|
||||
})
|
||||
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertMessageCount(success=1)
|
||||
self.mock_cluster_template_update.assert_called_once_with(
|
||||
request=test.IsHttpRequest(),
|
||||
ct_id=ct.id,
|
||||
name=new_name,
|
||||
plugin_name=ct.plugin_name,
|
||||
hadoop_version=ct.hadoop_version,
|
||||
description=ct.description,
|
||||
cluster_configs=ct.cluster_configs,
|
||||
node_groups=ct.node_groups,
|
||||
anti_affinity=ct.anti_affinity,
|
||||
use_autoconfig=False,
|
||||
shares=ct.shares,
|
||||
is_public=False,
|
||||
is_protected=False,
|
||||
domain_name=ct.domain_name
|
||||
)
|
@ -1,181 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 horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
from horizon import workflows
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.tables as ct_tables
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.tabs as _tabs
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.copy as copy_flow
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.create as create_flow
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.edit as edit_flow
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.forms.import_forms as import_forms
|
||||
|
||||
|
||||
class ClusterTemplateDetailsView(tabs.TabView):
|
||||
tab_group_class = _tabs.ClusterTemplateDetailsTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
page_title = "{{ template.name|default:template.id }}"
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
ct_id = self.kwargs["template_id"]
|
||||
try:
|
||||
return saharaclient.cluster_template_get(self.request, ct_id)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve details for '
|
||||
'cluster template "%s".') % ct_id
|
||||
redirect = self.get_redirect_url()
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ClusterTemplateDetailsView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
cluster_template = self.get_object()
|
||||
context['template'] = cluster_template
|
||||
context['url'] = self.get_redirect_url()
|
||||
context['actions'] = self._get_actions(cluster_template)
|
||||
return context
|
||||
|
||||
def _get_actions(self, cluster_template):
|
||||
table = ct_tables.ClusterTemplatesTable(self.request)
|
||||
return table.render_row_actions(cluster_template)
|
||||
|
||||
@staticmethod
|
||||
def get_redirect_url():
|
||||
return reverse("horizon:project:data_processing."
|
||||
"clusters:index")
|
||||
|
||||
|
||||
class CreateClusterTemplateView(workflows.WorkflowView):
|
||||
workflow_class = create_flow.CreateClusterTemplate
|
||||
success_url = ("horizon:project:data_processing.clusters"
|
||||
":create-cluster-template")
|
||||
classes = ("ajax-modal",)
|
||||
template_name = "cluster_templates/create.html"
|
||||
page_title = _("Create Cluster Template")
|
||||
|
||||
|
||||
class ConfigureClusterTemplateView(workflows.WorkflowView):
|
||||
workflow_class = create_flow.ConfigureClusterTemplate
|
||||
success_url = ("horizon:project:data_processing.clusters"
|
||||
":index")
|
||||
template_name = "cluster_templates/configure.html"
|
||||
page_title = _("Configure Cluster Template")
|
||||
|
||||
|
||||
class CopyClusterTemplateView(workflows.WorkflowView):
|
||||
workflow_class = copy_flow.CopyClusterTemplate
|
||||
success_url = ("horizon:project:data_processing.clusters"
|
||||
":index")
|
||||
template_name = "cluster_templates/configure.html"
|
||||
page_title = _("Copy Cluster Template")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CopyClusterTemplateView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
|
||||
context["template_id"] = kwargs["template_id"]
|
||||
return context
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
if not hasattr(self, "_object"):
|
||||
template_id = self.kwargs['template_id']
|
||||
try:
|
||||
template = saharaclient.cluster_template_get(self.request,
|
||||
template_id)
|
||||
except Exception:
|
||||
template = {}
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch cluster template."))
|
||||
self._object = template
|
||||
return self._object
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(CopyClusterTemplateView, self).get_initial()
|
||||
initial['template_id'] = self.kwargs['template_id']
|
||||
return initial
|
||||
|
||||
|
||||
class EditClusterTemplateView(CopyClusterTemplateView):
|
||||
workflow_class = edit_flow.EditClusterTemplate
|
||||
success_url = ("horizon:project:data_processing.clusters"
|
||||
":index")
|
||||
template_name = "cluster_templates/configure.html"
|
||||
|
||||
|
||||
class ImportClusterTemplateFileView(forms.ModalFormView):
|
||||
template_name = "cluster_templates/import.html"
|
||||
form_class = import_forms.ImportClusterTemplateFileForm
|
||||
submit_label = _("Next")
|
||||
submit_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:import-cluster-template-file")
|
||||
success_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:import-cluster-template-name")
|
||||
page_title = _("Import Cluster Template")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(ImportClusterTemplateFileView, self).get_form_kwargs()
|
||||
kwargs['next_view'] = ImportClusterTemplateNameView
|
||||
return kwargs
|
||||
|
||||
|
||||
class ImportClusterTemplateNameView(forms.ModalFormView):
|
||||
template_name = "cluster_templates/import.html"
|
||||
form_class = import_forms.ImportClusterTemplateNameForm
|
||||
submit_label = _("Next")
|
||||
submit_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:import-cluster-template-name")
|
||||
success_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:import-cluster-template-nodegroups")
|
||||
page_title = _("Import Cluster Template")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(ImportClusterTemplateNameView, self).get_form_kwargs()
|
||||
kwargs['next_view'] = ImportClusterTemplateNodegroupsView
|
||||
if 'template_upload' in self.kwargs:
|
||||
kwargs['template_upload'] = self.kwargs['template_upload']
|
||||
return kwargs
|
||||
|
||||
|
||||
class ImportClusterTemplateNodegroupsView(forms.ModalFormView):
|
||||
template_name = "cluster_templates/import_nodegroups.html"
|
||||
# template_name = "some_random_stuff.html"
|
||||
form_class = import_forms.ImportClusterTemplateNodegroupsForm
|
||||
submit_label = _("Import")
|
||||
submit_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:import-cluster-template-nodegroups")
|
||||
success_url = reverse_lazy("horizon:project:data_processing."
|
||||
"clusters:index")
|
||||
page_title = _("Import Cluster Template")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super(ImportClusterTemplateNodegroupsView,
|
||||
self).get_form_kwargs()
|
||||
if 'template_upload' in self.kwargs:
|
||||
kwargs['template_upload'] = self.kwargs['template_upload']
|
||||
return kwargs
|
@ -1,137 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.create as create_flow
|
||||
import sahara_dashboard.content.data_processing.utils. \
|
||||
workflow_helpers as wf_helpers
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
class CopyClusterTemplate(create_flow.ConfigureClusterTemplate):
|
||||
success_message = _("Cluster Template copy %s created")
|
||||
entry_point = "generalconfigaction"
|
||||
|
||||
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
|
||||
self.cluster_template_id = context_seed["template_id"]
|
||||
try:
|
||||
self.template = saharaclient.cluster_template_get(
|
||||
request,
|
||||
self.cluster_template_id)
|
||||
self._set_configs_to_copy(self.template.cluster_configs)
|
||||
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
version_attr = 'plugin_version'
|
||||
else:
|
||||
version_attr = 'hadoop_version'
|
||||
hadoop_version = getattr(self.template, version_attr)
|
||||
|
||||
request.GET = request.GET.copy()
|
||||
request.GET.update({"plugin_name": self.template.plugin_name,
|
||||
version_attr: hadoop_version,
|
||||
"aa_groups": self.template.anti_affinity})
|
||||
|
||||
super(CopyClusterTemplate, self).__init__(request, context_seed,
|
||||
entry_point, *args,
|
||||
**kwargs)
|
||||
# Initialize node groups.
|
||||
# TODO(rdopieralski) The same (or very similar) code appears
|
||||
# multiple times in this dashboard. It should be refactored to
|
||||
# a function.
|
||||
for step in self.steps:
|
||||
if isinstance(step, create_flow.ConfigureNodegroups):
|
||||
ng_action = step.action
|
||||
template_ngs = self.template.node_groups
|
||||
|
||||
if 'forms_ids' in request.POST:
|
||||
continue
|
||||
ng_action.groups = []
|
||||
for i, templ_ng in enumerate(template_ngs):
|
||||
group_name = "group_name_%d" % i
|
||||
template_id = "template_id_%d" % i
|
||||
count = "count_%d" % i
|
||||
serialized = "serialized_%d" % i
|
||||
|
||||
# save the original node group with all its fields in
|
||||
# case the template id is missing
|
||||
serialized_val = utils.serialize(
|
||||
json.dumps(wf_helpers.clean_node_group(templ_ng)))
|
||||
|
||||
ng = {
|
||||
"name": templ_ng["name"],
|
||||
"count": templ_ng["count"],
|
||||
"id": i,
|
||||
"deletable": "true",
|
||||
"serialized": serialized_val
|
||||
}
|
||||
if "node_group_template_id" in templ_ng:
|
||||
ng["template_id"] = templ_ng[
|
||||
"node_group_template_id"]
|
||||
ng_action.groups.append(ng)
|
||||
|
||||
wf_helpers.build_node_group_fields(
|
||||
ng_action, group_name, template_id, count,
|
||||
serialized)
|
||||
|
||||
elif isinstance(step, create_flow.GeneralConfig):
|
||||
fields = step.action.fields
|
||||
fields["cluster_template_name"].initial = (
|
||||
self.template.name "-copy")
|
||||
fields['use_autoconfig'].initial = (
|
||||
self.template.use_autoconfig)
|
||||
fields["description"].initial = self.template.description
|
||||
|
||||
elif isinstance(step, create_flow.SelectClusterShares):
|
||||
fields = step.action.fields
|
||||
fields["shares"].initial = (
|
||||
self._get_share_defaults(fields["shares"].choices)
|
||||
)
|
||||
fields['is_public'].initial = (
|
||||
self.template.is_public)
|
||||
fields['is_protected'].initial = (
|
||||
self.template.is_protected)
|
||||
|
||||
elif isinstance(step, create_flow.SelectDnsDomains):
|
||||
fields = step.action.fields
|
||||
fields["domain_name"].initial = self.template.domain_name
|
||||
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch template to copy."))
|
||||
|
||||
def _get_share_defaults(self, choices):
|
||||
values = dict()
|
||||
for i, choice in enumerate(choices):
|
||||
share_id = choice[0]
|
||||
s = [s for s in self.template.shares if s['id'] == share_id]
|
||||
if len(s) > 0:
|
||||
path = s[0]["path"] if "path" in s[0] else ""
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": s[0]["id"],
|
||||
"path": path,
|
||||
"access_level": s[0]["access_level"]
|
||||
}
|
||||
else:
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": None,
|
||||
"path": None,
|
||||
"access_level": None
|
||||
}
|
||||
return values
|
@ -1,433 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django import urls
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from sahara_dashboard.api import designate as designateclient
|
||||
from sahara_dashboard.api import manila as manilaclient
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content.data_processing.utils import helpers
|
||||
from sahara_dashboard.content.data_processing. \
|
||||
utils import anti_affinity as aa
|
||||
from sahara_dashboard.content.data_processing.utils \
|
||||
import acl as acl_utils
|
||||
import sahara_dashboard.content.data_processing. \
|
||||
utils.workflow_helpers as whelpers
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
class SelectPluginAction(workflows.Action,
|
||||
whelpers.PluginAndVersionMixin):
|
||||
hidden_create_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_create_field"}))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectPluginAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
sahara = saharaclient.client(request)
|
||||
self._generate_plugin_version_fields(sahara)
|
||||
|
||||
class Meta(object):
|
||||
name = _("Select plugin and hadoop version for cluster template")
|
||||
help_text_template = ("cluster_templates/"
|
||||
"_create_general_help.html")
|
||||
|
||||
|
||||
class SelectPlugin(workflows.Step):
|
||||
action_class = SelectPluginAction
|
||||
|
||||
|
||||
class CreateClusterTemplate(workflows.Workflow):
|
||||
slug = "create_cluster_template"
|
||||
name = _("Create Cluster Template")
|
||||
finalize_button_name = _("Next")
|
||||
success_message = _("Created")
|
||||
failure_message = _("Could not create")
|
||||
success_url = "horizon:project:data_processing.clusters:clusters-tab"
|
||||
default_steps = (SelectPlugin, )
|
||||
|
||||
def get_success_url(self):
|
||||
url = urls.reverse(self.success_url)
|
||||
return url
|
||||
|
||||
|
||||
class GeneralConfigAction(workflows.Action):
|
||||
hidden_configure_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))
|
||||
|
||||
hidden_to_delete_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_to_delete_field"}))
|
||||
|
||||
cluster_template_name = forms.CharField(label=_("Template Name"))
|
||||
|
||||
description = forms.CharField(label=_("Description"),
|
||||
required=False,
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
use_autoconfig = forms.BooleanField(
|
||||
label=_("Auto-configure"),
|
||||
help_text=_("If selected, instances of a cluster will be "
|
||||
"automatically configured during creation. Otherwise you "
|
||||
"should manually specify configuration values"),
|
||||
required=False,
|
||||
widget=forms.CheckboxInput(),
|
||||
initial=True,
|
||||
)
|
||||
|
||||
is_public = acl_utils.get_is_public_form(_("cluster template"))
|
||||
is_protected = acl_utils.get_is_protected_form(_("cluster template"))
|
||||
|
||||
anti_affinity = aa.anti_affinity_field()
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
self.fields["plugin_name"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=plugin
|
||||
)
|
||||
self.fields["hadoop_version"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=hadoop_version
|
||||
)
|
||||
|
||||
populate_anti_affinity_choices = aa.populate_anti_affinity_choices
|
||||
|
||||
def get_help_text(self):
|
||||
extra = dict()
|
||||
plugin_name, hadoop_version = whelpers\
|
||||
.get_plugin_and_hadoop_version(self.request)
|
||||
|
||||
extra["plugin_name"] = plugin_name
|
||||
extra["hadoop_version"] = hadoop_version
|
||||
|
||||
plugin = saharaclient.plugin_get_version_details(
|
||||
self.request, plugin_name, hadoop_version)
|
||||
extra["deprecated"] = whelpers.is_version_of_plugin_deprecated(
|
||||
plugin, hadoop_version)
|
||||
return super(GeneralConfigAction, self).get_help_text(extra)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(GeneralConfigAction, self).clean()
|
||||
if cleaned_data.get("hidden_configure_field", None) \
|
||||
== "create_nodegroup":
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Details")
|
||||
help_text_template = ("cluster_templates/_configure_general_help.html")
|
||||
|
||||
|
||||
class GeneralConfig(workflows.Step):
|
||||
action_class = GeneralConfigAction
|
||||
contributes = ("hidden_configure_field", )
|
||||
|
||||
def contribute(self, data, context):
|
||||
for k, v in data.items():
|
||||
context["general_" k] = v
|
||||
|
||||
post = self.workflow.request.POST
|
||||
context['anti_affinity_info'] = post.getlist("anti_affinity")
|
||||
return context
|
||||
|
||||
|
||||
class ConfigureNodegroupsAction(workflows.Action):
|
||||
hidden_nodegroups_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_nodegroups_field"}))
|
||||
forms_ids = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput())
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(ConfigureNodegroupsAction, self). \
|
||||
__init__(request, *args, **kwargs)
|
||||
# when we copy or edit a cluster template then
|
||||
# request contains valuable info in both GET and POST methods
|
||||
req = request.GET.copy()
|
||||
req.update(request.POST)
|
||||
plugin = req.get("plugin_name")
|
||||
version = req.get("hadoop_version", None) or req["plugin_version"]
|
||||
if plugin and not version:
|
||||
version_name = plugin "_version"
|
||||
version = req.get(version_name)
|
||||
|
||||
if not plugin or not version:
|
||||
self.templates = saharaclient.nodegroup_template_find(request)
|
||||
else:
|
||||
self.templates = saharaclient.nodegroup_template_find(
|
||||
request, plugin_name=plugin, hadoop_version=version)
|
||||
|
||||
deletable = req.get("deletable", dict())
|
||||
|
||||
if 'forms_ids' in req:
|
||||
self.groups = []
|
||||
for id in json.loads(req['forms_ids']):
|
||||
group_name = "group_name_" str(id)
|
||||
template_id = "template_id_" str(id)
|
||||
count = "count_" str(id)
|
||||
serialized = "serialized_" str(id)
|
||||
self.groups.append({"name": req[group_name],
|
||||
"template_id": req[template_id],
|
||||
"count": req[count],
|
||||
"id": id,
|
||||
"deletable": deletable.get(
|
||||
req[group_name], "true"),
|
||||
"serialized": req[serialized]})
|
||||
|
||||
whelpers.build_node_group_fields(self,
|
||||
group_name,
|
||||
template_id,
|
||||
count,
|
||||
serialized)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(ConfigureNodegroupsAction, self).clean()
|
||||
if cleaned_data.get("hidden_nodegroups_field", None) \
|
||||
== "create_nodegroup":
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Node Groups")
|
||||
|
||||
|
||||
class ConfigureNodegroups(workflows.Step):
|
||||
action_class = ConfigureNodegroupsAction
|
||||
contributes = ("hidden_nodegroups_field", )
|
||||
template_name = ("cluster_templates/cluster_node_groups_template.html")
|
||||
|
||||
def contribute(self, data, context):
|
||||
for k, v in data.items():
|
||||
context["ng_" k] = v
|
||||
return context
|
||||
|
||||
|
||||
class SelectClusterSharesAction(workflows.Action):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectClusterSharesAction, self).__init__(
|
||||
request, *args, **kwargs)
|
||||
|
||||
possible_shares = self.get_possible_shares(request)
|
||||
|
||||
self.fields["shares"] = whelpers.MultipleShareChoiceField(
|
||||
label=_("Select Shares"),
|
||||
widget=whelpers.ShareWidget(choices=possible_shares),
|
||||
required=False,
|
||||
choices=possible_shares
|
||||
)
|
||||
|
||||
def get_possible_shares(self, request):
|
||||
try:
|
||||
shares = manilaclient.share_list(request)
|
||||
choices = [(s.id, s.name) for s in shares]
|
||||
except Exception:
|
||||
exceptions.handle(request, _("Failed to get list of shares"))
|
||||
choices = []
|
||||
return choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SelectClusterSharesAction, self).clean()
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Shares")
|
||||
help_text = _("Select the manila shares for this cluster")
|
||||
|
||||
|
||||
class SelectClusterShares(workflows.Step):
|
||||
action_class = SelectClusterSharesAction
|
||||
|
||||
def contribute(self, data, context):
|
||||
post = self.workflow.request.POST
|
||||
shares_details = []
|
||||
for index in range(0, len(self.action.fields['shares'].choices) * 3):
|
||||
if index % 3 == 0:
|
||||
share = post.get("shares_{0}".format(index))
|
||||
if share:
|
||||
path = post.get("shares_{0}".format(index 1))
|
||||
permissions = post.get("shares_{0}".format(index 2))
|
||||
shares_details.append({
|
||||
"id": share,
|
||||
"path": path,
|
||||
"access_level": permissions
|
||||
})
|
||||
context['ct_shares'] = shares_details
|
||||
return context
|
||||
|
||||
|
||||
class SelectDnsDomainsAction(workflows.Action):
|
||||
domain_name = forms.DynamicChoiceField(
|
||||
label=_("Domain Name"),
|
||||
required=False
|
||||
)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectDnsDomainsAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
def _get_domain_choices(self, request):
|
||||
domains = designateclient.get_domain_names(request)
|
||||
choices = [(None, _('No domain is specified'))]
|
||||
choices.extend(
|
||||
[(domain.get('name'), domain.get('name')) for domain in domains])
|
||||
return choices
|
||||
|
||||
def populate_domain_name_choices(self, request, context):
|
||||
return self._get_domain_choices(request)
|
||||
|
||||
class Meta(object):
|
||||
name = _("DNS Domain Names")
|
||||
help_text_template = (
|
||||
"cluster_templates/_config_domain_names_help.html")
|
||||
|
||||
|
||||
class SelectDnsDomains(workflows.Step):
|
||||
action_class = SelectDnsDomainsAction
|
||||
|
||||
def contribute(self, data, context):
|
||||
for k, v in data.items():
|
||||
context["dns_" k] = v
|
||||
return context
|
||||
|
||||
|
||||
class ConfigureClusterTemplate(whelpers.ServiceParametersWorkflow,
|
||||
whelpers.StatusFormatMixin):
|
||||
slug = "configure_cluster_template"
|
||||
name = _("Create Cluster Template")
|
||||
finalize_button_name = _("Create")
|
||||
success_message = _("Created Cluster Template %s")
|
||||
name_property = "general_cluster_template_name"
|
||||
success_url = ("horizon:project:data_processing.clusters:"
|
||||
"cluster-templates-tab")
|
||||
default_steps = (GeneralConfig,
|
||||
ConfigureNodegroups)
|
||||
|
||||
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
|
||||
ConfigureClusterTemplate._cls_registry = []
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
general_parameters = hlps.get_cluster_general_configs(
|
||||
plugin,
|
||||
hadoop_version)
|
||||
service_parameters = hlps.get_targeted_cluster_configs(
|
||||
plugin,
|
||||
hadoop_version)
|
||||
|
||||
if saharaclient.base.is_service_enabled(request, 'share'):
|
||||
ConfigureClusterTemplate._register_step(self, SelectClusterShares)
|
||||
|
||||
if saharaclient.base.is_service_enabled(request, 'dns'):
|
||||
ConfigureClusterTemplate._register_step(self, SelectDnsDomains)
|
||||
|
||||
self._populate_tabs(general_parameters, service_parameters)
|
||||
|
||||
super(ConfigureClusterTemplate, self).__init__(request,
|
||||
context_seed,
|
||||
entry_point,
|
||||
*args, **kwargs)
|
||||
|
||||
def is_valid(self):
|
||||
steps_valid = True
|
||||
for step in self.steps:
|
||||
if not step.action.is_valid():
|
||||
steps_valid = False
|
||||
step.has_errors = True
|
||||
errors_fields = list(step.action.errors.keys())
|
||||
step.action.errors_fields = errors_fields
|
||||
if not steps_valid:
|
||||
return steps_valid
|
||||
return self.validate(self.context)
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
node_groups = []
|
||||
configs_dict = whelpers.parse_configs_from_context(context,
|
||||
self.defaults)
|
||||
|
||||
ids = json.loads(context['ng_forms_ids'])
|
||||
for id in ids:
|
||||
name = context['ng_group_name_' str(id)]
|
||||
template_id = context['ng_template_id_' str(id)]
|
||||
count = context['ng_count_' str(id)]
|
||||
|
||||
raw_ng = context.get("ng_serialized_" str(id))
|
||||
|
||||
if raw_ng and raw_ng != 'null':
|
||||
ng = json.loads(utils.deserialize(str(raw_ng)))
|
||||
else:
|
||||
ng = dict()
|
||||
ng["name"] = name
|
||||
ng["count"] = count
|
||||
if template_id and template_id != u'None':
|
||||
ng["node_group_template_id"] = template_id
|
||||
node_groups.append(ng)
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
ct_shares = []
|
||||
if "ct_shares" in context:
|
||||
ct_shares = context["ct_shares"]
|
||||
|
||||
domain = context.get('dns_domain_name', None)
|
||||
if domain == 'None':
|
||||
domain = None
|
||||
|
||||
# TODO(nkonovalov): Fix client to support default_image_id
|
||||
saharaclient.cluster_template_create(
|
||||
request,
|
||||
context["general_cluster_template_name"],
|
||||
plugin,
|
||||
hadoop_version,
|
||||
context["general_description"],
|
||||
configs_dict,
|
||||
node_groups,
|
||||
context["anti_affinity_info"],
|
||||
use_autoconfig=context['general_use_autoconfig'],
|
||||
shares=ct_shares,
|
||||
is_public=context['general_is_public'],
|
||||
is_protected=context['general_is_protected'],
|
||||
domain_name=domain
|
||||
)
|
||||
|
||||
hlps = helpers.Helpers(request)
|
||||
if hlps.is_from_guide():
|
||||
request.session["guide_cluster_template_name"] = (
|
||||
context["general_cluster_template_name"])
|
||||
self.success_url = (
|
||||
"horizon:project:data_processing.clusters:cluster_guide")
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Cluster template creation failed"))
|
||||
return False
|
@ -1,115 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.create as create_flow
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.copy as copy_flow
|
||||
import sahara_dashboard.content.data_processing. \
|
||||
utils.workflow_helpers as whelpers
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
class EditClusterTemplate(copy_flow.CopyClusterTemplate):
|
||||
success_message = _("Cluster Template %s updated")
|
||||
entry_point = "generalconfigaction"
|
||||
finalize_button_name = _("Update")
|
||||
name = _("Edit Cluster Template")
|
||||
|
||||
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
|
||||
try:
|
||||
super(EditClusterTemplate, self).__init__(request, context_seed,
|
||||
entry_point, *args,
|
||||
**kwargs)
|
||||
|
||||
for step in self.steps:
|
||||
if isinstance(step, create_flow.GeneralConfig):
|
||||
fields = step.action.fields
|
||||
fields["cluster_template_name"].initial = (
|
||||
self.template.name)
|
||||
fields["cluster_template_id"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=self.cluster_template_id)
|
||||
elif isinstance(step, create_flow.SelectDnsDomains):
|
||||
fields = step.action.fields
|
||||
fields["domain_name"].initial = self.template.domain_name
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch template to edit."))
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
node_groups = []
|
||||
configs_dict = whelpers.parse_configs_from_context(context,
|
||||
self.defaults)
|
||||
ids = json.loads(context['ng_forms_ids'])
|
||||
for id in ids:
|
||||
name = context['ng_group_name_' str(id)]
|
||||
template_id = context['ng_template_id_' str(id)]
|
||||
count = context['ng_count_' str(id)]
|
||||
|
||||
raw_ng = context.get("ng_serialized_" str(id))
|
||||
|
||||
if raw_ng and raw_ng != 'null':
|
||||
ng = json.loads(utils.deserialize(str(raw_ng)))
|
||||
else:
|
||||
ng = dict()
|
||||
ng["name"] = name
|
||||
ng["count"] = count
|
||||
if template_id and template_id != u'None':
|
||||
ng["node_group_template_id"] = template_id
|
||||
node_groups.append(ng)
|
||||
|
||||
plugin, hadoop_version = whelpers. \
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
ct_shares = []
|
||||
if "ct_shares" in context:
|
||||
ct_shares = context["ct_shares"]
|
||||
|
||||
domain = context.get('dns_domain_name', None)
|
||||
if domain == 'None':
|
||||
domain = None
|
||||
|
||||
saharaclient.cluster_template_update(
|
||||
request=request,
|
||||
ct_id=self.cluster_template_id,
|
||||
name=context["general_cluster_template_name"],
|
||||
plugin_name=plugin,
|
||||
hadoop_version=hadoop_version,
|
||||
description=context["general_description"],
|
||||
cluster_configs=configs_dict,
|
||||
node_groups=node_groups,
|
||||
anti_affinity=context["anti_affinity_info"],
|
||||
use_autoconfig=context['general_use_autoconfig'],
|
||||
shares=ct_shares,
|
||||
is_public=context['general_is_public'],
|
||||
is_protected=context['general_is_protected'],
|
||||
domain_name=domain
|
||||
)
|
||||
return True
|
||||
except exceptions.Conflict as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Cluster template update failed"))
|
||||
return False
|
@ -1,314 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.http import Http404 # noqa
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
from horizon.tables import base as tables_base
|
||||
from horizon.tabs import base as tabs_base
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content.data_processing \
|
||||
import tables as sahara_table
|
||||
from sahara_dashboard.content.data_processing.utils \
|
||||
import acl as acl_utils
|
||||
from sahara_dashboard.content.data_processing.utils import helpers
|
||||
|
||||
SAHARA_VERIFICATION_DISABLED = saharaclient.SAHARA_VERIFICATION_DISABLED
|
||||
|
||||
|
||||
class ClustersFilterAction(tables.FilterAction):
|
||||
filter_type = "server"
|
||||
filter_choices = (('name', _("Name"), True),
|
||||
('status', _("Status"), True))
|
||||
|
||||
|
||||
class ClusterGuide(tables.LinkAction):
|
||||
name = "cluster_guide"
|
||||
verbose_name = _("Cluster Creation Guide")
|
||||
url = "horizon:project:data_processing.clusters:cluster_guide"
|
||||
|
||||
|
||||
class CreateCluster(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Launch Cluster")
|
||||
url = "horizon:project:data_processing.clusters:create-cluster"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class ScaleCluster(tables.LinkAction):
|
||||
name = "scale"
|
||||
verbose_name = _("Scale Cluster")
|
||||
url = "horizon:project:data_processing.clusters:scale"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
def allowed(self, request, cluster=None):
|
||||
return cluster.status == "Active"
|
||||
|
||||
|
||||
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"Deleted Cluster",
|
||||
u"Deleted Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
saharaclient.cluster_delete(request, obj_id)
|
||||
|
||||
|
||||
class CheckClusterAction(tables.BatchAction):
|
||||
name = 'check_cluster'
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Start Verification",
|
||||
u"Start Verifications",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Started Verification",
|
||||
u"Started Verifications",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, datum_id):
|
||||
saharaclient.verification_update(request, datum_id, status='START')
|
||||
|
||||
|
||||
class ForceDeleteCluster(tables.DeleteAction):
|
||||
name = "force_delete"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Force Delete Cluster",
|
||||
u"Force Delete Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Force Deleted Cluster",
|
||||
u"Force Deleted Clusters",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
saharaclient.cluster_force_delete(request, obj_id)
|
||||
|
||||
|
||||
class UpdateClusterShares(tables.LinkAction):
|
||||
name = "update_shares"
|
||||
verbose_name = _("Update Shares")
|
||||
url = "horizon:project:data_processing.clusters:update-shares"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
def allowed(self, request, cluster=None):
|
||||
return cluster.status == "Active"
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, instance_id):
|
||||
try:
|
||||
return saharaclient.cluster_get(request, instance_id)
|
||||
except api_base.APIException as e:
|
||||
if e.error_code == 404:
|
||||
raise Http404
|
||||
else:
|
||||
messages.error(request,
|
||||
_("Unable to update row"))
|
||||
|
||||
|
||||
def get_instances_count(cluster):
|
||||
return sum([len(ng["instances"])
|
||||
for ng in cluster.node_groups])
|
||||
|
||||
|
||||
class RichErrorCell(tables_base.Cell):
|
||||
@property
|
||||
def status(self):
|
||||
# The error cell values becomes quite complex and cannot be handled
|
||||
# correctly with STATUS_CHOICES. Handling that explicitly.
|
||||
status = self.datum.status.lower()
|
||||
health = get_health_status_info(self.datum).lower()
|
||||
# error status always terminal
|
||||
if status == "error":
|
||||
return False
|
||||
if health == 'checking' or health == 'unknown':
|
||||
return None
|
||||
if status == "active":
|
||||
return True
|
||||
return None
|
||||
|
||||
|
||||
def get_rich_status_info(cluster):
|
||||
return {
|
||||
"status": cluster.status,
|
||||
"status_description": cluster.status_description
|
||||
}
|
||||
|
||||
|
||||
def rich_status_filter(status_dict):
|
||||
if status_dict['status'].lower() not in ['error', 'active']:
|
||||
return render_to_string("clusters/_in_progress.html", status_dict)
|
||||
# Render the status "as is" if no description is provided.
|
||||
if status_dict["status_description"]:
|
||||
# Error is rendered with a template containing an error description.
|
||||
return render_to_string("clusters/_rich_status.html", status_dict)
|
||||
return status_dict["status"]
|
||||
|
||||
|
||||
class ConfigureCluster(tables.LinkAction):
|
||||
name = "configure"
|
||||
verbose_name = _("Configure Cluster")
|
||||
url = "horizon:project:data_processing.clusters:configure-cluster"
|
||||
classes = ("ajax-modal", "configure-cluster-btn")
|
||||
icon = "plus"
|
||||
attrs = {"style": "display: none"}
|
||||
|
||||
|
||||
class MakePublic(acl_utils.MakePublic):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakePrivate(acl_utils.MakePrivate):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakeProtected(acl_utils.MakeProtected):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
class MakeUnProtected(acl_utils.MakeUnProtected):
|
||||
def change_rule_method(self, request, datum_id, **update_kwargs):
|
||||
saharaclient.cluster_update_acl_rules(
|
||||
request, datum_id, **update_kwargs)
|
||||
|
||||
|
||||
def get_health_status_info(cluster):
|
||||
try:
|
||||
return cluster.verification['status']
|
||||
except (AttributeError, KeyError):
|
||||
return 'UNKNOWN'
|
||||
|
||||
|
||||
def get_health_filter(health):
|
||||
if health == 'CHECKING':
|
||||
return render_to_string("clusters/_in_progress.html", {
|
||||
'status': _("Checking")})
|
||||
mapper = {'GREEN': 'success', 'YELLOW': 'warning',
|
||||
'RED': 'danger', 'CHECKING': 'info'}
|
||||
|
||||
label = mapper.get(health, 'default')
|
||||
return render_to_string('clusters/_health_status.html',
|
||||
{'status': health, 'label': label})
|
||||
|
||||
|
||||
class ClustersTable(sahara_table.SaharaPaginateTabbedTable):
|
||||
|
||||
tab_name = 'cluster_tabs%sclusters_tab' % tabs_base.SEPARATOR
|
||||
|
||||
class UptimeColumn(tables.Column):
|
||||
def get_data(self, cluster):
|
||||
return helpers.Helpers(None).get_duration(cluster.created_at)
|
||||
|
||||
name = tables.Column("name",
|
||||
verbose_name=_("Name"),
|
||||
link=("horizon:project:data_processing."
|
||||
"clusters:cluster-details"))
|
||||
|
||||
plugin = tables.Column("plugin_name",
|
||||
verbose_name=_("Plugin"))
|
||||
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
version_attr = "plugin_version"
|
||||
else:
|
||||
version_attr = "hadoop_version"
|
||||
version = tables.Column(version_attr,
|
||||
verbose_name=_("Version"))
|
||||
|
||||
# Status field need the whole cluster object to build the rich status.
|
||||
status = tables.Column(get_rich_status_info,
|
||||
verbose_name=_("Status"),
|
||||
filters=(rich_status_filter,))
|
||||
if not SAHARA_VERIFICATION_DISABLED:
|
||||
health = tables.Column(get_health_status_info,
|
||||
verbose_name=_("Health"),
|
||||
filters=(get_health_filter,))
|
||||
|
||||
instances_count = tables.Column(get_instances_count,
|
||||
verbose_name=_("Instances Count"))
|
||||
|
||||
uptime = UptimeColumn("uptime",
|
||||
verbose_name=_("Uptime"))
|
||||
|
||||
class Meta(object):
|
||||
name = "clusters"
|
||||
verbose_name = _("Clusters")
|
||||
row_class = UpdateRow
|
||||
cell_class = RichErrorCell
|
||||
status_columns = ["status"]
|
||||
if not SAHARA_VERIFICATION_DISABLED:
|
||||
status_columns.append("health")
|
||||
table_actions = (ClusterGuide,
|
||||
CreateCluster,
|
||||
ConfigureCluster,
|
||||
DeleteCluster,
|
||||
ClustersFilterAction)
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
table_actions = table_actions (ForceDeleteCluster,)
|
||||
table_actions_menu = (MakePublic, MakePrivate,
|
||||
MakeProtected, MakeUnProtected)
|
||||
if SAHARA_VERIFICATION_DISABLED:
|
||||
row_actions = (ScaleCluster,
|
||||
UpdateClusterShares,
|
||||
DeleteCluster, MakePublic, MakePrivate,
|
||||
MakeProtected, MakeUnProtected)
|
||||
else:
|
||||
row_actions = (ScaleCluster,
|
||||
UpdateClusterShares,
|
||||
DeleteCluster, MakePublic, MakePrivate,
|
||||
MakeProtected, MakeUnProtected,
|
||||
CheckClusterAction)
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
row_actions = row_actions (ForceDeleteCluster,)
|
@ -1,266 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 oslo_log import log as logging
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.api import neutron
|
||||
from openstack_dashboard.api import nova
|
||||
|
||||
from sahara_dashboard.content.data_processing.clusters.clusters \
|
||||
import tables as cluster_tables
|
||||
from sahara_dashboard.content.data_processing \
|
||||
import tabs as sahara_tabs
|
||||
from sahara_dashboard.content.data_processing.utils \
|
||||
import workflow_helpers as helpers
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ClustersTab(sahara_tabs.SaharaTableTab):
|
||||
table_classes = (cluster_tables.ClustersTable, )
|
||||
name = _("Clusters")
|
||||
slug = "clusters_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_clusters_data(self):
|
||||
try:
|
||||
table = self._tables['clusters']
|
||||
search_opts = {}
|
||||
filter = self.get_server_filter_info(table.request, table)
|
||||
if filter['value'] and filter['field']:
|
||||
search_opts = {filter['field']: filter['value']}
|
||||
clusters = saharaclient.cluster_list(self.request, search_opts)
|
||||
except Exception:
|
||||
clusters = []
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch cluster list"))
|
||||
return clusters
|
||||
|
||||
|
||||
class GeneralTab(tabs.Tab):
|
||||
name = _("General Info")
|
||||
slug = "cluster_details_tab"
|
||||
template_name = "clusters/_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
cluster_info = {}
|
||||
try:
|
||||
sahara = saharaclient.client(request)
|
||||
cluster = sahara.clusters.get(cluster_id)
|
||||
for info_key, info_val in cluster.info.items():
|
||||
for key, val in info_val.items():
|
||||
if str(val).startswith(('http://', 'https://')):
|
||||
cluster.info[info_key][key] = build_link(val)
|
||||
|
||||
try:
|
||||
base_image = glance.image_get(request,
|
||||
cluster.default_image_id)
|
||||
except Exception:
|
||||
exceptions.handle(
|
||||
request, _("Unable to fetch base image details"))
|
||||
base_image = {}
|
||||
|
||||
if getattr(cluster, 'cluster_template_id', None):
|
||||
cluster_template = saharaclient.safe_call(
|
||||
sahara.cluster_templates.get,
|
||||
cluster.cluster_template_id)
|
||||
else:
|
||||
cluster_template = None
|
||||
try:
|
||||
if getattr(cluster, 'neutron_management_network', None):
|
||||
net_id = cluster.neutron_management_network
|
||||
network = neutron.network_get(request, net_id)
|
||||
net_name = network.name_or_id
|
||||
else:
|
||||
net_name = None
|
||||
except Exception:
|
||||
exceptions.handle(
|
||||
request, _("Unable to fetch network details"))
|
||||
net_name = None
|
||||
|
||||
cluster_info.update({"cluster": cluster,
|
||||
"base_image": base_image,
|
||||
"cluster_template": cluster_template,
|
||||
"network": net_name})
|
||||
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
cluster_info["cluster"].hadoop_version = (
|
||||
cluster_info["cluster"].plugin_version
|
||||
)
|
||||
except Exception as e:
|
||||
LOG.error("Unable to fetch cluster details: %s" % str(e))
|
||||
|
||||
return cluster_info
|
||||
|
||||
|
||||
class ClusterConfigsDetails(tabs.Tab):
|
||||
name = _("Configuration Details")
|
||||
slug = "cluster_configs_details_tab"
|
||||
template_name = (
|
||||
"clusters/_cluster_configs_details.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
cluster = {}
|
||||
try:
|
||||
sahara = saharaclient.client(request)
|
||||
cluster = sahara.clusters.get(cluster_id)
|
||||
|
||||
except Exception as e:
|
||||
LOG.error("Unable to fetch cluster details: %s" % str(e))
|
||||
|
||||
return {'cluster': cluster}
|
||||
|
||||
|
||||
def build_link(url):
|
||||
return "<a href='" url "' target=\"_blank\">" url "</a>"
|
||||
|
||||
|
||||
class NodeGroupsTab(tabs.Tab):
|
||||
name = _("Node Groups")
|
||||
slug = "cluster_nodegroups_tab"
|
||||
template_name = "clusters/_nodegroups_details.html"
|
||||
|
||||
def get_context_data(self, request):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
try:
|
||||
sahara = saharaclient.client(request)
|
||||
cluster = sahara.clusters.get(cluster_id)
|
||||
for ng in cluster.node_groups:
|
||||
if ng["flavor_id"]:
|
||||
ng["flavor_name"] = (
|
||||
nova.flavor_get(request, ng["flavor_id"]).name)
|
||||
if ng["floating_ip_pool"]:
|
||||
ng["floating_ip_pool_name"] = (
|
||||
self._get_floating_ip_pool_name(
|
||||
request, ng["floating_ip_pool"]))
|
||||
|
||||
if ng.get("node_group_template_id", None):
|
||||
ng["node_group_template"] = saharaclient.safe_call(
|
||||
sahara.node_group_templates.get,
|
||||
ng["node_group_template_id"])
|
||||
|
||||
ng["security_groups_full"] = helpers.get_security_groups(
|
||||
request, ng["security_groups"])
|
||||
except Exception:
|
||||
cluster = {}
|
||||
exceptions.handle(request,
|
||||
_("Unable to get node group details."))
|
||||
|
||||
return {"cluster": cluster}
|
||||
|
||||
def _get_floating_ip_pool_name(self, request, pool_id):
|
||||
pools = [pool for pool in neutron.floating_ip_pools_list(
|
||||
request) if pool.id == pool_id]
|
||||
|
||||
return pools[0].name if pools else pool_id
|
||||
|
||||
|
||||
class Instance(object):
|
||||
def __init__(self, name=None, id=None, internal_ip=None,
|
||||
management_ip=None):
|
||||
self.name = name
|
||||
self.id = id
|
||||
self.internal_ip = internal_ip
|
||||
self.management_ip = management_ip
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
name = tables.Column("name",
|
||||
link="horizon:project:instances:detail",
|
||||
verbose_name=_("Name"))
|
||||
|
||||
internal_ip = tables.Column("internal_ip",
|
||||
verbose_name=_("Internal IP"))
|
||||
|
||||
management_ip = tables.Column("management_ip",
|
||||
verbose_name=_("Management IP"))
|
||||
|
||||
class Meta(object):
|
||||
name = "cluster_instances"
|
||||
verbose_name = _("Cluster Instances")
|
||||
|
||||
|
||||
class InstancesTab(tabs.TableTab):
|
||||
name = _("Instances")
|
||||
slug = "cluster_instances_tab"
|
||||
template_name = "clusters/_instances_details.html"
|
||||
table_classes = (InstancesTable, )
|
||||
|
||||
def get_cluster_instances_data(self):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
|
||||
try:
|
||||
sahara = saharaclient.client(self.request)
|
||||
cluster = sahara.clusters.get(cluster_id)
|
||||
|
||||
instances = []
|
||||
for ng in cluster.node_groups:
|
||||
for instance in ng["instances"]:
|
||||
instances.append(Instance(
|
||||
name=instance["instance_name"],
|
||||
id=instance["instance_id"],
|
||||
internal_ip=instance.get("internal_ip",
|
||||
"Not assigned"),
|
||||
management_ip=instance.get("management_ip",
|
||||
"Not assigned")))
|
||||
except Exception:
|
||||
instances = []
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch instance details."))
|
||||
return instances
|
||||
|
||||
|
||||
class EventLogTab(tabs.Tab):
|
||||
name = _("Cluster Events")
|
||||
slug = "cluster_event_log"
|
||||
template_name = "clusters/_event_log.html"
|
||||
|
||||
def get_context_data(self, request, **kwargs):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
kwargs["cluster_id"] = cluster_id
|
||||
kwargs['data_update_url'] = request.get_full_path()
|
||||
|
||||
return kwargs
|
||||
|
||||
|
||||
class HealthChecksTab(tabs.Tab):
|
||||
name = _("Cluster health checks")
|
||||
slug = 'cluster_health_checks'
|
||||
template_name = "clusters/_health_checks_table.html"
|
||||
|
||||
def get_context_data(self, request, **kwargs):
|
||||
cluster_id = self.tab_group.kwargs['cluster_id']
|
||||
kwargs['cluster_id'] = cluster_id
|
||||
kwargs['data_update_url'] = request.get_full_path()
|
||||
return kwargs
|
||||
|
||||
|
||||
class ClusterDetailsTabs(tabs.TabGroup):
|
||||
slug = "cluster_details"
|
||||
if saharaclient.SAHARA_VERIFICATION_DISABLED:
|
||||
tabs = (GeneralTab, ClusterConfigsDetails, NodeGroupsTab, InstancesTab,
|
||||
EventLogTab)
|
||||
else:
|
||||
tabs = (GeneralTab, ClusterConfigsDetails, NodeGroupsTab, InstancesTab,
|
||||
EventLogTab, HealthChecksTab)
|
||||
|
||||
sticky = True
|
@ -1,146 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 oslo_serialization import jsonutils
|
||||
|
||||
from openstack_dashboard import api as os_api
|
||||
|
||||
from sahara_dashboard import api
|
||||
from sahara_dashboard.test import helpers as test
|
||||
from sahara_dashboard.test.helpers import IsHttpRequest
|
||||
|
||||
|
||||
INDEX_URL = reverse('horizon:project:data_processing.clusters:clusters-tab')
|
||||
DETAILS_URL = reverse(
|
||||
'horizon:project:data_processing.clusters:details', args=['id'])
|
||||
|
||||
|
||||
class DataProcessingClusterTests(test.TestCase):
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_list',
|
||||
'image_list',
|
||||
'cluster_list',
|
||||
'nodegroup_template_list')})
|
||||
def test_index(self):
|
||||
self.mock_cluster_list.return_value = self.clusters.list()
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.mock_cluster_list.assert_called_once_with(IsHttpRequest(), {})
|
||||
self.assertTemplateUsed(res, 'clusters/index.html')
|
||||
self.assertContains(res, 'Clusters')
|
||||
self.assertContains(res, 'Name')
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_list', 'image_list',
|
||||
'plugin_get_version_details'),
|
||||
os_api.neutron: ('network_list',),
|
||||
os_api.nova: ('keypair_list',)})
|
||||
def test_launch_cluster_get_nodata(self):
|
||||
self.mock_cluster_template_list.return_value = []
|
||||
self.mock_image_list.return_value = []
|
||||
url = reverse(
|
||||
'horizon:project:data_processing.clusters:configure-cluster')
|
||||
res = self.client.get("%s?plugin_name=shoes&hadoop_version=1.1" % url)
|
||||
self.mock_cluster_template_list.assert_called_once_with(
|
||||
IsHttpRequest())
|
||||
self.mock_image_list.assert_called_once_with(IsHttpRequest())
|
||||
self.assertContains(res, "No Images Available")
|
||||
self.assertContains(res, "No Templates Available")
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_get',)})
|
||||
def test_event_log_tab(self):
|
||||
cluster = self.clusters.list()[-1]
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
|
||||
url = reverse(
|
||||
'horizon:project:data_processing.clusters:events', args=["cl2"])
|
||||
res = self.client.get(url)
|
||||
data = jsonutils.loads(res.content)
|
||||
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
IsHttpRequest(), "cl2", show_progress=True)
|
||||
self.assertIn("provision_steps", data)
|
||||
self.assertEqual(data["need_update"], False)
|
||||
|
||||
step_0 = data["provision_steps"][0]
|
||||
self.assertEqual(2, step_0["completed"])
|
||||
self.assertEqual(2, len(step_0["events"]))
|
||||
for evt in step_0["events"]:
|
||||
self.assertEqual(True, evt["successful"])
|
||||
|
||||
step_1 = data["provision_steps"][1]
|
||||
self.assertEqual(3, step_1["completed"])
|
||||
self.assertEqual(0, len(step_1["events"]))
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_get', )})
|
||||
def test_health_checks_tab_sc1(self):
|
||||
cluster = self.clusters.list()[-1]
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
|
||||
url = reverse(
|
||||
'horizon:project:data_processing.clusters:verifications',
|
||||
args=["cl2"])
|
||||
res = self.client.get(url)
|
||||
data = jsonutils.loads(res.content)
|
||||
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
IsHttpRequest(), "cl2")
|
||||
self.assertFalse(data['need_update'])
|
||||
check0 = data['checks'][0]
|
||||
check1 = data['checks'][1]
|
||||
self.assertEqual('success', check0['label'])
|
||||
self.assertEqual('danger', check1['label'])
|
||||
|
||||
self.assertEqual('GREEN', check0['status'])
|
||||
self.assertEqual('RED', check1['status'])
|
||||
self.assertEqual('0:07:40', check0['duration'])
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_get', )})
|
||||
def test_health_checks_tab_sc2(self):
|
||||
cluster = self.clusters.list()[0]
|
||||
cl1_id = 'ec9a0d28-5cfb-4028-a0b5-40afe23f1533'
|
||||
self.mock_cluster_get.return_value = cluster
|
||||
|
||||
url = reverse(
|
||||
'horizon:project:data_processing.clusters:verifications',
|
||||
args=[cl1_id])
|
||||
res = self.client.get(url)
|
||||
data = jsonutils.loads(res.content)
|
||||
|
||||
self.mock_cluster_get.assert_called_once_with(
|
||||
IsHttpRequest(), cl1_id)
|
||||
self.assertTrue(data['need_update'])
|
||||
check0 = data['checks'][0]
|
||||
check1 = data['checks'][1]
|
||||
self.assertEqual('info', check0['label'])
|
||||
self.assertEqual('danger', check1['label'])
|
||||
|
||||
self.assertEqual('CHECKING', check0['status'])
|
||||
self.assertEqual('RED', check1['status'])
|
||||
self.assertEqual('Houston, we have a problem', check1['description'])
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_list',
|
||||
'cluster_delete')})
|
||||
def test_delete(self):
|
||||
cluster = self.clusters.first()
|
||||
self.mock_cluster_list.return_value = self.clusters.list()
|
||||
self.mock_cluster_delete.return_value = None
|
||||
|
||||
form_data = {'action': 'clusters__delete__%s' % cluster.id}
|
||||
res = self.client.post(INDEX_URL, form_data)
|
||||
|
||||
self.mock_cluster_list.assert_called_once_with(
|
||||
IsHttpRequest(), {})
|
||||
self.mock_cluster_delete.assert_called_once_with(
|
||||
IsHttpRequest(), cluster.id)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertMessageCount(success=1)
|
@ -1,281 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import base as django_base
|
||||
from oslo_utils import timeutils
|
||||
from saharaclient.api.base import APIException
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from horizon.utils import memoized
|
||||
from horizon import workflows
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters.clusters. \
|
||||
tables as c_tables
|
||||
import sahara_dashboard.content.data_processing.clusters.clusters. \
|
||||
tabs as _tabs
|
||||
import sahara_dashboard.content.data_processing.clusters.clusters. \
|
||||
workflows.create as create_flow
|
||||
import sahara_dashboard.content.data_processing.clusters.clusters. \
|
||||
workflows.scale as scale_flow
|
||||
import sahara_dashboard.content.data_processing.clusters.clusters. \
|
||||
workflows.update as update_flow
|
||||
import sahara_dashboard.content.data_processing.utils.helpers as helpers
|
||||
|
||||
|
||||
class ClusterDetailsView(tabs.TabView):
|
||||
tab_group_class = _tabs.ClusterDetailsTabs
|
||||
template_name = 'horizon/common/_detail.html'
|
||||
page_title = "{{ cluster.name|default:cluster.id }}"
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
cl_id = self.kwargs["cluster_id"]
|
||||
try:
|
||||
return saharaclient.cluster_get(self.request, cl_id)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve details for cluster "%s".') % cl_id
|
||||
redirect = self.get_redirect_url()
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ClusterDetailsView, self).get_context_data(**kwargs)
|
||||
cluster = self.get_object()
|
||||
context['cluster'] = cluster
|
||||
context['url'] = self.get_redirect_url()
|
||||
context['actions'] = self._get_actions(cluster)
|
||||
return context
|
||||
|
||||
def _get_actions(self, cluster):
|
||||
table = c_tables.ClustersTable(self.request)
|
||||
return table.render_row_actions(cluster)
|
||||
|
||||
@staticmethod
|
||||
def get_redirect_url():
|
||||
return reverse("horizon:project:data_processing.clusters:index")
|
||||
|
||||
|
||||
class ClusterEventsView(django_base.View):
|
||||
|
||||
@staticmethod
|
||||
def _created_at_key(obj):
|
||||
return timeutils.parse_isotime(obj["created_at"])
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
cluster_id = kwargs.get("cluster_id")
|
||||
time_helpers = helpers.Helpers(request)
|
||||
|
||||
try:
|
||||
cluster = saharaclient.cluster_get(request, cluster_id,
|
||||
show_progress=True)
|
||||
node_group_mapping = {}
|
||||
for node_group in cluster.node_groups:
|
||||
node_group_mapping[node_group["id"]] = node_group["name"]
|
||||
|
||||
provision_steps = cluster.provision_progress
|
||||
|
||||
# Sort by create time
|
||||
provision_steps = sorted(provision_steps,
|
||||
key=ClusterEventsView._created_at_key,
|
||||
reverse=True)
|
||||
|
||||
for step in provision_steps:
|
||||
# Sort events of the steps also
|
||||
step["events"] = sorted(step["events"],
|
||||
key=ClusterEventsView._created_at_key,
|
||||
reverse=True)
|
||||
|
||||
successful_events_count = 0
|
||||
|
||||
for event in step["events"]:
|
||||
if event["node_group_id"]:
|
||||
event["node_group_name"] = node_group_mapping[
|
||||
event["node_group_id"]]
|
||||
|
||||
event_result = _("Unknown")
|
||||
if event["successful"] is True:
|
||||
successful_events_count = 1
|
||||
event_result = _("Completed Successfully")
|
||||
elif event["successful"] is False:
|
||||
event_result = _("Failed")
|
||||
|
||||
event["result"] = event_result
|
||||
|
||||
if not event["event_info"]:
|
||||
event["event_info"] = _("No info available")
|
||||
|
||||
step["duration"] = time_helpers.get_duration(
|
||||
step["created_at"],
|
||||
step["updated_at"])
|
||||
step['started_at'] = time_helpers.to_time_zone(
|
||||
step["created_at"], localize=True)
|
||||
result = _("In progress")
|
||||
step["completed"] = successful_events_count
|
||||
|
||||
if step["successful"] is True:
|
||||
step["completed"] = step["total"]
|
||||
result = _("Completed Successfully")
|
||||
elif step["successful"] is False:
|
||||
result = _("Failed")
|
||||
|
||||
step["result"] = result
|
||||
|
||||
status = cluster.status.lower()
|
||||
need_update = status not in ("active", "error")
|
||||
except APIException:
|
||||
# Cluster is not available. Returning empty event log.
|
||||
need_update = False
|
||||
provision_steps = []
|
||||
|
||||
context = {"provision_steps": provision_steps,
|
||||
"need_update": need_update}
|
||||
|
||||
return HttpResponse(json.dumps(context),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class ClusterHealthChecksView(django_base.View):
|
||||
_date_format = "%Y-%m-%dT%H:%M:%S"
|
||||
_status_in_progress = 'CHECKING'
|
||||
|
||||
def _get_checks(self, cluster):
|
||||
try:
|
||||
return cluster.verification['checks']
|
||||
except (AttributeError, KeyError):
|
||||
return []
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
time_helpers = helpers.Helpers(request)
|
||||
cluster_id = kwargs.get("cluster_id")
|
||||
need_update, not_done_count, checks = False, 0, []
|
||||
mapping_to_label_type = {'red': 'danger', 'yellow': 'warning',
|
||||
'green': 'success', 'checking': 'info'}
|
||||
try:
|
||||
cluster = saharaclient.cluster_get(request, cluster_id)
|
||||
for check in self._get_checks(cluster):
|
||||
check['label'] = mapping_to_label_type.get(
|
||||
check['status'].lower())
|
||||
|
||||
if not check['description']:
|
||||
check['description'] = _("No description")
|
||||
|
||||
if check['status'] == self._status_in_progress:
|
||||
not_done_count = 1
|
||||
check['duration'] = time_helpers.get_duration(
|
||||
check['created_at'], check['updated_at'])
|
||||
checks.append(check)
|
||||
except APIException:
|
||||
need_update = False
|
||||
checks = []
|
||||
if not_done_count > 0:
|
||||
need_update = True
|
||||
context = {"checks": checks,
|
||||
"need_update": need_update}
|
||||
|
||||
return HttpResponse(json.dumps(context),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class CreateClusterView(workflows.WorkflowView):
|
||||
workflow_class = create_flow.CreateCluster
|
||||
success_url = \
|
||||
"horizon:project:data_processing.clusters:create-cluster"
|
||||
classes = ("ajax-modal",)
|
||||
template_name = "clusters/create.html"
|
||||
page_title = _("Launch Cluster")
|
||||
|
||||
|
||||
class ConfigureClusterView(workflows.WorkflowView):
|
||||
workflow_class = create_flow.ConfigureCluster
|
||||
success_url = "horizon:project:data_processing.clusters:index"
|
||||
template_name = "clusters/configure.html"
|
||||
page_title = _("Configure Cluster")
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(ConfigureClusterView, self).get_initial()
|
||||
initial.update(self.kwargs)
|
||||
return initial
|
||||
|
||||
|
||||
class ScaleClusterView(workflows.WorkflowView):
|
||||
workflow_class = scale_flow.ScaleCluster
|
||||
success_url = "horizon:project:data_processing.clusters:index"
|
||||
classes = ("ajax-modal",)
|
||||
template_name = "clusters/scale.html"
|
||||
page_title = _("Scale Cluster")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(ScaleClusterView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
|
||||
context["cluster_id"] = kwargs["cluster_id"]
|
||||
return context
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
if not hasattr(self, "_object"):
|
||||
template_id = self.kwargs['cluster_id']
|
||||
try:
|
||||
template = saharaclient.cluster_template_get(self.request,
|
||||
template_id)
|
||||
except Exception:
|
||||
template = None
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch cluster template."))
|
||||
self._object = template
|
||||
return self._object
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(ScaleClusterView, self).get_initial()
|
||||
initial.update({'cluster_id': self.kwargs['cluster_id']})
|
||||
return initial
|
||||
|
||||
|
||||
class UpdateClusterSharesView(workflows.WorkflowView):
|
||||
workflow_class = update_flow.UpdateShares
|
||||
success_url = "horizon:project:data_processing.clusters"
|
||||
classes = ("ajax-modal",)
|
||||
template_name = "clusters/update.html"
|
||||
page_title = _("Update Cluster Shares")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateClusterSharesView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
context["cluster_id"] = kwargs["cluster_id"]
|
||||
return context
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
if not hasattr(self, "_object"):
|
||||
cluster_id = self.kwargs['cluster_id']
|
||||
try:
|
||||
cluster = saharaclient.cluster_get(self.request, cluster_id)
|
||||
except Exception:
|
||||
cluster = None
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to fetch cluster."))
|
||||
self._object = cluster
|
||||
return self._object
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(UpdateClusterSharesView, self).get_initial()
|
||||
initial.update({
|
||||
'cluster_id': self.kwargs['cluster_id'],
|
||||
'cluster': self.get_object()})
|
||||
return initial
|
@ -1,246 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from openstack_dashboard.api import nova
|
||||
|
||||
from sahara_dashboard.content.data_processing.utils \
|
||||
import acl as acl_utils
|
||||
from sahara_dashboard.content.data_processing.utils import neutron_support
|
||||
import sahara_dashboard.content.data_processing.utils. \
|
||||
workflow_helpers as whelpers
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.create as t_flows
|
||||
|
||||
|
||||
KEYPAIR_IMPORT_URL = "horizon:project:key_pairs:import"
|
||||
BASE_IMAGE_URL = "horizon:project:data_processing.clusters:register"
|
||||
TEMPLATE_UPLOAD_URL = (
|
||||
"horizon:project:data_processing.clusters:upload_file")
|
||||
|
||||
|
||||
class SelectPluginAction(t_flows.SelectPluginAction):
|
||||
class Meta(object):
|
||||
name = _("Select plugin and hadoop version for cluster")
|
||||
help_text_template = "clusters/_create_general_help.html"
|
||||
|
||||
|
||||
class SelectPlugin(t_flows.SelectPlugin):
|
||||
action_class = SelectPluginAction
|
||||
|
||||
|
||||
class CreateCluster(t_flows.CreateClusterTemplate):
|
||||
slug = "create_cluster"
|
||||
name = _("Launch Cluster")
|
||||
success_url = "horizon:project:data_processing.clusters:clusters-tab"
|
||||
default_steps = (SelectPlugin,)
|
||||
|
||||
|
||||
class GeneralConfigAction(workflows.Action):
|
||||
populate_neutron_management_network_choices = \
|
||||
neutron_support.populate_neutron_management_network_choices
|
||||
|
||||
hidden_configure_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_configure_field"}))
|
||||
|
||||
hidden_to_delete_field = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.HiddenInput(attrs={"class": "hidden_to_delete_field"}))
|
||||
|
||||
cluster_name = forms.CharField(label=_("Cluster Name"))
|
||||
|
||||
description = forms.CharField(label=_("Description"),
|
||||
required=False,
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
cluster_template = forms.DynamicChoiceField(
|
||||
label=_("Cluster Template"),
|
||||
initial=(None, "None"),
|
||||
add_item_link=TEMPLATE_UPLOAD_URL)
|
||||
|
||||
cluster_count = forms.IntegerField(min_value=1,
|
||||
label=_("Cluster Count"),
|
||||
initial=1,
|
||||
help_text=(
|
||||
_("Number of clusters to launch.")))
|
||||
|
||||
image = forms.DynamicChoiceField(label=_("Base Image"),
|
||||
add_item_link=BASE_IMAGE_URL)
|
||||
|
||||
keypair = forms.DynamicChoiceField(
|
||||
label=_("Keypair"),
|
||||
required=False,
|
||||
help_text=_("Which keypair to use for authentication."),
|
||||
add_item_link=KEYPAIR_IMPORT_URL)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(GeneralConfigAction, self).__init__(request, *args, **kwargs)
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
if saharaclient.base.is_service_enabled(request, 'network'):
|
||||
self.fields["neutron_management_network"] = forms.ChoiceField(
|
||||
label=_("Neutron Management Network"),
|
||||
choices=self.populate_neutron_management_network_choices(
|
||||
request, {})
|
||||
)
|
||||
|
||||
self.fields['is_public'] = acl_utils.get_is_public_form(
|
||||
_("cluster"))
|
||||
self.fields['is_protected'] = acl_utils.get_is_protected_form(
|
||||
_("cluster"))
|
||||
|
||||
self.fields["plugin_name"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=plugin
|
||||
)
|
||||
self.fields["hadoop_version"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=hadoop_version
|
||||
)
|
||||
|
||||
def populate_image_choices(self, request, context):
|
||||
return whelpers.populate_image_choices(self, request, context)
|
||||
|
||||
def populate_keypair_choices(self, request, context):
|
||||
try:
|
||||
keypairs = nova.keypair_list(request)
|
||||
except Exception:
|
||||
keypairs = []
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch keypair choices."))
|
||||
keypair_list = [(kp.name, kp.name) for kp in keypairs]
|
||||
keypair_list.insert(0, ("", _("No keypair")))
|
||||
|
||||
return keypair_list
|
||||
|
||||
def populate_cluster_template_choices(self, request, context):
|
||||
templates = saharaclient.cluster_template_list(request)
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
choices = []
|
||||
for template in templates:
|
||||
version = (
|
||||
getattr(template, "hadoop_version", None) or
|
||||
template.plugin_version
|
||||
)
|
||||
if version == hadoop_version and template.plugin_name == plugin:
|
||||
choices.append((template.id, template.name))
|
||||
|
||||
if not choices:
|
||||
choices.append(("", _("No Templates Available")))
|
||||
# cluster_template_id comes from cluster templates table, when
|
||||
# Create Cluster from template is clicked there
|
||||
selected_template_name = None
|
||||
req = request.GET or request.POST
|
||||
if req.get("cluster_template_name"):
|
||||
selected_template_name = (
|
||||
req.get("cluster_template_name"))
|
||||
if selected_template_name:
|
||||
for template in templates:
|
||||
if template.name == selected_template_name:
|
||||
selected_template_id = template.id
|
||||
break
|
||||
else:
|
||||
selected_template_id = (
|
||||
req.get("cluster_template_id", None))
|
||||
|
||||
for template in templates:
|
||||
if template.id == selected_template_id:
|
||||
self.fields['cluster_template'].initial = template.id
|
||||
|
||||
return choices
|
||||
|
||||
def get_help_text(self):
|
||||
extra = dict()
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(self.request)
|
||||
extra["plugin_name"] = plugin
|
||||
extra["hadoop_version"] = hadoop_version
|
||||
return super(GeneralConfigAction, self).get_help_text(extra)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(GeneralConfigAction, self).clean()
|
||||
if cleaned_data.get("hidden_configure_field", None) \
|
||||
== "create_nodegroup":
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Configure Cluster")
|
||||
help_text_template = "clusters/_configure_general_help.html"
|
||||
|
||||
|
||||
class GeneralConfig(workflows.Step):
|
||||
action_class = GeneralConfigAction
|
||||
contributes = ("hidden_configure_field", )
|
||||
|
||||
def contribute(self, data, context):
|
||||
for k, v in data.items():
|
||||
context["general_" k] = v
|
||||
|
||||
return context
|
||||
|
||||
|
||||
class ConfigureCluster(whelpers.StatusFormatMixin, workflows.Workflow):
|
||||
slug = "configure_cluster"
|
||||
name = _("Launch Cluster")
|
||||
finalize_button_name = _("Launch")
|
||||
success_message = _("Launched Cluster %s")
|
||||
name_property = "general_cluster_name"
|
||||
success_url = "horizon:project:data_processing.clusters:clusters-tab"
|
||||
default_steps = (GeneralConfig, )
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
# TODO(nkonovalov) Implement AJAX Node Groups.
|
||||
node_groups = None
|
||||
|
||||
plugin, hadoop_version = whelpers.\
|
||||
get_plugin_and_hadoop_version(request)
|
||||
|
||||
cluster_template_id = context["general_cluster_template"] or None
|
||||
user_keypair = context["general_keypair"] or None
|
||||
image_id = context["general_image"] or None
|
||||
saharaclient.cluster_create(
|
||||
request,
|
||||
context["general_cluster_name"],
|
||||
plugin, hadoop_version,
|
||||
cluster_template_id=cluster_template_id,
|
||||
default_image_id=image_id,
|
||||
description=context["general_description"],
|
||||
node_groups=node_groups,
|
||||
user_keypair_id=user_keypair,
|
||||
count=context['general_cluster_count'],
|
||||
net_id=context.get("general_neutron_management_network", None),
|
||||
is_public=context['general_is_public'],
|
||||
is_protected=context['general_is_protected']
|
||||
)
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to create the cluster'))
|
||||
return False
|
@ -1,172 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from horizon import exceptions
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
cluster_templates.workflows.create as clt_create_flow
|
||||
import sahara_dashboard.content.data_processing.clusters. \
|
||||
clusters.workflows.create as cl_create_flow
|
||||
from sahara_dashboard.content.data_processing.utils import workflow_helpers
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
class NodeGroupsStep(clt_create_flow.ConfigureNodegroups):
|
||||
pass
|
||||
|
||||
|
||||
class ScaleCluster(cl_create_flow.ConfigureCluster,
|
||||
workflow_helpers.StatusFormatMixin):
|
||||
slug = "scale_cluster"
|
||||
name = _("Scale Cluster")
|
||||
finalize_button_name = _("Scale")
|
||||
success_url = "horizon:project:data_processing.clusters:index"
|
||||
default_steps = (NodeGroupsStep, )
|
||||
|
||||
def __init__(self, request, context_seed, entry_point, *args, **kwargs):
|
||||
ScaleCluster._cls_registry = []
|
||||
|
||||
self.success_message = _("Scaled cluster successfully started.")
|
||||
|
||||
cluster_id = context_seed["cluster_id"]
|
||||
try:
|
||||
cluster = saharaclient.cluster_get(request, cluster_id)
|
||||
plugin = cluster.plugin_name
|
||||
if saharaclient.VERSIONS.active == '2':
|
||||
version_attr = 'plugin_version'
|
||||
else:
|
||||
version_attr = 'hadoop_version'
|
||||
hadoop_version = getattr(cluster, version_attr)
|
||||
|
||||
# Initialize deletable node groups.
|
||||
deletable = dict()
|
||||
for group in cluster.node_groups:
|
||||
deletable[group["name"]] = "false"
|
||||
request.GET = request.GET.copy()
|
||||
request.GET.update({
|
||||
"cluster_id": cluster_id,
|
||||
"plugin_name": plugin,
|
||||
version_attr: hadoop_version,
|
||||
"deletable": deletable
|
||||
})
|
||||
|
||||
super(ScaleCluster, self).__init__(request, context_seed,
|
||||
entry_point, *args,
|
||||
**kwargs)
|
||||
# Initialize node groups.
|
||||
for step in self.steps:
|
||||
if not isinstance(step, clt_create_flow.ConfigureNodegroups):
|
||||
continue
|
||||
ng_action = step.action
|
||||
template_ngs = cluster.node_groups
|
||||
|
||||
if 'forms_ids' in request.POST:
|
||||
continue
|
||||
ng_action.groups = []
|
||||
for i, templ_ng in enumerate(template_ngs):
|
||||
group_name = "group_name_%d" % i
|
||||
template_id = "template_id_%d" % i
|
||||
count = "count_%d" % i
|
||||
serialized = "serialized_%d" % i
|
||||
|
||||
serialized_val = utils.serialize(json.dumps(
|
||||
workflow_helpers.clean_node_group(templ_ng)))
|
||||
|
||||
ng_action.groups.append({
|
||||
"name": templ_ng["name"],
|
||||
"template_id": templ_ng["node_group_template_id"],
|
||||
"count": templ_ng["count"],
|
||||
"id": i,
|
||||
"deletable": "false",
|
||||
"serialized": serialized_val
|
||||
})
|
||||
workflow_helpers.build_node_group_fields(ng_action,
|
||||
group_name,
|
||||
template_id,
|
||||
count,
|
||||
serialized)
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch cluster to scale"))
|
||||
|
||||
def format_status_message(self, message):
|
||||
# Scaling form requires special handling because it has no Cluster name
|
||||
# in it's context
|
||||
|
||||
error_description = getattr(self, 'error_description', None)
|
||||
if error_description:
|
||||
return error_description
|
||||
else:
|
||||
return self.success_message
|
||||
|
||||
def handle(self, request, context):
|
||||
cluster_id = request.GET["cluster_id"]
|
||||
try:
|
||||
cluster = saharaclient.cluster_get(request, cluster_id)
|
||||
existing_node_groups = set([])
|
||||
for ng in cluster.node_groups:
|
||||
existing_node_groups.add(ng["name"])
|
||||
|
||||
scale_object = dict()
|
||||
|
||||
ids = json.loads(context["ng_forms_ids"])
|
||||
|
||||
for _id in ids:
|
||||
name = context["ng_group_name_%s" % _id]
|
||||
template_id = context["ng_template_id_%s" % _id]
|
||||
count = context["ng_count_%s" % _id]
|
||||
|
||||
if name not in existing_node_groups:
|
||||
if "add_node_groups" not in scale_object:
|
||||
scale_object["add_node_groups"] = []
|
||||
|
||||
scale_object["add_node_groups"].append(
|
||||
{"name": name,
|
||||
"node_group_template_id": template_id,
|
||||
"count": int(count)})
|
||||
else:
|
||||
old_count = None
|
||||
for ng in cluster.node_groups:
|
||||
if name == ng["name"]:
|
||||
old_count = ng["count"]
|
||||
break
|
||||
|
||||
if old_count != count:
|
||||
if "resize_node_groups" not in scale_object:
|
||||
scale_object["resize_node_groups"] = []
|
||||
|
||||
scale_object["resize_node_groups"].append(
|
||||
{"name": name,
|
||||
"count": int(count)}
|
||||
)
|
||||
except Exception:
|
||||
scale_object = {}
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch cluster to scale."))
|
||||
|
||||
try:
|
||||
saharaclient.cluster_scale(request, cluster_id, scale_object)
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Scale cluster operation failed"))
|
||||
return False
|
@ -1,132 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 saharaclient.api import base as api_base
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import workflows
|
||||
|
||||
from sahara_dashboard.api import manila as manilaclient
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
import sahara_dashboard.content.data_processing. \
|
||||
utils.workflow_helpers as whelpers
|
||||
|
||||
|
||||
class SelectSharesAction(workflows.Action):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectSharesAction, self).__init__(
|
||||
request, *args, **kwargs)
|
||||
|
||||
possible_shares = self.get_possible_shares(request)
|
||||
|
||||
cluster_id = [x["cluster_id"] for x in args if "cluster_id" in x][0]
|
||||
self.fields["cluster_id"] = forms.CharField(
|
||||
widget=forms.HiddenInput(),
|
||||
initial=cluster_id)
|
||||
|
||||
self.fields["shares"] = whelpers.MultipleShareChoiceField(
|
||||
label=_("Select Shares"),
|
||||
widget=whelpers.ShareWidget(choices=possible_shares),
|
||||
required=False,
|
||||
choices=possible_shares,
|
||||
)
|
||||
|
||||
cluster = [x["cluster"] for x in args if "cluster" in x][0]
|
||||
self.fields["shares"].initial = (
|
||||
self._get_share_defaults(cluster.shares,
|
||||
self.fields["shares"]))
|
||||
|
||||
def _get_share_defaults(self, cluster_shares, share_field):
|
||||
values = dict()
|
||||
choices = share_field.choices
|
||||
for i, choice in enumerate(choices):
|
||||
share_id = choice[0]
|
||||
s = [s for s in cluster_shares if s['id'] == share_id]
|
||||
if len(s) > 0:
|
||||
path = s[0]["path"] if "path" in s[0] else ""
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": s[0]["id"],
|
||||
"path": path,
|
||||
"access_level": s[0]["access_level"]
|
||||
}
|
||||
else:
|
||||
values["share_id_{0}".format(i)] = {
|
||||
"id": None,
|
||||
"path": None,
|
||||
"access_level": None
|
||||
}
|
||||
return values
|
||||
|
||||
def get_possible_shares(self, request):
|
||||
try:
|
||||
shares = manilaclient.share_list(request)
|
||||
choices = [(s.id, s.name) for s in shares]
|
||||
except Exception:
|
||||
exceptions.handle(request, _("Failed to get list of shares"))
|
||||
choices = []
|
||||
return choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(SelectSharesAction, self).clean()
|
||||
self._errors = dict()
|
||||
return cleaned_data
|
||||
|
||||
class Meta(object):
|
||||
name = _("Shares")
|
||||
help_text = _("Select the manila shares for this cluster")
|
||||
|
||||
|
||||
class SelectShares(workflows.Step):
|
||||
action_class = SelectSharesAction
|
||||
depends_on = ("cluster_id", "cluster")
|
||||
|
||||
def contribute(self, data, context):
|
||||
post = self.workflow.request.POST
|
||||
shares_details = []
|
||||
for index in range(0, len(self.action.fields['shares'].choices) * 3):
|
||||
if index % 3 == 0:
|
||||
share = post.get("shares_{0}".format(index))
|
||||
if share:
|
||||
path = post.get("shares_{0}".format(index 1))
|
||||
permissions = post.get("shares_{0}".format(index 2))
|
||||
shares_details.append({
|
||||
"id": share,
|
||||
"path": path,
|
||||
"access_level": permissions
|
||||
})
|
||||
context['cluster_shares'] = shares_details
|
||||
return context
|
||||
|
||||
|
||||
class UpdateShares(workflows.Workflow):
|
||||
slug = "update_cluster_shares"
|
||||
name = _("Update Cluster Shares")
|
||||
success_message = _("Updated")
|
||||
failure_message = _("Could not update cluster shares")
|
||||
success_url = "horizon:project:data_processing.clusters:index"
|
||||
default_steps = (SelectShares,)
|
||||
|
||||
def handle(self, request, context):
|
||||
try:
|
||||
saharaclient.cluster_update_shares(
|
||||
request, context["cluster_id"], context["cluster_shares"])
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Cluster share update failed."))
|
||||
return False
|
@ -1,118 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
from openstack_dashboard.api import glance
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
|
||||
|
||||
class ImageForm(forms.SelfHandlingForm):
|
||||
image_id = forms.CharField(widget=forms.HiddenInput())
|
||||
tags_list = forms.CharField(widget=forms.HiddenInput())
|
||||
user_name = forms.CharField(max_length=80, label=_("User Name"))
|
||||
description = forms.CharField(max_length=80,
|
||||
label=_("Description"),
|
||||
required=False,
|
||||
widget=forms.Textarea(attrs={'rows': 4}))
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
image_id = data['image_id']
|
||||
user_name = data['user_name']
|
||||
desc = data['description']
|
||||
saharaclient.image_update(request, image_id, user_name, desc)
|
||||
|
||||
image_tags = json.loads(data["tags_list"])
|
||||
saharaclient.image_tags_update(request, image_id, image_tags)
|
||||
updated_image = saharaclient.image_get(request, image_id)
|
||||
messages.success(request,
|
||||
_("Successfully updated image."))
|
||||
return updated_image
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_("Failed to update image."))
|
||||
return False
|
||||
|
||||
|
||||
class EditTagsForm(ImageForm):
|
||||
image_id = forms.CharField(widget=forms.HiddenInput())
|
||||
|
||||
|
||||
class RegisterImageForm(ImageForm):
|
||||
image_id = forms.ChoiceField(label=_("Image"))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(RegisterImageForm, self).__init__(request, *args, **kwargs)
|
||||
self._populate_image_id_choices()
|
||||
|
||||
def _populate_image_id_choices(self):
|
||||
images = self._get_available_images(self.request)
|
||||
choices = [(image.id, image.name)
|
||||
for image in images
|
||||
if image.to_dict()['properties'].get(
|
||||
"image_type") != "snapshot"]
|
||||
if choices:
|
||||
choices.insert(0, ("", _("Select Image")))
|
||||
else:
|
||||
choices.insert(0, ("", _("No images available.")))
|
||||
self.fields['image_id'].choices = choices
|
||||
|
||||
def _get_images(self, request, filter):
|
||||
try:
|
||||
images, _more, _prev = (
|
||||
glance.image_list_detailed(request, filters=filter))
|
||||
except Exception:
|
||||
images = []
|
||||
exceptions.handle(request,
|
||||
_("Unable to retrieve images with filter %s.") %
|
||||
filter)
|
||||
return images
|
||||
|
||||
def _get_public_images(self, request):
|
||||
filter = {"is_public": True,
|
||||
"status": "active"}
|
||||
return self._get_images(request, filter)
|
||||
|
||||
def _get_tenant_images(self, request):
|
||||
filter = {"owner": request.user.tenant_id,
|
||||
"status": "active"}
|
||||
return self._get_images(request, filter)
|
||||
|
||||
def _get_available_images(self, request):
|
||||
|
||||
images = self._get_tenant_images(request)
|
||||
if request.user.is_superuser:
|
||||
images = self._get_public_images(request)
|
||||
|
||||
final_images = []
|
||||
|
||||
try:
|
||||
image_ids = set(img.id for img in saharaclient.image_list(request))
|
||||
except Exception:
|
||||
image_ids = set()
|
||||
exceptions.handle(request,
|
||||
_("Unable to fetch available images."))
|
||||
|
||||
for image in images:
|
||||
if (image not in final_images and
|
||||
image.id not in image_ids and
|
||||
image.container_format not in ('aki', 'ari')):
|
||||
final_images.append(image)
|
||||
return final_images
|
@ -1,80 0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from django import template
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import ngettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
|
||||
|
||||
class EditTagsAction(tables.LinkAction):
|
||||
name = "edit_tags"
|
||||
verbose_name = _("Edit Tags")
|
||||
url = "horizon:project:data_processing.clusters:edit_tags"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
|
||||
def tags_to_string(image):
|
||||
template_name = 'image_registry/_list_tags.html'
|
||||
context = {"image": image}
|
||||
return template.loader.render_to_string(template_name, context)
|
||||
|
||||
|
||||
class RegisterImage(tables.LinkAction):
|
||||
name = "register"
|
||||
verbose_name = _("Register Image")
|
||||
url = "horizon:project:data_processing.clusters:register"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class UnregisterImages(tables.DeleteAction):
|
||||
name = "unregister"
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ngettext_lazy(
|
||||
u"Unregister Image",
|
||||
u"Unregister Images",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ngettext_lazy(
|
||||
u"Unregistered Image",
|
||||
u"Unregistered Images",
|
||||
count
|
||||
)
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
saharaclient.image_unregister(request, obj_id)
|
||||
|
||||
|
||||
class ImageRegistryTable(tables.DataTable):
|
||||
name = tables.Column("name",
|
||||
verbose_name=_("Image"),
|
||||
link=("horizon:project:"
|
||||
"images:images:detail"))
|
||||
tags = tables.Column(tags_to_string,
|
||||
verbose_name=_("Tags"))
|
||||
user = tables.Column("username", verbose_name=_("User"))
|
||||
|
||||
class Meta(object):
|
||||
name = "image_registry"
|
||||
verbose_name = _("Image Registry")
|
||||
table_actions = (RegisterImage, UnregisterImages,)
|
||||
row_actions = (EditTagsAction, UnregisterImages,)
|
@ -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.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content.data_processing.clusters.image_registry \
|
||||
import tables as image_registry_tables
|
||||
|
||||
|
||||
class ImageRegistryTab(tabs.TableTab):
|
||||
table_classes = (image_registry_tables.ImageRegistryTable, )
|
||||
name = _("Image Registry")
|
||||
slug = "image_registry_tab"
|
||||
template_name = "horizon/common/_detail_table.html"
|
||||
|
||||
def get_image_registry_data(self):
|
||||
try:
|
||||
images = saharaclient.image_list(self.request)
|
||||
except Exception:
|
||||
images = []
|
||||
msg = _('Unable to retrieve image list')
|
||||
exceptions.handle(self.request, msg)
|
||||
return images
|
@ -1,129 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 openstack_dashboard import api as dash_api
|
||||
|
||||
from sahara_dashboard import api
|
||||
from sahara_dashboard.test import helpers as test
|
||||
from sahara_dashboard.test.helpers import IsHttpRequest
|
||||
|
||||
INDEX_URL = reverse(
|
||||
'horizon:project:data_processing.clusters:image-registry-tab')
|
||||
REGISTER_URL = reverse(
|
||||
'horizon:project:data_processing.clusters:register')
|
||||
SUCCESS_URL = reverse(
|
||||
'horizon:project:data_processing.clusters:index')
|
||||
|
||||
|
||||
class DataProcessingImageRegistryTests(test.TestCase):
|
||||
|
||||
@test.create_mocks({api.sahara: ('cluster_template_list',
|
||||
'image_list',
|
||||
'cluster_list',
|
||||
'nodegroup_template_list')})
|
||||
def test_index(self):
|
||||
self.mock_image_list.return_value = self.images.list()
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
|
||||
self.mock_image_list.assert_called_once_with(
|
||||
IsHttpRequest())
|
||||
self.assertTemplateUsed(res, 'clusters/index.html')
|
||||
self.assertContains(res, 'Image Registry')
|
||||
self.assertContains(res, 'Image')
|
||||
self.assertContains(res, 'Tags')
|
||||
|
||||
@test.create_mocks({api.sahara: ('image_get',
|
||||
'image_update',
|
||||
'image_tags_update',
|
||||
'image_list'),
|
||||
dash_api.glance: ('image_list_detailed',)})
|
||||
def test_register(self):
|
||||
image = self.images.first()
|
||||
image_id = image.id
|
||||
test_username = 'myusername'
|
||||
test_description = 'mydescription'
|
||||
self.mock_image_get.return_value = image
|
||||
self.mock_image_list_detailed.return_value = (
|
||||
self.images.list(), False, False)
|
||||
self.mock_image_update.return_value = True
|
||||
self.mock_image_tags_update.return_value = True
|
||||
self.mock_image_list.return_value = []
|
||||
|
||||
res = self.client.post(
|
||||
REGISTER_URL,
|
||||
{'image_id': image_id,
|
||||
'user_name': test_username,
|
||||
'description': test_description,
|
||||
'tags_list': '{}'})
|
||||
|
||||
self.mock_image_list_detailed.assert_called_once_with(
|
||||
IsHttpRequest(),
|
||||
filters={'owner': self.user.id,
|
||||
'status': 'active'})
|
||||
self.mock_image_update.assert_called_once_with(
|
||||
IsHttpRequest(), image_id, test_username, test_description)
|
||||
self.mock_image_tags_update.assert_called_once_with(
|
||||
IsHttpRequest(), image_id, {})
|
||||
self.mock_image_list.assert_called_once_with(
|
||||
IsHttpRequest())
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, SUCCESS_URL)
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
@test.create_mocks({api.sahara: ('image_list',
|
||||
'image_unregister')})
|
||||
def test_unregister(self):
|
||||
image = self.images.first()
|
||||
self.mock_image_list.return_value = self.images.list()
|
||||
self.mock_image_unregister.return_value = None
|
||||
|
||||
form_data = {'action': 'image_registry__unregister__%s' % image.id}
|
||||
res = self.client.post(INDEX_URL, form_data)
|
||||
|
||||
self.mock_image_list.assert_called_once_with(
|
||||
IsHttpRequest())
|
||||
self.mock_image_unregister.assert_called_once_with(
|
||||
IsHttpRequest(), image.id)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
self.assertMessageCount(success=1)
|
||||
|
||||
@test.create_mocks({api.sahara: ('image_get',
|
||||
'image_update',
|
||||
'image_tags_update')})
|
||||
def test_edit_tags(self):
|
||||
image = self.registered_images.first()
|
||||
self.mock_image_get.return_value = image
|
||||
self.mock_image_update.return_value = True
|
||||
self.mock_image_tags_update.return_value = True
|
||||
|
||||
edit_tags_url = reverse(
|
||||
'horizon:project:data_processing.clusters:edit_tags',
|
||||
args=[image.id])
|
||||
res = self.client.post(
|
||||
edit_tags_url,
|
||||
{'image_id': image.id,
|
||||
'user_name': image.username,
|
||||
'description': image.description,
|
||||
'tags_list': '{"0": "mytag"}'})
|
||||
|
||||
self.mock_image_update.assert_called_once_with(
|
||||
IsHttpRequest(), image.id, image.username,
|
||||
image.description)
|
||||
self.mock_image_tags_update.assert_called_once_with(
|
||||
IsHttpRequest(), image.id, {"0": "mytag"})
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, SUCCESS_URL)
|
||||
self.assertMessageCount(success=1)
|
@ -1,109 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 collections import OrderedDict
|
||||
import json
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon.utils import memoized
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
from sahara_dashboard.content. \
|
||||
data_processing.clusters.image_registry.forms import EditTagsForm
|
||||
from sahara_dashboard.content. \
|
||||
data_processing.clusters.image_registry.forms import RegisterImageForm
|
||||
from sahara_dashboard import utils
|
||||
|
||||
|
||||
def update_context_with_plugin_tags(request, context):
|
||||
try:
|
||||
plugins = saharaclient.plugin_list(request)
|
||||
except Exception:
|
||||
plugins = []
|
||||
msg = _("Unable to process plugin tags")
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
plugins_object = dict()
|
||||
for plugin in plugins:
|
||||
plugins_object[plugin.name] = OrderedDict()
|
||||
for version in sorted(plugin.versions, reverse=True,
|
||||
key=utils.smart_sort_helper):
|
||||
try:
|
||||
details = saharaclient. \
|
||||
plugin_get_version_details(request,
|
||||
plugin.name,
|
||||
version)
|
||||
plugins_object[plugin.name][version] = (
|
||||
details.required_image_tags)
|
||||
except Exception:
|
||||
msg = _("Unable to process plugin tags")
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
context["plugins"] = plugins_object
|
||||
|
||||
|
||||
class EditTagsView(forms.ModalFormView):
|
||||
form_class = EditTagsForm
|
||||
template_name = 'image_registry/edit_tags.html'
|
||||
success_url = reverse_lazy(
|
||||
'horizon:project:data_processing.clusters:index')
|
||||
page_title = _("Edit Image Tags")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EditTagsView, self).get_context_data(**kwargs)
|
||||
context['image'] = self.get_object()
|
||||
update_context_with_plugin_tags(self.request, context)
|
||||
return context
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
image = saharaclient.image_get(self.request,
|
||||
self.kwargs["image_id"])
|
||||
except Exception:
|
||||
image = None
|
||||
msg = _("Unable to fetch the image details")
|
||||
exceptions.handle(self.request, msg)
|
||||
return image
|
||||
|
||||
def get_initial(self):
|
||||
image = self.get_object()
|
||||
|
||||
return {"image_id": image.id,
|
||||
"tags_list": json.dumps(image.tags),
|
||||
"user_name": image.username,
|
||||
"description": image.description}
|
||||
|
||||
|
||||
class RegisterImageView(forms.ModalFormView):
|
||||
form_class = RegisterImageForm
|
||||
template_name = 'image_registry/register_image.html'
|
||||
success_url = reverse_lazy(
|
||||
'horizon:project:data_processing.clusters:index')
|
||||
page_title = _("Register Image")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(RegisterImageView, self).get_context_data(**kwargs)
|
||||
context['action_url'] = ('horizon:project'
|
||||
':data_processing.clusters:register')
|
||||
update_context_with_plugin_tags(self.request, context)
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
# need this initialization to allow registration
|
||||
# of images without tags
|
||||
return {"tags_list": json.dumps([])}
|
@ -1,165 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 forms
|
||||
from openstack_dashboard.api import neutron
|
||||
from openstack_dashboard.dashboards.project.instances \
|
||||
import utils as nova_utils
|
||||
from oslo_serialization import jsonutils as json
|
||||
from saharaclient.api import base as api_base
|
||||
|
||||
from sahara_dashboard.api import sahara as saharaclient
|
||||
|
||||
BASE_IMAGE_URL = "horizon:project:data_processing.clusters:register"
|
||||
|
||||
|
||||
class ImportNodegroupTemplateFileForm(forms.SelfHandlingForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Import Node Group Template")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.next_view = kwargs.pop('next_view')
|
||||
super(ImportNodegroupTemplateFileForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
|
||||
template_upload = forms.FileField(
|
||||
label=_('Template File'),
|
||||
required=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
kwargs = {'template_upload': data['template_upload']}
|
||||
request.method = 'GET'
|
||||
return self.next_view.as_view()(request, **kwargs)
|
||||
|
||||
|
||||
class ImportNodegroupTemplateDetailsForm(forms.SelfHandlingForm):
|
||||
|
||||
class Meta(object):
|
||||
name = _("Import Node Group Template")
|
||||
|
||||
template = forms.CharField(
|
||||
widget=forms.widgets.HiddenInput)
|
||||
|
||||
name = forms.CharField(label=_("Name"),
|
||||
required=False,
|
||||
help_text=_("Name must be provided "
|
||||
"either here or in the template. If "
|
||||
"provided in both places, this one "
|
||||
"will be used."))
|
||||
|
||||
security_groups = forms.MultipleChoiceField(
|
||||
label=_("Security Groups"),
|
||||
widget=forms.CheckboxSelectMultiple(),
|
||||
help_text=_("Launch instances in these security groups. "
|
||||
"Auto security group will be determined by the "
|
||||
"value present in the imported template."),
|
||||
required=False)
|
||||
|
||||
floating_ip_pool = forms.ChoiceField(
|
||||
label=_("Floating IP Pool"),
|
||||
required=False)
|
||||
|
||||
flavor = forms.ChoiceField(label=_("OpenStack Flavor"))
|
||||
|
||||
image_id = forms.DynamicChoiceField(label=_("Base Image"),
|
||||
add_item_link=BASE_IMAGE_URL)
|
||||
|
||||
def _populate_image_choices(self, request, plugin, hadoop_version):
|
||||
all_images = saharaclient.image_list(request)
|
||||
details = saharaclient.plugin_get_version_details(request,
|
||||
plugin,
|
||||
hadoop_version)
|
||||
return [(image.id, image.name) for image in all_images
|
||||
if (set(details.required_image_tags).
|
||||
issubset(set(image.tags)))]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
request = args[0]
|
||||
template_string = ""
|
||||
|
||||
if "template_upload" in kwargs:
|
||||
template_upload = kwargs.pop('template_upload')
|
||||
super(ImportNodegroupTemplateDetailsForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
|
||||
template_string = template_upload.read()
|
||||
self.fields["template"].initial = template_string
|
||||
|
||||
else:
|
||||
super(ImportNodegroupTemplateDetailsForm, self).__init__(
|
||||
*args, **kwargs)
|
||||
template_string = self.data["template"]
|
||||
|
||||
template_json = json.loads(template_string)
|
||||
template_json = template_json["node_group_template"]
|
||||
|
||||
security_group_list = neutron.security_group_list(request)
|
||||
security_group_choices = \
|
||||
[(sg.id, sg.name) for sg in security_group_list]
|
||||
self.fields["security_groups"].choices = security_group_choices
|
||||
|
||||
pools = neutron.floating_ip_pools_list(request)
|
||||
pool_choices = [(pool.id, pool.name) for pool in pools]
|
||||
pool_choices.insert(0, (None, "Do not assign floating IPs"))
|
||||
self.fields["floating_ip_pool"].choices = pool_choices
|
||||
|
||||
flavors = nova_utils.flavor_list(request)
|
||||
if flavors:
|
||||
self.fields["flavor"].choices = nova_utils.sort_flavor_list(
|
||||
request, flavors)
|
||||
else:
|
||||
self.fields["flavor"].choices = []
|
||||
|
||||
version = (template_json.get("hadoop_version", None) or
|
||||
template_json["plugin_version"])
|
||||
self.fields["image_id"].choices = \
|
||||
self._populate_image_choices(request,
|
||||
template_json["plugin_name"],
|
||||
version)
|
||||
except (ValueError, KeyError):
|
||||
raise exceptions.BadRequest(_("Could not parse template"))
|
||||
except Exception:
|
||||
exceptions.handle(request)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
template = data["template"]
|
||||
template = json.loads(template)
|
||||
template = template["node_group_template"]
|
||||
|
||||
if not data["name"] and "name" not in template.keys():
|
||||
return False
|
||||
if data["name"]:
|
||||
template["name"] = data["name"]
|
||||
|
||||
template["security_groups"] = data["security_groups"]
|
||||
template["floating_ip_pool"] = data["floating_ip_pool"]
|
||||
template["flavor_id"] = data["flavor"]
|
||||
template["image_id"] = data["image_id"]
|
||||
|
||||
saharaclient.nodegroup_template_create(request, **template)
|
||||
return True
|
||||
except api_base.APIException as e:
|
||||
self.error_description = str(e)
|
||||
return False
|
||||
except Exception as e:
|
||||
if isinstance(e, TypeError):
|
||||
raise exceptions.BadRequest(
|
||||
_("Template JSON contained invalid key"))
|
||||
else:
|
||||
raise exceptions.BadRequest(_("Could not parse template"))
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user