Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I1447e5228c33f78ae84ff7ad70539fe54a605fe6
This commit is contained in:
parent
08089b01c4
commit
c3f0f80247
|
@ -1 +0,0 @@
|
|||
horizon/static/horizon/lib/**
|
51
.eslintrc
51
.eslintrc
|
@ -1,51 +0,0 @@
|
|||
# Set up globals
|
||||
globals:
|
||||
angular: false
|
||||
|
||||
extends: openstack
|
||||
|
||||
# Most environment options are not explicitly enabled or disabled, only
|
||||
# included here for completeness' sake. They are commented out, because the
|
||||
# global updates.py script would otherwise override them during a global
|
||||
# requirements synchronization.
|
||||
#
|
||||
# Individual projects should choose which platforms they deploy to.
|
||||
|
||||
env:
|
||||
# browser global variables.
|
||||
browser: true
|
||||
|
||||
# Adds all of the Jasmine testing global variables for version 1.3 and 2.0.
|
||||
jasmine: true
|
||||
|
||||
|
||||
# Below we adjust rules specific to horizon's usage of openstack's linting
|
||||
# rules, and its own plugin inclusions.
|
||||
rules:
|
||||
#############################################################################
|
||||
# Disabled Rules from eslint-config-openstack
|
||||
#############################################################################
|
||||
valid-jsdoc: [1, {
|
||||
requireParamDescription: false
|
||||
}]
|
||||
no-undefined: 1
|
||||
brace-style: 1
|
||||
no-extra-parens: 1
|
||||
callback-return: 1
|
||||
block-scoped-var: 1
|
||||
quote-props: 0
|
||||
space-in-parens: 1
|
||||
no-use-before-define: 1
|
||||
no-unneeded-ternary: 1
|
||||
|
||||
#############################################################################
|
||||
# Angular Plugin Customization
|
||||
#############################################################################
|
||||
|
||||
angular/controller-as-vm:
|
||||
- 1
|
||||
- "ctrl"
|
||||
|
||||
# Remove after migrating to angular 1.4 or later.
|
||||
angular/no-cookiestore:
|
||||
- 1
|
|
@ -1,48 +0,0 @@
|
|||
*.egg*
|
||||
*.mo
|
||||
*.pot
|
||||
*.pyc
|
||||
*.sw?
|
||||
*.sqlite3
|
||||
*.lock
|
||||
.environment_version
|
||||
.selenium_log
|
||||
.coverage*
|
||||
.noseids
|
||||
.DS_STORE
|
||||
.DS_Store
|
||||
/cover
|
||||
coverage.xml
|
||||
coverage-karma
|
||||
nosetests.xml
|
||||
*nose_results.html
|
||||
pep8.txt
|
||||
pylint.txt
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
reports
|
||||
openstack_dashboard/local/*
|
||||
!openstack_dashboard/local/local_settings.py.example
|
||||
!openstack_dashboard/local/enabled/_50__settings.py.example
|
||||
!openstack_dashboard/local/local_settings.d
|
||||
openstack_dashboard/local/local_settings.d/*
|
||||
!openstack_dashboard/local/local_settings.d/*.example
|
||||
openstack_dashboard/test/.secret_key_store
|
||||
openstack_dashboard/test/integration_tests/local-horizon.conf
|
||||
openstack_dashboard/test/integration_tests/test_reports/
|
||||
openstack_dashboard/wsgi/horizon.wsgi
|
||||
doc/build/
|
||||
/static/
|
||||
integration_tests_screenshots/
|
||||
.venv
|
||||
.tox
|
||||
node_modules
|
||||
npm-debug.log
|
||||
build
|
||||
dist
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
tags
|
||||
ghostdriver.log
|
||||
.testrepository
|
||||
.idea
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/horizon.git
|
13
.mailmap
13
.mailmap
|
@ -1,13 +0,0 @@
|
|||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
||||
<ghe@debian.org> <ghe.rivero@stackops.com>
|
||||
<jake@ansolabs.com> <admin@jakedahn.com>
|
||||
<launchpad@markgius.com> <mgius7096@gmail.com>
|
||||
<yorik.sar@gmail.com> <yorik@ytaraday>
|
||||
<jeblair@hp.com> <james.blair@rackspace.com>
|
||||
<ke.wu@ibeca.me> <ke.wu@nebula.com>
|
||||
Zhongyue Luo <zhongyue.nah@intel.com> <lzyeval@gmail.com>
|
||||
Joe Gordon <joe.gordon0@gmail.com> <jogo@cloudscaling.com>
|
||||
Kun Huang <gareth@unitedstack.com> <academicgareth@gmail.com>
|
||||
Zhenguo Niu <zhenguo@unitedstack.com> <Niu.ZGlinux@gmail.com>
|
42
.pylintrc
42
.pylintrc
|
@ -1,42 +0,0 @@
|
|||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
[MASTER]
|
||||
# Add <file or directory> to the black list. It should be a base name, not a
|
||||
# path. You may set this option multiple times.
|
||||
ignore=test
|
||||
|
||||
[Messages Control]
|
||||
# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
|
||||
# C0111: Don't require docstrings on every method
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable=C0111,W0511,W0142,W0622
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowecased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
|
||||
|
||||
# Module names matching keystone-* are ok (files in bin/)
|
||||
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(keystone-[a-z0-9_-]+))$
|
||||
|
||||
# Don't require docstrings on tests.
|
||||
no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
|
||||
|
||||
[Design]
|
||||
max-public-methods=100
|
||||
min-public-methods=0
|
||||
max-args=6
|
||||
|
||||
[Variables]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
|
@ -1,4 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_command=./run_tests.sh -N --no-pep8 --with-xunit
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -1,18 +0,0 @@
|
|||
If you would like to contribute to the development of OpenStack,
|
||||
you must follow the steps documented at:
|
||||
|
||||
http://docs.openstack.org/developer/horizon/contributing.html
|
||||
|
||||
or http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/horizon
|
15
HACKING.rst
15
HACKING.rst
|
@ -1,15 +0,0 @@
|
|||
Horizon Style Commandments
|
||||
==========================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
http://docs.openstack.org/developer/hacking/
|
||||
- Step 2: Read [hacking] section in tox.ini to find the list of names which
|
||||
can be imported directly without triggering the "H302: import only modules"
|
||||
flake8 warning
|
||||
- Step 3: Read on
|
||||
|
||||
Horizon Specific Commandments
|
||||
-----------------------------
|
||||
|
||||
- Read the Horizon contributing documentation at http://docs.openstack.org/developer/horizon/contributing.html
|
||||
- [M322] Method's default argument shouldn't be mutable.
|
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.
|
||||
|
19
MANIFEST.in
19
MANIFEST.in
|
@ -1,19 +0,0 @@
|
|||
recursive-include doc *.py *.rst *.scss *.js *.html *.conf *.jpg *.gif *.png
|
||||
recursive-include horizon *.html *.scss *.js *.csv *.template *.tmpl *.mo *.po
|
||||
recursive-include openstack_dashboard *.html *.js *.scss *.mo *.po *.example *.eot *.svg *.ttf *.woff *.png *.ico *.wsgi *.gif *.csv *.template
|
||||
recursive-include tools *.py *.sh
|
||||
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include LICENSE
|
||||
include Makefile
|
||||
include manage.py
|
||||
include README.rst
|
||||
include run_tests.sh
|
||||
include tox.ini
|
||||
include doc/Makefile
|
||||
include doc/source/_templates/.placeholder
|
||||
include requirements.txt
|
||||
include test-requirements.txt
|
||||
|
||||
exclude openstack_dashboard/local/local_settings.py
|
23
Makefile
23
Makefile
|
@ -1,23 +0,0 @@
|
|||
PYTHON=`which python`
|
||||
DESTDIR=/
|
||||
PROJECT=horizon
|
||||
|
||||
all:
|
||||
@echo "make source - Create source package"
|
||||
@echo "make install - Install on local system"
|
||||
@echo "make buildrpm - Generate a rpm package"
|
||||
@echo "make clean - Get rid of scratch and byte files"
|
||||
|
||||
source:
|
||||
$(PYTHON) setup.py sdist $(COMPILE)
|
||||
|
||||
install:
|
||||
$(PYTHON) setup.py install --root $(DESTDIR) $(COMPILE)
|
||||
|
||||
buildrpm:
|
||||
$(PYTHON) setup.py bdist_rpm --post-install=rpm/postinstall --pre-uninstall=rpm/preuninstall
|
||||
|
||||
clean:
|
||||
$(PYTHON) setup.py clean
|
||||
rm -rf build/ MANIFEST
|
||||
find . -name '*.pyc' -delete
|
|
@ -0,0 +1,14 @@
|
|||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
50
README.rst
50
README.rst
|
@ -1,50 +0,0 @@
|
|||
=============================
|
||||
Horizon (OpenStack Dashboard)
|
||||
=============================
|
||||
|
||||
Horizon is a Django-based project aimed at providing a complete OpenStack
|
||||
Dashboard along with an extensible framework for building new dashboards
|
||||
from reusable components. The ``openstack_dashboard`` module is a reference
|
||||
implementation of a Django site that uses the ``horizon`` app to provide
|
||||
web-based interactions with the various OpenStack projects.
|
||||
|
||||
* Release management: https://launchpad.net/horizon
|
||||
* Blueprints and feature specifications: https://blueprints.launchpad.net/horizon
|
||||
* Issue tracking: https://bugs.launchpad.net/horizon
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/horizon.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
Using Horizon
|
||||
=============
|
||||
|
||||
See ``doc/source/install/index.rst`` about how to install Horizon
|
||||
in your OpenStack setup. It describes the example steps and
|
||||
has pointers for more detailed settings and configurations.
|
||||
|
||||
It is also available at
|
||||
http://docs.openstack.org/horizon/install/index.html.
|
||||
|
||||
Getting Started for Developers
|
||||
==============================
|
||||
|
||||
``doc/source/quickstart.rst`` or
|
||||
http://docs.openstack.org/horizon/contributor/quickstart.html
|
||||
describes how to setup Horizon development environment and start development.
|
||||
|
||||
Building Contributor Documentation
|
||||
==================================
|
||||
|
||||
This documentation is written by contributors, for contributors.
|
||||
|
||||
The source is maintained in the ``doc/source`` directory using
|
||||
`reStructuredText`_ and built by `Sphinx`_
|
||||
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx-doc.org/
|
||||
|
||||
To build the docs, use::
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
Results are in the ``doc/build/html`` directory
|
|
@ -1,6 +0,0 @@
|
|||
[extractors]
|
||||
django = django_babel.extract:extract_django
|
||||
|
||||
[python: **.py]
|
||||
[django: **/templates/**.html]
|
||||
[django: **/templates/**.csv]
|
|
@ -1,14 +0,0 @@
|
|||
[extractors]
|
||||
# We use a custom extractor to find translatable strings in AngularJS
|
||||
# templates. The extractor is included in horizon.utils for now.
|
||||
# See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for
|
||||
# details on how this works.
|
||||
angular = horizon.utils.babel_extract_angular:extract_angular
|
||||
|
||||
[javascript: **.js]
|
||||
|
||||
# We need to look into all static folders for HTML files.
|
||||
# The **/static ensures that we also search within
|
||||
# /openstack_dashboard/dashboards/XYZ/static which will ensure
|
||||
# that plugins are also translated.
|
||||
[angular: **/static/**.html]
|
153
doc/Makefile
153
doc/Makefile
|
@ -1,153 +0,0 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Horizon.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Horizon.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/Horizon"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Horizon"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
@ -1,59 +0,0 @@
|
|||
=======================
|
||||
Create and manage roles
|
||||
=======================
|
||||
|
||||
A role is a personality that a user assumes to perform a specific set
|
||||
of operations. A role includes a set of rights and privileges. A user
|
||||
assumes that role inherits those rights and privileges.
|
||||
|
||||
.. note::
|
||||
|
||||
OpenStack Identity service defines a user's role on a
|
||||
project, but it is completely up to the individual service
|
||||
to define what that role means. This is referred to as the
|
||||
service's policy. To get details about what the privileges
|
||||
for each role are, refer to the ``policy.json`` file
|
||||
available for each service in the
|
||||
``/etc/SERVICE/policy.json`` file. For example, the
|
||||
policy defined for OpenStack Identity service is defined
|
||||
in the ``/etc/keystone/policy.json`` file.
|
||||
|
||||
Create a role
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
#. On the :guilabel:`Identity` tab, click the :guilabel:`Roles` category.
|
||||
#. Click the :guilabel:`Create Role` button.
|
||||
|
||||
In the :guilabel:`Create Role` window, enter a name for the role.
|
||||
#. Click the :guilabel:`Create Role` button to confirm your changes.
|
||||
|
||||
Edit a role
|
||||
~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`Identity` project from the
|
||||
drop-down list.
|
||||
#. On the :guilabel:`Identity` tab, click the :guilabel:`Roles` category.
|
||||
#. Click the :guilabel:`Edit` button.
|
||||
|
||||
In the :guilabel:`Update Role` window, enter a new name for the role.
|
||||
#. Click the :guilabel:`Update Role` button to confirm your changes.
|
||||
|
||||
.. note::
|
||||
|
||||
Using the dashboard, you can edit only the name assigned to
|
||||
a role.
|
||||
|
||||
Delete a role
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`Identity` project from the
|
||||
drop-down list.
|
||||
#. On the :guilabel:`Identity` tab, click the :guilabel:`Roles` category.
|
||||
#. Select the role you want to delete and click the :guilabel:`Delete
|
||||
Roles` button.
|
||||
#. In the :guilabel:`Confirm Delete Roles` window, click :guilabel:`Delete
|
||||
Roles` to confirm the deletion.
|
||||
|
||||
You cannot undo this action.
|
|
@ -1,34 +0,0 @@
|
|||
============================================
|
||||
Launch and manage stacks using the Dashboard
|
||||
============================================
|
||||
|
||||
The Orchestration service provides a template-based
|
||||
orchestration engine for the OpenStack cloud. Orchestration
|
||||
services create and manage cloud infrastructure
|
||||
resources such as storage, networking, instances, and
|
||||
applications as a repeatable running environment.
|
||||
|
||||
Administrators use templates to create stacks, which are
|
||||
collections of resources. For example, a stack might
|
||||
include instances, floating IPs, volumes,
|
||||
security groups, or users. The Orchestration service
|
||||
offers access to all OpenStack
|
||||
core services via a single modular template, with additional
|
||||
orchestration capabilities such as auto-scaling and basic
|
||||
high availability.
|
||||
|
||||
For information about:
|
||||
|
||||
* administrative tasks on the command-line, see
|
||||
the `OpenStack Administrator Guide
|
||||
<https://docs.openstack.org/admin-guide/cli-admin-manage-stacks.html>`__.
|
||||
|
||||
.. note::
|
||||
|
||||
There are no administration-specific tasks that can be done through
|
||||
the Dashboard.
|
||||
|
||||
* the basic creation and deletion of Orchestration stacks, refer to
|
||||
the `OpenStack End User Guide
|
||||
<https://docs.openstack.org/user-guide/dashboard-stacks.html>`__.
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
=====================================
|
||||
Customize and configure the Dashboard
|
||||
=====================================
|
||||
|
||||
Once you have the Dashboard installed, you can customize the way
|
||||
it looks and feels to suit the needs of your environment, your
|
||||
project, or your business.
|
||||
|
||||
You can also configure the Dashboard for a secure HTTPS deployment, or
|
||||
an HTTP deployment. The standard OpenStack installation uses a non-encrypted
|
||||
HTTP channel, but you can enable SSL support for the Dashboard.
|
||||
|
||||
For information on configuring HTTPS or HTTP, see :ref:`configure_dashboard`.
|
||||
|
||||
.. This content is out of date as of the Mitaka release, and needs an
|
||||
.. update to reflect the most recent work on themeing - JR -.
|
||||
|
||||
Customize the Dashboard
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The OpenStack Dashboard on Ubuntu installs the
|
||||
``openstack-dashboard-ubuntu-theme`` package by default. If you do not
|
||||
want to use this theme, remove it and its dependencies:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# apt-get remove --auto-remove openstack-dashboard-ubuntu-theme
|
||||
|
||||
.. note::
|
||||
|
||||
This guide focuses on the ``local_settings.py`` file.
|
||||
|
||||
The following Dashboard content can be customized to suit your needs:
|
||||
|
||||
* Logo
|
||||
* Site colors
|
||||
* HTML title
|
||||
* Logo link
|
||||
* Help URL
|
||||
|
||||
Logo and site colors
|
||||
--------------------
|
||||
|
||||
#. Create two PNG logo files with transparent backgrounds using
|
||||
the following sizes:
|
||||
|
||||
- Login screen: 365 x 50
|
||||
- Logged in banner: 216 x 35
|
||||
|
||||
#. Upload your new images to
|
||||
``/usr/share/openstack-dashboard/openstack_dashboard/static/dashboard/img/``.
|
||||
|
||||
#. Create a CSS style sheet in
|
||||
``/usr/share/openstack-dashboard/openstack_dashboard/static/dashboard/scss/``.
|
||||
|
||||
#. Change the colors and image file names as appropriate. Ensure the
|
||||
relative directory paths are the same. The following example file
|
||||
shows you how to customize your CSS file:
|
||||
|
||||
.. code-block:: css
|
||||
|
||||
/*
|
||||
* New theme colors for dashboard that override the defaults:
|
||||
* dark blue: #355796 / rgb(53, 87, 150)
|
||||
* light blue: #BAD3E1 / rgb(186, 211, 225)
|
||||
*
|
||||
* By Preston Lee <plee@tgen.org>
|
||||
*/
|
||||
h1.brand {
|
||||
background: #355796 repeat-x top left;
|
||||
border-bottom: 2px solid #BAD3E1;
|
||||
}
|
||||
h1.brand a {
|
||||
background: url(../img/my_cloud_logo_small.png) top left no-repeat;
|
||||
}
|
||||
#splash .login {
|
||||
background: #355796 url(../img/my_cloud_logo_medium.png) no-repeat center 35px;
|
||||
}
|
||||
#splash .login .modal-header {
|
||||
border-top: 1px solid #BAD3E1;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: none !important;
|
||||
background-color: #355796 !important;
|
||||
border: none !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:active {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: #BAD3E1 !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#. Open the following HTML template in an editor of your choice:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
/usr/share/openstack-dashboard/openstack_dashboard/templates/_stylesheets.html
|
||||
|
||||
#. Add a line to include your newly created style sheet. For example,
|
||||
``custom.css`` file:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<link href='{{ STATIC_URL }}bootstrap/css/bootstrap.min.css' media='screen' rel='stylesheet' />
|
||||
<link href='{{ STATIC_URL }}dashboard/css/{% choose_css %}' media='screen' rel='stylesheet' />
|
||||
<link href='{{ STATIC_URL }}dashboard/css/custom.css' media='screen' rel='stylesheet' />
|
||||
|
||||
#. Restart the Apache service.
|
||||
|
||||
#. To view your changes, reload your Dashboard. If necessary, go back
|
||||
and modify your CSS file as appropriate.
|
||||
|
||||
HTML title
|
||||
----------
|
||||
|
||||
#. Set the HTML title, which appears at the top of the browser window, by
|
||||
adding the following line to ``local_settings.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SITE_BRANDING = "Example, Inc. Cloud"
|
||||
|
||||
#. Restart Apache for this change to take effect.
|
||||
|
||||
Logo link
|
||||
---------
|
||||
|
||||
#. The logo also acts as a hyperlink. The default behavior is to redirect
|
||||
to ``horizon:user_home``. To change this, add the following attribute to
|
||||
``local_settings.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SITE_BRANDING_LINK = "http://example.com"
|
||||
|
||||
#. Restart Apache for this change to take effect.
|
||||
|
||||
Help URL
|
||||
--------
|
||||
|
||||
#. By default, the help URL points to https://docs.openstack.org. To change
|
||||
this, edit the following attribute in ``local_settings.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
HORIZON_CONFIG["help_url"] = "http://openstack.mycompany.org"
|
||||
|
||||
#. Restart Apache for this change to take effect.
|
||||
|
||||
.. _configure_dashboard:
|
||||
|
||||
Configure the Dashboard
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following section on configuring the Dashboard for a
|
||||
secure HTTPS deployment, or a HTTP deployment, uses concrete
|
||||
examples to ensure the procedure is clear. The file path varies
|
||||
by distribution, however. If needed, you can also configure
|
||||
the VNC window size in the Dashboard.
|
||||
|
||||
Configure the Dashboard for HTTP
|
||||
--------------------------------
|
||||
|
||||
You can configure the Dashboard for a simple HTTP deployment.
|
||||
The standard installation uses a non-encrypted HTTP channel.
|
||||
|
||||
#. Specify the host for your Identity service endpoint in the
|
||||
``local_settings.py`` file with the ``OPENSTACK_HOST`` setting.
|
||||
|
||||
The following example shows this setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import os
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
PROD = True
|
||||
USE_SSL = False
|
||||
|
||||
SITE_BRANDING = 'OpenStack Dashboard'
|
||||
|
||||
# Ubuntu-specific: Enables an extra panel in the 'Settings' section
|
||||
# that easily generates a Juju environments.yaml for download,
|
||||
# preconfigured with endpoints and credentials required for bootstrap
|
||||
# and service deployment.
|
||||
ENABLE_JUJU_PANEL = True
|
||||
|
||||
# Note: You should change this value
|
||||
SECRET_KEY = 'elj1IWiLoWHgryYxFT6j7cM5fGOOxWY0'
|
||||
|
||||
# Specify a regular expression to validate user passwords.
|
||||
# HORIZON_CONFIG = {
|
||||
# "password_validator": {
|
||||
# "regex": '.*',
|
||||
# "help_text": _("Your password does not meet the requirements.")
|
||||
# }
|
||||
# }
|
||||
|
||||
LOCAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION' : '127.0.0.1:11211'
|
||||
}
|
||||
}
|
||||
|
||||
# Send email to the console by default
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
# Or send them to /dev/null
|
||||
#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
|
||||
|
||||
# Configure these for your outgoing email host
|
||||
# EMAIL_HOST = 'smtp.my-company.com'
|
||||
# EMAIL_PORT = 25
|
||||
# EMAIL_HOST_USER = 'djangomail'
|
||||
# EMAIL_HOST_PASSWORD = 'top-secret!'
|
||||
|
||||
# For multiple regions uncomment this configuration, and add (endpoint, title).
|
||||
# AVAILABLE_REGIONS = [
|
||||
# ('http://cluster1.example.com:5000/v2.0', 'cluster1'),
|
||||
# ('http://cluster2.example.com:5000/v2.0', 'cluster2'),
|
||||
# ]
|
||||
|
||||
OPENSTACK_HOST = "127.0.0.1"
|
||||
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_HOST
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member"
|
||||
|
||||
# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the
|
||||
# capabilities of the auth backend for Keystone.
|
||||
# If Keystone has been configured to use LDAP as the auth backend then set
|
||||
# can_edit_user to False and name to 'ldap'.
|
||||
#
|
||||
# TODO(tres): Remove these once Keystone has an API to identify auth backend.
|
||||
OPENSTACK_KEYSTONE_BACKEND = {
|
||||
'name': 'native',
|
||||
'can_edit_user': True
|
||||
}
|
||||
|
||||
# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints
|
||||
# in the Keystone service catalog. Use this setting when Horizon is running
|
||||
# external to the OpenStack environment. The default is 'internalURL'.
|
||||
#OPENSTACK_ENDPOINT_TYPE = "publicURL"
|
||||
|
||||
# The number of Swift containers and objects to display on a single page before
|
||||
# providing a paging element (a "more" link) to paginate results.
|
||||
API_RESULT_LIMIT = 1000
|
||||
|
||||
# If you have external monitoring links, eg:
|
||||
# EXTERNAL_MONITORING = [
|
||||
# ['Nagios','http://foo.com'],
|
||||
# ['Ganglia','http://bar.com'],
|
||||
# ]
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
# When set to True this will disable all logging except
|
||||
# for loggers specified in this configuration dictionary. Note that
|
||||
# if nothing is specified here and disable_existing_loggers is True,
|
||||
# django.db.backends will still log unless it is disabled explicitly.
|
||||
'disable_existing_loggers': False,
|
||||
'handlers': {
|
||||
'null': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'django.utils.log.NullHandler',
|
||||
},
|
||||
'console': {
|
||||
# Set the level to "DEBUG" for verbose output logging.
|
||||
'level': 'INFO',
|
||||
'class': 'logging.StreamHandler',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
# Logging from django.db.backends is VERY verbose, send to null
|
||||
# by default.
|
||||
'django.db.backends': {
|
||||
'handlers': ['null'],
|
||||
'propagate': False,
|
||||
},
|
||||
'horizon': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'novaclient': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'keystoneclient': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
},
|
||||
'nose.plugins.manager': {
|
||||
'handlers': ['console'],
|
||||
'propagate': False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The service catalog configuration in the Identity service determines
|
||||
whether a service appears in the Dashboard.
|
||||
For the full listing, see :ref:`install-settings`.
|
||||
|
||||
#. Restart the Apache HTTP Server.
|
||||
|
||||
#. Restart ``memcached``.
|
||||
|
||||
Configure the Dashboard for HTTPS
|
||||
---------------------------------
|
||||
|
||||
You can configure the Dashboard for a secured HTTPS deployment.
|
||||
While the standard installation uses a non-encrypted HTTP channel,
|
||||
you can enable SSL support for the Dashboard.
|
||||
|
||||
This example uses the ``http://openstack.example.com`` domain.
|
||||
Use a domain that fits your current setup.
|
||||
|
||||
#. In the ``local_settings.py`` file, update the following options:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
USE_SSL = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
|
||||
To enable HTTPS, the ``USE_SSL = True`` option is required.
|
||||
|
||||
The other options require that HTTPS is enabled;
|
||||
these options defend against cross-site scripting.
|
||||
|
||||
#. Edit the ``openstack-dashboard.conf`` file as shown in the
|
||||
**Example After**:
|
||||
|
||||
**Example Before**
|
||||
|
||||
.. code-block:: apacheconf
|
||||
|
||||
WSGIScriptAlias / /usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi
|
||||
WSGIDaemonProcess horizon user=www-data group=www-data processes=3 threads=10
|
||||
Alias /static /usr/share/openstack-dashboard/openstack_dashboard/static/
|
||||
<Directory /usr/share/openstack-dashboard/openstack_dashboard/wsgi>
|
||||
# For Apache http server 2.2 and earlier:
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
|
||||
# For Apache http server 2.4 and later:
|
||||
# Require all granted
|
||||
</Directory>
|
||||
|
||||
**Example After**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName openstack.example.com
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
|
||||
</IfModule>
|
||||
<IfModule !mod_rewrite.c>
|
||||
RedirectPermanent / https://openstack.example.com
|
||||
</IfModule>
|
||||
</VirtualHost>
|
||||
<VirtualHost *:443>
|
||||
ServerName openstack.example.com
|
||||
|
||||
SSLEngine On
|
||||
# Remember to replace certificates and keys with valid paths in your environment
|
||||
SSLCertificateFile /etc/apache2/SSL/openstack.example.com.crt
|
||||
SSLCACertificateFile /etc/apache2/SSL/openstack.example.com.crt
|
||||
SSLCertificateKeyFile /etc/apache2/SSL/openstack.example.com.key
|
||||
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown
|
||||
|
||||
# HTTP Strict Transport Security (HSTS) enforces that all communications
|
||||
# with a server go over SSL. This mitigates the threat from attacks such
|
||||
# as SSL-Strip which replaces links on the wire, stripping away https prefixes
|
||||
# and potentially allowing an attacker to view confidential information on the
|
||||
# wire
|
||||
Header add Strict-Transport-Security "max-age=15768000"
|
||||
|
||||
WSGIScriptAlias / /usr/share/openstack-dashboard/openstack_dashboard/wsgi/django.wsgi
|
||||
WSGIDaemonProcess horizon user=www-data group=www-data processes=3 threads=10
|
||||
Alias /static /usr/share/openstack-dashboard/openstack_dashboard/static/
|
||||
<Directory /usr/share/openstack-dashboard/openstack_dashboard/wsgi>
|
||||
# For Apache http server 2.2 and earlier:
|
||||
<ifVersion <2.4>
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</ifVersion>
|
||||
# For Apache http server 2.4 and later:
|
||||
<ifVersion >=2.4>
|
||||
#The following two lines have been added by bms for error "AH01630: client denied
|
||||
#by server configuration:
|
||||
#/usr/share/openstack-dashboard/openstack_dashboard/static/dashboard/cssa"
|
||||
Options All
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</ifVersion>
|
||||
</Directory>
|
||||
<Directory /usr/share/openstack-dashboard/static>
|
||||
<ifVersion >=2.4>
|
||||
Options All
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</ifVersion>
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
In this configuration, the Apache HTTP Server listens on port 443 and
|
||||
redirects all non-secure requests to the HTTPS protocol. The secured
|
||||
section defines the private key, public key, and certificate to use.
|
||||
|
||||
#. Restart the Apache HTTP Server.
|
||||
|
||||
#. Restart ``memcached``.
|
||||
|
||||
If you try to access the Dashboard through HTTP, the browser redirects
|
||||
you to the HTTPS page.
|
||||
|
||||
.. note::
|
||||
|
||||
Configuring the Dashboard for HTTPS also requires enabling SSL for
|
||||
the noVNC proxy service. On the controller node, add the following
|
||||
additional options to the ``[DEFAULT]`` section of the
|
||||
``/etc/nova/nova.conf`` file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
# ...
|
||||
ssl_only = true
|
||||
cert = /etc/apache2/SSL/openstack.example.com.crt
|
||||
key = /etc/apache2/SSL/openstack.example.com.key
|
||||
|
||||
On the compute nodes, ensure the ``nonvncproxy_base_url`` option
|
||||
points to a URL with an HTTPS scheme:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[DEFAULT]
|
||||
# ...
|
||||
novncproxy_base_url = https://controller:6080/vnc_auto.html
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 67 KiB |
Binary file not shown.
Before Width: | Height: | Size: 85 KiB |
|
@ -1,38 +0,0 @@
|
|||
====================
|
||||
Administration Guide
|
||||
====================
|
||||
|
||||
The OpenStack Dashboard is a web-based interface that allows you to
|
||||
manage OpenStack resources and services. The Dashboard allows you to
|
||||
interact with the OpenStack Compute cloud controller using the OpenStack
|
||||
APIs. For more information about installing and configuring the
|
||||
Dashboard, see the `Installation Tutorials and Guides
|
||||
<https://docs.openstack.org/project-install-guide/ocata/>`__
|
||||
for your operating system.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
customize-configure.rst
|
||||
sessions.rst
|
||||
manage-images.rst
|
||||
admin-manage-roles.rst
|
||||
manage-projects-and-users.rst
|
||||
manage-instances.rst
|
||||
manage-flavors.rst
|
||||
manage-volumes.rst
|
||||
manage-shares.rst
|
||||
set-quotas.rst
|
||||
manage-resources.rst
|
||||
manage-host-aggregates.rst
|
||||
admin-manage-stacks.rst
|
||||
|
||||
- To deploy the dashboard, see the `OpenStack dashboard documentation
|
||||
<https://docs.openstack.org/developer/horizon/topics/deployment.html>`__.
|
||||
- To launch instances with the dashboard as an end user, see the
|
||||
`Launch and manage instances
|
||||
<https://docs.openstack.org/user-guide/launch-instances.html>`__.
|
||||
in the OpenStack End User Guide.
|
||||
- To create and manage ports, see the `Create and manage networks
|
||||
<https://docs.openstack.org/user-guide/dashboard-create-networks.html#create-a-port>`__
|
||||
section of the OpenStack End User Guide.
|
|
@ -1,167 +0,0 @@
|
|||
==============
|
||||
Manage flavors
|
||||
==============
|
||||
|
||||
In OpenStack, a flavor defines the compute, memory, and storage
|
||||
capacity of a virtual server, also known as an instance. As an
|
||||
administrative user, you can create, edit, and delete flavors.
|
||||
|
||||
As of Newton, there are no default flavors. The following table
|
||||
lists the default flavors for Mitaka and earlier.
|
||||
|
||||
============ ========= =============== =============
|
||||
Flavor VCPUs Disk (in GB) RAM (in MB)
|
||||
============ ========= =============== =============
|
||||
m1.tiny 1 1 512
|
||||
m1.small 1 20 2048
|
||||
m1.medium 2 40 4096
|
||||
m1.large 4 80 8192
|
||||
m1.xlarge 8 160 16384
|
||||
============ ========= =============== =============
|
||||
|
||||
Create flavors
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
#. In the :guilabel:`Admin` tab, open the :guilabel:`System`
|
||||
tab and click the :guilabel:`Flavors` category.
|
||||
#. Click :guilabel:`Create Flavor`.
|
||||
#. In the :guilabel:`Create Flavor` window, enter or select the
|
||||
parameters for the flavor in the :guilabel:`Flavor Information` tab.
|
||||
|
||||
.. figure:: figures/create_flavor.png
|
||||
|
||||
**Dashboard — Create Flavor**
|
||||
|
||||
========================= =======================================
|
||||
**Name** Enter the flavor name.
|
||||
**ID** Unique ID (integer or UUID) for the
|
||||
new flavor. If specifying 'auto', a
|
||||
UUID will be automatically generated.
|
||||
**VCPUs** Enter the number of virtual CPUs to
|
||||
use.
|
||||
**RAM (MB)** Enter the amount of RAM to use, in
|
||||
megabytes.
|
||||
**Root Disk (GB)** Enter the amount of disk space in
|
||||
gigabytes to use for the root (/)
|
||||
partition.
|
||||
**Ephemeral Disk (GB)** Enter the amount of disk space in
|
||||
gigabytes to use for the ephemeral
|
||||
partition. If unspecified, the value
|
||||
is 0 by default.
|
||||
|
||||
Ephemeral disks offer machine local
|
||||
disk storage linked to the lifecycle
|
||||
of a VM instance. When a VM is
|
||||
terminated, all data on the ephemeral
|
||||
disk is lost. Ephemeral disks are not
|
||||
included in any snapshots.
|
||||
**Swap Disk (MB)** Enter the amount of swap space (in
|
||||
megabytes) to use. If unspecified,
|
||||
the default is 0.
|
||||
**RX/TX Factor** Optional property allows servers with
|
||||
a different bandwidth to be created
|
||||
with the RX/TX Factor. The default
|
||||
value is 1. That is, the new bandwidth
|
||||
is the same as that of the attached
|
||||
network.
|
||||
========================= =======================================
|
||||
|
||||
#. In the :guilabel:`Flavor Access` tab, you can control access to
|
||||
the flavor by moving projects from the :guilabel:`All Projects`
|
||||
column to the :guilabel:`Selected Projects` column.
|
||||
|
||||
Only projects in the :guilabel:`Selected Projects` column can
|
||||
use the flavor. If there are no projects in the right column,
|
||||
all projects can use the flavor.
|
||||
#. Click :guilabel:`Create Flavor`.
|
||||
|
||||
Update flavors
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
#. In the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Flavors` category.
|
||||
#. Select the flavor that you want to edit. Click :guilabel:`Edit
|
||||
Flavor`.
|
||||
#. In the :guilabel:`Edit Flavor` window, you can change the flavor
|
||||
name, VCPUs, RAM, root disk, ephemeral disk, and swap disk values.
|
||||
#. Click :guilabel:`Save`.
|
||||
|
||||
Update Metadata
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
#. In the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Flavors` category.
|
||||
#. Select the flavor that you want to update. In the drop-down
|
||||
list, click :guilabel:`Update Metadata` or click :guilabel:`No` or
|
||||
:guilabel:`Yes` in the :guilabel:`Metadata` column.
|
||||
#. In the :guilabel:`Update Flavor Metadata` window, you can customize
|
||||
some metadata keys, then add it to this flavor and set them values.
|
||||
#. Click :guilabel:`Save`.
|
||||
|
||||
**Optional metadata keys**
|
||||
|
||||
+-------------------------------+-------------------------------+
|
||||
| | quota:cpu_shares |
|
||||
| +-------------------------------+
|
||||
| **CPU limits** | quota:cpu_period |
|
||||
| +-------------------------------+
|
||||
| | quota:cpu_limit |
|
||||
| +-------------------------------+
|
||||
| | quota:cpu_reservation |
|
||||
| +-------------------------------+
|
||||
| | quota:cpu_quota |
|
||||
+-------------------------------+-------------------------------+
|
||||
| | quota:disk_read_bytes_sec |
|
||||
| +-------------------------------+
|
||||
| **Disk tuning** | quota:disk_read_iops_sec |
|
||||
| +-------------------------------+
|
||||
| | quota:disk_write_bytes_sec |
|
||||
| +-------------------------------+
|
||||
| | quota:disk_write_iops_sec |
|
||||
| +-------------------------------+
|
||||
| | quota:disk_total_bytes_sec |
|
||||
| +-------------------------------+
|
||||
| | quota:disk_total_iops_sec |
|
||||
+-------------------------------+-------------------------------+
|
||||
| | quota:vif_inbound_average |
|
||||
| +-------------------------------+
|
||||
| **Bandwidth I/O** | quota:vif_inbound_burst |
|
||||
| +-------------------------------+
|
||||
| | quota:vif_inbound_peak |
|
||||
| +-------------------------------+
|
||||
| | quota:vif_outbound_average |
|
||||
| +-------------------------------+
|
||||
| | quota:vif_outbound_burst |
|
||||
| +-------------------------------+
|
||||
| | quota:vif_outbound_peak |
|
||||
+-------------------------------+-------------------------------+
|
||||
| **Watchdog behavior** | hw:watchdog_action |
|
||||
+-------------------------------+-------------------------------+
|
||||
| | hw_rng:allowed |
|
||||
| +-------------------------------+
|
||||
| **Random-number generator** | hw_rng:rate_bytes |
|
||||
| +-------------------------------+
|
||||
| | hw_rng:rate_period |
|
||||
+-------------------------------+-------------------------------+
|
||||
|
||||
For information about supporting metadata keys, see the
|
||||
the Compute service documentation.
|
||||
|
||||
Delete flavors
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
#. In the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Flavors` category.
|
||||
#. Select the flavors that you want to delete.
|
||||
#. Click :guilabel:`Delete Flavors`.
|
||||
#. In the :guilabel:`Confirm Delete Flavors` window, click
|
||||
:guilabel:`Delete Flavors` to confirm the deletion. You cannot
|
||||
undo this action.
|
|
@ -1,77 +0,0 @@
|
|||
=================================
|
||||
Create and manage host aggregates
|
||||
=================================
|
||||
|
||||
Host aggregates enable administrative users to assign key-value pairs to
|
||||
groups of machines.
|
||||
|
||||
Each node can have multiple aggregates and each aggregate can have
|
||||
multiple key-value pairs. You can assign the same key-value pair to
|
||||
multiple aggregates.
|
||||
|
||||
The scheduler uses this information to make scheduling decisions.
|
||||
For information, see
|
||||
`Scheduling <https://docs.openstack.org/ocata/config-reference/compute/schedulers.html>`__.
|
||||
|
||||
To create a host aggregate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab and click
|
||||
the :guilabel:`Host Aggregates` category.
|
||||
|
||||
#. Click :guilabel:`Create Host Aggregate`.
|
||||
|
||||
#. In the :guilabel:`Create Host Aggregate` dialog box, enter or select the
|
||||
following values on the :guilabel:`Host Aggregate Information` tab:
|
||||
|
||||
- :guilabel:`Name`: The host aggregate name.
|
||||
|
||||
- :guilabel:`Availability Zone`: The cloud provider defines the default
|
||||
availability zone, such as ``us-west``, ``apac-south``, or
|
||||
``nova``. You can target the host aggregate, as follows:
|
||||
|
||||
- When the host aggregate is exposed as an availability zone,
|
||||
select the availability zone when you launch an instance.
|
||||
|
||||
- When the host aggregate is not exposed as an availability zone,
|
||||
select a flavor and its extra specs to target the host
|
||||
aggregate.
|
||||
|
||||
#. Assign hosts to the aggregate using the :guilabel:`Manage Hosts within
|
||||
Aggregate` tab in the same dialog box.
|
||||
|
||||
To assign a host to the aggregate, click **+** for the host. The host
|
||||
moves from the :guilabel:`All available hosts` list to the
|
||||
:guilabel:`Selected hosts` list.
|
||||
|
||||
You can add one host to one or more aggregates. To add a host to an
|
||||
existing aggregate, edit the aggregate.
|
||||
|
||||
To manage host aggregates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Select the :guilabel:`admin` project from the drop-down list at the top
|
||||
of the page.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab and click
|
||||
the :guilabel:`Host Aggregates` category.
|
||||
|
||||
- To edit host aggregates, select the host aggregate that you want
|
||||
to edit. Click :guilabel:`Edit Host Aggregate`.
|
||||
|
||||
In the :guilabel:`Edit Host Aggregate` dialog box, you can change the
|
||||
name and availability zone for the aggregate.
|
||||
|
||||
- To manage hosts, locate the host aggregate that you want to edit
|
||||
in the table. Click :guilabel:`More` and select :guilabel:`Manage Hosts`.
|
||||
|
||||
In the :guilabel:`Add/Remove Hosts to Aggregate` dialog box,
|
||||
click **+** to assign a host to an aggregate. Click **-** to
|
||||
remove a host that is assigned to an aggregate.
|
||||
|
||||
- To delete host aggregates, locate the host aggregate that you want
|
||||
to edit in the table. Click :guilabel:`More` and select
|
||||
:guilabel:`Delete Host Aggregate`.
|
|
@ -1,115 +0,0 @@
|
|||
========================
|
||||
Create and manage images
|
||||
========================
|
||||
|
||||
As an administrative user, you can create and manage images
|
||||
for the projects to which you belong. You can also create
|
||||
and manage images for users in all projects to which you have
|
||||
access.
|
||||
|
||||
To create and manage images in specified projects as an end
|
||||
user, see the `upload and manage images with Dashboard in
|
||||
OpenStack End User Guide
|
||||
<https://docs.openstack.org/user-guide/dashboard-manage-images.html>`_
|
||||
and `manage images with CLI in OpenStack End User Guide
|
||||
<https://docs.openstack.org/user-guide/common/cli-manage-images.html>`_ .
|
||||
|
||||
To create and manage images as an administrator for other
|
||||
users, use the following procedures.
|
||||
|
||||
Create images
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
For details about image creation, see the `Virtual Machine Image
|
||||
Guide <https://docs.openstack.org/image-guide/>`_.
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Images` category. The images that you
|
||||
can administer for cloud users appear on this page.
|
||||
#. Click :guilabel:`Create Image`, which opens the
|
||||
:guilabel:`Create An Image` window.
|
||||
|
||||
.. figure:: figures/create_image.png
|
||||
|
||||
**Figure Dashboard — Create Image**
|
||||
|
||||
#. In the :guilabel:`Create An Image` window, enter or select the
|
||||
following values:
|
||||
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Name` | Enter a name for the image. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Description` | Enter a brief description of |
|
||||
| | the image. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Image Source` | Choose the image source from |
|
||||
| | the dropdown list. Your choices |
|
||||
| | are :guilabel:`Image Location` |
|
||||
| | and :guilabel:`Image File`. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Image File` or | Based on your selection, there |
|
||||
| :guilabel:`Image Location` | is an :guilabel:`Image File` or |
|
||||
| | :guilabel:`Image Location` |
|
||||
| | field. You can include the |
|
||||
| | location URL or browse for the |
|
||||
| | image file on your file system |
|
||||
| | and add it. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Format` | Select the image format. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Architecture` | Specify the architecture. For |
|
||||
| | example, ``i386`` for a 32-bit |
|
||||
| | architecture or ``x86_64`` for |
|
||||
| | a 64-bit architecture. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Minimum Disk (GB)` | Leave this field empty. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Minimum RAM (MB)` | Leave this field empty. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Copy Data` | Specify this option to copy |
|
||||
| | image data to the Image service.|
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Public` | Select this option to make the |
|
||||
| | image public to all users. |
|
||||
+-------------------------------+---------------------------------+
|
||||
| :guilabel:`Protected` | Select this option to ensure |
|
||||
| | that only users with |
|
||||
| | permissions can delete it. |
|
||||
+-------------------------------+---------------------------------+
|
||||
|
||||
#. Click :guilabel:`Create Image`.
|
||||
|
||||
The image is queued to be uploaded. It might take several minutes
|
||||
before the status changes from ``Queued`` to ``Active``.
|
||||
|
||||
Update images
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Images` category.
|
||||
#. Select the images that you want to edit. Click :guilabel:`Edit Image`.
|
||||
#. In the :guilabel:`Edit Image` window, you can change the image name.
|
||||
|
||||
Select the :guilabel:`Public` check box to make the image public.
|
||||
Clear this check box to make the image private. You cannot change
|
||||
the :guilabel:`Kernel ID`, :guilabel:`Ramdisk ID`, or
|
||||
:guilabel:`Architecture` attributes for an image.
|
||||
#. Click :guilabel:`Edit Image`.
|
||||
|
||||
Delete images
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
#. On the :guilabel:`Admin tab`, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Images` category.
|
||||
#. Select the images that you want to delete.
|
||||
#. Click :guilabel:`Delete Images`.
|
||||
#. In the :guilabel:`Confirm Delete Images` window, click :guilabel:`Delete
|
||||
Images` to confirm the deletion.
|
||||
|
||||
You cannot undo this action.
|
|
@ -1,77 +0,0 @@
|
|||
================
|
||||
Manage instances
|
||||
================
|
||||
|
||||
As an administrative user, you can manage instances for users in various
|
||||
projects. You can view, terminate, edit, perform a soft or hard reboot,
|
||||
create a snapshot from, and migrate instances. You can also view the
|
||||
logs for instances or launch a VNC console for an instance.
|
||||
|
||||
For information about using the Dashboard to launch instances as an end
|
||||
user, see the `OpenStack End User Guide <https://docs.openstack.org/user-guide/dashboard-launch-instances.html>`__.
|
||||
|
||||
Create instance snapshots
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Instances` category.
|
||||
|
||||
#. Select an instance to create a snapshot from it. From the
|
||||
Actions drop-down list, select :guilabel:`Create Snapshot`.
|
||||
|
||||
#. In the :guilabel:`Create Snapshot` window, enter a name for the snapshot.
|
||||
|
||||
#. Click :guilabel:`Create Snapshot`. The Dashboard shows the instance snapshot
|
||||
in the :guilabel:`Images` category.
|
||||
|
||||
#. To launch an instance from the snapshot, select the snapshot and
|
||||
click :guilabel:`Launch`. For information about launching
|
||||
instances, see the
|
||||
`OpenStack End User Guide <https://docs.openstack.org/user-guide/dashboard-launch-instances.html>`__.
|
||||
|
||||
Control the state of an instance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Instances` category.
|
||||
|
||||
#. Select the instance for which you want to change the state.
|
||||
|
||||
#. From the drop-down list in the Actions column,
|
||||
select the state.
|
||||
|
||||
Depending on the current state of the instance, you can perform various
|
||||
actions on the instance. For example, pause, un-pause, suspend, resume,
|
||||
soft or hard reboot, or terminate (actions in red are dangerous).
|
||||
|
||||
.. figure:: figures/change_instance_state.png
|
||||
:width: 100%
|
||||
|
||||
**Figure Dashboard — Instance Actions**
|
||||
|
||||
|
||||
Track usage
|
||||
~~~~~~~~~~~
|
||||
|
||||
Use the :guilabel:`Overview` category to track usage of instances
|
||||
for each project.
|
||||
|
||||
You can track costs per month by showing meters like number of VCPUs,
|
||||
disks, RAM, and uptime of all your instances.
|
||||
|
||||
#. Log in to the Dashboard and select the :guilabel:`admin` project from the
|
||||
drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Overview` category.
|
||||
|
||||
#. Select a month and click :guilabel:`Submit` to query the instance usage for
|
||||
that month.
|
||||
|
||||
#. Click :guilabel:`Download CSV Summary` to download a CSV summary.
|
|
@ -1,102 +0,0 @@
|
|||
Manage projects and users
|
||||
=========================
|
||||
|
||||
OpenStack administrators can create projects, and create accounts for new users
|
||||
using the OpenStack Dasboard. Projects own specific resources in your
|
||||
OpenStack environment. You can associate users with roles, projects, or both.
|
||||
|
||||
Add a new project
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Projects`.
|
||||
#. Select the :guilabel:`Create Project` push button.
|
||||
The :guilabel:`Create Project` window will open.
|
||||
#. Enter the Project name and description. Leave the :guilabel:`Domain ID`
|
||||
field set at *default*.
|
||||
#. Click :guilabel:`Create Project`.
|
||||
|
||||
.. note::
|
||||
|
||||
Your new project will appear in the list of projects displayed under the
|
||||
:guilabel:`Projects` page of the dashboard. Projects are listed in
|
||||
alphabetical order, and you can check on the **Project ID**, **Domain
|
||||
name**, and status of the project in this section.
|
||||
|
||||
Delete a project
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Projects`.
|
||||
#. Select the checkbox to the left of the project you would like to delete.
|
||||
#. Click on the :guilabel:`Delete Projects` push button.
|
||||
|
||||
Update a project
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Projects`.
|
||||
#. Locate the project you wish to update, and under the :guilabel:`Actions`
|
||||
column click on the drop down arrow next to the :guilabel:`Manage Members`
|
||||
push button. The :guilabel:`Update Project` window will open.
|
||||
#. Update the name of the project, enable the project, or disable the project
|
||||
as needed.
|
||||
|
||||
Add a new user
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Users`.
|
||||
#. Click :guilabel:`Create User`.
|
||||
#. Enter a :guilabel:`Domain Name`, the :guilabel:`Username`, and a
|
||||
:guilabel:`password` for the new user. Enter an email for the new user,
|
||||
and specify which :guilabel:`Primary Project` they belong to. Leave the
|
||||
:guilabel:`Domain ID` field set at *default*. You can also enter a
|
||||
decription for the new user.
|
||||
#. Click the :guilabel:`Create User` push button.
|
||||
|
||||
.. note::
|
||||
|
||||
The new user will then appear in the list of projects displayed under
|
||||
the :guilabel:`Users` page of the dashboard. You can check on the
|
||||
**User Name**, **User ID**, **Domain name**, and the User status in this
|
||||
section.
|
||||
|
||||
Delete a new user
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Users`.
|
||||
#. Select the checkbox to the left of the user you would like to delete.
|
||||
#. Click on the :guilabel:`Delete Users` push button.
|
||||
|
||||
Update a user
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Users`.
|
||||
#. Locate the User you would like to update, and select the :guilabel:`Edit`
|
||||
push button under the :guilabel:`Actions` column.
|
||||
#. Adjust the :guilabel:`Domain Name`, :guilabel:`User Name`,
|
||||
:guilabel:`Description`, :guilabel:`Email`, and :guilabel:`Primary Project`.
|
||||
|
||||
Enable or disable a user
|
||||
------------------------
|
||||
|
||||
#. Log into the OpenStack Dashboard as the Admin user.
|
||||
#. Click on the :guilabel:`Identity` label on the left column, and click
|
||||
:guilabel:`Users`.
|
||||
#. Locate the User you would like to update, and select the arrow to the right
|
||||
of the :guilabel:`Edit` push button. This will open a drop down menu.
|
||||
#. Select :guilabel:`Disable User`.
|
||||
|
||||
.. note::
|
||||
|
||||
To reactivate a disabled user, select :guilabel:`Enable User` under
|
||||
the drop down menu.
|
|
@ -1,10 +0,0 @@
|
|||
====================
|
||||
View cloud resources
|
||||
====================
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
manage-services.rst
|
||||
view-cloud-resources.rst
|
|
@ -1,37 +0,0 @@
|
|||
=========================
|
||||
View services information
|
||||
=========================
|
||||
|
||||
As an administrative user, you can view information for OpenStack services.
|
||||
|
||||
#. Log in to the Dashboard and select the
|
||||
:guilabel:`admin` project from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`System Information` category.
|
||||
|
||||
View the following information on these tabs:
|
||||
|
||||
* :guilabel:`Services`:
|
||||
Displays the internal name and the public OpenStack name
|
||||
for each service, the host on which the service runs,
|
||||
and whether or not the service is enabled.
|
||||
|
||||
* :guilabel:`Compute Services`:
|
||||
Displays information specific to the Compute service. Both host
|
||||
and zone are listed for each service, as well as its
|
||||
activation status.
|
||||
|
||||
* :guilabel:`Block Storage Services`:
|
||||
Displays information specific to the Block Storage service. Both host
|
||||
and zone are listed for each service, as well as its
|
||||
activation status.
|
||||
|
||||
* :guilabel:`Network Agents`:
|
||||
Displays the network agents active within the cluster, such as L3 and
|
||||
DHCP agents, and the status of each agent.
|
||||
|
||||
* :guilabel:`Orchestration Services`:
|
||||
Displays information specific to the Orchestration service. Name,
|
||||
engine id, host and topic are listed for each service, as well as its
|
||||
activation status.
|
|
@ -1,149 +0,0 @@
|
|||
=============================
|
||||
Manage shares and share types
|
||||
=============================
|
||||
|
||||
Shares are file storage that instances can access. Users can
|
||||
allow or deny a running instance to have access to a share at any time.
|
||||
For information about using the Dashboard to create and manage shares as
|
||||
an end user, see the
|
||||
`OpenStack End User Guide <https://docs.openstack.org/user-guide/dashboard-manage-shares.html>`_.
|
||||
|
||||
As an administrative user, you can manage shares and share types for users
|
||||
in various projects. You can create and delete share types, and view
|
||||
or delete shares.
|
||||
|
||||
.. _create-a-share-type:
|
||||
|
||||
Create a share type
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin`
|
||||
project from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Shares` category.
|
||||
|
||||
#. Click the :guilabel:`Share Types` tab, and click
|
||||
:guilabel:`Create Share Type` button. In the
|
||||
:guilabel:`Create Share Type` window, enter or select the
|
||||
following values.
|
||||
|
||||
:guilabel:`Name`: Enter a name for the share type.
|
||||
|
||||
:guilabel:`Driver handles share servers`: Choose True or False
|
||||
|
||||
:guilabel:`Extra specs`: To add extra specs, use key=value.
|
||||
|
||||
#. Click :guilabel:`Create Share Type` button to confirm your changes.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Update share type
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin` project from
|
||||
the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Shares` category.
|
||||
|
||||
#. Click the :guilabel:`Share Types` tab, select the share type
|
||||
that you want to update.
|
||||
|
||||
#. Select :guilabel:`Update Share Type` from Actions.
|
||||
|
||||
#. In the :guilabel:`Update Share Type` window, update extra specs.
|
||||
|
||||
:guilabel:`Extra specs`: To add extra specs, use key=value.
|
||||
To unset extra specs, use key.
|
||||
|
||||
#. Click :guilabel:`Update Share Type` button to confirm your changes.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Delete share types
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you delete a share type, shares of that type are not deleted.
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin` project from
|
||||
the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Shares` category.
|
||||
|
||||
#. Click the :guilabel:`Share Types` tab, select the share type
|
||||
or types that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Share Types` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Share Types` window, click the
|
||||
:guilabel:`Delete Share Types` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Delete shares
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Shares` category.
|
||||
|
||||
#. Select the share or shares that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Shares` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Shares` window, click the
|
||||
:guilabel:`Delete Shares` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Delete share server
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Share Servers` category.
|
||||
|
||||
#. Select the share that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Share Server` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Share Server` window, click the
|
||||
:guilabel:`Delete Share Server` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Delete share networks
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the Dashboard and choose the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Share Networks` category.
|
||||
|
||||
#. Select the share network or share networks that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Share Networks` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Share Networks` window, click the
|
||||
:guilabel:`Delete Share Networks` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
|
@ -1,168 +0,0 @@
|
|||
===============================
|
||||
Manage volumes and volume types
|
||||
===============================
|
||||
|
||||
Volumes are the Block Storage devices that you attach to instances to enable
|
||||
persistent storage. Users can attach a volume to a running instance or detach
|
||||
a volume and attach it to another instance at any time. For information about
|
||||
using the dashboard to create and manage volumes as an end user, see the
|
||||
`OpenStack End User Guide <https://docs.openstack.org/user-guide/dashboard-manage-volumes.html>`_.
|
||||
|
||||
As an administrative user, you can manage volumes and volume types for users
|
||||
in various projects. You can create and delete volume types, and you can view
|
||||
and delete volumes. Note that a volume can be encrypted by using the steps
|
||||
outlined below.
|
||||
|
||||
.. _create-a-volume-type:
|
||||
|
||||
Create a volume type
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin`
|
||||
project from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Volumes` category.
|
||||
|
||||
#. Click the :guilabel:`Volume Types` tab, and click
|
||||
:guilabel:`Create Volume Type` button. In the
|
||||
:guilabel:`Create Volume Type` window, enter a name for the volume type.
|
||||
|
||||
#. Click :guilabel:`Create Volume Type` button to confirm your changes.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Create an encrypted volume type
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Create a volume type using the steps above for :ref:`create-a-volume-type`.
|
||||
|
||||
#. Click :guilabel:`Create Encryption` in the Actions column of the newly
|
||||
created volume type.
|
||||
|
||||
#. Configure the encrypted volume by setting the parameters below from
|
||||
available options (see table):
|
||||
|
||||
Provider
|
||||
Specifies the class responsible for configuring the encryption.
|
||||
Control Location
|
||||
Specifies whether the encryption is from the front end (nova) or the
|
||||
back end (cinder).
|
||||
Cipher
|
||||
Specifies the encryption algorithm.
|
||||
Key Size (bits)
|
||||
Specifies the encryption key size.
|
||||
|
||||
#. Click :guilabel:`Create Volume Type Encryption`.
|
||||
|
||||
.. figure:: figures/create_volume_type_encryption.png
|
||||
|
||||
**Encryption Options**
|
||||
|
||||
The table below provides a few alternatives available for creating encrypted
|
||||
volumes.
|
||||
|
||||
+--------------------+-----------------------+----------------------------+
|
||||
| Encryption | Parameter | Comments |
|
||||
| parameters | options | |
|
||||
+====================+=======================+============================+
|
||||
| Provider |nova.volume.encryptors.|Allows easier import and |
|
||||
| |luks.LuksEncryptor |migration of imported |
|
||||
| |(Recommended) |encrypted volumes, and |
|
||||
| | |allows access key to be |
|
||||
| | |changed without |
|
||||
| | |re-encrypting the volume |
|
||||
+ +-----------------------+----------------------------+
|
||||
| |nova.volume.encryptors.|Less disk overhead than |
|
||||
| |cryptsetup. |LUKS |
|
||||
| |CryptsetupEncryptor | |
|
||||
+--------------------+-----------------------+----------------------------+
|
||||
| Control Location | front-end |The encryption occurs within|
|
||||
| | (Recommended) |nova so that the data |
|
||||
| | |transmitted over the network|
|
||||
| | |is encrypted |
|
||||
| | | |
|
||||
+ +-----------------------+----------------------------+
|
||||
| | back-end |This could be selected if a |
|
||||
| | |cinder plug-in supporting |
|
||||
| | |an encrypted back-end block |
|
||||
| | |storage device becomes |
|
||||
| | |available in the future. |
|
||||
| | |TLS or other network |
|
||||
| | |encryption would also be |
|
||||
| | |needed to protect data as it|
|
||||
| | |traverses the network |
|
||||
+--------------------+-----------------------+----------------------------+
|
||||
| Cipher | aes-xts-plain64 |See NIST reference below |
|
||||
| | (Recommended) |to see advantages* |
|
||||
+ +-----------------------+----------------------------+
|
||||
| | aes-cbc-essiv |Note: On the command line, |
|
||||
| | |type 'cryptsetup benchmark' |
|
||||
| | |for additional options |
|
||||
+--------------------+-----------------------+----------------------------+
|
||||
| Key Size (bits)| 512 (Recommended for |Using this selection for |
|
||||
| | aes-xts-plain64. 256 |aes-xts, the underlying key |
|
||||
| | should be used for |size would only be 256-bits*|
|
||||
| | aes-cbc-essiv) | |
|
||||
+ +-----------------------+----------------------------+
|
||||
| | 256 |Using this selection for |
|
||||
| | |aes-xts, the underlying key |
|
||||
| | |size would only be 128-bits*|
|
||||
+--------------------+-----------------------+----------------------------+
|
||||
|
||||
`*` Source `NIST SP 800-38E <http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38e.pdf>`_
|
||||
|
||||
.. note::
|
||||
|
||||
To see further information and CLI instructions, see
|
||||
`Create an encrypted volume type
|
||||
<https://docs.openstack.org/ocata/config-reference/block-storage/volume-encryption.html>`_
|
||||
in the OpenStack Configuration Reference.
|
||||
|
||||
Delete volume types
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you delete a volume type, volumes of that type are not deleted.
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project from
|
||||
the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Volumes` category.
|
||||
|
||||
#. Click the :guilabel:`Volume Types` tab, select the volume type
|
||||
or types that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Volume Types` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Volume Types` window, click the
|
||||
:guilabel:`Delete Volume Types` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
||||
|
||||
Delete volumes
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
When you delete an instance, the data of its attached volumes is not
|
||||
destroyed.
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Volumes` category.
|
||||
|
||||
#. Select the volume or volumes that you want to delete.
|
||||
|
||||
#. Click :guilabel:`Delete Volumes` button.
|
||||
|
||||
#. In the :guilabel:`Confirm Delete Volumes` window, click the
|
||||
:guilabel:`Delete Volumes` button to confirm the action.
|
||||
|
||||
.. note::
|
||||
|
||||
A message indicates whether the action succeeded.
|
|
@ -1,216 +0,0 @@
|
|||
========================================
|
||||
Set up session storage for the Dashboard
|
||||
========================================
|
||||
|
||||
The Dashboard uses `Django sessions
|
||||
framework <https://docs.djangoproject.com/en/dev/topics/http/sessions/>`__
|
||||
to handle user session data. However, you can use any available session
|
||||
back end. You customize the session back end through the
|
||||
``SESSION_ENGINE`` setting in your ``local_settings.py`` file.
|
||||
|
||||
After architecting and implementing the core OpenStack
|
||||
services and other required services, combined with the Dashboard
|
||||
service steps below, users and administrators can use
|
||||
the OpenStack dashboard. Refer to the `OpenStack Dashboard
|
||||
<https://docs.openstack.org/user-guide/dashboard.html>`__
|
||||
chapter of the OpenStack End User Guide for
|
||||
further instructions on logging in to the Dashboard.
|
||||
|
||||
The following sections describe the pros and cons of each option as it
|
||||
pertains to deploying the Dashboard.
|
||||
|
||||
Local memory cache
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Local memory storage is the quickest and easiest session back end to set
|
||||
up, as it has no external dependencies whatsoever. It has the following
|
||||
significant drawbacks:
|
||||
|
||||
- No shared storage across processes or workers.
|
||||
- No persistence after a process terminates.
|
||||
|
||||
The local memory back end is enabled as the default for Horizon solely
|
||||
because it has no dependencies. It is not recommended for production
|
||||
use, or even for serious development work.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
CACHES = {
|
||||
'default' : {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'
|
||||
}
|
||||
}
|
||||
|
||||
You can use applications such as ``Memcached`` or ``Redis`` for external
|
||||
caching. These applications offer persistence and shared storage and are
|
||||
useful for small-scale deployments and development.
|
||||
|
||||
Memcached
|
||||
---------
|
||||
|
||||
Memcached is a high-performance and distributed memory object caching
|
||||
system providing in-memory key-value store for small chunks of arbitrary
|
||||
data.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Memcached service running and accessible.
|
||||
- Python module ``python-memcached`` installed.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION': 'my_memcached_host:11211',
|
||||
}
|
||||
}
|
||||
|
||||
Redis
|
||||
-----
|
||||
|
||||
Redis is an open source, BSD licensed, advanced key-value store. It is
|
||||
often referred to as a data structure server.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Redis service running and accessible.
|
||||
- Python modules ``redis`` and ``django-redis`` installed.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "redis_cache.cache.RedisCache",
|
||||
"LOCATION": "127.0.0.1:6379:1",
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "redis_cache.client.DefaultClient",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Initialize and configure the database
|
||||
-------------------------------------
|
||||
|
||||
Database-backed sessions are scalable, persistent, and can be made
|
||||
high-concurrency and highly available.
|
||||
|
||||
However, database-backed sessions are one of the slower session storages
|
||||
and incur a high overhead under heavy usage. Proper configuration of
|
||||
your database deployment can also be a substantial undertaking and is
|
||||
far beyond the scope of this documentation.
|
||||
|
||||
#. Start the MySQL command-line client.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mysql -u root -p
|
||||
|
||||
#. Enter the MySQL root user's password when prompted.
|
||||
#. To configure the MySQL database, create the dash database.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mysql> CREATE DATABASE dash;
|
||||
|
||||
#. Create a MySQL user for the newly created dash database that has full
|
||||
control of the database. Replace DASH\_DBPASS with a password for the
|
||||
new user.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mysql> GRANT ALL PRIVILEGES ON dash.* TO 'dash'@'%' IDENTIFIED BY 'DASH_DBPASS';
|
||||
mysql> GRANT ALL PRIVILEGES ON dash.* TO 'dash'@'localhost' IDENTIFIED BY 'DASH_DBPASS';
|
||||
|
||||
#. Enter ``quit`` at the ``mysql>`` prompt to exit MySQL.
|
||||
|
||||
#. In the ``local_settings.py`` file, change these options:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
|
||||
DATABASES = {
|
||||
'default': {
|
||||
# Database configuration here
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'dash',
|
||||
'USER': 'dash',
|
||||
'PASSWORD': 'DASH_DBPASS',
|
||||
'HOST': 'localhost',
|
||||
'default-character-set': 'utf8'
|
||||
}
|
||||
}
|
||||
|
||||
#. After configuring the ``local_settings.py`` file as shown, you can run the
|
||||
:command:`manage.py syncdb` command to populate this newly created
|
||||
database.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# /usr/share/openstack-dashboard/manage.py syncdb
|
||||
|
||||
#. The following output is returned:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
Installing custom SQL ...
|
||||
Installing indexes ...
|
||||
DEBUG:django.db.backends:(0.008) CREATE INDEX `django_session_c25c2c28` ON `django_session` (`expire_date`);; args=()
|
||||
No fixtures found.
|
||||
|
||||
#. To avoid a warning when you restart Apache on Ubuntu, create a
|
||||
``blackhole`` directory in the Dashboard directory, as follows.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# mkdir -p /var/lib/dash/.blackhole
|
||||
|
||||
#. Restart the Apache service.
|
||||
|
||||
#. On Ubuntu, restart the ``nova-api`` service to ensure that the API server
|
||||
can connect to the Dashboard without error.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# service nova-api restart
|
||||
|
||||
Cached database
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
To mitigate the performance issues of database queries, you can use the
|
||||
Django ``cached_db`` session back end, which utilizes both your database
|
||||
and caching infrastructure to perform write-through caching and
|
||||
efficient retrieval.
|
||||
|
||||
Enable this hybrid setting by configuring both your database and cache,
|
||||
as discussed previously. Then, set the following value:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
|
||||
|
||||
Cookies
|
||||
~~~~~~~
|
||||
|
||||
If you use Django 1.4 or later, the ``signed_cookies`` back end avoids
|
||||
server load and scaling problems.
|
||||
|
||||
This back end stores session data in a cookie, which is stored by the
|
||||
user's browser. The back end uses a cryptographic signing technique to
|
||||
ensure session data is not tampered with during transport. This is not
|
||||
the same as encryption; session data is still readable by an attacker.
|
||||
|
||||
The pros of this engine are that it requires no additional dependencies
|
||||
or infrastructure overhead, and it scales indefinitely as long as the
|
||||
quantity of session data being stored fits into a normal cookie.
|
||||
|
||||
The biggest downside is that it places session data into storage on the
|
||||
user's machine and transports it over the wire. It also limits the
|
||||
quantity of session data that can be stored.
|
||||
|
||||
See the Django `cookie-based
|
||||
sessions <https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-cookie-based-sessions>`__
|
||||
documentation.
|
|
@ -1,117 +0,0 @@
|
|||
.. _dashboard-set-quotas:
|
||||
|
||||
======================
|
||||
View and manage quotas
|
||||
======================
|
||||
|
||||
.. |nbsp| unicode:: 0xA0 .. nbsp
|
||||
:trim:
|
||||
|
||||
To prevent system capacities from being exhausted without notification,
|
||||
you can set up quotas. Quotas are operational limits. For example, the
|
||||
number of gigabytes allowed for each project can be controlled so that
|
||||
cloud resources are optimized. Quotas can be enforced at both the project
|
||||
and the project-user level.
|
||||
|
||||
Typically, you change quotas when a project needs more than ten
|
||||
volumes or 1 |nbsp| TB on a compute node.
|
||||
|
||||
Using the Dashboard, you can view default Compute and Block Storage
|
||||
quotas for new projects, as well as update quotas for existing projects.
|
||||
|
||||
.. note::
|
||||
|
||||
Using the command-line interface, you can manage quotas for the
|
||||
OpenStack Compute service, the OpenStack Block Storage service, and
|
||||
the OpenStack Networking service (see `OpenStack Administrator Guide
|
||||
<https://docs.openstack.org/admin-guide/cli-set-quotas.html>`_).
|
||||
Additionally, you can update Compute service quotas for
|
||||
project users.
|
||||
|
||||
The following table describes the Compute and Block Storage service quotas:
|
||||
|
||||
.. _compute_quotas:
|
||||
|
||||
**Quota Descriptions**
|
||||
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Quota Name | Defines the number of | Service |
|
||||
+====================+====================================+===============+
|
||||
| Gigabytes | Volume gigabytes allowed for | Block Storage |
|
||||
| | each project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Instances | Instances allowed for each | Compute |
|
||||
| | project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Injected Files | Injected files allowed for each | Compute |
|
||||
| | project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Injected File | Content bytes allowed for each | Compute |
|
||||
| Content Bytes | injected file. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Keypairs | Number of keypairs. | Compute |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Metadata Items | Metadata items allowed for each | Compute |
|
||||
| | instance. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| RAM (MB) | RAM megabytes allowed for | Compute |
|
||||
| | each instance. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Security Groups | Security groups allowed for each | Compute |
|
||||
| | project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Security Group | Security group rules allowed for | Compute |
|
||||
| Rules | each project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Snapshots | Volume snapshots allowed for | Block Storage |
|
||||
| | each project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| VCPUs | Instance cores allowed for each | Compute |
|
||||
| | project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
| Volumes | Volumes allowed for each | Block Storage |
|
||||
| | project. | |
|
||||
+--------------------+------------------------------------+---------------+
|
||||
|
||||
.. _dashboard_view_quotas_procedure:
|
||||
|
||||
View default project quotas
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Defaults` category.
|
||||
|
||||
#. The default quota values are displayed.
|
||||
|
||||
.. note::
|
||||
|
||||
You can sort the table by clicking on either the
|
||||
:guilabel:`Quota Name` or :guilabel:`Limit` column headers.
|
||||
|
||||
.. _dashboard_update_project_quotas:
|
||||
|
||||
Update project quotas
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, open the :guilabel:`System` tab
|
||||
and click the :guilabel:`Defaults` category.
|
||||
|
||||
#. Click the :guilabel:`Update Defaults` button.
|
||||
|
||||
#. In the :guilabel:`Update Default Quotas` window,
|
||||
you can edit the default quota values.
|
||||
|
||||
#. Click the :guilabel:`Update Defaults` button.
|
||||
|
||||
.. note::
|
||||
|
||||
The dashboard does not show all possible project quotas.
|
||||
To view and update the quotas for a service, use its
|
||||
command-line client. See `OpenStack Administrator Guide
|
||||
<https://docs.openstack.org/admin-guide/cli-set-quotas.html>`_.
|
|
@ -1,41 +0,0 @@
|
|||
===========================
|
||||
View cloud usage statistics
|
||||
===========================
|
||||
|
||||
The Telemetry service provides user-level usage data for
|
||||
OpenStack-based clouds, which can be used for customer billing, system
|
||||
monitoring, or alerts. Data can be collected by notifications sent by
|
||||
existing OpenStack components (for example, usage events emitted from
|
||||
Compute) or by polling the infrastructure (for example, libvirt).
|
||||
|
||||
.. note::
|
||||
|
||||
You can only view metering statistics on the dashboard (available
|
||||
only to administrators).
|
||||
The Telemetry service must be set up and administered through the
|
||||
:command:`ceilometer` command-line interface (CLI).
|
||||
|
||||
For basic administration information, refer to the `Measure Cloud
|
||||
Resources <https://docs.openstack.org/user-guide/cli-ceilometer.html>`_
|
||||
chapter in the OpenStack End User Guide.
|
||||
|
||||
.. _dashboard-view-resource-stats:
|
||||
|
||||
View resource statistics
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard and select the :guilabel:`admin` project
|
||||
from the drop-down list.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, click the :guilabel:`Resource Usage` category.
|
||||
|
||||
#. Click the:
|
||||
|
||||
* :guilabel:`Usage Report` tab to view a usage report per project
|
||||
by specifying the time period (or even use a calendar to define
|
||||
a date range).
|
||||
|
||||
* :guilabel:`Stats` tab to view a multi-series line chart with
|
||||
user-defined meters. You group by project, define the value type
|
||||
(min, max, avg, or sum), and specify the time period (or even use
|
||||
a calendar to define a date range).
|
|
@ -1,339 +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.
|
||||
#
|
||||
# Horizon documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Oct 27 11:38:59 2011.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import django
|
||||
|
||||
# NOTE(amotoki): Sphinx 1.6.x catches warnings from imported modules.
|
||||
# Ignore warnings from openstack_dashboard.settings in the doc build.
|
||||
# This can be dropped once Sphinx correctly ignore such warnings.
|
||||
logging.getLogger('openstack_dashboard.settings').setLevel(logging.ERROR)
|
||||
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
|
||||
|
||||
sys.path.insert(0, ROOT)
|
||||
|
||||
# This is required for ReadTheDocs.org, but isn't a bad idea anyway.
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openstack_dashboard.settings')
|
||||
|
||||
import horizon.version
|
||||
|
||||
django.setup()
|
||||
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- 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 = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.viewcode',
|
||||
'openstackdocstheme',
|
||||
]
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/horizon'
|
||||
bug_project = 'horizon'
|
||||
bug_tag = 'documentation'
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Horizon'
|
||||
copyright = u'2012, OpenStack Foundation'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = horizon.version.version_info.version_string()
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = horizon.version.version_info.release_string()
|
||||
|
||||
# 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 = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
modindex_common_prefix = ['horizon.', 'openstack_dashboard.']
|
||||
|
||||
primary_domain = 'py'
|
||||
nitpicky = 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_path = ['.']
|
||||
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 = {
|
||||
"nosidebar": "false"
|
||||
}
|
||||
|
||||
# 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 = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# 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 = 'Horizondoc'
|
||||
|
||||
# -- Options for LaTeX output -------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Horizon.tex', u'Horizon Documentation',
|
||||
u'OpenStack Foundation', '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', 'horizon', u'Horizon Documentation',
|
||||
[u'OpenStack'], 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', 'Horizon', u'Horizon Documentation', u'OpenStack',
|
||||
'Horizon', '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'
|
||||
|
||||
|
||||
# -- Options for Epub output --------------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = u'Horizon'
|
||||
epub_author = u'OpenStack'
|
||||
epub_publisher = u'OpenStack'
|
||||
epub_copyright = u'2012, OpenStack'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
# epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
# epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be an ISBN number
|
||||
# or the project homepage.
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
# epub_uid = ''
|
||||
|
||||
# A tuple containing the cover image and cover page html template filenames.
|
||||
# epub_cover = ()
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_pre_files = []
|
||||
|
||||
# HTML files shat should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
# epub_exclude_files = []
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
# epub_tocdepth = 3
|
||||
|
||||
# Allow duplicate toc entries.
|
||||
# epub_tocdup = True
|
|
@ -1,179 +0,0 @@
|
|||
================
|
||||
Branding Horizon
|
||||
================
|
||||
|
||||
As of the Liberty release, Horizon has begun to conform more strictly to
|
||||
Bootstrap standards in an effort to embrace more responsive web design as well
|
||||
as alleviate the future need to re-brand new functionality for every release.
|
||||
|
||||
Supported Components
|
||||
--------------------
|
||||
The following components, organized by release, are the only ones that make
|
||||
full use of the Bootstrap theme architecture.
|
||||
|
||||
* 8.0.0 (Liberty)
|
||||
|
||||
* `Top Navbar`_
|
||||
* `Side Nav`_
|
||||
* `Pie Charts`_
|
||||
|
||||
* 9.0.0 (Mitaka)
|
||||
|
||||
* Tables_
|
||||
* `Bar Charts`_
|
||||
* Login_
|
||||
* Tabs_
|
||||
* Alerts_
|
||||
* Checkboxes_
|
||||
|
||||
Step 1
|
||||
------
|
||||
|
||||
The first step needed to create a custom branded theme for Horizon is to create
|
||||
a custom Bootstrap theme. There are several tools to aid in this. Some of the
|
||||
more useful ones include:
|
||||
|
||||
- `Bootswatchr`_
|
||||
- `Paintstrap`_
|
||||
- `Bootstrap`_
|
||||
|
||||
.. note::
|
||||
|
||||
Bootstrap uses LESS by default, but we use SCSS. All of the above
|
||||
tools will provide the ``variables.less`` file, which will need to be
|
||||
converted to ``_variables.scss``
|
||||
|
||||
Top Navbar
|
||||
----------
|
||||
|
||||
The top navbar in Horizon now uses a native Bootstrap ``navbar``. There are a
|
||||
number of variables that can be used to customize this element. Please see the
|
||||
**Navbar** section of your variables file for specifics on what can be set: any
|
||||
variables that use ``navbar-default``.
|
||||
|
||||
It is important to also note that the navbar now uses native Bootstrap
|
||||
dropdowns, which are customizable with variables. Please see the **Dropdowns**
|
||||
section of your variables file.
|
||||
|
||||
The top navbar is now responsive on smaller screens. When the window size hits
|
||||
your ``$screen-sm`` value, the topbar will compress into a design that is
|
||||
better suited for small screens.
|
||||
|
||||
Side Nav
|
||||
--------
|
||||
|
||||
The side navigation component has been refactored to use the native Stacked
|
||||
Pills element from Bootstrap. See **Pills** section of your variables file
|
||||
for specific variables to customize.
|
||||
|
||||
Charts
|
||||
------
|
||||
|
||||
Pie Charts
|
||||
~~~~~~~~~~
|
||||
|
||||
Pie Charts are SVG elements. SVG elements allow CSS customizations for
|
||||
only a basic element's look and feel (i.e. colors, size).
|
||||
|
||||
Since there is no native element in Bootstrap specifically for pie charts,
|
||||
the look and feel of the charts are inheriting from other elements of the
|
||||
theme. Please see ``_pie_charts.scss`` for specifics.
|
||||
|
||||
.. _Bar Charts:
|
||||
|
||||
Bar Charts
|
||||
~~~~~~~~~~
|
||||
|
||||
Bar Charts can be either a Bootstrap Progress Bar or an SVG element. Either
|
||||
implementation will use the Bootstrap Progress Bar styles.
|
||||
|
||||
The SVG implementation will not make use of the customized Progress Bar
|
||||
height though, so it is recommended that Bootstrap Progress Bars are used
|
||||
whenever possible.
|
||||
|
||||
Please see ``_bar_charts.scss`` for specifics on what can be customized for
|
||||
SVGs. See the **Progress bars** section of your variables file for specific
|
||||
variables to customize.
|
||||
|
||||
Tables
|
||||
------
|
||||
|
||||
The standard Django tables now make use of the native Bootstrap table markup.
|
||||
See **Tables** section of your variables file for variables to customize.
|
||||
|
||||
The standard Bootstrap tables will be borderless by default. If you wish to
|
||||
add a border, like the ``default`` theme, see
|
||||
``openstack_dashboard/themes/default/horizon/components/_tables.scss``
|
||||
|
||||
.. _Login:
|
||||
|
||||
Login
|
||||
-----
|
||||
|
||||
Login Splash Page
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The login splash page now uses a standard Bootstrap panel in its
|
||||
implementation. See the **Panels** section in your variables file to variables
|
||||
to easily customize.
|
||||
|
||||
Modal Login
|
||||
~~~~~~~~~~~
|
||||
|
||||
The modal login experience, as used when switching regions, uses a standard
|
||||
Bootstrap dialog. See the **Modals** section of your variables file for
|
||||
specific variables to customize.
|
||||
|
||||
Tabs
|
||||
----
|
||||
|
||||
The standard tabs make use of the native Bootstrap tab markup.
|
||||
|
||||
See **Tabs** section of your variables file for variables to customize.
|
||||
|
||||
Alerts
|
||||
------
|
||||
|
||||
Alerts use the basic Bootstrap brand colors. See **Colors** section of your
|
||||
variables file for specifics.
|
||||
|
||||
Checkboxes
|
||||
----------
|
||||
|
||||
Horizon uses icon fonts to represent checkboxes. In order to customize
|
||||
this, you simply need to override the standard scss. For an example of
|
||||
this, see themes/material/static/horizon/components/_checkboxes.scss
|
||||
|
||||
Bootswatch and Material Design
|
||||
------------------------------
|
||||
|
||||
`Bootswatch`_ is a collection of free themes for Bootstrap and is now
|
||||
available for use in Horizon.
|
||||
|
||||
In order to showcase what can be done to enhance an existing Bootstrap theme,
|
||||
Horizon now includes a secondary theme, roughly based on `Google's Material
|
||||
Design`_ called ``material``. Bootswatch's **Paper** is a simple Bootstrap
|
||||
implementation of Material Design and is used by ``material``.
|
||||
|
||||
Bootswatch provides a number of other themes, that once Horizon is fully theme
|
||||
compliant, will allow easy toggling and customizations for darker or
|
||||
accessibility driven experiences.
|
||||
|
||||
Development Tips
|
||||
----------------
|
||||
|
||||
When developing a new theme for Horizon, it is required that the dynamically
|
||||
generated `static` directory be cleared after each change and the server
|
||||
restarted. This is not always ideal. If you wish to develop and not have
|
||||
to restart the server each time, it is recommended that you configure your
|
||||
development environment to not run in OFFLINE mode. Simply verify the
|
||||
following settings in your local_settings.py::
|
||||
|
||||
COMPRESS_OFFLINE = False
|
||||
COMPRESS_ENABLED = False
|
||||
|
||||
.. _Bootstrap: http://getbootstrap.com/customize/
|
||||
.. _Bootswatch: http://bootswatch.com
|
||||
.. _Bootswatchr: http://bootswatchr.com/create#!
|
||||
.. _Paintstrap: http://paintstrap.com
|
||||
.. _Google's Material Design: https://www.google.com/design/spec/material-design/introduction.html
|
|
@ -1,408 +0,0 @@
|
|||
.. _install-customizing:
|
||||
|
||||
===================
|
||||
Customizing Horizon
|
||||
===================
|
||||
|
||||
.. seealso::
|
||||
|
||||
You may also be interested in :doc:`themes` and :doc:`branding`.
|
||||
|
||||
Changing the Site Title
|
||||
=======================
|
||||
|
||||
The OpenStack Dashboard Site Title branding (i.e. "**OpenStack** Dashboard")
|
||||
can be overwritten by adding the attribute ``SITE_BRANDING``
|
||||
to ``local_settings.py`` with the value being the desired name.
|
||||
|
||||
The file ``local_settings.py`` can be found at the Horizon directory path of
|
||||
``openstack_dashboard/local/local_settings.py``.
|
||||
|
||||
Changing the Brand Link
|
||||
=======================
|
||||
|
||||
The logo also acts as a hyperlink. The default behavior is to redirect to
|
||||
``horizon:user_home``. By adding the attribute ``SITE_BRANDING_LINK`` with
|
||||
the desired url target e.g., ``http://sample-company.com`` in
|
||||
``local_settings.py``, the target of the hyperlink can be changed.
|
||||
|
||||
Customizing the Footer
|
||||
======================
|
||||
|
||||
It is possible to customize the global and login footers using a theme's
|
||||
template override. Simply add ``_footer.html`` for a global footer
|
||||
override or ``_login_footer.html`` for the login page's footer to your
|
||||
theme's template directory.
|
||||
|
||||
Modifying Existing Dashboards and Panels
|
||||
========================================
|
||||
|
||||
If you wish to alter dashboards or panels which are not part of your codebase,
|
||||
you can specify a custom python module which will be loaded after the entire
|
||||
Horizon site has been initialized, but prior to the URLconf construction.
|
||||
This allows for common site-customization requirements such as:
|
||||
|
||||
* Registering or unregistering panels from an existing dashboard.
|
||||
* Changing the names of dashboards and panels.
|
||||
* Re-ordering panels within a dashboard or panel group.
|
||||
|
||||
Default Horizon panels are loaded based upon files within the
|
||||
openstack_dashboard/enabled/ folder. These files are loaded based upon the
|
||||
filename order, with space left for more files to be added. There are some
|
||||
example files available within this folder, with the .example suffix
|
||||
added. Developers and deployers should strive to use this method of
|
||||
customization as much as possible, and support for this is given preference
|
||||
over more exotic methods such as monkey patching and overrides files.
|
||||
|
||||
.. _horizon-customization-module:
|
||||
|
||||
Horizon customization module (overrides)
|
||||
========================================
|
||||
|
||||
Horizon has a global overrides mechanism available to perform customizations
|
||||
that are not yet customizable via configuration settings. This file can perform
|
||||
monkey patching and other forms of customization which are not possible via the
|
||||
enabled folder's customization method.
|
||||
|
||||
This method of customization is meant to be available for deployers of Horizon,
|
||||
and use of this should be avoided by Horizon plugins at all cost. Plugins
|
||||
needing this level of monkey patching and flexibility should instead look for
|
||||
changing their __init__.py file and performing customizations through other
|
||||
means.
|
||||
|
||||
To specify the python module containing your modifications, add the key
|
||||
``customization_module`` to your ``HORIZON_CONFIG`` dictionary in
|
||||
``local_settings.py``. The value should be a string containing the path to your
|
||||
module in dotted python path notation. Example::
|
||||
|
||||
HORIZON_CONFIG["customization_module"] = "my_project.overrides"
|
||||
|
||||
You can do essentially anything you like in the customization module. For
|
||||
example, you could change the name of a panel::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
# Rename "User Settings" to "User Options"
|
||||
settings = horizon.get_dashboard("settings")
|
||||
user_panel = settings.get_panel("user")
|
||||
user_panel.name = _("User Options")
|
||||
|
||||
Or get the instances panel::
|
||||
|
||||
projects_dashboard = horizon.get_dashboard("project")
|
||||
instances_panel = projects_dashboard.get_panel("instances")
|
||||
|
||||
Or just remove it entirely::
|
||||
|
||||
projects_dashboard.unregister(instances_panel.__class__)
|
||||
|
||||
You cannot unregister a ``default_panel``. If you wish to remove a
|
||||
``default_panel``, you need to make a different panel in the dashboard as a
|
||||
``default_panel`` and then unregister the former. For example, if you wished
|
||||
to remove the ``overview_panel`` from the ``Project`` dashboard, you could do
|
||||
the following::
|
||||
|
||||
project = horizon.get_dashboard('project')
|
||||
project.default_panel = "instances"
|
||||
overview = project.get_panel('overview')
|
||||
project.unregister(overview.__class__)
|
||||
|
||||
You can also override existing methods with your own versions::
|
||||
|
||||
from openstack_dashboard.dashboards.admin.info import tabs
|
||||
from openstack_dashboard.dashboards.project.instances import tables
|
||||
|
||||
NO = lambda *x: False
|
||||
|
||||
tabs.HeatServiceTab.allowed = NO
|
||||
tables.AssociateIP.allowed = NO
|
||||
tables.SimpleAssociateIP.allowed = NO
|
||||
tables.SimpleDisassociateIP.allowed = NO
|
||||
|
||||
You could also customize what columns are displayed in an existing
|
||||
table, by redefining the ``columns`` attribute of its ``Meta``
|
||||
class. This can be achieved in 3 steps:
|
||||
|
||||
#. Extend the table that you wish to modify
|
||||
#. Redefine the ``columns`` attribute under the ``Meta`` class for this
|
||||
new table
|
||||
#. Modify the ``table_class`` attribute for the related view so that it
|
||||
points to the new table
|
||||
|
||||
|
||||
For example, if you wished to remove the Admin State column from the
|
||||
:class:`~openstack_dashboard.dashboards.admin.networks.tables.NetworksTable`,
|
||||
you could do the following::
|
||||
|
||||
from openstack_dashboard.dashboards.project.networks import tables
|
||||
from openstack_dashboard.dashboards.project.networks import views
|
||||
|
||||
class MyNetworksTable(tables.NetworksTable):
|
||||
|
||||
class Meta(tables.NetworksTable.Meta):
|
||||
columns = ('name', 'subnets', 'shared', 'status')
|
||||
|
||||
views.IndexView.table_class = MyNetworksTable
|
||||
|
||||
If you want to add a column you can override the parent table in a
|
||||
similar way, add the new column definition and then use the ``Meta``
|
||||
``columns`` attribute to control the column order as needed.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
``my_project.overrides`` needs to be importable by the python process running
|
||||
Horizon.
|
||||
If your module is not installed as a system-wide python package,
|
||||
you can either make it installable (e.g., with a setup.py)
|
||||
or you can adjust the python path used by your WSGI server to include its location.
|
||||
|
||||
Probably the easiest way is to add a ``python-path`` argument to
|
||||
the ``WSGIDaemonProcess`` line in Apache's Horizon config.
|
||||
|
||||
Assuming your ``my_project`` module lives in ``/opt/python/my_project``,
|
||||
you'd make it look like the following::
|
||||
|
||||
WSGIDaemonProcess [... existing options ...] python-path=/opt/python
|
||||
|
||||
|
||||
Customize the project and user table columns
|
||||
============================================
|
||||
|
||||
|
||||
Keystone V3 has a place to store extra information regarding project and user.
|
||||
Using the override mechanism described in :ref:`horizon-customization-module`,
|
||||
Horizon is able to show these extra information as a custom column.
|
||||
For example, if a user in Keystone has an attribute ``phone_num``, you could
|
||||
define new column::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard.dashboards.identity.users import tables as user_tables
|
||||
from openstack_dashboard.dashboards.identity.users import views
|
||||
|
||||
class MyUsersTable(user_tables.UsersTable):
|
||||
phone_num = tables.Column('phone_num',
|
||||
verbose_name=_('Phone Number'),
|
||||
form_field=forms.CharField(),)
|
||||
|
||||
class Meta(user_tables.UsersTable.Meta):
|
||||
columns = ('name', 'description', 'phone_num')
|
||||
|
||||
views.IndexView.table_class = MyUsersTable
|
||||
|
||||
|
||||
Customize Angular dashboards
|
||||
============================
|
||||
|
||||
In Angular, you may write a plugin to extend certain features. Two components
|
||||
in the Horizon framework that make this possible are the extensibility service
|
||||
and the resource type registry service. The ``extensibleService`` allows
|
||||
certain Horizon elements to be extended dynamically, including add, remove, and
|
||||
replace. The ``resourceTypeRegistry`` service provides methods to set and get
|
||||
information pertaining to a resource type object. We use Heat type names like
|
||||
``OS::Glance::Image`` as our reference name.
|
||||
|
||||
Some information you may place in the registry include:
|
||||
|
||||
* API to fetch data from
|
||||
* Property names
|
||||
* Actions (e.g. "Create Volume")
|
||||
* URL paths to detail view or detail drawer
|
||||
* Property information like labels or formatting for property values
|
||||
|
||||
These properties in the registry use the extensibility service (as of Newton
|
||||
release):
|
||||
|
||||
* globalActions
|
||||
* batchActions
|
||||
* itemActions
|
||||
* detailViews
|
||||
* tableColumns
|
||||
* filterFacets
|
||||
|
||||
Using the information from the registry, we can build out our dashboard panels.
|
||||
Panels use the high-level directive ``hzResourceTable`` that replaces common
|
||||
templates so we do not need to write boilerplate HTML and controller code. It
|
||||
gives developers a quick way to build a new table or change an existing table.
|
||||
|
||||
.. note::
|
||||
|
||||
You may still choose to use the HTML template for complete control of form
|
||||
and functionality. For example, you may want to create a custom footer.
|
||||
You may also use the ``hzDynamicTable`` directive (what ``hzResourceTable``
|
||||
uses under the hood) directly. However, neither of these is extensible.
|
||||
You would need to override the panel completely.
|
||||
|
||||
This is a sample module file to demonstrate how to make some customizations to
|
||||
the Images Panel.::
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.images')
|
||||
.run(customizeImagePanel);
|
||||
|
||||
customizeImagePanel.$inject = [
|
||||
'horizon.framework.conf.resource-type-registry.service',
|
||||
'horizon.app.core.images.basePath',
|
||||
'horizon.app.core.images.resourceType',
|
||||
'horizon.app.core.images.actions.surprise.service'
|
||||
];
|
||||
|
||||
function customizeImagePanel(registry, basePath, imageResourceType, surpriseService) {
|
||||
// get registry for ``OS::Glance::Image``
|
||||
registry = registry.getResourceType(imageResourceType);
|
||||
|
||||
// replace existing Size column to make the font color red
|
||||
var column = {
|
||||
id: 'size',
|
||||
priority: 2,
|
||||
template: '<a style="color:red;">{$ item.size | bytes $}</a>'
|
||||
};
|
||||
registry.tableColumns.replace('size', column);
|
||||
|
||||
// add a new detail view
|
||||
registry.detailsViews
|
||||
.append({
|
||||
id: 'anotherDetailView',
|
||||
name: gettext('Another Detail View'),
|
||||
template: basePath + 'demo/detail.html'
|
||||
});
|
||||
|
||||
// set a different summary drawer template
|
||||
registry.setSummaryTemplateUrl(basePath + 'demo/drawer.html');
|
||||
|
||||
// add a new global action
|
||||
registry.globalActions
|
||||
.append({
|
||||
id: 'surpriseAction',
|
||||
service: surpriseService,
|
||||
template: {
|
||||
text: gettext('Surprise')
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
Additionally, you should have content defined in ``detail.html`` and
|
||||
``drawer.html``, as well as define the ``surpriseService`` which is based off
|
||||
the ``actions`` directive and needs allowed and perform methods defined.
|
||||
|
||||
|
||||
Icons
|
||||
=====
|
||||
|
||||
Horizon uses font icons from Font Awesome. Please see `Font Awesome`_ for
|
||||
instructions on how to use icons in the code.
|
||||
|
||||
To add icon to Table Action, use icon property. Example:
|
||||
|
||||
class CreateSnapshot(tables.LinkAction):
|
||||
name = "snapshot"
|
||||
verbose_name = _("Create Snapshot")
|
||||
icon = "camera"
|
||||
|
||||
Additionally, the site-wide default button classes can be configured by
|
||||
setting ``ACTION_CSS_CLASSES`` to a tuple of the classes you wish to appear
|
||||
on all action buttons in your ``local_settings.py`` file.
|
||||
|
||||
|
||||
Custom Stylesheets
|
||||
==================
|
||||
|
||||
It is possible to define custom stylesheets for your dashboards. Horizon's base
|
||||
template ``openstack_dashboard/templates/base.html`` defines multiple blocks
|
||||
that can be overridden.
|
||||
|
||||
To define custom css files that apply only to a specific dashboard, create
|
||||
a base template in your dashboard's templates folder, which extends Horizon's
|
||||
base template e.g. ``openstack_dashboard/dashboards/my_custom_dashboard/
|
||||
templates/my_custom_dashboard/base.html``.
|
||||
|
||||
In this template, redefine ``block css``. (Don't forget to include
|
||||
``_stylesheets.html`` which includes all Horizon's default stylesheets.)::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block css %}
|
||||
{% include "_stylesheets.html" %}
|
||||
|
||||
{% load compress %}
|
||||
{% compress css %}
|
||||
<link href='{{ STATIC_URL }}my_custom_dashboard/scss/my_custom_dashboard.scss' type='text/scss' media='screen' rel='stylesheet' />
|
||||
{% endcompress %}
|
||||
{% endblock %}
|
||||
|
||||
The custom stylesheets then reside in the dashboard's own ``static`` folder
|
||||
``openstack_dashboard/dashboards/my_custom_dashboard/static/
|
||||
my_custom_dashboard/scss/my_custom_dashboard.scss``.
|
||||
|
||||
All dashboard's templates have to inherit from dashboard's base.html::
|
||||
|
||||
{% extends 'my_custom_dashboard/base.html' %}
|
||||
...
|
||||
|
||||
|
||||
Custom Javascript
|
||||
=================
|
||||
|
||||
Similarly to adding custom styling (see above), it is possible to include
|
||||
custom javascript files.
|
||||
|
||||
All Horizon's javascript files are listed in the ``openstack_dashboard/
|
||||
templates/horizon/_scripts.html`` partial template, which is included in
|
||||
Horizon's base template in ``block js``.
|
||||
|
||||
To add custom javascript files, create an ``_scripts.html`` partial template in
|
||||
your dashboard ``openstack_dashboard/dashboards/my_custom_dashboard/
|
||||
templates/my_custom_dashboard/_scripts.html`` which extends
|
||||
``horizon/_scripts.html``. In this template override the
|
||||
``block custom_js_files`` including your custom javascript files::
|
||||
|
||||
{% extends 'horizon/_scripts.html' %}
|
||||
|
||||
{% block custom_js_files %}
|
||||
<script src='{{ STATIC_URL }}my_custom_dashboard/js/my_custom_js.js' type='text/javascript' charset='utf-8'></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
In your dashboard's own base template ``openstack_dashboard/dashboards/
|
||||
my_custom_dashboard/templates/my_custom_dashboard/base.html`` override
|
||||
``block js`` with inclusion of dashboard's own ``_scripts.html``::
|
||||
|
||||
{% block js %}
|
||||
{% include "my_custom_dashboard/_scripts.html" %}
|
||||
{% endblock %}
|
||||
|
||||
The result is a single compressed js file consisting both Horizon and
|
||||
dashboard's custom scripts.
|
||||
|
||||
Additionally, some marketing and analytics scripts require you to place them
|
||||
within the page's <head> tag. To do this, place them within the
|
||||
``horizon/_custom_head_js.html`` file. Similar to the ``_scripts.html`` file
|
||||
mentioned above, you may link to an existing file::
|
||||
|
||||
<script src='{{ STATIC_URL }}/my_custom_dashboard/js/my_marketing_js.js' type='text/javascript' charset='utf-8'></script>
|
||||
|
||||
or you can paste your script directly in the file, being sure to use
|
||||
appropriate tags::
|
||||
|
||||
<script type="text/javascript">
|
||||
//some javascript
|
||||
</script>
|
||||
|
||||
|
||||
Customizing Meta Attributes
|
||||
===========================
|
||||
|
||||
To add custom metadata attributes to your project's base template, include
|
||||
them in the ``horizon/_custom_meta.html`` file. The contents of this file will
|
||||
be inserted into the page's <head> just after the default Horizon meta tags.
|
||||
|
||||
.. _Font Awesome: https://fortawesome.github.io/Font-Awesome/
|
|
@ -1,12 +0,0 @@
|
|||
===================
|
||||
Configuration Guide
|
||||
===================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
settings
|
||||
pluggable_panels
|
||||
customizing
|
||||
themes
|
||||
branding
|
|
@ -1,289 +0,0 @@
|
|||
.. _pluggable-settings-label:
|
||||
|
||||
===========================
|
||||
Pluggable Panels and Groups
|
||||
===========================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Horizon allows dashboards, panels and panel groups to be added without
|
||||
modifying the default settings. Pluggable settings are a mechanism to allow
|
||||
settings to be stored in separate files. Those files are read at startup and
|
||||
used to modify the default settings.
|
||||
|
||||
The default location for the dashboard configuration files is
|
||||
``openstack_dashboard/enabled``, with another directory,
|
||||
``openstack_dashboard/local/enabled`` for local overrides. Both sets of files
|
||||
will be loaded, but the settings in ``openstack_dashboard/local/enabled`` will
|
||||
overwrite the default ones. The settings are applied in alphabetical order of
|
||||
the filenames. If the same dashboard has configuration files in ``enabled`` and
|
||||
``local/enabled``, the local name will be used. Note, that since names of
|
||||
python modules can't start with a digit, the files are usually named with a
|
||||
leading underscore and a number, so that you can control their order easily.
|
||||
|
||||
General Pluggbale Settings
|
||||
==========================
|
||||
|
||||
Before we describe the specific use cases, the following keys can be used in
|
||||
any pluggable settings file:
|
||||
|
||||
``ADD_EXCEPTIONS``
|
||||
------------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
A dictionary of exception classes to be added to ``HORIZON['exceptions']``.
|
||||
|
||||
``ADD_INSTALLED_APPS``
|
||||
----------------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
A list of applications to be prepended to ``INSTALLED_APPS``.
|
||||
This is needed to expose static files from a plugin.
|
||||
|
||||
``ADD_ANGULAR_MODULES``
|
||||
-----------------------
|
||||
|
||||
.. versionadded:: 2014.2(Juno)
|
||||
|
||||
A list of AngularJS modules to be loaded when Angular bootstraps. These modules
|
||||
are added as dependencies on the root Horizon application ``horizon``.
|
||||
|
||||
``ADD_JS_FILES``
|
||||
----------------
|
||||
|
||||
.. versionadded:: 2014.2(Juno)
|
||||
|
||||
A list of javascript source files to be included in the compressed set of files
|
||||
that are loaded on every page. This is needed for AngularJS modules that are
|
||||
referenced in ``ADD_ANGULAR_MODULES`` and therefore need to be included in
|
||||
every page.
|
||||
|
||||
``ADD_JS_SPEC_FILES``
|
||||
---------------------
|
||||
|
||||
.. versionadded:: 2015.1(Kilo)
|
||||
|
||||
A list of javascript spec files to include for integration with the Jasmine
|
||||
spec runner. Jasmine is a behavior-driven development framework for testing
|
||||
JavaScript code.
|
||||
|
||||
``ADD_SCSS_FILES``
|
||||
------------------
|
||||
|
||||
.. versionadded:: 8.0.0(Liberty)
|
||||
|
||||
A list of scss files to be included in the compressed set of files that are
|
||||
loaded on every page. We recommend one scss file per dashboard, use @import if
|
||||
you need to include additional scss files for panels.
|
||||
|
||||
.. _auto_discover_static_files:
|
||||
|
||||
``AUTO_DISCOVER_STATIC_FILES``
|
||||
------------------------------
|
||||
|
||||
.. versionadded:: 8.0.0(Liberty)
|
||||
|
||||
If set to ``True``, JavaScript files and static angular html template files
|
||||
will be automatically discovered from the `static` folder in each apps listed
|
||||
in ADD_INSTALLED_APPS.
|
||||
|
||||
JavaScript source files will be ordered based on naming convention: files with
|
||||
extension `.module.js` listed first, followed by other JavaScript source files.
|
||||
|
||||
JavaScript files for testing will also be ordered based on naming convention:
|
||||
files with extension `.mock.js` listed first, followed by files with extension
|
||||
`.spec.js`.
|
||||
|
||||
If ADD_JS_FILES and/or ADD_JS_SPEC_FILES are also specified, files manually
|
||||
listed there will be appended to the auto-discovered files.
|
||||
|
||||
``DISABLED``
|
||||
------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
If set to ``True``, this settings file will not be added to the settings.
|
||||
|
||||
``UPDATE_HORIZON_CONFIG``
|
||||
-------------------------
|
||||
|
||||
.. versionadded:: 2014.2(Juno)
|
||||
|
||||
A dictionary of values that will replace the values in ``HORIZON_CONFIG``.
|
||||
|
||||
|
||||
Pluggable Settings for Dashboards
|
||||
=================================
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The following keys are specific to registering a dashboard:
|
||||
|
||||
|
||||
``DASHBOARD``
|
||||
-------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the dashboard to be added to ``HORIZON['dashboards']``. Required.
|
||||
|
||||
``DEFAULT``
|
||||
-----------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
If set to ``True``, this dashboard will be set as the default dashboard.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
To disable a dashboard locally, create a file
|
||||
``openstack_dashboard/local/enabled/_40_dashboard-name.py`` with the following
|
||||
content::
|
||||
|
||||
DASHBOARD = '<dashboard-name>'
|
||||
DISABLED = True
|
||||
|
||||
To add a Tuskar-UI (Infrastructure) dashboard, you have to install it, and then
|
||||
create a file ``openstack_dashboard/local/enabled/_50_tuskar.py`` with::
|
||||
|
||||
from tuskar_ui import exceptions
|
||||
|
||||
DASHBOARD = 'infrastructure'
|
||||
ADD_INSTALLED_APPS = [
|
||||
'tuskar_ui.infrastructure',
|
||||
]
|
||||
ADD_EXCEPTIONS = {
|
||||
'recoverable': exceptions.RECOVERABLE,
|
||||
'not_found': exceptions.NOT_FOUND,
|
||||
'unauthorized': exceptions.UNAUTHORIZED,
|
||||
}
|
||||
|
||||
|
||||
Pluggable Settings for Panels
|
||||
=============================
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The following keys are specific to registering or removing a panel:
|
||||
|
||||
``PANEL``
|
||||
---------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the panel to be added to ``HORIZON_CONFIG``. Required.
|
||||
|
||||
``PANEL_DASHBOARD``
|
||||
-------------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the dashboard the ``PANEL`` associated with. Required.
|
||||
|
||||
|
||||
``PANEL_GROUP``
|
||||
---------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the panel group the ``PANEL`` is associated with. If you want the
|
||||
panel to show up without a panel group, use the panel group "default".
|
||||
|
||||
``DEFAULT_PANEL``
|
||||
-----------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
If set, it will update the default panel of the ``PANEL_DASHBOARD``.
|
||||
|
||||
``ADD_PANEL``
|
||||
-------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
Python panel class of the ``PANEL`` to be added.
|
||||
|
||||
``REMOVE_PANEL``
|
||||
----------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
If set to ``True``, the PANEL will be removed from PANEL_DASHBOARD/PANEL_GROUP.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
To add a new panel to the Admin panel group in Admin dashboard, create a file
|
||||
``openstack_dashboard/local/enabled/_60_admin_add_panel.py`` with the following
|
||||
content::
|
||||
|
||||
PANEL = 'plugin_panel'
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
PANEL_GROUP = 'admin'
|
||||
ADD_PANEL = 'test_panels.plugin_panel.panel.PluginPanel'
|
||||
|
||||
To remove Info panel from Admin panel group in Admin dashboard locally, create
|
||||
a file ``openstack_dashboard/local/enabled/_70_admin_remove_panel.py`` with
|
||||
the following content::
|
||||
|
||||
PANEL = 'info'
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
PANEL_GROUP = 'admin'
|
||||
REMOVE_PANEL = True
|
||||
|
||||
To change the default panel of Admin dashboard to Instances panel, create a
|
||||
file ``openstack_dashboard/local/enabled/_80_admin_default_panel.py`` with the
|
||||
following content::
|
||||
|
||||
PANEL = 'instances'
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
PANEL_GROUP = 'admin'
|
||||
DEFAULT_PANEL = 'instances'
|
||||
|
||||
Pluggable Settings for Panel Groups
|
||||
===================================
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
|
||||
The following keys are specific to registering a panel group:
|
||||
|
||||
``PANEL_GROUP``
|
||||
---------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the panel group to be added to ``HORIZON_CONFIG``. Required.
|
||||
|
||||
``PANEL_GROUP_NAME``
|
||||
--------------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The display name of the PANEL_GROUP. Required.
|
||||
|
||||
``PANEL_GROUP_DASHBOARD``
|
||||
-------------------------
|
||||
|
||||
.. versionadded:: 2014.1(Icehouse)
|
||||
|
||||
The slug of the dashboard the ``PANEL_GROUP`` associated with. Required.
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
To add a new panel group to the Admin dashboard, create a file
|
||||
``openstack_dashboard/local/enabled/_90_admin_add_panel_group.py`` with the
|
||||
following content::
|
||||
|
||||
PANEL_GROUP = 'plugin_panel_group'
|
||||
PANEL_GROUP_NAME = 'Plugin Panel Group'
|
||||
PANEL_GROUP_DASHBOARD = 'admin'
|
File diff suppressed because it is too large
Load Diff
|
@ -1,181 +0,0 @@
|
|||
======
|
||||
Themes
|
||||
======
|
||||
|
||||
As of the Kilo release, styling for the OpenStack Dashboard can be altered
|
||||
through the use of a theme. A theme is a directory containing a
|
||||
``_variables.scss`` file to override the color codes used throughout the SCSS
|
||||
and a ``_styles.scss`` file with additional styles to load after dashboard
|
||||
styles have loaded.
|
||||
|
||||
As of the Mitaka release, Horizon can be configured to run with multiple
|
||||
themes available at run time. It uses a browser cookie to allow users to
|
||||
toggle between the configured themes. By default, Horizon is configured
|
||||
with the two standard themes available: 'default' and 'material'.
|
||||
|
||||
To configure or alter the available themes, set ``AVAILABLE_THEMES`` in
|
||||
``local_settings.py`` to a list of tuples, such that
|
||||
``('name', 'label', 'path')``
|
||||
|
||||
``name``
|
||||
The key by which the theme value is stored within the cookie
|
||||
|
||||
``label``
|
||||
The label shown in the theme toggle under the User Menu
|
||||
|
||||
``path``
|
||||
The directory location for the theme. The path must be relative to the
|
||||
``openstack_dashboard`` directory or an absolute path to an accessible
|
||||
location on the file system
|
||||
|
||||
To use a custom theme, set ``AVAILABLE_THEMES`` in ``local_settings.py`` to
|
||||
a list of themes. If you wish to run in a mode similar to legacy Horizon,
|
||||
set ``AVAILABLE_THEMES`` with a single tuple, and the theme toggle will not
|
||||
be available at all through the application to allow user configuration themes.
|
||||
|
||||
For example, a configuration with multiple themes::
|
||||
|
||||
AVAILABLE_THEMES = [
|
||||
('default', 'Default', 'themes/default'),
|
||||
('material', 'Material', 'themes/material'),
|
||||
]
|
||||
|
||||
A configuration with a single theme::
|
||||
|
||||
AVAILABLE_THEMES = [
|
||||
('default', 'Default', 'themes/default'),
|
||||
]
|
||||
|
||||
Both the Dashboard custom variables and Bootstrap variables can be overridden.
|
||||
For a full list of the Dashboard SCSS variables that can be changed,
|
||||
see the variables file at
|
||||
``openstack_dashboard/static/dashboard/scss/_variables.scss``.
|
||||
|
||||
In order to build a custom theme, both ``_variables.scss`` and ``_styles.scss``
|
||||
are required and ``_variables.scss`` must provide all the default Bootstrap
|
||||
variables.
|
||||
|
||||
Inherit from an Existing Theme
|
||||
------------------------------
|
||||
|
||||
Custom themes must implement all of the Bootstrap variables required by
|
||||
Horizon in ``_variables.scss`` and ``_styles.scss``. To make this easier, you
|
||||
can inherit the variables needed in the default theme and only override those
|
||||
that you need to customize. To inherit from the default theme, put this in your
|
||||
theme's ``_variables.scss``::
|
||||
|
||||
@import "/themes/default/variables";
|
||||
|
||||
Once you have made your changes you must re-generate the static files with
|
||||
``tox -e manage -- collectstatic``.
|
||||
|
||||
By default, all of the themes configured by ``AVAILABLE_THEMES`` setting are
|
||||
collected by horizon during the `collectstatic` process. By default, the themes
|
||||
are collected into the dynamic `static/themes` directory, but this location can
|
||||
be customized via the ``local_settings.py`` variable: ``THEME_COLLECTION_DIR``
|
||||
|
||||
Once collected, any theme configured via ``AVAILABLE_THEMES`` is available to
|
||||
inherit from by importing its variables and styles from its collection
|
||||
directory. The following is an example of inheriting from the material theme::
|
||||
|
||||
@import "/themes/material/variables";
|
||||
@import "/themes/material/styles";
|
||||
|
||||
Bootswatch
|
||||
~~~~~~~~~~
|
||||
|
||||
Horizon packages the Bootswatch SCSS files for use with its ``material`` theme.
|
||||
Because of this, it is simple to use an existing Bootswatch theme as a base.
|
||||
This is due to the fact that Bootswatch is loaded as a 3rd party static asset,
|
||||
and therefore is automatically collected into the `static` directory in
|
||||
`/horizon/lib/`. The following is an example of how to inherit from
|
||||
Bootswatch's ``darkly`` theme::
|
||||
|
||||
@import "/horizon/lib/bootswatch/darkly/variables";
|
||||
@import "/horizon/lib/bootswatch/darkly/bootswatch";
|
||||
|
||||
|
||||
Organizing Your Theme Directory
|
||||
-------------------------------
|
||||
|
||||
A custom theme directory can be organized differently, depending on the
|
||||
level of customization that is desired, as it can include static files
|
||||
as well as Django templates. It can include special subdirectories that will
|
||||
be used differently: ``static``, ``templates`` and ``img``.
|
||||
|
||||
The ``static`` Folder
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the theme folder contains a sub-folder called ``static``, then that sub
|
||||
folder will be used as the **static root of the theme**. I.e., Horizon will
|
||||
look in that sub-folder for the _variables.scss and _styles.scss files.
|
||||
The contents of this folder will also be served up at ``/static/custom``.
|
||||
|
||||
The ``templates`` Folder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the theme folder contains a sub-folder ``templates``, then the path
|
||||
to that sub-folder will be prepended to the ``TEMPLATE_DIRS`` tuple to
|
||||
allow for theme specific template customizations.
|
||||
|
||||
Using the ``templates`` Folder
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Any Django template that is used in Horizon can be overridden through a theme.
|
||||
This allows highly customized user experiences to exist within the scope of
|
||||
different themes. Any template that is overridden must adhere to the same
|
||||
directory structure that the extending template expects.
|
||||
|
||||
For example, if you wish to customize the sidebar, Horizon expects the template
|
||||
to live at ``horizon/_sidebar.html``. You would need to duplicate that
|
||||
directory structure under your templates directory, such that your override
|
||||
would live at ``{ theme_path }/templates/horizon/_sidebar.html``.
|
||||
|
||||
The ``img`` Folder
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If the static root of the theme folder contains an ``img`` directory,
|
||||
then all images that make use of the {% themable_asset %} templatetag
|
||||
can be overridden.
|
||||
|
||||
These assets include logo.svg, splash-logo.svg and favicon.ico, however
|
||||
overriding the SVG/GIF assets used by Heat within the `dashboard/img` folder
|
||||
is not currently supported.
|
||||
|
||||
Customizing the Logo
|
||||
--------------------
|
||||
|
||||
Simple
|
||||
~~~~~~
|
||||
|
||||
If you wish to customize the logo that is used on the splash screen or in the
|
||||
top navigation bar, then you need to create an ``img`` directory under your
|
||||
theme's static root directory and place your custom ``logo.svg`` or
|
||||
``logo-splash.svg`` within it.
|
||||
|
||||
If you wish to override the ``logo.svg`` using the previous method, and if the
|
||||
image used is larger than the height of the top navigation, then the image will
|
||||
be constrained to fit within the height of nav. You can customize the height
|
||||
of the top navigation bar by customizing the SCSS variable: ``$navbar-height``.
|
||||
If the image's height is smaller than the navbar height, then the image
|
||||
will retain its original resolution and size, and simply be centered
|
||||
vertically in the available space.
|
||||
|
||||
Prior to the Kilo release the images files inside of Horizon needed to be
|
||||
replaced by your images files or the Horizon stylesheets needed to be altered
|
||||
to point to the location of your image.
|
||||
|
||||
Advanced
|
||||
~~~~~~~~
|
||||
|
||||
If you need to do more to customize the logo than simply replacing the existing
|
||||
PNG, then you can also override the _brand.html through a custom theme. To use
|
||||
this technique, simply add a ``templates/header/_brand.html`` to the root of
|
||||
your custom theme, and add markup directly to the file. For an example of how
|
||||
to do this, see
|
||||
``openstack_dashboard/themes/material/templates/header/_brand.html``.
|
||||
|
||||
The splash / login panel can also be customized by adding
|
||||
``templates/auth/_splash.html``. See
|
||||
``openstack_dashboard/themes/material/templates/auth/_splash.html`` for an
|
||||
example.
|
|
@ -1,695 +0,0 @@
|
|||
==================
|
||||
Contributing Guide
|
||||
==================
|
||||
|
||||
First and foremost, thank you for wanting to contribute! It's the only way
|
||||
open source works!
|
||||
|
||||
Before you dive into writing patches, here are some of the basics:
|
||||
|
||||
* Project page: http://launchpad.net/horizon
|
||||
* Bug tracker: https://bugs.launchpad.net/horizon
|
||||
* Source code: https://github.com/openstack/horizon
|
||||
* Code review: https://review.openstack.org/#q,status:open+project:openstack/horizon,n,z
|
||||
* Continuous integration:
|
||||
* Jenkins: https://jenkins.openstack.org
|
||||
* Zuul: http://status.openstack.org/zuul
|
||||
* IRC Channel: #openstack-horizon on Freenode.
|
||||
|
||||
Making Contributions
|
||||
====================
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
We'll start by assuming you've got a working checkout of the repository (if
|
||||
not then please see the :ref:`quickstart`).
|
||||
|
||||
Second, you'll need to take care of a couple administrative tasks:
|
||||
|
||||
#. Create an account on Launchpad.
|
||||
#. Sign the `OpenStack Contributor License Agreement`_ and follow the associated
|
||||
instructions to verify your signature.
|
||||
#. Join the `Horizon Developers`_ team on Launchpad.
|
||||
#. Follow the `instructions for setting up git-review`_ in your
|
||||
development environment.
|
||||
|
||||
Whew! Got all that? Okay! You're good to go.
|
||||
|
||||
.. _`OpenStack Contributor License Agreement`: http://wiki.openstack.org/CLA
|
||||
.. _`Horizon Developers`: https://launchpad.net/~horizon
|
||||
.. _`instructions for setting up git-review`: http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Ways To Contribute
|
||||
------------------
|
||||
|
||||
The easiest way to get started with Horizon's code is to pick a bug on
|
||||
Launchpad that interests you, and start working on that. Bugs tagged as
|
||||
``low-hanging-fruit`` are a good place to start. Alternatively, if there's an
|
||||
OpenStack API feature you would like to see implemented in Horizon feel free
|
||||
to try building it.
|
||||
|
||||
If those are too big, there are lots of great ways to get involved without
|
||||
plunging in head-first:
|
||||
|
||||
* Report bugs, triage new tickets, and review old tickets on
|
||||
the `bug tracker`_.
|
||||
* Propose ideas for improvements via `Launchpad Blueprints`_, via the
|
||||
mailing list on the project page, or on IRC.
|
||||
* Write documentation!
|
||||
* Write unit tests for untested code!
|
||||
* Help improve the `User Experience Design`_ or contribute to the
|
||||
`Persona Working Group`_.
|
||||
|
||||
.. _`bug tracker`: https://bugs.launchpad.net/horizon
|
||||
.. _`Launchpad Blueprints`: https://blueprints.launchpad.net/horizon
|
||||
.. _`User Experience Design`: https://wiki.openstack.org/wiki/UX#Getting_Started
|
||||
.. _`Persona Working Group`: https://wiki.openstack.org/wiki/Personas
|
||||
|
||||
Choosing Issues To Work On
|
||||
--------------------------
|
||||
|
||||
In general, if you want to write code, there are three cases for issues
|
||||
you might want to work on:
|
||||
|
||||
#. Confirmed bugs
|
||||
#. Approved blueprints (features)
|
||||
#. New bugs you've discovered
|
||||
|
||||
If you have an idea for a new feature that isn't in a blueprint yet, it's
|
||||
a good idea to write the blueprint first, so you don't end up writing a bunch
|
||||
of code that may not go in the direction the community wants.
|
||||
|
||||
For bugs, open the bug first, but if you can reproduce the bug reliably and
|
||||
identify its cause then it's usually safe to start working on it. However,
|
||||
getting independent confirmation (and verifying that it's not a duplicate)
|
||||
is always a good idea if you can be patient.
|
||||
|
||||
After You Write Your Patch
|
||||
--------------------------
|
||||
|
||||
Once you've made your changes, there are a few things to do:
|
||||
|
||||
* Make sure the unit tests and linting tasks pass by running ``tox``
|
||||
* Take a look at your patch in API profiler, i.e. how it impacts the
|
||||
performance. See `Profiling Pages`_.
|
||||
* Make sure your code is ready for translation: See :ref:`pseudo_translation`.
|
||||
* Make sure your code is up-to-date with the latest master:
|
||||
``git pull --rebase``
|
||||
* Finally, run ``git review`` to upload your changes to Gerrit for review.
|
||||
|
||||
The Horizon core developers will be notified of the new review and will examine
|
||||
it in a timely fashion, either offering feedback or approving it to be merged.
|
||||
If the review is approved, it is sent to Jenkins to verify the unit tests pass
|
||||
and it can be merged cleanly. Once Jenkins approves it, the change will be
|
||||
merged to the master repository and it's time to celebrate!
|
||||
|
||||
Profiling Pages
|
||||
---------------
|
||||
|
||||
In the Ocata release of Horizon a new "OpenStack Profiler" panel was
|
||||
introduced. Once it is enabled and all prerequisites are set up, you can see
|
||||
which API calls Horizon actually makes when rendering a specific page. To
|
||||
re-render the page while profiling it, you'll need to use the "Profile"
|
||||
dropdown menu located in the top right corner of the screen. In order to
|
||||
be able to use "Profile" menu, the following steps need to be completed:
|
||||
|
||||
#. Enable the Developer dashboard by copying ``_9001_developer.py`` from
|
||||
``openstack_dashboard/contrib/developer/enabled/`` to
|
||||
``openstack_dashboard/local/enabled/``.
|
||||
#. Copy
|
||||
``openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py.example``
|
||||
to ``openstack_dashboard/local/local_settings.d/_9030_profiler_settings.py``
|
||||
#. Copy ``openstack_dashboard/contrib/developer/enabled/_9030_profiler.py`` to
|
||||
``openstack_dashboard/local/enabled/_9030_profiler.py``.
|
||||
#. To support storing profiler data on server-side, MongoDB cluster needs to be
|
||||
installed on your Devstack host (default configuration), see
|
||||
`Installing MongoDB`_. Then, change the ``bindIp`` key in
|
||||
``/etc/mongod.conf`` to ``0.0.0.0`` and invoke
|
||||
``sudo service mongod restart``.
|
||||
#. Collect and compress static assets with
|
||||
``tox -e manage -- collectstatic -c`` and ``tox -e manage -- compress``
|
||||
#. Restart the web server.
|
||||
#. The "Profile" drop-down menu should appear in the top-right corner, you are
|
||||
ready to profile your pages!
|
||||
|
||||
.. _installing MongoDB: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/#install-mongodb-community-edition
|
||||
|
||||
Etiquette
|
||||
=========
|
||||
|
||||
The community's guidelines for etiquette are fairly simple:
|
||||
|
||||
* Treat everyone respectfully and professionally.
|
||||
* If a bug is "in progress" in the bug tracker, don't start working on it
|
||||
without contacting the author. Try on IRC, or via the launchpad email
|
||||
contact link. If you don't get a response after a reasonable time, then go
|
||||
ahead. Checking first avoids duplicate work and makes sure nobody's toes
|
||||
get stepped on.
|
||||
* If a blueprint is assigned, even if it hasn't been started, be sure you
|
||||
contact the assignee before taking it on. These larger issues often have a
|
||||
history of discussion or specific implementation details that the assignee
|
||||
may be aware of that you are not.
|
||||
* Please don't re-open tickets closed by a core developer. If you disagree with
|
||||
the decision on the ticket, the appropriate solution is to take it up on
|
||||
IRC or the mailing list.
|
||||
* Give credit where credit is due; if someone helps you substantially with
|
||||
a piece of code, it's polite (though not required) to thank them in your
|
||||
commit message.
|
||||
|
||||
Code Style
|
||||
==========
|
||||
|
||||
As a project, Horizon adheres to code quality standards.
|
||||
|
||||
Python
|
||||
------
|
||||
|
||||
We follow PEP8_ for all our Python code, and use ``pep8.py`` (available
|
||||
via the shortcut ``tox -e pep8``) to validate that our code
|
||||
meets proper Python style guidelines.
|
||||
|
||||
.. _PEP8: http://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
Django
|
||||
------
|
||||
|
||||
Additionally, we follow `Django's style guide`_ for templates, views, and
|
||||
other miscellany.
|
||||
|
||||
.. _Django's style guide: https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/
|
||||
|
||||
JavaScript
|
||||
----------
|
||||
|
||||
The following standards are divided into required and recommended sections.
|
||||
Our main goal in establishing these best practices is to have code that is
|
||||
reliable, readable, and maintainable.
|
||||
|
||||
Required
|
||||
~~~~~~~~
|
||||
|
||||
**Reliable**
|
||||
|
||||
* The code has to work on the stable and latest versions of Firefox, Chrome,
|
||||
Safari, and Opera web browsers, and on Microsoft Internet Explorer 11 and
|
||||
later.
|
||||
|
||||
* If you turned compression off during development via ``COMPRESS_ENABLED =
|
||||
False`` in local_settings.py, re-enable compression and test your code
|
||||
before submitting.
|
||||
|
||||
* Use ``===`` as opposed to ``==`` for equality checks. The ``==`` will do a
|
||||
type cast before comparing, which can lead to unwanted results.
|
||||
|
||||
.. note::
|
||||
|
||||
If typecasting is desired, explicit casting is preferred to keep the
|
||||
meaning of your code clear.
|
||||
|
||||
* Keep document reflows to a minimum. DOM manipulation is expensive, and can
|
||||
become a performance issue. If you are accessing the DOM, make sure that you
|
||||
are doing it in the most optimized way. One example is to build up a document
|
||||
fragment and then append the fragment to the DOM in one pass instead of doing
|
||||
multiple smaller DOM updates.
|
||||
|
||||
* Use “strict”, enclosing each JavaScript file inside a self-executing
|
||||
function. The self-executing function keeps the strict scoped to the file,
|
||||
so its variables and methods are not exposed to other JavaScript files in
|
||||
the product.
|
||||
|
||||
.. Note ::
|
||||
Using strict will throw exceptions for common coding errors, like
|
||||
accessing global vars, that normally are not flagged.
|
||||
|
||||
Example:
|
||||
::
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
// code...
|
||||
})();
|
||||
|
||||
* Use ``forEach`` | ``each`` when looping whenever possible. AngularJS and
|
||||
jQuery both provide for each loops that provide both iteration and scope.
|
||||
|
||||
AngularJS:
|
||||
::
|
||||
|
||||
angular.forEach(objectToIterateOver, function(value, key) {
|
||||
// loop logic
|
||||
});
|
||||
|
||||
jQuery:
|
||||
::
|
||||
|
||||
$.each(objectToIterateOver, function(key, value) {
|
||||
// loop logic
|
||||
});
|
||||
|
||||
* Do not put variables or functions in the global namespace. There are several
|
||||
reasons why globals are bad, one being that all JavaScript included in an
|
||||
application runs in the same scope. The issue with that is if another script
|
||||
has the same method or variable names they overwrite each other.
|
||||
* Always put ``var`` in front of your variables. Not putting ``var`` in front
|
||||
of a variable puts that variable into the global space, see above.
|
||||
* Do not use ``eval( )``. The eval (expression) evaluates the expression
|
||||
passed to it. This can open up your code to security vulnerabilities and
|
||||
other issues.
|
||||
* Do not use '``with`` object {code}'. The ``with`` statement is used to access
|
||||
properties of an object. The issue with ``with`` is that its execution is not
|
||||
consistent, so by reading the statement in the code it is not always clear
|
||||
how it is being used.
|
||||
|
||||
**Readable & Maintainable**
|
||||
|
||||
* Give meaningful names to methods and variables.
|
||||
* Avoid excessive nesting.
|
||||
* Avoid HTML and CSS in JS code. HTML and CSS belong in templates and
|
||||
stylesheets respectively. For example:
|
||||
|
||||
* In our HTML files, we should focus on layout.
|
||||
|
||||
1. Reduce the small/random ``<script>`` and ``<style>`` elements in HTML.
|
||||
|
||||
2. Avoid in-lining styles into element in HTML. Use attributes and
|
||||
classes instead.
|
||||
|
||||
* In our JS files, we should focus on logic rather than attempting to
|
||||
manipulate/style elements.
|
||||
|
||||
1. Avoid statements such as ``element.css({property1,property2...})`` they
|
||||
belong in a CSS class.
|
||||
|
||||
2. Avoid statements such as ``$("<div><span>abc</span></div>")`` they
|
||||
belong in a HTML template file. Use ``show`` | ``hide`` | ``clone``
|
||||
elements if dynamic content is required.
|
||||
|
||||
3. Avoid using classes for detection purposes only, instead, defer to
|
||||
attributes. For example to find a div:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div class="something"></div>
|
||||
$(".something").html("Don't find me this way!");
|
||||
|
||||
is better found like:
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div data-something></div>
|
||||
$("div[data-something]").html("You found me correctly!");
|
||||
|
||||
* Avoid commented-out code.
|
||||
* Avoid dead code.
|
||||
|
||||
**Performance**
|
||||
|
||||
* Avoid creating instances of the same object repeatedly within the same scope.
|
||||
Instead, assign the object to a variable and re-use the existing object. For
|
||||
example:
|
||||
::
|
||||
|
||||
$(document).on('click', function() { /* do something. */ });
|
||||
$(document).on('mouseover', function() { /* do something. */ });
|
||||
|
||||
A better approach:
|
||||
::
|
||||
|
||||
var $document = $(document);
|
||||
$document.on('click', function() { /* do something. */ });
|
||||
$document.on('mouseover', function() { /* do something. */ });
|
||||
|
||||
In the first approach a jQuery object for ``document`` is created each time.
|
||||
The second approach creates only one jQuery object and reuses it. Each object
|
||||
needs to be created, uses memory, and needs to be garbage collected.
|
||||
|
||||
Recommended
|
||||
~~~~~~~~~~~
|
||||
|
||||
**Readable & Maintainable**
|
||||
|
||||
* Put a comment at the top of every file explaining what the purpose of this
|
||||
file is when the naming is not obvious. This guideline also applies to
|
||||
methods and variables.
|
||||
* Source-code formatting – (or “beautification”) is recommended but should be
|
||||
used with caution. Keep in mind that if you reformat an entire file that was
|
||||
not previously formatted the same way, it will mess up the diff during the
|
||||
code review. It is best to use a formatter when you are working on a new file
|
||||
by yourself, or with others who are using the same formatter. You can also
|
||||
choose to format a selected portion of a file only. Instructions for setting
|
||||
up ESLint for Eclipse, Sublime Text, Notepad++ and WebStorm/PyCharm are
|
||||
provided_.
|
||||
* Use 2 spaces for code indentation.
|
||||
* Use ``{ }`` for ``if``, ``for``, ``while`` statements, and don't combine them
|
||||
on one line.
|
||||
::
|
||||
|
||||
// Do this //Not this // Not this
|
||||
if(x) { if(x) if(x) y =x;
|
||||
y=x; y=x;
|
||||
}
|
||||
|
||||
* Use ESLint in your development environment.
|
||||
|
||||
.. _provided: https://wiki.openstack.org/wiki/Horizon/Javascript/EditorConfig
|
||||
|
||||
AngularJS
|
||||
---------
|
||||
|
||||
.. Note::
|
||||
|
||||
This section is intended as a quick intro to contributing with AngularJS. For
|
||||
more detailed information, check the :ref:`topics-angularjs`.
|
||||
|
||||
"John Papa Style Guide"
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The John Papa Style Guide is the primary point of reference for Angular
|
||||
code style. This style guide has been endorsed by the AngularJS
|
||||
team::
|
||||
|
||||
"The most current and detailed Angular Style Guide is the
|
||||
community-driven effort led by John Papa and Todd Motto."
|
||||
|
||||
- http://angularjs.blogspot.com/2014/02/an-angularjs-style-guide-and-best.html
|
||||
|
||||
The style guide is found at the below location:
|
||||
|
||||
https://github.com/johnpapa/angular-styleguide
|
||||
|
||||
When reviewing / writing, please refer to the sections of this guide.
|
||||
If an issue is encountered, note it with a comment and provide a link back
|
||||
to the specific issue. For example, code should use named functions. A
|
||||
review noting this should provide the following link in the comments:
|
||||
|
||||
https://github.com/johnpapa/angular-styleguide#style-y024
|
||||
|
||||
In addition to John Papa, the following guidelines are divided into
|
||||
required and recommended sections.
|
||||
|
||||
Required
|
||||
~~~~~~~~
|
||||
|
||||
* Scope is not the model (model is your JavaScript Objects). The scope
|
||||
references the model. Use isolate scopes wherever possible.
|
||||
|
||||
* https://github.com/angular/angular.js/wiki/Understanding-Scopes
|
||||
* Read-only in templates.
|
||||
* Write-only in controllers.
|
||||
|
||||
* Since Django already uses ``{{ }}``, use ``{$ $}`` or ``{% verbatim %}``
|
||||
instead.
|
||||
|
||||
ESLint
|
||||
------
|
||||
|
||||
ESLint is a great tool to be used during your code editing to improve
|
||||
JavaScript quality by checking your code against a configurable list of checks.
|
||||
Therefore, JavaScript developers should configure their editors to use ESLint
|
||||
to warn them of any such errors so they can be addressed. Since ESLint has a
|
||||
ton of configuration options to choose from, links are provided below to the
|
||||
options Horizon wants enforced along with the instructions for setting up
|
||||
ESLint for Eclipse, Sublime Text, Notepad++ and WebStorm/PyCharm.
|
||||
|
||||
Instructions for setting up ESLint: `ESLint setup instructions`_
|
||||
|
||||
.. Note ::
|
||||
ESLint is part of the automated unit tests performed by Jenkins. The
|
||||
automated test use the default configurations, which are less strict than
|
||||
the configurations we recommended to run in your local development
|
||||
environment.
|
||||
|
||||
.. _ESLint setup instructions: https://wiki.openstack.org/wiki/Horizon/Javascript/EditorConfig
|
||||
|
||||
CSS
|
||||
---
|
||||
|
||||
Style guidelines for CSS are currently quite minimal. Do your best to make the
|
||||
code readable and well-organized. Two spaces are preferred for indentation
|
||||
so as to match both the JavaScript and HTML files.
|
||||
|
||||
JavaScript and CSS libraries using xstatic
|
||||
------------------------------------------
|
||||
|
||||
We do not bundle third-party code in Horizon's source tree. Instead, we package
|
||||
the required files as xstatic Python packages and add them as dependencies to
|
||||
Horizon.
|
||||
|
||||
To create a new xstatic package:
|
||||
|
||||
1. Check if the library is already packaged as xstatic on PyPi, by searching
|
||||
for the library name. If it already is, go to step 5. If it is, but not in
|
||||
the right version, contact the original packager to have them update it.
|
||||
2. Package the library as an xstatic package by following the instructions in
|
||||
xstatic documentation_. Install the xstatic-release_ script and follow
|
||||
the instructions that come with it.
|
||||
3. `Create a new repository under OpenStack`_. Use "xstatic-core" and
|
||||
"xstatic-ptl" groups for the ACLs. Make sure to include the
|
||||
``-pypi-wheel-upload`` job in the project config.
|
||||
4. `Set up PyPi`_ to allow OpenStack (the "openstackci" user) to publish your
|
||||
package.
|
||||
5. Add the new package to `global-requirements`_.
|
||||
|
||||
To make a new release of the package, you need to:
|
||||
|
||||
1. Ensure the version information in the
|
||||
`xstatic/pkg/<package name>/__init__.py` file is up to date,
|
||||
especially the `BUILD`.
|
||||
2. Push your updated package up for review in gerrit.
|
||||
3. Once the review is approved and the change merged, `request a release`_ by
|
||||
updating or creating the appropriate file for the xstatic package
|
||||
in the `releases repository`_ under `deliverables/_independent`. That
|
||||
will cause it to be automatically packaged and released to PyPi.
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that once a package is released, you can not "un-release" it. You
|
||||
should never attempt to modify, delete or rename a released package without
|
||||
a lot of careful planning and feedback from all projects that use it.
|
||||
|
||||
For the purpose of fixing packaging mistakes, xstatic has the build number
|
||||
mechanism. Simply fix the error, increment the build number and release the
|
||||
newer package.
|
||||
|
||||
.. _documentation: http://xstatic.rtfd.org/en/latest/packaging.html
|
||||
.. _xstatic-release: https://pypi.python.org/pypi/xstatic-release
|
||||
.. _`Create a new repository under OpenStack`: http://docs.openstack.org/infra/manual/creators.html
|
||||
.. _`request a release`: http://git.openstack.org/cgit/openstack/releases/tree/README.rst
|
||||
.. _`releases repository`: http://git.openstack.org/cgit/openstack/releases
|
||||
.. _`Set up PyPi`: http://docs.openstack.org/infra/manual/creators.html#give-openstack-permission-to-publish-releases
|
||||
.. _global-requirements: https://github.com/openstack/requirements/blob/master/global-requirements.txt
|
||||
|
||||
|
||||
Integrating a new xstatic package into Horizon
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Having done a release of an xstatic package:
|
||||
|
||||
1. Look for the `upper-constraints.txt`_ edit related to the xstatic release
|
||||
that was just performed. One will be created automatically by the release
|
||||
process in the ``openstack/requirements`` project with the topic
|
||||
`new-release`_. You should -1 that patch until you are confident Horizon
|
||||
does not break (or you have generated a patch to fix Horizon for that
|
||||
release.) If no upper-constraints.txt patch is automatically generated,
|
||||
ensure the releases yaml file created in the `releases repository`_ has the
|
||||
"include-pypi-link: yes" setting.
|
||||
2. Pull that patch down so you have the edited upper-constraints.txt file
|
||||
locally.
|
||||
3. Set the environment variable `UPPER_CONSTRAINTS_FILE` to the edited
|
||||
upper-constraints.txt file name and run tests or local development server
|
||||
through tox. This will pull in the precise version of the xstatic package
|
||||
that you need.
|
||||
4. Move on to releasing once you're happy the Horizon changes are stable.
|
||||
|
||||
Releasing a new compatible version of Horizon to address issues in the new
|
||||
xstatic release:
|
||||
|
||||
1. Continue to -1 the upper-constraints.txt patch above until this process is
|
||||
complete. A +1 from a Horizon developer will indicate to the requirements
|
||||
team that the upper-constraints.txt patch is OK to merge.
|
||||
2. When submitting your changes to Horizon to address issues around the new
|
||||
xstatic release, use a Depends-On: referencing the upper-constraints.txt
|
||||
review. This will cause the OpenStack testing infrastructure to pull in your
|
||||
updated xstatic package as well.
|
||||
3. Merge the upper-constraints.txt patch and the Horizon patch noting that
|
||||
Horizon's gate may be broken in the interim between these steps, so try to
|
||||
minimise any delay there. With the Depends-On it's actually safe to +W the
|
||||
Horizon patch, which will be held up until the related upper-constraints.txt
|
||||
patch merges.
|
||||
4. Once the upper-constraints.txt patch merges, you should propose a patch to
|
||||
global-requirements which bumps the minimum version of the package up to the
|
||||
upper-constraints version so that deployers / packagers who don't honor
|
||||
upper-constraints still get compatible versions of the packages.
|
||||
|
||||
.. _upper-constraints.txt: https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt
|
||||
.. _new-release: https://review.openstack.org/#/q/status:open+project:openstack/requirements+branch:master+topic:new-release
|
||||
|
||||
|
||||
HTML
|
||||
----
|
||||
|
||||
Again, readability is paramount; however be conscientious of how the browser
|
||||
will handle whitespace when rendering the output. Two spaces is the preferred
|
||||
indentation style to match all front-end code.
|
||||
|
||||
Exception Handling
|
||||
------------------
|
||||
|
||||
Avoid propogating direct exception messages thrown by OpenStack APIs to the UI.
|
||||
It is a precaution against giving obscure or possibly sensitive data to a user.
|
||||
These error messages from the API are also not translatable. Until there is a
|
||||
standard error handling framework implemented by the services which presents
|
||||
clean and translated messages, horizon catches all the exceptions thrown by the
|
||||
API and normalizes them in :func:`horizon.exceptions.handle`.
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Horizon's documentation is written in reStructuredText (reST) and uses Sphinx
|
||||
for additional parsing and functionality, and should follow standard practices
|
||||
for writing reST. This includes:
|
||||
|
||||
* Flow paragraphs such that lines wrap at 80 characters or less.
|
||||
* Use proper grammar, spelling, capitalization and punctuation at all times.
|
||||
* Make use of Sphinx's autodoc feature to document modules, classes
|
||||
and functions. This keeps the docs close to the source.
|
||||
* Where possible, use Sphinx's cross-reference syntax (e.g.
|
||||
``:class:`~horizon.foo.Bar``) when referring to other Horizon components.
|
||||
The better-linked our docs are, the easier they are to use.
|
||||
|
||||
Be sure to generate the documentation before submitting a patch for review.
|
||||
Unexpected warnings often appear when building the documentation, and slight
|
||||
reST syntax errors frequently cause links or cross-references not to work
|
||||
correctly.
|
||||
|
||||
Documentation is generated with Sphinx using the tox command. To create HTML
|
||||
docs and man pages:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
The results are in the doc/build/html and doc/build/man directories
|
||||
respectively.
|
||||
|
||||
Conventions
|
||||
-----------
|
||||
|
||||
Simply by convention, we have a few rules about naming:
|
||||
|
||||
* The term "project" is used in place of Keystone's "tenant" terminology
|
||||
in all user-facing text. The term "tenant" is still used in API code to
|
||||
make things more obvious for developers.
|
||||
|
||||
* The term "dashboard" refers to a top-level dashboard class, and "panel" to
|
||||
the sub-items within a dashboard. Referring to a panel as a dashboard is
|
||||
both confusing and incorrect.
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
Release notes for a patch should be included in the patch with the
|
||||
associated changes whenever possible. This allow for simpler tracking. It also
|
||||
enables a single cherry pick to be done if the change is backported to a
|
||||
previous release. In some cases, such as a feature that is provided via
|
||||
multiple patches, release notes can be done in a follow-on review.
|
||||
|
||||
If the following applies to the patch, a release note is required:
|
||||
|
||||
* The deployer needs to take an action when upgrading
|
||||
* A new feature is implemented
|
||||
* Function was removed (hopefully it was deprecated)
|
||||
* Current behavior is changed
|
||||
* A new config option is added that the deployer should consider changing from
|
||||
the default
|
||||
* A security bug is fixed
|
||||
|
||||
A release note is suggested if a long-standing or important bug is fixed.
|
||||
Otherwise, a release note is not required.
|
||||
|
||||
Horizon uses `reno <http://docs.openstack.org/developer/reno/usage.html>`_ to
|
||||
generate release notes. Please read the docs for details. In summary, use
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ tox -e venv -- reno new <bug-,bp-,whatever>
|
||||
|
||||
Then edit the sample file that was created and push it with your change.
|
||||
|
||||
To see the results:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git commit # Commit the change because reno scans git log.
|
||||
|
||||
$ tox -e releasenotes
|
||||
|
||||
Then look at the generated release notes files in releasenotes/build/html in
|
||||
your favorite browser.
|
||||
|
||||
Core Reviewer Team
|
||||
==================
|
||||
|
||||
The Horizon Core Reviewer Team is responsible for many aspects of the
|
||||
Horizon project. These include, but are not limited to:
|
||||
|
||||
- Mentor community contributors in solution design, testing, and the
|
||||
review process
|
||||
- Actively reviewing patch submissions, considering whether the patch:
|
||||
- is functional
|
||||
- fits the use-cases and vision of the project
|
||||
- is complete in terms of testing, documentation, and release notes
|
||||
- takes into consideration upgrade concerns from previous versions
|
||||
- Assist in bug triage and delivery of bug fixes
|
||||
- Curating the gate and triaging failures
|
||||
- Maintaining accurate, complete, and relevant documentation
|
||||
- Ensuring the level of testing is adequate and remains relevant as
|
||||
features are added
|
||||
- Answering questions and participating in mailing list discussions
|
||||
- Interfacing with other OpenStack teams
|
||||
|
||||
In essence, core reviewers share the following common ideals:
|
||||
|
||||
- They share responsibility in the project's success in its mission.
|
||||
- They value a healthy, vibrant, and active developer and user community.
|
||||
- They have made a long-term, recurring time investment to improve the project.
|
||||
- They spend their time doing what needs to be done to ensure the
|
||||
project's success, not necessarily what is the most interesting or
|
||||
fun.
|
||||
- A core reviewer's responsibility doesn't end with merging code.
|
||||
|
||||
Core Reviewer Expectations
|
||||
--------------------------
|
||||
|
||||
Members of the core reviewer team are expected to:
|
||||
|
||||
- Attend and participate in the weekly IRC meetings (if your timezone allows)
|
||||
- Monitor and participate in-channel at #openstack-horizon
|
||||
- Monitor and participate in [Horizon] discussions on the mailing list
|
||||
- Participate in related design summit sessions at the OpenStack
|
||||
Summits and Project Team Gatherings
|
||||
- Review patch submissions actively and consistently
|
||||
|
||||
Please note in-person attendance at design summits, mid-cycles, and
|
||||
other code sprints is not a requirement to be a core reviewer.
|
||||
Participation can also include contributing to the design documents
|
||||
discussed at the design sessions.
|
||||
|
||||
Active and consistent review of review activity, bug triage and other
|
||||
activity will be performed monthly and fed back to the Core Reviewer Team
|
||||
so everyone knows how things are progressing.
|
||||
|
||||
Code Merge Responsibilities
|
||||
---------------------------
|
||||
|
||||
While everyone is encouraged to review changes, members of the core
|
||||
reviewer team have the ability to +2/-2 and +A changes to these
|
||||
repositories. This is an extra level of responsibility not to be taken
|
||||
lightly. Correctly merging code requires not only understanding the
|
||||
code itself, but also how the code affects things like documentation,
|
||||
testing, upgrade impacts and interactions with other projects. It also
|
||||
means you pay attention to release milestones and understand if a
|
||||
patch you are merging is marked for the release, especially critical
|
||||
during the feature freeze.
|
|
@ -1,39 +0,0 @@
|
|||
.. _faq:
|
||||
|
||||
==========================
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
What is the relationship between ``Dashboards``, ``Panels``, and navigation?
|
||||
|
||||
The navigational structure is strongly encouraged to flow from
|
||||
``Dashboard`` objects as top-level navigation items to ``Panel`` objects as
|
||||
sub-navigation items as in the current implementation. Template tags
|
||||
are provided to automatically generate this structure.
|
||||
|
||||
That said, you are not required to use the provided tools and can write
|
||||
templates and URLconfs by hand to create any desired structure.
|
||||
|
||||
Does a panel have to be an app in ``INSTALLED_APPS``?
|
||||
|
||||
A panel can live in any Python module. It can be a standalone which ties
|
||||
into an existing dashboard, or it can be contained alongside others within
|
||||
a larger dashboard "app". There is no strict enforcement here. Python
|
||||
is "a language for consenting adults." A module containing a Panel does
|
||||
not need to be added to ``INSTALLED_APPS``, but this is a common and
|
||||
convenient way to load a standalone panel.
|
||||
|
||||
Could I hook an external service into a panel using, for example, an iFrame?
|
||||
|
||||
Panels are just entry-points to hook views into the larger dashboard
|
||||
navigational structure and enforce common attributes like RBAC. The
|
||||
views and corresponding templates can contain anything you would like,
|
||||
including iFrames.
|
||||
|
||||
What does this mean for visual design?
|
||||
|
||||
The ability to add an arbitrary number of top-level navigational items
|
||||
(``Dashboard`` objects) poses a new design challenge. Horizon's lead
|
||||
designer has taken on the challenge of providing a reference design
|
||||
for Horizon which supports this possibility.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
===========================
|
||||
Contributor Documentation
|
||||
===========================
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
intro
|
||||
quickstart
|
||||
contributing
|
||||
testing
|
||||
tutorials/index
|
||||
topics/index
|
||||
ref/index
|
||||
faq
|
|
@ -1,118 +0,0 @@
|
|||
.. _contributor-intro:
|
||||
|
||||
==============
|
||||
Horizon Basics
|
||||
==============
|
||||
|
||||
Values
|
||||
======
|
||||
|
||||
"Think simple" as my old master used to say - meaning reduce
|
||||
the whole of its parts into the simplest terms, getting back
|
||||
to first principles.
|
||||
|
||||
-- Frank Lloyd Wright
|
||||
|
||||
Horizon holds several key values at the core of its design and architecture:
|
||||
|
||||
* Core Support: Out-of-the-box support for all core OpenStack projects.
|
||||
* Extensible: Anyone can add a new component as a "first-class citizen".
|
||||
* Manageable: The core codebase should be simple and easy-to-navigate.
|
||||
* Consistent: Visual and interaction paradigms are maintained throughout.
|
||||
* Stable: A reliable API with an emphasis on backwards-compatibility.
|
||||
* Usable: Providing an *awesome* interface that people *want* to use.
|
||||
|
||||
The only way to attain and uphold those ideals is to make it *easy* for
|
||||
developers to implement those values.
|
||||
|
||||
History
|
||||
=======
|
||||
|
||||
Horizon started life as a single app to manage OpenStack's compute project.
|
||||
As such, all it needed was a set of views, templates, and API calls.
|
||||
|
||||
From there it grew to support multiple OpenStack projects and APIs gradually,
|
||||
arranged rigidly into "dash" and "syspanel" groupings.
|
||||
|
||||
During the "Diablo" release cycle an initial plugin system was added using
|
||||
signals to hook in additional URL patterns and add links into the "dash"
|
||||
and "syspanel" navigation.
|
||||
|
||||
This incremental growth served the goal of "Core Support" phenomenally, but
|
||||
left "Extensible" and "Manageable" behind. And while the other key values took
|
||||
shape of their own accord, it was time to re-architect for an extensible,
|
||||
modular future.
|
||||
|
||||
|
||||
The Current Architecture & How It Meets Our Values
|
||||
==================================================
|
||||
|
||||
At its core, **Horizon should be a registration pattern for
|
||||
applications to hook into**. Here's what that means and how it is
|
||||
implemented in terms of our values:
|
||||
|
||||
Core Support
|
||||
------------
|
||||
|
||||
Horizon ships with three central dashboards, a "User Dashboard", a
|
||||
"System Dashboard", and a "Settings" dashboard. Between these three they
|
||||
cover the core OpenStack applications and deliver on Core Support.
|
||||
|
||||
The Horizon application also ships with a set of API abstractions
|
||||
for the core OpenStack projects in order to provide a consistent, stable set
|
||||
of reusable methods for developers. Using these abstractions, developers
|
||||
working on Horizon don't need to be intimately familiar with the APIs of
|
||||
each OpenStack project.
|
||||
|
||||
Extensible
|
||||
----------
|
||||
|
||||
A Horizon dashboard application is based around the :class:`~horizon.Dashboard`
|
||||
class that provides a consistent API and set of capabilities for both
|
||||
core OpenStack dashboard apps shipped with Horizon and equally for third-party
|
||||
apps. The :class:`~horizon.Dashboard` class is treated as a top-level
|
||||
navigation item.
|
||||
|
||||
Should a developer wish to provide functionality within an existing dashboard
|
||||
(e.g. adding a monitoring panel to the user dashboard) the simple registration
|
||||
pattern makes it possible to write an app which hooks into other dashboards
|
||||
just as easily as creating a new dashboard. All you have to do is import the
|
||||
dashboard you wish to modify.
|
||||
|
||||
Manageable
|
||||
----------
|
||||
|
||||
Within the application, there is a simple method for registering a
|
||||
:class:`~horizon.Panel` (sub-navigation items). Each panel contains the
|
||||
necessary logic (views, forms, tests, etc.) for that interface. This granular
|
||||
breakdown prevents files (such as ``api.py``) from becoming thousands of
|
||||
lines long and makes code easy to find by correlating it directly to the
|
||||
navigation.
|
||||
|
||||
Consistent
|
||||
----------
|
||||
|
||||
By providing the necessary core classes to build from, as well as a
|
||||
solid set of reusable templates and additional tools (base form classes,
|
||||
base widget classes, template tags, and perhaps even class-based views)
|
||||
we can maintain consistency across applications.
|
||||
|
||||
Stable
|
||||
------
|
||||
|
||||
By architecting around these core classes and reusable components we
|
||||
create an implicit contract that changes to these components will be
|
||||
made in the most backwards-compatible ways whenever possible.
|
||||
|
||||
Usable
|
||||
------
|
||||
|
||||
Ultimately that's up to each and every developer that touches the code,
|
||||
but if we get all the other goals out of the way then we are free to focus
|
||||
on the best possible experience.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :ref:`quickstart` A short guide to getting started with using Horizon.
|
||||
* :ref:`faq` Common questions and answers.
|
||||
* :ref:`glossary` Common terms and their definitions.
|
|
@ -1,320 +0,0 @@
|
|||
.. _quickstart:
|
||||
|
||||
==========
|
||||
Quickstart
|
||||
==========
|
||||
|
||||
.. Note ::
|
||||
|
||||
This section has been tested for Horizon on Ubuntu (16.04-64) and RPM-based
|
||||
(RHEL 7.x) distributions. Feel free to add notes and any changes according
|
||||
to your experiences or operating system.
|
||||
|
||||
Linux Systems
|
||||
=============
|
||||
|
||||
Install the prerequisite packages.
|
||||
|
||||
On Ubuntu
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt-get install git python-pip
|
||||
|
||||
On RPM-based distributions (e.g., Fedora/RHEL/CentOS/Scientific Linux)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo yum install gcc git-core python-devel python-virtualenv openssl-devel libffi-devel which
|
||||
|
||||
.. note::
|
||||
|
||||
Some tests rely on the Chrome web browser being installed. While the above
|
||||
requirements will allow you to run and manually test Horizon, you will
|
||||
need to install Chrome to run the full test suite.
|
||||
|
||||
Setup
|
||||
=====
|
||||
|
||||
To begin setting up a Horizon development environment simply clone the Horizon
|
||||
git repository from https://git.openstack.org/cgit/openstack/horizon
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ git clone https://git.openstack.org/openstack/horizon
|
||||
|
||||
Next you will need to configure Horizon by adding a ``local_settings.py`` file.
|
||||
A good starting point is to use the example config with the following command,
|
||||
from within the ``horizon`` directory.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
|
||||
|
||||
Horizon connects to the rest of OpenStack via a Keystone service catalog. By
|
||||
default Horizon looks for an endpoint at ``http://localhost:5000/v2.0``; this
|
||||
can be customised by modifying the ``OPENSTACK_HOST`` and
|
||||
``OPENSTACK_KEYSTONE_URL`` values in
|
||||
``openstack_dashboard/local/local_settings.py``
|
||||
|
||||
.. note::
|
||||
|
||||
The DevStack project (http://devstack.org/) can be used to install
|
||||
an OpenStack development environment from scratch. For a local.conf that
|
||||
enables most services that Horizon supports managing, see
|
||||
:ref:`local-conf`
|
||||
|
||||
Horizon uses ``tox`` to manage virtual environments for testing and other
|
||||
development tasks. You can install it with
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install tox
|
||||
|
||||
The ``tox`` environments provide wrappers around ``manage.py``. For more
|
||||
information on ``manage.py``, which is a Django command, see
|
||||
https://docs.djangoproject.com/en/dev/ref/django-admin/
|
||||
|
||||
To start the Horizon development server use the command below
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ tox -e runserver
|
||||
|
||||
.. note::
|
||||
|
||||
The default port for runserver is 8000 which is already consumed by
|
||||
heat-api-cfn in DevStack. If running in DevStack
|
||||
``tox -e runserver -- localhost:9000`` will start the test server at
|
||||
``http://localhost:9000``
|
||||
|
||||
Once the Horizon server is running, point a web browser to ``http://localhost``
|
||||
or to the IP and port the server is listening for. Enter your Keystone
|
||||
credentials, log in and you'll be presented with the Horizon dashboard.
|
||||
Congratulations!
|
||||
|
||||
Managing Settings
|
||||
=================
|
||||
|
||||
You can save changes you made to
|
||||
``openstack_dashboard/local/local_settings.py`` with the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python manage.py migrate_settings --gendiff
|
||||
|
||||
.. note::
|
||||
|
||||
This creates a ``local_settings.diff`` file which is a diff between
|
||||
``local_settings.py`` and ``local_settings.py.example``
|
||||
|
||||
If you upgrade Horizon, you might need to update your
|
||||
``openstack_dashboard/local/local_settings.py`` file with new parameters from
|
||||
``openstack_dashboard/local/local_settings.py.example`` to do so, first update
|
||||
Horizon
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ git remote update && git pull --ff-only origin master
|
||||
|
||||
Then update your ``openstack_dashboard/local/local_settings.py`` file
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mv openstack_dashboard/local/local_settings.py openstack_dashboard/local/local_settings.py.old
|
||||
$ python manage.py migrate_settings
|
||||
|
||||
.. note::
|
||||
|
||||
This applies ``openstack_dashboard/local/local_settings.diff`` on
|
||||
``openstack_dashboard/local/local_settings.py.example`` to regenerate an
|
||||
``openstack_dashboard/local/local_settings.py`` file.
|
||||
The migration can sometimes have difficulties to migrate some settings, if
|
||||
this happens you will be warned with a conflict message pointing to an
|
||||
``openstack_dashboard/local/local_settings.py_Some_DateTime.rej`` file.
|
||||
In this file, you will see the lines which could not be automatically
|
||||
changed and you will have to redo only these few changes manually instead
|
||||
of modifying the full
|
||||
``openstack_dashboard/local/local_settings.py.example`` file.
|
||||
|
||||
When all settings have been migrated, it is safe to regenerate a clean diff in
|
||||
order to prevent Conflicts for future migrations
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mv openstack_dashboard/local/local_settings.diff openstack_dashboard/local/local_settings.diff.old
|
||||
$ python manage.py migrate_settings --gendiff
|
||||
|
||||
Editing Horizon's Source
|
||||
========================
|
||||
|
||||
Although DevStack installs and configures an instance of Horizon when running
|
||||
stack.sh, the preferred development setup follows the instructions above on the
|
||||
server/VM running DevStack. There are several advantages to maintaining a
|
||||
separate copy of the Horizon repo, rather than editing the DevStack installed
|
||||
copy.
|
||||
|
||||
- Source code changes aren't as easily lost when running ``unstack.sh`` /
|
||||
``stack.sh``
|
||||
- The development server picks up source code changes while still running.
|
||||
- Log messages and print statements go directly to the console.
|
||||
- Debugging with ``pdb`` becomes much simpler to interact with.
|
||||
|
||||
.. note::
|
||||
|
||||
To ensure that JS and CSS changes are picked up without a server restart, you
|
||||
can disable compression with ``COMPRESS_ENABLED = False`` in your local
|
||||
settings file.
|
||||
|
||||
Horizon's Structure
|
||||
===================
|
||||
|
||||
This project is a bit different from other OpenStack projects in that it has
|
||||
two very distinct components underneath it: ``horizon``, and
|
||||
``openstack_dashboard``.
|
||||
|
||||
The ``horizon`` directory holds the generic libraries and components that can
|
||||
be used in any Django project.
|
||||
|
||||
The ``openstack_dashboard`` directory contains a reference Django project that
|
||||
uses ``horizon``.
|
||||
|
||||
For development, both pieces share an environment which (by default) is
|
||||
built with the ``tools/install_venv.py`` script. That script creates a
|
||||
virtualenv and installs all the necessary packages.
|
||||
|
||||
If dependencies are added to either ``horizon`` or ``openstack_dashboard``,
|
||||
they should be added to ``requirements.txt``.
|
||||
|
||||
Project Structure
|
||||
=================
|
||||
|
||||
Dashboard configuration
|
||||
-----------------------
|
||||
|
||||
To add a new dashboard to your project, you need to add a configuration file to
|
||||
``openstack_dashboard/local/enabled`` directory. For more information on this,
|
||||
see :ref:`pluggable-settings-label`.
|
||||
|
||||
URLs
|
||||
----
|
||||
|
||||
Then you add a single line to your project's ``urls.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
url(r'', include(horizon.urls)),
|
||||
|
||||
Those urls are automatically constructed based on the registered Horizon apps.
|
||||
If a different URL structure is desired it can be constructed by hand.
|
||||
|
||||
Templates
|
||||
---------
|
||||
|
||||
Pre-built template tags generate navigation. In your ``nav.html``
|
||||
template you might have the following
|
||||
|
||||
.. code-block:: htmldjango
|
||||
|
||||
{% load horizon %}
|
||||
|
||||
<div class='nav'>
|
||||
{% horizon_main_nav %}
|
||||
</div>
|
||||
|
||||
And in your ``sidebar.html`` you might have
|
||||
|
||||
.. code-block:: htmldjango
|
||||
|
||||
{% load horizon %}
|
||||
|
||||
<div class='sidebar'>
|
||||
{% horizon_dashboard_nav %}
|
||||
</div>
|
||||
|
||||
These template tags are aware of the current "active" dashboard and panel
|
||||
via template context variables and will render accordingly.
|
||||
|
||||
Application Design
|
||||
==================
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
An application would have the following structure (we'll use project as
|
||||
an example)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
project/
|
||||
|---__init__.py
|
||||
|---dashboard.py <-----Registers the app with Horizon and sets dashboard properties
|
||||
|---overview/
|
||||
|---images/
|
||||
|-- images
|
||||
|-- __init__.py
|
||||
|---panel.py <-----Registers the panel in the app and defines panel properties
|
||||
|-- snapshots/
|
||||
|-- templates/
|
||||
|-- tests.py
|
||||
|-- urls.py
|
||||
|-- views.py
|
||||
...
|
||||
...
|
||||
|
||||
Dashboard Classes
|
||||
-----------------
|
||||
|
||||
Inside of ``dashboard.py`` you would have a class definition and the
|
||||
registration process
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import horizon
|
||||
|
||||
....
|
||||
# ObjectStorePanels is an example for a PanelGroup
|
||||
# for panel classes in general, see below
|
||||
class ObjectStorePanels(horizon.PanelGroup):
|
||||
slug = "object_store"
|
||||
name = _("Object Store")
|
||||
panels = ('containers',)
|
||||
|
||||
|
||||
class Project(horizon.Dashboard):
|
||||
name = _("Project") # Appears in navigation
|
||||
slug = "project" # Appears in URL
|
||||
# panels may be strings or refer to classes, such as
|
||||
# ObjectStorePanels
|
||||
panels = (BasePanels, NetworkPanels, ObjectStorePanels)
|
||||
default_panel = 'overview'
|
||||
...
|
||||
|
||||
horizon.register(Project)
|
||||
|
||||
Panel Classes
|
||||
-------------
|
||||
|
||||
To connect a :class:`~horizon.Panel` with a :class:`~horizon.Dashboard` class
|
||||
you register it in a ``panel.py`` file
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.project import dashboard
|
||||
|
||||
|
||||
class Images(horizon.Panel):
|
||||
name = "Images"
|
||||
slug = 'images'
|
||||
permissions = ('openstack.roles.admin', 'my.openstack.permission',)
|
||||
policy_rules = (('endpoint', 'endpoint:rule'),)
|
||||
|
||||
# You could also register your panel with another application's dashboard
|
||||
dashboard.Project.register(Images)
|
||||
|
||||
By default a :class:`~horizon.Panel` class looks for a ``urls.py`` file in the
|
||||
same directory as ``panel.py`` to include in the rollup of url patterns from
|
||||
panels to dashboards to Horizon, resulting in a wholly extensible, configurable
|
||||
URL structure.
|
|
@ -1,6 +0,0 @@
|
|||
==========================
|
||||
Horizon Context Processors
|
||||
==========================
|
||||
|
||||
.. automodule:: horizon.context_processors
|
||||
:members:
|
|
@ -1,6 +0,0 @@
|
|||
==================
|
||||
Horizon Decorators
|
||||
==================
|
||||
|
||||
.. automodule:: horizon.decorators
|
||||
:members:
|
|
@ -1,6 +0,0 @@
|
|||
==================
|
||||
Horizon Exceptions
|
||||
==================
|
||||
|
||||
.. automodule:: horizon.exceptions
|
||||
:members:
|
|
@ -1,99 +0,0 @@
|
|||
=============
|
||||
Horizon Forms
|
||||
=============
|
||||
|
||||
Horizon ships with some very useful base form classes, form fields,
|
||||
class-based views, and javascript helpers which streamline most of the common
|
||||
tasks related to form handling.
|
||||
|
||||
Form Classes
|
||||
============
|
||||
|
||||
.. automodule:: horizon.forms.base
|
||||
:members:
|
||||
|
||||
Form Fields
|
||||
===========
|
||||
|
||||
.. automodule:: horizon.forms.fields
|
||||
:members:
|
||||
|
||||
Form Views
|
||||
==========
|
||||
|
||||
.. automodule:: horizon.forms.views
|
||||
:members:
|
||||
|
||||
Forms Javascript
|
||||
================
|
||||
|
||||
Switchable Fields
|
||||
-----------------
|
||||
|
||||
By marking fields with the ``"switchable"`` and ``"switched"`` classes along
|
||||
with defining a few data attributes you can programmatically hide, show,
|
||||
and rename fields in a form.
|
||||
|
||||
The triggers are fields using a ``select`` input widget, marked with the
|
||||
"switchable" class, and defining a "data-slug" attribute. When they are changed,
|
||||
any input with the ``"switched"`` class and defining a ``"data-switch-on"``
|
||||
attribute which matches the ``select`` input's ``"data-slug"`` attribute will be
|
||||
evaluated for necessary changes. In simpler terms, if the ``"switched"`` target
|
||||
input's ``"switch-on"`` matches the ``"slug"`` of the ``"switchable"`` trigger
|
||||
input, it gets switched. Simple, right?
|
||||
|
||||
The ``"switched"`` inputs also need to define states. For each state in which
|
||||
the input should be shown, it should define a data attribute like the
|
||||
following: ``data-<slug>-<value>="<desired label>"``. When the switch event
|
||||
happens the value of the ``"switchable"`` field will be compared to the
|
||||
data attributes and the correct label will be applied to the field. If
|
||||
a corresponding label for that value is *not* found, the field will
|
||||
be hidden instead.
|
||||
|
||||
A simplified example is as follows::
|
||||
|
||||
source = forms.ChoiceField(
|
||||
label=_('Source'),
|
||||
choices=[
|
||||
('cidr', _('CIDR')),
|
||||
('sg', _('Security Group'))
|
||||
],
|
||||
widget=forms.ThemableSelectWidget(attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'source'
|
||||
})
|
||||
)
|
||||
|
||||
cidr = fields.IPField(
|
||||
label=_("CIDR"),
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'switched',
|
||||
'data-switch-on': 'source',
|
||||
'data-source-cidr': _('CIDR')
|
||||
})
|
||||
)
|
||||
|
||||
security_group = forms.ChoiceField(
|
||||
label=_('Security Group'),
|
||||
required=False,
|
||||
widget=forms.ThemableSelectWidget(attrs={
|
||||
'class': 'switched',
|
||||
'data-switch-on': 'source',
|
||||
'data-source-sg': _('Security Group')
|
||||
})
|
||||
)
|
||||
|
||||
That code would create the ``"switchable"`` control field ``source``, and the
|
||||
two ``"switched"`` fields ``cidr`` and ``security group`` which are hidden or
|
||||
shown depending on the value of ``source``.
|
||||
|
||||
.. note::
|
||||
|
||||
A field can only safely define one slug in its ``"switch-on"`` attribute.
|
||||
While switching on multiple fields is possible, the behavior is very hard to
|
||||
predict due to the events being fired from the various switchable fields in
|
||||
order. You generally end up just having it hidden most of the time by
|
||||
accident, so it's not recommended. Instead just add a second field to the
|
||||
form and control the two independently, then merge their results in the
|
||||
form's clean or handle methods at the end.
|
|
@ -1,48 +0,0 @@
|
|||
==================
|
||||
The horizon Module
|
||||
==================
|
||||
|
||||
.. module:: horizon
|
||||
|
||||
Horizon ships with a single point of contact for hooking into your project if
|
||||
you aren't developing your own :class:`~horizon.Dashboard` or
|
||||
:class:`~horizon.Panel`::
|
||||
|
||||
import horizon
|
||||
|
||||
From there you can access all the key methods you need.
|
||||
|
||||
Horizon
|
||||
=======
|
||||
|
||||
.. attribute:: urls
|
||||
|
||||
The auto-generated URLconf for horizon. Usage::
|
||||
|
||||
url(r'', include(horizon.urls)),
|
||||
|
||||
.. autofunction:: register
|
||||
.. autofunction:: unregister
|
||||
.. autofunction:: get_absolute_url
|
||||
.. autofunction:: get_user_home
|
||||
.. autofunction:: get_dashboard
|
||||
.. autofunction:: get_default_dashboard
|
||||
.. autofunction:: get_dashboards
|
||||
|
||||
Dashboard
|
||||
=========
|
||||
|
||||
.. autoclass:: Dashboard
|
||||
:members:
|
||||
|
||||
Panel
|
||||
=====
|
||||
|
||||
.. autoclass:: Panel
|
||||
:members:
|
||||
|
||||
Panel Group
|
||||
===========
|
||||
|
||||
.. autoclass:: PanelGroup
|
||||
:members:
|
|
@ -1,21 +0,0 @@
|
|||
=============
|
||||
API Reference
|
||||
=============
|
||||
|
||||
In-depth documentation for Horizon framework and its APIs.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
run_tests
|
||||
horizon
|
||||
workflows
|
||||
tables
|
||||
tabs
|
||||
forms
|
||||
middleware
|
||||
context_processors
|
||||
decorators
|
||||
exceptions
|
||||
test
|
||||
local_conf
|
|
@ -1,81 +0,0 @@
|
|||
.. _local-conf:
|
||||
|
||||
==========
|
||||
local.conf
|
||||
==========
|
||||
|
||||
Configuring DevStack for Horizon
|
||||
================================
|
||||
|
||||
Place the following content into ``devstack/local.conf`` to start the services
|
||||
that Horizon supports in DevStack when ``stack.sh`` is run. If you need to use
|
||||
this with a stable branch you need to add ``stable/<branch name>`` to the end
|
||||
of each ``enable_plugin`` line (e.g. ``stable/mitaka``). You can also check
|
||||
out DevStack using a stable branch tag. For more information on DevStack,
|
||||
see https://docs.openstack.org/developer/devstack/
|
||||
|
||||
::
|
||||
|
||||
[[local|localrc]]
|
||||
|
||||
ADMIN_PASSWORD=secret
|
||||
DATABASE_PASSWORD=$ADMIN_PASSWORD
|
||||
RABBIT_PASSWORD=$ADMIN_PASSWORD
|
||||
SERVICE_PASSWORD=$ADMIN_PASSWORD
|
||||
|
||||
# Recloning will ensure that your stack is up to date. The downside
|
||||
# is overhead on restarts and potentially losing a stable environment.
|
||||
# If set to `yes`, will reclone all repos every time stack.sh is run.
|
||||
# The default is `no`.
|
||||
#
|
||||
# RECLONE=yes
|
||||
|
||||
# By default `stack.sh` will only install Python packages if no version is
|
||||
# currently installed, or the current version does not match a specified
|
||||
# requirement. If `PIP_UPGRADE` is set to `True` then existing required
|
||||
# Python packages will be upgraded to the most recent version that matches
|
||||
# requirements. This is generally recommended, as most of OpenStack is
|
||||
# tested on latest packages, rather than older versions. The default is
|
||||
# False.
|
||||
#
|
||||
# PIP_UPGRADE=TRUE
|
||||
|
||||
# Set `OFFLINE` to `True` to configure `stack.sh` to run cleanly without
|
||||
# Internet access. `stack.sh` must have been previously run with Internet
|
||||
# access to install prerequisites and fetch repositories.
|
||||
#
|
||||
# OFFLINE=True
|
||||
|
||||
# Note: there are several network setting changes that may be
|
||||
# required to get networking properly configured in your environment.
|
||||
# This file is just using the defaults set up by devstack.
|
||||
# For a more detailed treatment of devstack network configuration
|
||||
# options, please see: http://devstack.org/guides/single-machine.html
|
||||
|
||||
# Horizon is enabled by default in Devstack, but since we're developing
|
||||
# it's advised to use a separate clone. To disable horizon in DevStack,
|
||||
# speeding up stack time, use:
|
||||
#
|
||||
# disable_service horizon
|
||||
|
||||
### Supported Services
|
||||
# The following panels and plugins are part of the Horizon tree
|
||||
# and currently supported by the Horizon maintainers
|
||||
|
||||
# Enable Swift (Object Store) without replication
|
||||
enable_service s-proxy s-object s-container s-account
|
||||
SWIFT_HASH=66a3d6b56c1f479c8b4e70ab5c2000f5
|
||||
SWIFT_REPLICAS=1
|
||||
SWIFT_DATA_DIR=$DEST/data/swift
|
||||
|
||||
# Enable Heat
|
||||
enable_plugin heat https://git.openstack.org/openstack/heat
|
||||
|
||||
### Plugins
|
||||
# Horizon has a large number of plugins, documented at
|
||||
# http://docs.openstack.org/developer/horizon/plugin_registry.html
|
||||
# See the individual repos for information on installing them.
|
||||
|
||||
[[post-config|$GLANCE_API_CONF]]
|
||||
[DEFAULT]
|
||||
default_store=file
|
|
@ -1,15 +0,0 @@
|
|||
==================
|
||||
Horizon Middleware
|
||||
==================
|
||||
|
||||
HorizonMiddleware
|
||||
-----------------
|
||||
|
||||
.. autoclass:: horizon.middleware.HorizonMiddleware
|
||||
:members:
|
||||
|
||||
OperationLogMiddleware
|
||||
----------------------
|
||||
|
||||
.. autoclass:: horizon.middleware.OperationLogMiddleware
|
||||
:members:
|
|
@ -1,309 +0,0 @@
|
|||
=======================
|
||||
The run_tests.sh Script
|
||||
=======================
|
||||
|
||||
.. warning::
|
||||
|
||||
This script is deprecated as of Newton (11.0), and will be removed in
|
||||
Queens (13.0), in favor of tox. The tox docs can be found at
|
||||
https://tox.readthedocs.io/en/latest/
|
||||
|
||||
Horizon ships with a script called ``run_tests.sh`` at the root of the
|
||||
repository. This script provides many crucial functions for the project,
|
||||
and also makes several otherwise complex tasks trivial for you as a
|
||||
developer.
|
||||
|
||||
First Run
|
||||
=========
|
||||
|
||||
If you start with a clean copy of the Horizon repository, the first thing
|
||||
you should do is to run ``./run_tests.sh`` from the root of the repository.
|
||||
This will do two things for you:
|
||||
|
||||
#. Set up a virtual environment for both the ``horizon`` module and
|
||||
the ``openstack_dashboard`` project using ``./tools/install_venv.py``.
|
||||
#. Run the tests for both ``horizon`` and ``openstack_dashboard`` using
|
||||
their respective environments and verify that everything is working.
|
||||
|
||||
Setting up the environment the first time can take several minutes, but only
|
||||
needs to be done once. If dependencies are added in the future, updating the
|
||||
environments will be necessary but not as time consuming.
|
||||
|
||||
I just want to run the tests!
|
||||
=============================
|
||||
|
||||
Running the full set of unit tests quickly and easily is the main goal of this
|
||||
script. All you need to do is::
|
||||
|
||||
./run_tests.sh
|
||||
|
||||
Yep, that's it. However, for a more thorough test run you can include the
|
||||
Selenium tests by using the ``--with-selenium`` flag::
|
||||
|
||||
./run_tests.sh --with-selenium
|
||||
|
||||
If you run horizon in a minimal installation VM, you will probably need
|
||||
the following (steps for Fedora 18 minimal installation):
|
||||
|
||||
#. Install these packages in the VM:
|
||||
``yum install xorg-x11-xauth xorg-x11-fonts-Type1.noarch``.
|
||||
#. Install firefox in the VM:
|
||||
``yum install firefox``.
|
||||
#. Connect to the VM by ``ssh -X``
|
||||
(if you run ``set|grep DISP``, you should see that the DISPLAY is set).
|
||||
#. Run ``./run_tests.sh --with-selenium``.
|
||||
|
||||
Running a subset of tests
|
||||
-------------------------
|
||||
|
||||
Instead of running all tests, you can specify an individual directory, file,
|
||||
class, or method that contains test code.
|
||||
|
||||
To run the tests in the ``horizon/test/tests/tables.py`` file::
|
||||
|
||||
./run_tests.sh horizon.test.tests.tables
|
||||
|
||||
To run the tests in the `WorkflowsTests` class in
|
||||
``horizon/test/tests/workflows``::
|
||||
|
||||
./run_tests.sh horizon.test.tests.workflows:WorkflowsTests
|
||||
|
||||
To run just the `WorkflowsTests.test_workflow_view` test method::
|
||||
|
||||
./run_tests.sh horizon.test.tests.workflows:WorkflowsTests.test_workflow_view
|
||||
|
||||
Running the integration tests
|
||||
-----------------------------
|
||||
|
||||
The Horizon integration tests treat Horizon as a black box, and similar
|
||||
to Tempest must be run against an existing OpenStack system. These
|
||||
tests are not run by default.
|
||||
|
||||
#. Update the configuration file
|
||||
`openstack_dashboard/test/integration_tests/horizon.conf` as
|
||||
required (the format is similar to the Tempest configuration file).
|
||||
|
||||
#. Run the tests with the following command: ::
|
||||
|
||||
$ ./run_tests.sh --integration
|
||||
|
||||
Like for the unit tests, you can choose to only run a subset. ::
|
||||
|
||||
$ ./run_tests.sh --integration openstack_dashboard.test.integration_tests.tests.test_login
|
||||
|
||||
|
||||
Using Dashboard and Panel Templates
|
||||
===================================
|
||||
|
||||
Horizon has a set of convenient management commands for creating new
|
||||
dashboards and panels based on basic templates.
|
||||
|
||||
Dashboards
|
||||
----------
|
||||
|
||||
To create a new dashboard, run the following::
|
||||
|
||||
./run_tests.sh -m startdash <dash_name>
|
||||
|
||||
This will create a directory with the given dashboard name, a ``dashboard.py``
|
||||
module with the basic dashboard code filled in, and various other common
|
||||
"boilerplate" code.
|
||||
|
||||
Available options:
|
||||
|
||||
* ``--target``: the directory in which the dashboard files should be created.
|
||||
Default: A new directory within the current directory.
|
||||
|
||||
Panels
|
||||
------
|
||||
|
||||
To create a new panel, run the following::
|
||||
|
||||
./run_tests -m startpanel <panel_name>
|
||||
|
||||
This will create a directory with the given panel name, and ``panel.py``
|
||||
module with the basic panel code filled in, and various other common
|
||||
"boilerplate" code.
|
||||
|
||||
Available options:
|
||||
|
||||
* ``-d``, ``--dashboard``: The dotted python path to your dashboard app (the
|
||||
module which contains the ``dashboard.py`` file.). If not specified, the
|
||||
target dashboard should be specified in a pluggable settings file for the
|
||||
panel.
|
||||
* ``--target``: the directory in which the panel files should be created.
|
||||
If the value is ``auto`` the panel will be created as a new directory inside
|
||||
the dashboard module's directory structure. Default: A new directory within
|
||||
the current directory.
|
||||
|
||||
JavaScript Tests
|
||||
----------------
|
||||
|
||||
You can also run JavaScript unit tests using Karma. Karma is a test
|
||||
environment that allows for multiple test runners and reporters, including
|
||||
such features as code coverage. Karma allows developer to run tests live,
|
||||
as it can watch source and test files for changes.
|
||||
|
||||
The default configuration also performs coverage reports, which are saved
|
||||
to ``cover/horizon/`` and ``cover/openstack_dashboard/``.
|
||||
|
||||
To run the Karma tests for Horizon and Dashboard using the `run_tests.sh`
|
||||
script::
|
||||
|
||||
./run_tests.sh --karma
|
||||
|
||||
To run the Karma tests for Horizon and Dashboard using `npm`::
|
||||
|
||||
npm install # You only need to execute this once.
|
||||
npm test
|
||||
|
||||
.. note:: These two methods are equivalent. The former merely executes
|
||||
the latter.
|
||||
|
||||
|
||||
JavaScript Code Style Checks
|
||||
----------------------------
|
||||
|
||||
You can run the JavaScript code style checks, or linting, using eslint.
|
||||
ESLint is a permissively licensed, sophisticated language parser and
|
||||
linter that confirms both our style guidelines, and checks the code for
|
||||
common errors that may create unexpected behavior.
|
||||
|
||||
To run eslint for Horizon and Dashboard using the `run_tests.sh`
|
||||
script::
|
||||
|
||||
./run_tests.sh --eslint
|
||||
|
||||
To run eslint for Horizon and Dashboard using `npm`::
|
||||
|
||||
npm install # You only need to execute this once.
|
||||
npm run lint
|
||||
|
||||
.. note:: These two methods are equivalent. The former merely executes
|
||||
the latter.
|
||||
|
||||
Give me metrics!
|
||||
================
|
||||
|
||||
You can generate various reports and metrics using command line arguments
|
||||
to ``run_tests.sh``.
|
||||
|
||||
ESLint
|
||||
------
|
||||
|
||||
To run ESLint, a JavaScript code style checker::
|
||||
|
||||
./run_tests.sh --eslint
|
||||
|
||||
Coverage
|
||||
--------
|
||||
|
||||
To run coverage reports::
|
||||
|
||||
./run_tests.sh --coverage
|
||||
|
||||
The reports are saved to ``./reports/`` and ``./coverage.xml``.
|
||||
|
||||
PEP8
|
||||
----
|
||||
|
||||
You can check for PEP8 violations as well::
|
||||
|
||||
./run_tests.sh --pep8
|
||||
|
||||
The results are saved to ``./pep8.txt``.
|
||||
|
||||
PyLint
|
||||
------
|
||||
|
||||
For more detailed code analysis you can run::
|
||||
|
||||
./run_tests.sh --pylint
|
||||
|
||||
The output will be saved in ``./pylint.txt``.
|
||||
|
||||
Tab Characters
|
||||
--------------
|
||||
|
||||
For those who dislike having a mix of tab characters and spaces for indentation
|
||||
there's a command to check for that in Python, CSS, JavaScript and HTML files::
|
||||
|
||||
./run_tests.sh --tabs
|
||||
|
||||
This will output a total "tab count" and a list of the offending files.
|
||||
|
||||
Running the development server
|
||||
==============================
|
||||
|
||||
As an added bonus, you can run Django's development server directly from
|
||||
the root of the repository with ``run_tests.sh`` like so::
|
||||
|
||||
./run_tests.sh --runserver
|
||||
|
||||
This is effectively just an alias for::
|
||||
|
||||
./tools/with_venv.sh ./manage.py runserver
|
||||
|
||||
Generating the documentation
|
||||
============================
|
||||
|
||||
You can build Horizon's documentation automatically by running::
|
||||
|
||||
./run_tests.sh --docs
|
||||
|
||||
The output is stored in ``./doc/build/html/``.
|
||||
|
||||
Updating the translation files
|
||||
==============================
|
||||
|
||||
You can update all of the translation files for both the ``horizon`` app and
|
||||
``openstack_dashboard`` project with a single command::
|
||||
|
||||
./run_tests.sh --makemessages
|
||||
|
||||
or, more compactly::
|
||||
|
||||
./run_tests.sh --m
|
||||
|
||||
Starting clean
|
||||
==============
|
||||
|
||||
If you ever want to start clean with a new environment for Horizon, you can
|
||||
run::
|
||||
|
||||
./run_tests.sh --force
|
||||
|
||||
That will blow away the existing environments and create new ones for you.
|
||||
|
||||
Non-interactive Mode
|
||||
====================
|
||||
|
||||
There is an optional flag which will run the script in a non-interactive
|
||||
(and eventually less verbose) mode::
|
||||
|
||||
./run_tests.sh --quiet
|
||||
|
||||
This will automatically take the default action for actions which would
|
||||
normally prompt for user input such as installing/updating the environment.
|
||||
|
||||
Environment Backups
|
||||
===================
|
||||
|
||||
To speed up the process of doing clean checkouts, running continuous
|
||||
integration tests, etc. there are options for backing up the current
|
||||
environment and restoring from a backup::
|
||||
|
||||
./run_tests.sh --restore-environment
|
||||
./run_tests.sh --backup-environment
|
||||
|
||||
The environment backup is stored in ``/tmp/.horizon_environment/``.
|
||||
|
||||
Environment Versioning
|
||||
======================
|
||||
|
||||
Horizon keeps track of changes to the environment by comparing the
|
||||
current requirements files (``requirements.txt`` and
|
||||
``test-requirements.txt``) and the files last time the virtual
|
||||
environment was created or updated. If there is any difference,
|
||||
the virtual environment will be update automatically when you run
|
||||
``run_tests.sh``.
|
|
@ -1,107 +0,0 @@
|
|||
.. _ref-datatables:
|
||||
|
||||
==================
|
||||
Horizon DataTables
|
||||
==================
|
||||
|
||||
.. module:: horizon.tables
|
||||
|
||||
Horizon includes a componentized API for programmatically creating tables
|
||||
in the UI. Why would you want this? It means that every table renders
|
||||
correctly and consistently, table-level and row-level actions all have a
|
||||
consistent API and appearance, and generally you don't have to reinvent the
|
||||
wheel or copy-and-paste every time you need a new table!
|
||||
|
||||
.. seealso::
|
||||
|
||||
For usage information, tips & tricks and more examples check out
|
||||
the :ref:`topics-datatables`.
|
||||
|
||||
DataTable
|
||||
=========
|
||||
|
||||
The core class which defines the high-level structure of the table being
|
||||
represented. Example::
|
||||
|
||||
class MyTable(DataTable):
|
||||
name = Column('name')
|
||||
email = Column('email')
|
||||
|
||||
class Meta(object):
|
||||
name = "my_table"
|
||||
table_actions = (MyAction, MyOtherAction)
|
||||
row_actions = (MyAction)
|
||||
|
||||
A full reference is included below:
|
||||
|
||||
.. autoclass:: DataTable
|
||||
:members:
|
||||
|
||||
DataTable Options
|
||||
=================
|
||||
|
||||
The following options can be defined in a ``Meta`` class inside a
|
||||
:class:`.DataTable` class. Example::
|
||||
|
||||
class MyTable(DataTable):
|
||||
class Meta(object):
|
||||
name = "my_table"
|
||||
verbose_name = "My Table"
|
||||
|
||||
.. autoclass:: horizon.tables.base.DataTableOptions
|
||||
:members:
|
||||
|
||||
FormsetDataTable
|
||||
================
|
||||
|
||||
You can integrate the :class:`.DataTable` with a Django Formset using one of
|
||||
following classes:
|
||||
|
||||
.. autoclass:: horizon.tables.formset.FormsetDataTableMixin
|
||||
:members:
|
||||
|
||||
.. autoclass:: horizon.tables.formset.FormsetDataTable
|
||||
:members:
|
||||
|
||||
Table Components
|
||||
================
|
||||
|
||||
.. autoclass:: Column
|
||||
:members:
|
||||
|
||||
.. autoclass:: Row
|
||||
:members:
|
||||
|
||||
Actions
|
||||
=======
|
||||
|
||||
.. autoclass:: Action
|
||||
:members:
|
||||
|
||||
.. autoclass:: LinkAction
|
||||
:members:
|
||||
|
||||
.. autoclass:: FilterAction
|
||||
:members:
|
||||
|
||||
.. autoclass:: FixedFilterAction
|
||||
:members:
|
||||
|
||||
.. autoclass:: BatchAction
|
||||
:members:
|
||||
|
||||
.. autoclass:: DeleteAction
|
||||
:members:
|
||||
|
||||
.. autoclass:: UpdateAction
|
||||
:members:
|
||||
|
||||
Class-Based Views
|
||||
=================
|
||||
|
||||
Several class-based views are provided to make working with DataTables
|
||||
easier in your UI.
|
||||
|
||||
.. autoclass:: DataTableView
|
||||
|
||||
.. autoclass:: MultiTableView
|
|
@ -1,45 +0,0 @@
|
|||
==========================
|
||||
Horizon Tabs and TabGroups
|
||||
==========================
|
||||
|
||||
.. module:: horizon.tabs
|
||||
|
||||
Horizon includes a set of reusable components for programmatically
|
||||
building tabbed interfaces with fancy features like dynamic AJAX loading
|
||||
and nearly effortless templating and styling.
|
||||
|
||||
Tab Groups
|
||||
==========
|
||||
|
||||
For any tabbed interface, your fundamental element is the tab group which
|
||||
contains all your tabs. This class provides a dead-simple API for building
|
||||
tab groups and encapsulates all the necessary logic behind the scenes.
|
||||
|
||||
.. autoclass:: TabGroup
|
||||
:members:
|
||||
|
||||
Tabs
|
||||
====
|
||||
|
||||
The tab itself is the discrete unit for a tab group, representing one
|
||||
view of data.
|
||||
|
||||
.. autoclass:: Tab
|
||||
:members:
|
||||
|
||||
.. autoclass:: TableTab
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
TabView
|
||||
=======
|
||||
|
||||
There is also a useful and simple generic class-based view for handling
|
||||
the display of a :class:`~horizon.tabs.TabGroup` class.
|
||||
|
||||
.. autoclass:: TabView
|
||||
:members:
|
||||
|
||||
.. autoclass:: TabbedTableView
|
||||
:members:
|
|
@ -1,25 +0,0 @@
|
|||
========================
|
||||
Horizon TestCase Classes
|
||||
========================
|
||||
|
||||
.. module:: horizon.test.helpers
|
||||
|
||||
Horizon provides a base test case class which provides several useful
|
||||
pre-prepared attributes for testing Horizon components.
|
||||
|
||||
.. autoclass:: TestCase
|
||||
:members:
|
||||
|
||||
.. module :: openstack_dashboard.test.helpers
|
||||
|
||||
The OpenStack Dashboard also provides test case classes for greater
|
||||
ease-of-use when testing APIs and OpenStack-specific auth scenarios.
|
||||
|
||||
.. autoclass:: TestCase
|
||||
:members:
|
||||
|
||||
.. autoclass:: APITestCase
|
||||
:members:
|
||||
|
||||
.. autoclass:: BaseAdminViewTests
|
||||
:members:
|
|
@ -1,40 +0,0 @@
|
|||
.. _ref-workflows:
|
||||
|
||||
=================
|
||||
Horizon Workflows
|
||||
=================
|
||||
|
||||
.. module:: horizon.workflows
|
||||
|
||||
One of the most challenging aspects of building a compelling user experience
|
||||
is crafting complex multi-part workflows. Horizon's ``workflows`` module
|
||||
aims to bring that capability within everyday reach.
|
||||
|
||||
.. seealso::
|
||||
|
||||
For usage information, tips & tricks and more examples check out the
|
||||
:ref:`topics-workflows`.
|
||||
|
||||
Workflows
|
||||
=========
|
||||
|
||||
.. autoclass:: Workflow
|
||||
:members:
|
||||
|
||||
Steps
|
||||
=====
|
||||
|
||||
.. autoclass:: Step
|
||||
:members:
|
||||
|
||||
Actions
|
||||
=======
|
||||
|
||||
.. autoclass:: Action
|
||||
:members:
|
||||
|
||||
WorkflowView
|
||||
============
|
||||
|
||||
.. autoclass:: WorkflowView
|
||||
:members:
|
|
@ -1,173 +0,0 @@
|
|||
=======================
|
||||
Horizon's tests and you
|
||||
=======================
|
||||
|
||||
How to run the tests
|
||||
====================
|
||||
|
||||
Because Horizon is composed of both the ``horizon`` app and the
|
||||
``openstack_dashboard`` reference project, there are in fact two sets of unit
|
||||
tests. While they can be run individually without problem, there is an easier
|
||||
way:
|
||||
|
||||
Included at the root of the repository is the ``tox.ini`` config
|
||||
which invokes both sets of tests, and optionally generates analyses on both
|
||||
components in the process. ``tox`` is what Jenkins uses to verify the
|
||||
stability of the project, so you should make sure you run it and it passes
|
||||
before you submit any pull requests/patches.
|
||||
|
||||
To run all tests::
|
||||
|
||||
$ tox
|
||||
|
||||
It's also possible to run a subset of the tests. Open ``tox.ini`` in the
|
||||
Horizon root directory to see a list of test environments. You can read more
|
||||
about tox in general at https://tox.readthedocs.io/en/latest/.
|
||||
|
||||
By default running the Selenium tests will open your Firefox browser (you have
|
||||
to install it first, else an error is raised), and you will be able to see the
|
||||
tests actions::
|
||||
|
||||
$ tox -e selenium
|
||||
|
||||
If you want to run the suite headless, without being able to see them (as they
|
||||
are ran on Jenkins), you can run the tests::
|
||||
|
||||
$ tox -e selenium-headless
|
||||
|
||||
Selenium will use a virtual display in this case, instead of your own. In order
|
||||
to run the tests this way you have to install the dependency `xvfb`, like
|
||||
this::
|
||||
|
||||
$ sudo apt-get install xvfb
|
||||
|
||||
for a Debian OS flavour, or for Fedora/Red Hat flavours::
|
||||
|
||||
$ sudo yum install xorg-x11-server-Xvfb
|
||||
|
||||
If you can't run a virtual display, or would prefer not to, you can use the
|
||||
PhantomJS web driver instead::
|
||||
|
||||
$ tox -e selenium-phantomjs
|
||||
|
||||
If you need to install PhantomJS, you may do so with `npm` like this::
|
||||
|
||||
$ npm -g install phantomjs
|
||||
|
||||
Alternatively, many distributions have system packages for PhantomJS, or
|
||||
it can be downloaded from http://phantomjs.org/download.html.
|
||||
|
||||
tox Test Environments
|
||||
=====================
|
||||
|
||||
This is a list of test environments available to be executed by
|
||||
``tox -e <name>``.
|
||||
|
||||
pep8
|
||||
----
|
||||
|
||||
Runs pep8, which is a tool that checks Python code style. You can read more
|
||||
about pep8 at https://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
py27
|
||||
----
|
||||
|
||||
Runs the Python unit tests against the current default version of Django
|
||||
with Python 2.7 environment. Check ``requirements.txt`` in horizon
|
||||
repository to know which version of Django is actually used.
|
||||
|
||||
All other dependencies are as defined by the upper-constraints file at
|
||||
https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt
|
||||
|
||||
You can run a subset of the tests by passing the test path as an argument to
|
||||
tox::
|
||||
|
||||
$ tox -e py27 -- openstack_dashboard.dashboards.identity.users.tests
|
||||
|
||||
The following is more example to run a specific test class and a
|
||||
specific test:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ tox -e py27 -- openstack_dashboard.dashboards.identity.users.tests:UsersViewTests
|
||||
$ tox -e py27 -- openstack_dashboard.dashboards.identity.users.tests:UsersViewTests.test_index
|
||||
|
||||
You can also pass other arguments. For example, to drop into a live debugger
|
||||
when a test fails you can use::
|
||||
|
||||
$ tox -e py27 -- --pdb
|
||||
|
||||
py27dj18, py27dj19, py27dj110
|
||||
-----------------------------
|
||||
|
||||
Runs the Python unit tests against Django 1.8, Django 1.9 and Django 1.10
|
||||
respectively
|
||||
|
||||
|
||||
py35
|
||||
----
|
||||
|
||||
Runs the Python unit tests with a Python 3.5 environment.
|
||||
|
||||
releasenotes
|
||||
------------
|
||||
|
||||
Outputs Horizons release notes as HTML to ``releasenotes/build/html``.
|
||||
|
||||
Also takes an alternative builder as an optional argument, such as
|
||||
``tox -e docs -- <builder>``, which will output to
|
||||
``releasenotes/build/<builder>``. Available builders are listed at
|
||||
http://www.sphinx-doc.org/en/latest/builders.html
|
||||
|
||||
This environment also runs the documentation style checker ``doc8`` against
|
||||
RST and YAML files under ``releasenotes/source`` to keep the documentation
|
||||
style consistent. If you would like to run ``doc8`` manually, see **docs**
|
||||
environment below.
|
||||
|
||||
npm
|
||||
---
|
||||
|
||||
Installs the npm dependencies listed in ``package.json`` and runs the
|
||||
JavaScript tests. Can also take optional arguments, which will be executed
|
||||
as an npm script following the dependency install, instead of ``test``.
|
||||
|
||||
Example::
|
||||
|
||||
$ tox -e npm -- lintq
|
||||
|
||||
docs
|
||||
----
|
||||
|
||||
Outputs Horizons documentation as HTML to ``doc/build/html``.
|
||||
|
||||
Also takes an alternative builder as an optional argument, such as
|
||||
``tox -e docs -- <builder>``, which will output to ``doc/build/<builder>``.
|
||||
Available builders are listed at
|
||||
http://www.sphinx-doc.org/en/latest/builders.html
|
||||
|
||||
Example::
|
||||
|
||||
$ tox -e docs -- latexpdf
|
||||
|
||||
This environment also runs the documentation style checker ``doc8`` against
|
||||
RST files under ``doc/source`` to keep the documentation style consistent.
|
||||
If you would like to run ``doc8`` manually, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# Activate virtualenv
|
||||
$ . .tox/docs/bin/activate
|
||||
$ doc8 doc/source
|
||||
|
||||
Writing tests
|
||||
=============
|
||||
|
||||
Horizon uses Django's unit test machinery (which extends Python's ``unittest2``
|
||||
library) as the core of its test suite. As such, all tests for the Python code
|
||||
should be written as unit tests. No doctests please.
|
||||
|
||||
In general new code without unit tests will not be accepted, and every bugfix
|
||||
*must* include a regression test.
|
||||
|
||||
For a much more in-depth discussion of testing, see the :ref:`testing topic
|
||||
guide <topics-testing>`.
|
|
@ -1,361 +0,0 @@
|
|||
.. _topics-angularjs:
|
||||
|
||||
=====================
|
||||
AngularJS Topic Guide
|
||||
=====================
|
||||
|
||||
.. Note::
|
||||
This guide is a work in progress. It has been uploaded to encourage faster
|
||||
reviewing and code development in Angular, and to help the community
|
||||
standardize on a set of guidelines. There are notes inline on sections
|
||||
that are likely to change soon, and the docs will be updated promptly
|
||||
after any changes.
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
The tooling for AngularJS testing and code linting relies on npm, the
|
||||
node package manager, and thus relies on Node.js. While it is not a
|
||||
prerequisite to developing with Horizon, it is advisable to install Node.js,
|
||||
either through `downloading <https://nodejs.org/download/>`_ or
|
||||
`via a package manager <https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager>`_.
|
||||
|
||||
Once you have npm available on your system, run ``npm install`` from the
|
||||
horizon root directory.
|
||||
|
||||
.. _js_code_style:
|
||||
|
||||
Code Style
|
||||
==========
|
||||
|
||||
We currently use the `Angular Style Guide`_ by John Papa as reference material.
|
||||
When reviewing AngularJS code, it is helpful to link directly to the style
|
||||
guide to reinforce a point, e.g.
|
||||
https://github.com/johnpapa/angular-styleguide#style-y024
|
||||
|
||||
.. _Angular Style Guide: https://github.com/johnpapa/angular-styleguide
|
||||
|
||||
ESLint
|
||||
------
|
||||
|
||||
ESLint is a tool for identifying and reporting on patterns in your JS code, and
|
||||
is part of the automated tests run by Jenkins. You can run ESLint from the
|
||||
horizon root directory with ``tox -e npm -- lint``, or alternatively on a
|
||||
specific directory or file with ``eslint file.js``.
|
||||
|
||||
Horizon includes a `.eslintrc` in its root directory, that is used by the
|
||||
local tests. An explanation of the options, and details of others you may want
|
||||
to use, can be found in the
|
||||
`ESLint user guide <http://eslint.org/docs/user-guide/configuring>`_.
|
||||
|
||||
Application Structure
|
||||
=====================
|
||||
|
||||
OpenStack Dashboard is an example of a Horizon-based Angular application. Other
|
||||
applications built on the Horizon framework can follow a similar structure. It
|
||||
is composed of two key Angular modules:
|
||||
|
||||
**app.module.js** - The root of the application. Defines the modules required by
|
||||
the application, and includes modules from its pluggable dashboards.
|
||||
|
||||
**framework.module.js** - Reusable Horizon components. It is one of the
|
||||
application dependencies.
|
||||
|
||||
.. _js_file_structure:
|
||||
|
||||
File Structure
|
||||
==============
|
||||
|
||||
Horizon has three kinds of angular code:
|
||||
|
||||
1. Specific to one dashboard in the OpenStack Dashboard application
|
||||
2. Specific to the OpenStack Dashboard application, but reusable by multiple
|
||||
dashboards
|
||||
3. Reusable by any application based on the Horizon framework
|
||||
|
||||
When adding code to horizon, consider whether it is dashboard-specific or
|
||||
should be broken out as a reusable utility or widget.
|
||||
|
||||
Code specific to one dashboard
|
||||
------------------------------
|
||||
|
||||
Code that isn't shared beyond a single dashboard is placed in
|
||||
``openstack_dashboard/dashboards/mydashboard/static``. Entire dashboards may be
|
||||
enabled or disabled using Horizon's plugin mechanism. Therefore no dashboards
|
||||
other than ``mydashboard`` can safely use this code.
|
||||
|
||||
The ``openstack_dashboard/dashboards/static`` directory structure determines
|
||||
how the code is deployed and matches the module structure.
|
||||
For example:
|
||||
::
|
||||
|
||||
openstack_dashboard/dashboards/identity/static/dashboard/identity/
|
||||
├── identity.module.js
|
||||
├── identity.module.spec.js
|
||||
└── identity.scss
|
||||
|
||||
Because the code is in ``openstack_dashboard/dashboards/identity`` we know it
|
||||
is specific to just the ``identity`` dashboard and not used by any others.
|
||||
|
||||
Code shared by multiple dashboards
|
||||
----------------------------------
|
||||
|
||||
Views or utilities needed by multiple dashboards are placed in
|
||||
``openstack_dashboard/static/app``. For example:
|
||||
::
|
||||
|
||||
openstack_dashboard/static/app/core/cloud-services/
|
||||
├── cloud-services.module.js
|
||||
├── cloud-services.spec.js
|
||||
├── hz-if-settings.directive.js
|
||||
└── hz-if-settings.directive.spec.js
|
||||
|
||||
The ``cloud-services`` module is used by panels in multiple dashboards. It
|
||||
cannot be placed within ``openstack_dashboard/dashboards/mydashboard`` because
|
||||
disabling that one dashboard would break others. Therefore, it is included as
|
||||
part of the application ``core`` module. Code in ``app/`` is guaranteed to
|
||||
always be present, even if all other dashboards are disabled.
|
||||
|
||||
Reusable components
|
||||
-------------------
|
||||
|
||||
Finally, components that are easily reused by any application are placed in
|
||||
``horizon/static/framework/``. These do not contain URLs or business logic
|
||||
that is specific to any application (even the OpenStack Dashboard application).
|
||||
|
||||
The modal directive ``horizon/static/framework/widgets/modal/`` is a good
|
||||
example of a reusable component.
|
||||
|
||||
One folder per component
|
||||
------------------------
|
||||
|
||||
Each component should have its own folder, with the code broken up into one JS
|
||||
component per file. (See `Single Responsibility <https://github.com/johnpapa/angular-styleguide#single-responsibility>`_
|
||||
in the style guide).
|
||||
Each folder may include styling (``*.scss``), as well as templates (``*.html``)
|
||||
and tests (``*.spec.js``).
|
||||
You may also include examples, by appending ``.example``.
|
||||
|
||||
For larger components, such as workflows with multiple steps, consider breaking
|
||||
the code down further. For example, the Launch Instance workflow, has one
|
||||
directory per step. See
|
||||
``openstack_dashboard/dashboards/project/static/dashboard/project/workflow/launch-instance/``
|
||||
|
||||
SCSS files
|
||||
----------
|
||||
|
||||
The top-level SCSS file in ``openstack_dashboard/static/app/_app.scss``. It
|
||||
includes any styling that is part of the application ``core`` and may be
|
||||
reused by multiple dashboards. SCSS files that are specific to a particular
|
||||
dashboard are linked to the application by adding them in that dashboard's
|
||||
enabled file. For example, `_1920_project_containers_panel.py` is the enabled
|
||||
file for the ``Project`` dashboard's ``Container`` panel and includes:
|
||||
::
|
||||
|
||||
ADD_SCSS_FILES = [
|
||||
'dashboard/project/containers/_containers.scss',
|
||||
]
|
||||
|
||||
Styling files are hierarchical, and include any direct child SCSS files. For
|
||||
example, ``project.scss`` would includes the ``workflow`` SCSS file, which in
|
||||
turn includes any launch instance styling:
|
||||
::
|
||||
|
||||
@import "workflow/workflow";
|
||||
|
||||
This allows the application to easily include all needed styling, simply by
|
||||
including a dashboard's top-level SCSS file.
|
||||
|
||||
Module Structure
|
||||
================
|
||||
|
||||
Horizon Angular modules use names that map to the source code directory
|
||||
structure. This provides namespace isolation for modules and services, which
|
||||
makes dependency injection clearer. It also reduces code conflicts where two
|
||||
different modules define a module, service or constant of the same name. For
|
||||
example:
|
||||
::
|
||||
|
||||
openstack_dashboard/dashboards/identity/static/dashboard/identity/
|
||||
└── identity.module.js
|
||||
|
||||
The preferred Angular module name in this example is
|
||||
``horizon.dashboard.identity``. The ``horizon`` part of the module name maps to
|
||||
the ``static`` directory and indicates this is a ``horizon`` based application.
|
||||
``dashboard.identity`` maps to folders that are created within ``static``. This
|
||||
allows a direct mapping between the angular module name of
|
||||
``horizon.dashboard.identity`` and the source code directory of
|
||||
``static\dashboard\identity``.
|
||||
|
||||
Services and constants within these modules should all start with their module
|
||||
name to avoid dependency injection collisions. For example:
|
||||
::
|
||||
|
||||
$provide.constant('horizon.dashboard.identity.basePath', path);
|
||||
|
||||
Directives do not require the module name but are encouraged to begin with the
|
||||
``hz`` prefix. For example:
|
||||
::
|
||||
|
||||
.directive('hzMagicSearchBar', hzMagicSearchBar);
|
||||
|
||||
Finally, each module lists its child modules as a dependency. This allows the
|
||||
root module to be included by an application, which will automatically define
|
||||
all child modules. For example:
|
||||
::
|
||||
|
||||
.module('horizon.framework', [
|
||||
'horizon.framework.conf',
|
||||
'horizon.framework.util',
|
||||
'horizon.framework.widgets'
|
||||
])
|
||||
|
||||
``horizon.framework`` declares a dependency on ``horizon.framework.widgets``,
|
||||
which declares dependencies on each individual widget. This allows the
|
||||
application to access any widget, simply by depending on the top-level
|
||||
``horizon.framework`` module.
|
||||
|
||||
Testing
|
||||
=======
|
||||
|
||||
1. Open <dev_server_ip:port>/jasmine in a browser. The development server can
|
||||
be run with ``tox -e runserver`` from the horizon root directory; by
|
||||
default, this will run the development server at ``http://localhost:8000``.
|
||||
2. ``tox -e npm`` from the horizon root directory.
|
||||
|
||||
The code linting job can be run with ``tox -e npm -- lint``. If there are many
|
||||
warnings, you can also use ``tox -e npm -- lintq`` to see only errors and
|
||||
ignore warnings.
|
||||
|
||||
For more detailed information, see :ref:`topics-javascript-testing`.
|
||||
|
||||
Translation (Internationalization and Localization)
|
||||
===================================================
|
||||
|
||||
See :ref:`making_strings_translatable` for information on the translation
|
||||
architecture and how to ensure your code is translatable.
|
||||
|
||||
Creating your own panel
|
||||
=======================
|
||||
|
||||
.. Note::
|
||||
This section will be extended as standard practices are adopted upstream.
|
||||
Currently, it may be useful to look at the Project Images Panel as a
|
||||
complete reference. Since Newton, it is Angular by default (set to True in the
|
||||
ANGULAR_FEATURES dict in ``settings.py``).
|
||||
You may track all the changes made to the Image Panel
|
||||
`here <https://github.com/openstack/horizon/commits/master/openstack_dashboard/static/app/core/images>`__
|
||||
|
||||
.. Note::
|
||||
Currently, Angular module names must still be manually declared with
|
||||
``ADD_ANGULAR_MODULES``, even when using automatic file discovery.
|
||||
|
||||
This section serves as a basic introduction to writing your own panel for
|
||||
horizon, using AngularJS. A panel may be included with the plugin system, or it
|
||||
may be part of the upstream horizon project.
|
||||
|
||||
Upstream
|
||||
--------
|
||||
|
||||
JavaScript files can be discovered automatically, handled manually, or a mix of
|
||||
the two. Where possible, use the automated mechanism.
|
||||
To use the automatic functionality, add::
|
||||
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
||||
to your enabled file (``enabled/<plugin_name>.py``). To make this possible,
|
||||
you need to follow some structural conventions:
|
||||
|
||||
- Static files should be put in a ``static/`` folder, which should be found
|
||||
directly under the folder for the dashboard/panel/panel groups Python
|
||||
package.
|
||||
- JS code that defines an Angular module should be in a file with extension of
|
||||
``.module.js``.
|
||||
- JS code for testing should be named with extension of ``.mock.js`` and of
|
||||
``.spec.js``.
|
||||
- Angular templates should have extension of ``.html``.
|
||||
|
||||
You can read more about the functionality in the
|
||||
:ref:`auto_discover_static_files` section of the settings documentation.
|
||||
|
||||
To manually add files, add the following arrays and file paths to the enabled
|
||||
file:
|
||||
::
|
||||
|
||||
ADD_JS_FILES = [
|
||||
...
|
||||
'path-to/my-angular-code.js',
|
||||
...
|
||||
]
|
||||
|
||||
ADD_JS_SPEC_FILES = [
|
||||
...
|
||||
'path-to/my-angular-code.spec.js',
|
||||
...
|
||||
]
|
||||
|
||||
ADD_ANGULAR_MODULES = [
|
||||
...
|
||||
'my.angular.code',
|
||||
...
|
||||
]
|
||||
|
||||
Plugins
|
||||
-------
|
||||
|
||||
Add a new panel/ panel group/ dashboard (See :ref:`tutorials-dashboard`).
|
||||
JavaScript file inclusion is the same as the Upstream process.
|
||||
|
||||
To include external stylesheets, you must ensure that ``ADD_SCSS_FILES`` is
|
||||
defined in your enabled file, and add the relevant filepath, as below:
|
||||
::
|
||||
|
||||
ADD_SCSS_FILES = [
|
||||
...
|
||||
'path-to/my-styles.scss',
|
||||
...
|
||||
]
|
||||
|
||||
.. Note::
|
||||
We highly recommend using a single SCSS file for your plugin. SCSS supports
|
||||
nesting with @import, so if you have multiple files (i.e. per panel styling)
|
||||
it is best to import them all into one, and include that single file. You can
|
||||
read more in the `SASS documentation`_.
|
||||
|
||||
.. _SASS documentation: http://sass-lang.com/documentation/file.SASS_REFERENCE.html#import
|
||||
|
||||
Schema Forms
|
||||
============
|
||||
|
||||
`JSON schemas`_ are used to define model layout and then `angular-schema-form`_
|
||||
is used to create forms from that schema. Horizon adds some functionality on
|
||||
top of that to make things even easier through ``ModalFormService`` which will
|
||||
open a modal with the form inside.
|
||||
|
||||
A very simple example::
|
||||
|
||||
var schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", minLength: 2, title: "Name", description: "Name or alias" },
|
||||
title: {
|
||||
type: "string",
|
||||
enum: ['dr','jr','sir','mrs','mr','NaN','dj']
|
||||
}
|
||||
}
|
||||
};
|
||||
var model = {name: '', title: ''};
|
||||
var config = {
|
||||
title: gettext('Create Container'),
|
||||
schema: schema,
|
||||
form: ['*'],
|
||||
model: model
|
||||
};
|
||||
ModalFormService.open(config).then(submit); // returns a promise
|
||||
|
||||
function submit() {
|
||||
// do something with model.name and model.title
|
||||
}
|
||||
|
||||
.. _JSON schemas: http://json-schema.org/
|
||||
.. _angular-schema-form: https://github.com/json-schema-form/angular-schema-form/blob/master/docs/index.md
|
|
@ -1,20 +0,0 @@
|
|||
============
|
||||
Topic Guides
|
||||
============
|
||||
|
||||
Information on how to work with specific areas of Horizon can be found in
|
||||
the following topic guides.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
workflows
|
||||
tables
|
||||
policy
|
||||
microversion_support
|
||||
angularjs
|
||||
testing
|
||||
javascript_testing
|
||||
styling
|
||||
translation
|
||||
packaging
|
|
@ -1,313 +0,0 @@
|
|||
.. _topics-javascript-testing:
|
||||
|
||||
==================
|
||||
JavaScript Testing
|
||||
==================
|
||||
|
||||
There are multiple components in our JavaScript testing framework:
|
||||
|
||||
* `Jasmine`_ is our testing framework, so this defines the syntax and file
|
||||
structure we use to test our JavaScript.
|
||||
* `Karma`_ is our test runner. Amongst other things, this lets us run the
|
||||
tests against multiple browsers and generate test coverage reports.
|
||||
Alternatively, tests can be run inside the browser with the Jasmine spec
|
||||
runner.
|
||||
* `PhantomJS`_ provides a headless WebKit (the browser engine). This gives us
|
||||
native support for many web features without relying on specific browsers
|
||||
being installed.
|
||||
* `ESLint`_ is a pluggable code linting utility. This will catch small errors
|
||||
and inconsistencies in your JS, which may lead to bigger issues later on.
|
||||
See :ref:`js_code_style` for more detail.
|
||||
|
||||
Jasmine uses specs (``.spec.js``) which are kept with the JavaScript files
|
||||
that they are testing. See the :ref:`js_file_structure` section or the
|
||||
`Examples`_ below for more detail on this.
|
||||
|
||||
.. _Jasmine: https://jasmine.github.io/2.3/introduction.html
|
||||
.. _Karma: https://karma-runner.github.io/
|
||||
.. _PhantomJS: http://phantomjs.org/
|
||||
.. _ESLint: http://eslint.org/
|
||||
|
||||
Running Tests
|
||||
=============
|
||||
|
||||
Tests can be run in two ways:
|
||||
|
||||
1. Open <dev_server_ip:port>/jasmine in a browser. The development server can be
|
||||
run with ``tox -e runserver`` from the horizon root directory.
|
||||
2. ``tox -e npm`` from the horizon root directory. This runs Karma,
|
||||
so it will run all the tests against PhantomJS and generate coverage
|
||||
reports.
|
||||
|
||||
The code linting job can be run with ``tox -e npm -- lint``, or
|
||||
``tox -e npm -- lintq`` to show errors, but not warnings.
|
||||
|
||||
To decipher where tests are failing it may be useful to use Jasmine in the
|
||||
browser to run individual tests to see where the tests are specifically
|
||||
breaking. To do this, navigate to your local horizon in the browser and add
|
||||
'/jasmine' to the end of the url. e.g: 'http://localhost:8000/jasmine'. Once
|
||||
you have the jasmine report you may click on the title of an individual test to
|
||||
re-run just that test. From here, you can also use chrome dev tools or similar
|
||||
to set breakpoints in the code by accessing the 'Sources' tab and clicking on
|
||||
lines of code where you wish to break the code. This will then show you the
|
||||
exact places where the code breaks.
|
||||
|
||||
Coverage Reports
|
||||
----------------
|
||||
|
||||
Our Karma setup includes a plugin to generate test coverage reports. When
|
||||
developing, be sure to check the coverage reports on the master branch and
|
||||
compare your development branch; this will help identify missing tests.
|
||||
|
||||
To generate coverage reports, run ``tox -e npm``. The coverage reports can be
|
||||
found at ``cover/horizon/`` (framework tests) and ``cover/openstack_dashboard/``
|
||||
(dashboard tests). Load ``<browser>/index.html`` in a browser to view the
|
||||
reports.
|
||||
|
||||
Writing Tests
|
||||
=============
|
||||
|
||||
Jasmine uses suites and specs:
|
||||
|
||||
* Suites begin with a call to ``describe``, which takes two parameters; a
|
||||
string and a function. The string is a name or title for the spec suite,
|
||||
whilst the function is a block that implements the suite.
|
||||
* Specs begin with a call to ``it``, which also takes a string and a function
|
||||
as parameters. The string is a name or title, whilst the function is a
|
||||
block with one or more expectations (``expect``) that test the state of
|
||||
the code. An expectation in Jasmine is an assertion that is either true or
|
||||
false; every expectation in a spec must be true for the spec to pass.
|
||||
|
||||
``.spec.js`` files can be handled manually or automatically. To use the
|
||||
automatic file discovery add::
|
||||
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
||||
to your enabled file. JS code for testing should use the extensions
|
||||
``.mock.js`` and ``.spec.js``.
|
||||
|
||||
You can read more about the functionality in the
|
||||
:ref:`auto_discover_static_files` section of the settings documentation.
|
||||
|
||||
To manually add specs, add the following array and relevant file paths to your
|
||||
enabled file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
ADD_JS_SPEC_FILES = [
|
||||
...
|
||||
'path_to/my_angular_code.spec.js',
|
||||
...
|
||||
]
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
.. Note::
|
||||
|
||||
The code below is just for example purposes, and may not be current in
|
||||
horizon. Ellipses (...) are used to represent code that has been
|
||||
removed for the sake of brevity.
|
||||
|
||||
Example 1 - A reusable component in the **horizon** directory
|
||||
-------------------------------------------------------------
|
||||
|
||||
File tree:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
horizon/static/framework/widgets/modal
|
||||
├── modal.controller.js
|
||||
├── modal.module.js
|
||||
├── modal.service.js
|
||||
└── modal.spec.js
|
||||
|
||||
Lines added to ``horizon/test/jasmine/jasmine_tests.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class ServicesTests(test.JasmineTests):
|
||||
sources = [
|
||||
...
|
||||
'framework/widgets/modal/modal.module.js',
|
||||
'framework/widgets/modal/modal.controller.js',
|
||||
'framework/widgets/modal/modal.service.js',
|
||||
...
|
||||
]
|
||||
|
||||
specs = [
|
||||
...
|
||||
'framework/widgets/modal/modal.spec.js',
|
||||
...
|
||||
]
|
||||
|
||||
``modal.spec.js``:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
...
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
describe('horizon.framework.widgets.modal module', function() {
|
||||
|
||||
beforeEach(module('horizon.framework'));
|
||||
|
||||
describe('simpleModalCtrl', function() {
|
||||
var scope;
|
||||
var modalInstance;
|
||||
var context;
|
||||
var ctrl;
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
scope = {};
|
||||
modalInstance = {
|
||||
close: function() {},
|
||||
dismiss: function() {}
|
||||
};
|
||||
context = { what: 'is it' };
|
||||
ctrl = $controller('simpleModalCtrl', {
|
||||
$scope: scope,
|
||||
$modalInstance: modalInstance,
|
||||
context: context
|
||||
});
|
||||
}));
|
||||
|
||||
it('establishes a controller', function() {
|
||||
expect(ctrl).toBeDefined();
|
||||
});
|
||||
|
||||
it('sets context on the scope', function() {
|
||||
expect(scope.context).toBeDefined();
|
||||
expect(scope.context).toEqual({ what: 'is it' });
|
||||
});
|
||||
|
||||
it('sets action functions', function() {
|
||||
expect(scope.submit).toBeDefined();
|
||||
expect(scope.cancel).toBeDefined();
|
||||
});
|
||||
|
||||
it('makes submit close the modal instance', function() {
|
||||
expect(scope.submit).toBeDefined();
|
||||
spyOn(modalInstance, 'close');
|
||||
scope.submit();
|
||||
expect(modalInstance.close.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('makes cancel close the modal instance', function() {
|
||||
expect(scope.cancel).toBeDefined();
|
||||
spyOn(modalInstance, 'dismiss');
|
||||
scope.cancel();
|
||||
expect(modalInstance.dismiss).toHaveBeenCalledWith('cancel');
|
||||
});
|
||||
});
|
||||
|
||||
...
|
||||
|
||||
});
|
||||
})();
|
||||
|
||||
Example 2 - Panel-specific code in the **openstack_dashboard** directory
|
||||
------------------------------------------------------------------------
|
||||
|
||||
File tree:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
openstack_dashboard/static/dashboard/launch-instance/network/
|
||||
├── network.help.html
|
||||
├── network.html
|
||||
├── network.js
|
||||
├── network.scss
|
||||
└── network.spec.js
|
||||
|
||||
|
||||
Lines added to ``openstack_dashboard/enabled/_10_project.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
LAUNCH_INST = 'dashboard/launch-instance/'
|
||||
|
||||
ADD_JS_FILES = [
|
||||
...
|
||||
LAUNCH_INST + 'network/network.js',
|
||||
...
|
||||
]
|
||||
|
||||
ADD_JS_SPEC_FILES = [
|
||||
...
|
||||
LAUNCH_INST + 'network/network.spec.js',
|
||||
...
|
||||
]
|
||||
|
||||
``network.spec.js``:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
...
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
describe('Launch Instance Network Step', function() {
|
||||
|
||||
describe('LaunchInstanceNetworkCtrl', function() {
|
||||
var scope;
|
||||
var ctrl;
|
||||
|
||||
beforeEach(module('horizon.dashboard.project.workflow.launch-instance'));
|
||||
|
||||
beforeEach(inject(function($controller) {
|
||||
scope = {
|
||||
model: {
|
||||
newInstanceSpec: {networks: ['net-a']},
|
||||
networks: ['net-a', 'net-b']
|
||||
}
|
||||
};
|
||||
ctrl = $controller('LaunchInstanceNetworkCtrl', {$scope:scope});
|
||||
}));
|
||||
|
||||
it('has correct network statuses', function() {
|
||||
expect(ctrl.networkStatuses).toBeDefined();
|
||||
expect(ctrl.networkStatuses.ACTIVE).toBeDefined();
|
||||
expect(ctrl.networkStatuses.DOWN).toBeDefined();
|
||||
expect(Object.keys(ctrl.networkStatuses).length).toBe(2);
|
||||
});
|
||||
|
||||
it('has correct network admin states', function() {
|
||||
expect(ctrl.networkAdminStates).toBeDefined();
|
||||
expect(ctrl.networkAdminStates.UP).toBeDefined();
|
||||
expect(ctrl.networkAdminStates.DOWN).toBeDefined();
|
||||
expect(Object.keys(ctrl.networkStatuses).length).toBe(2);
|
||||
});
|
||||
|
||||
it('defines a multiple-allocation table', function() {
|
||||
expect(ctrl.tableLimits).toBeDefined();
|
||||
expect(ctrl.tableLimits.maxAllocation).toBe(-1);
|
||||
});
|
||||
|
||||
it('contains its own labels', function() {
|
||||
expect(ctrl.label).toBeDefined();
|
||||
expect(Object.keys(ctrl.label).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('contains help text for the table', function() {
|
||||
expect(ctrl.tableHelpText).toBeDefined();
|
||||
expect(ctrl.tableHelpText.allocHelpText).toBeDefined();
|
||||
expect(ctrl.tableHelpText.availHelpText).toBeDefined();
|
||||
});
|
||||
|
||||
it('uses scope to set table data', function() {
|
||||
expect(ctrl.tableDataMulti).toBeDefined();
|
||||
expect(ctrl.tableDataMulti.available).toEqual(['net-a', 'net-b']);
|
||||
expect(ctrl.tableDataMulti.allocated).toEqual(['net-a']);
|
||||
expect(ctrl.tableDataMulti.displayedAllocated).toEqual([]);
|
||||
expect(ctrl.tableDataMulti.displayedAvailable).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
...
|
||||
|
||||
});
|
||||
})();
|
|
@ -1,47 +0,0 @@
|
|||
============================
|
||||
Horizon Microversion Support
|
||||
============================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Several services use API microversions, which allows consumers of that API to
|
||||
specify an exact version when making a request. This can be useful in ensuring
|
||||
a feature continues to work as expected across many service releases.
|
||||
|
||||
Adding a feature that was introduced in a microversion
|
||||
======================================================
|
||||
|
||||
1. Add the feature to the ``MICROVERSION_FEATURES`` dict in
|
||||
``openstack_dashboard/api/microversions.py`` under the appropriate
|
||||
service name. The feature should have at least two versions listed; the
|
||||
minimum version (i.e. the version that introduced the feature) and
|
||||
the current working version. Providing multiple versions reduces project
|
||||
maintenance overheads and helps Horizon work with older service
|
||||
deployments.
|
||||
|
||||
2. Use the ``is_feature_available`` function for your service to show or hide
|
||||
the function.::
|
||||
|
||||
from openstack_dashboard.api import service
|
||||
|
||||
...
|
||||
|
||||
def allowed(self, request):
|
||||
return service.is_feature_available('feature')
|
||||
|
||||
3. Send the correct microversion with ``get_microversion`` function in the API
|
||||
layer.::
|
||||
|
||||
def resource_list(request):
|
||||
try:
|
||||
microversion = get_microversion(request, 'feature')
|
||||
client = serviceclient(request, microversion)
|
||||
return client.resource_list()
|
||||
|
||||
Microversion references
|
||||
=======================
|
||||
|
||||
:Nova: http://docs.openstack.org/developer/nova/api_microversion_history.html
|
||||
:Cinder: http://docs.openstack.org/developer/cinder/devref/api_microversion_history.html
|
||||
:API-WG: http://specs.openstack.org/openstack/api-wg/guidelines/microversion_specification.html
|
|
@ -1,218 +0,0 @@
|
|||
==================
|
||||
Packaging Software
|
||||
==================
|
||||
|
||||
|
||||
Software packages
|
||||
-----------------
|
||||
|
||||
This section describes some general things that a developer should know about
|
||||
packaging software. This content is mostly derived from best practices.
|
||||
|
||||
A developer building a package is comparable to an engineer building a car
|
||||
with only a manual and very few tools. If the engineer needs a specific tool
|
||||
to build the car, he must create the tool, too.
|
||||
|
||||
As a developer, if you are going to add a library named “foo”, the package
|
||||
must adhere to the following standards:
|
||||
|
||||
- Be a free package created with free software.
|
||||
- Include all tools that are required to build the package.
|
||||
- Have an active and responsive upstream to maintain the package.
|
||||
- Adhere to Filesystem Hierarchy Standards (FHS). A specific file system
|
||||
layout is not required.
|
||||
|
||||
|
||||
Embedded copies not allowed
|
||||
---------------------------
|
||||
|
||||
Imagine if all packages had a local copy of jQuery. If a security hole is
|
||||
discovered in jQuery, we must write more than 90 patches in Debian, one for
|
||||
each package that includes a copy. This is simply not practical. Therefore,
|
||||
it is unacceptable for Horizon to copy code from other repositories when
|
||||
creating a package. Copying code from another repository tends to create a
|
||||
fork, diverging from the upstream code. The fork includes code that is not
|
||||
being maintained, so if a bug is discovered in the original upstream, it
|
||||
cannot easily be fixed by updating a single package.
|
||||
|
||||
Another reason to avoid copying a library into Horizon source code is that
|
||||
it might create conflicting licenses. Distributing sources with conflicting
|
||||
licenses in one tarball revokes rights in best case. In the worst case, you
|
||||
could be held legally responsible.
|
||||
|
||||
|
||||
Free software
|
||||
-------------
|
||||
|
||||
Red Hat, Debian, and SUSE distributions are made only of free software (free
|
||||
as in Libre, or free speech). The software that we include in our repository
|
||||
is free. The tools are also free, and available in the distribution.
|
||||
|
||||
Because package maintainers care about the quality of the packages we upload,
|
||||
we run tests that are available from upstream repositories. This also
|
||||
qualifies test requirements as build requirements. The same rules apply for
|
||||
building the software as for the software itself. Special build requirements
|
||||
that are not included in the overall distribution are not allowed.
|
||||
|
||||
An example of historically limiting, non-free software is Selenium. For a
|
||||
long time, Selenium was only available from the non-free repositories of
|
||||
Debian. The reason was that upstream included some .xpi binaries. These .xpi
|
||||
included some Windows .dll and Linux .so files. Because they could not be
|
||||
rebuilt from the source, all of python-selenium was declared non-free. If we
|
||||
made Horizon build-depends on python-selenium, this would mean Horizon
|
||||
wouldn't be in Debian main anymore (contrib and non-free are *not* considered
|
||||
part of Debian). Recently, the package maintainer of python-selenium decided
|
||||
to remove the .xpi files from python-selenium, and upload it to Debian
|
||||
Experimental (this time, in main, not in non-free). If at some point it is
|
||||
possible for Horizon to use python-selenium (without the non-free .xpi files),
|
||||
then we could run Selenium tests at package build time.
|
||||
|
||||
|
||||
Running unit tests at build time
|
||||
--------------------------------
|
||||
|
||||
The build environment inside a distribution is not exactly the same as the
|
||||
one in the OpenStack gate. For example, versions of a given library can be
|
||||
slightly different from the one in the gate. We want to detect when
|
||||
problematic differences exist so that we can fix them. Whenever possible, try
|
||||
to make the lives of the package maintainer easier, and allow them (or help
|
||||
them) to run unit tests.
|
||||
|
||||
|
||||
Minified JavaScript policy
|
||||
--------------------------
|
||||
|
||||
In free software distributions that actively maintain OpenStack packages (such
|
||||
as RDO, Debian, and Ubuntu), minified JavaScript is considered non-free. This
|
||||
means that minified JavaScript should *not* be present in upstream source
|
||||
code. At the very least, a non-minified version should be present next to the
|
||||
minified version. Also, be aware of potential security issues with minifiers.
|
||||
This `blog post`_ explains it very well.
|
||||
|
||||
.. _`blog post`: https://zyan.scripts.mit.edu/blog/backdooring-js/
|
||||
|
||||
|
||||
Component version
|
||||
-----------------
|
||||
|
||||
Be careful about the version of all the components you use in your
|
||||
application. Since it is not acceptable to embed a given component within
|
||||
Horizon, we must use what is in the distribution, including all fonts,
|
||||
JavaScript, etc. This is where it becomes a bit tricky.
|
||||
|
||||
In most distributions, it is not acceptable to have multiple versions of the
|
||||
same piece of software. In Red Hat systems, it is technically possible to
|
||||
install 2 versions of one library at the same time, but a few restrictions
|
||||
apply, especially for usage. However, package maintainers try to avoid
|
||||
multiple versions as much as possible. For package dependency resolution, it
|
||||
might be necessary to provide packages for depending packages as well. For
|
||||
example, if you had Django-1.4 and Django-1.8 in the same release, you must
|
||||
provide Horizon built for Django-1.4 and another package providing Horizon
|
||||
built for Django-1.8. This is a large effort and needs to be evaluated
|
||||
carefully.
|
||||
|
||||
In Debian, it is generally forbidden to have multiple versions of the same
|
||||
library in the same Debian release. Very few exceptions exist.
|
||||
|
||||
Component versioning has consequences for an upstream author willing to
|
||||
integrate their software in a downstream distribution. The best situation
|
||||
is when it is possible to support whatever version is currently available
|
||||
in the target distributions, up to the latest version upstream. Declaring
|
||||
lower and upper bounds within your requirements.txt does not solve the issue.
|
||||
It allows all the tests to pass on gate because they are run against a narrow
|
||||
set of versions in requirements.txt. The downstream distribution might still
|
||||
have some dependencies with versions outside of the range that is specified
|
||||
in requirements.txt. These dependencies may lead to failures that are not
|
||||
caught in the OpenStack gate.
|
||||
|
||||
At times it might not be possible to support all versions of a library. It
|
||||
might be too much work, or it might be very hard to test in the gate. In this
|
||||
case, it is best to use whatever is available inside the target distributions.
|
||||
For example, Horizon currently supports jQuery >= 1.7.2, as this is what is
|
||||
currently available in Debian Jessie and Ubuntu Trusty (the last LTS).
|
||||
|
||||
You can search in a distribution for a piece of software foo using a command
|
||||
like ``dnf search foo``, or ``zypper se -s foo``. ``dnf info foo`` returns
|
||||
more detailed information about the package.
|
||||
|
||||
|
||||
Filesystem Hierarchy Standards
|
||||
------------------------------
|
||||
|
||||
Every distribution must comply with the Filesystem Hierarchy Standards (FHS).
|
||||
The FHS defines a set of rules that we *must* follow as package
|
||||
maintainers. Some of the most important ones are:
|
||||
|
||||
- /usr is considered read only. Software must not write in /usr at
|
||||
runtime. However, it is fine for a package post-installation script to write
|
||||
in /usr. When this rule was not followed, distributions had to write many
|
||||
tricks to convince Horizon to write in ``/var/lib`` only. For example,
|
||||
distributions wrote symlinks to ``/var/lib/openstack-dashboard``, or patched
|
||||
the default ``local_settings.py`` to write the ``SECRET_KEY`` in /var.
|
||||
- Configuration must always be in /etc, no matter what. When this rule
|
||||
was not followed, package maintainers had to place symlinks to
|
||||
``/etc/openstack-dashboard/local_settings`` in Red Hat based distributions
|
||||
instead of using directly
|
||||
``/usr/share/openstack-dashboard/openstack_dashboard/local/local_settings.py``
|
||||
which Horizon expects. In Debian,the configuration file is named
|
||||
``/etc/openstack-dashboard/local_settings.py.``
|
||||
|
||||
|
||||
Packaging Horizon
|
||||
-----------------
|
||||
|
||||
|
||||
Why we use XStatic
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
XStatic provides the following features that are not currently available
|
||||
by default with systems like NPM and Grunt:
|
||||
|
||||
- Dependency checks: XStatic checks that dependencies, such as fonts
|
||||
and JavaScript libs, are available in downstream distributions.
|
||||
- Reusable components across projects: The XStatic system ensures
|
||||
components are reusable by other packages, like Fuel.
|
||||
- System-wide registry of static content: XStatic brings a system-wide
|
||||
registry of components, so that it is easy to check if one is missing. For
|
||||
example, it can detect if there is no egg-info, or a broken package
|
||||
dependency exists.
|
||||
- No embedded content: The XStatic system helps us avoid embedding files that
|
||||
are already available in the distribution, for example, libjs-* or fonts-*
|
||||
packages. It even provides a compatibility layer for distributions. Not
|
||||
every distribution places static files in the same position in the file
|
||||
system. If you are packaging an XStatic package for your distribution, make
|
||||
sure that you are using the static files provided by that specific
|
||||
distribution. Having put together an XStatic package is *no* guarantee to
|
||||
get it into a distribution. XStatic provides only the abstraction layer to
|
||||
use distribution provided static files.
|
||||
- Package build systems are disconnected from the outside network (for
|
||||
several reasons). Other packaging systems download dependencies directly
|
||||
from the internet without verifying that the downloaded file is intact,
|
||||
matches a provided checksum, etc. With these other systems, there is no way
|
||||
to provide a mirror, a proxy or a cache, making builds even more unstable
|
||||
when minor networking issues are encountered.
|
||||
|
||||
The previous features are critical requirements of the Horizon packaging
|
||||
system. Any new system *must* keep these features. Although XStatic may mean
|
||||
a few additional steps from individual developers, those steps help maintain
|
||||
consistency and prevent errors across the project.
|
||||
|
||||
|
||||
Packaging Horizon for distributions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Horizon is a Python module. Preferably, it is installed at the default
|
||||
location for python. In Fedora and openSUSE, this is
|
||||
``/usr/lib/python2.7/site-packages/horizon``, and in Debian/Ubuntu it is
|
||||
``/usr/lib/python2.7/dist-packages/horizon``.
|
||||
|
||||
Configuration files should reside under ``/etc/openstack-dashboard``.
|
||||
Policy files should be created and modified there as well.
|
||||
|
||||
It is expected that ``manage.py collectstatic`` will be run during package
|
||||
build.
|
||||
This is the `recommended way`_ for Django applications.
|
||||
Depending on configuration, it might be required to ``manage.py compress``
|
||||
during package build, too.
|
||||
|
||||
.. _`recommended way`: https://docs.djangoproject.com/en/dev/howto/static-files/deployment/
|
|
@ -1,183 +0,0 @@
|
|||
.. _topics-policy:
|
||||
|
||||
============================================================
|
||||
Horizon Policy Enforcement (RBAC: Role Based Access Control)
|
||||
============================================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Horizon's policy enforcement builds on the oslo_policy engine.
|
||||
The basis of which is ``openstack_auth/policy.py``.
|
||||
Services in OpenStack use the oslo policy engine to define policy rules
|
||||
to limit access to APIs based primarily on role grants and resource
|
||||
ownership.
|
||||
|
||||
The Keystone v3 API provides an interface for creating/reading/updating
|
||||
policy files in the keystone database. However, at this time services
|
||||
do not load the policy files into Keystone. Thus, the implementation in
|
||||
Horizon is based on copies of policy.json files found in the service's
|
||||
source code. The long-term goal is to read/utilize/update these policy
|
||||
files in Horizon.
|
||||
|
||||
The service rules files are loaded into the policy engine to determine
|
||||
access rights to actions and service APIs.
|
||||
|
||||
Horizon Settings
|
||||
================
|
||||
|
||||
There are a few settings that must be in place for the Horizon policy
|
||||
engine to work.
|
||||
|
||||
``POLICY_FILES_PATH``
|
||||
---------------------
|
||||
|
||||
Default: ``os.path.join(ROOT_PATH, "conf")``
|
||||
|
||||
Specifies where service based policy files are located. These are used to
|
||||
define the policy rules actions are verified against. This value must contain
|
||||
the files listed in ``POLICY_FILES`` or all policy checks will pass.
|
||||
|
||||
.. note::
|
||||
|
||||
The path to deployment specific policy files can be specified in
|
||||
``local_settings.py`` to override the default location.
|
||||
|
||||
|
||||
``POLICY_FILES``
|
||||
----------------
|
||||
|
||||
Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}``
|
||||
|
||||
This should essentially be the mapping of the contents of ``POLICY_FILES_PATH``
|
||||
to service types. When policy.json files are added to the directory
|
||||
``POLICY_FILES_PATH``, they should be included here too. Without this mapping,
|
||||
there is no way to map service types with policy rules, thus two policy.json
|
||||
files containing a "default" rule would be ambiguous.
|
||||
|
||||
.. note::
|
||||
|
||||
Deployment specific policy files can be specified in ``local_settings.py``
|
||||
to override the default policy files. It is imperative that these policy
|
||||
files match those deployed in the target OpenStack installation. Otherwise,
|
||||
the displayed actions and the allowed action will not match.
|
||||
|
||||
``POLICY_CHECK_FUNCTION``
|
||||
-------------------------
|
||||
|
||||
Default: ``policy.check``
|
||||
|
||||
This value should not be changed, although removing it would be a means to
|
||||
bypass all policy checks. Set it to ``None`` in ``local_settings.py`` to
|
||||
do this.
|
||||
|
||||
|
||||
How user's roles are determined
|
||||
===============================
|
||||
|
||||
Each policy check uses information about the user stored on the request to
|
||||
determine the user's roles. This information was extracted from the scoped
|
||||
token received from Keystone when authenticating.
|
||||
|
||||
Entity ownership is also a valid role. To verify access to specific entities
|
||||
like a project, the target must be specified. See the section
|
||||
:ref:`rule targets <rule_targets>` later in this document.
|
||||
|
||||
How to Utilize RBAC
|
||||
===================
|
||||
|
||||
The primary way to add role based access control checks to panels is in the
|
||||
definition of table actions. When implementing a derived action class,
|
||||
setting the :attr:`~horizon.tables.Action.policy_rules` attribute to valid
|
||||
policy rules will force a policy check before the
|
||||
:meth:`horizon.tables.Action.allowed` method is called on the action. These
|
||||
rules are defined in the policy files pointed to by ``POLICY_PATH`` and
|
||||
``POLICY_FILES``. The rules are role based, where entity owner is also a
|
||||
role. The format for the ``policy_rules`` is a list of two item tuples. The
|
||||
first component of the tuple is the scope of the policy rule, this is the
|
||||
service type. This informs the policy engine which policy file to reference.
|
||||
The second component is the rule to enforce from the policy file specified by
|
||||
the scope. An example tuple is::
|
||||
|
||||
("identity", "identity:get_user")
|
||||
|
||||
x tuples can be added to enforce x rules.
|
||||
|
||||
.. note::
|
||||
|
||||
If a rule specified is not found in the policy file, the policy check
|
||||
will return False and the action will not be allowed.
|
||||
|
||||
The secondary way to add a role based check is to directly use the
|
||||
:meth:`~openstack_dashboard.policy.check` method. The method takes a list
|
||||
of actions, same format as the :attr:`~horizon.tables.Action.policy_rules`
|
||||
attribute detailed above; the current request object; and a dictionary of
|
||||
action targets. This is the method that :class:`horizon.tables.Action` class
|
||||
utilizes. Examples look like::
|
||||
|
||||
from openstack_dashboard import policy
|
||||
|
||||
allowed = policy.check((("identity", "identity:get_user"),
|
||||
("identity", "identity:get_project"),), request)
|
||||
|
||||
can_see = policy.check((("identity", "identity:get_user"),), request,
|
||||
target={"domain_id": domainId})
|
||||
|
||||
.. note::
|
||||
|
||||
Any time multiple rules are specified in a single `policy.check` method
|
||||
call, the result is the logical `and` of each rule check. So, if any
|
||||
rule fails verification, the result is `False`.
|
||||
|
||||
The third way to add a role based check is in javascript files. Use the method
|
||||
'ifAllowed()' in file 'openstack_dashboard.static.app.core.policy.service.js'.
|
||||
The method takes a list of actions, similar format with the
|
||||
:attr:`~horizon.tables.Action.policy_rules` attribute detailed above.
|
||||
An Example looks like::
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.identity.users')
|
||||
.controller('identityUsersTableController', identityUsersTableController);
|
||||
|
||||
identityUsersTableController.$inject = [
|
||||
'horizon.app.core.openstack-service-api.policy',
|
||||
];
|
||||
|
||||
function identityUsersTableController(toast, gettext, policy, keystone) {
|
||||
var rules = [['identity', 'identity:list_users']];
|
||||
policy.ifAllowed({ rules: rules }).then(policySuccess, policyFailed);
|
||||
}
|
||||
|
||||
The fourth way to add a role based check is in html files. Use angular
|
||||
directive 'hz-if-policies' in file
|
||||
'openstack_dashboard/static/app/core/cloud-services/hz-if-policies.directive.js'.
|
||||
Assume you have the following policy defined in your angular controller::
|
||||
|
||||
ctrl.policy = { rules: [["identity", "identity:update_user"]] }
|
||||
|
||||
Then in your HTML, use it like so::
|
||||
|
||||
<div hz-if-policies='ctrl.policy'>
|
||||
<span>I am visible if the policy is allowed!</span>
|
||||
</div>
|
||||
|
||||
.. _rule_targets:
|
||||
|
||||
Rule Targets
|
||||
============
|
||||
|
||||
Some rules allow access if the user owns the entity. Policy check targets
|
||||
specify particular entities to check for user ownership. The target parameter
|
||||
to the :meth:`~openstack_dashboard.policy.check` method is a simple dictionary.
|
||||
For instance, the target for checking access a project looks like::
|
||||
|
||||
{"project_id": "0905760626534a74979afd3f4a9d67f1"}
|
||||
|
||||
If the value matches the ``project_id`` to which the user's token is scoped,
|
||||
then access is allowed.
|
||||
|
||||
When deriving the :class:`horizon.tables.Action` class for use in a table, if
|
||||
a policy check is desired for a particular target, the implementer should
|
||||
override the :meth:`horizon.tables.Action.get_policy_target` method. This
|
||||
allows a programmatic way to specify the target based on the current datum. The
|
||||
value returned should be the target dictionary.
|
|
@ -1,130 +0,0 @@
|
|||
=========================
|
||||
Styling in Horizon (SCSS)
|
||||
=========================
|
||||
|
||||
Horizon uses `SCSS`_ (not to be confused with Sass) to style its HTML. This
|
||||
guide is targeted at developers adding code to upstream Horizon. For
|
||||
information on creating your own branding/theming, see
|
||||
:ref:`install-customizing`.
|
||||
|
||||
.. _SCSS: http://sass-lang.com/guide
|
||||
|
||||
Code Layout
|
||||
===========
|
||||
|
||||
The base SCSS can be found at ``openstack_dashboard/static/dashboard/scss/``.
|
||||
This directory should **only** contain the minimal styling for functionality
|
||||
code that isn't configurable by themes. ``horizon.scss`` is a top level file
|
||||
that imports from the ``components/`` directory, as well as other base styling
|
||||
files; potentially some basic page layout rules that Horizon relies on to
|
||||
function.
|
||||
|
||||
.. Note::
|
||||
Currently, a great deal of theming is also kept in the ``horizon.scss`` file
|
||||
in this directory, but that will be reduced as we proceed with the new code
|
||||
design.
|
||||
|
||||
Horizon's ``default`` theme stylesheets can be found at
|
||||
``openstack_dashboard/themes/default/``.
|
||||
|
||||
::
|
||||
|
||||
├── _styles.scss
|
||||
├── _variables.scss
|
||||
├── bootstrap/
|
||||
└── ...
|
||||
└── horizon/
|
||||
└── ...
|
||||
|
||||
The top level ``_styles.scss`` and ``_variables.scss`` contain imports from
|
||||
the ``bootstrap`` and ``horizon`` directories.
|
||||
|
||||
The "bootstrap" directory
|
||||
-------------------------
|
||||
|
||||
This directory contains overrides and customization of Bootstrap variables, and
|
||||
markup used by Bootstrap components. This should **only** override existing
|
||||
Bootstrap content. For examples of these components, see the
|
||||
`Theme Preview Panel`_.
|
||||
|
||||
::
|
||||
|
||||
bootstrap/
|
||||
├── _styles.scss
|
||||
├── _variables.scss
|
||||
└── components/
|
||||
├── _component_0.scss
|
||||
├── _component_1.scss
|
||||
└── ...
|
||||
|
||||
- ``_styles.scss`` imports the SCSS defined for each component.
|
||||
- ``_variables.scss`` contains the definitions for every Bootstrap variable.
|
||||
These variables can be altered to affect the look and feel of Horizon's
|
||||
default theme.
|
||||
- The ``components`` directory contains overrides for Bootstrap components,
|
||||
such as tables or navbars.
|
||||
|
||||
The "horizon" directory
|
||||
-----------------------
|
||||
|
||||
This directory contains SCSS that is absolutely specific to Horizon; code here
|
||||
should **not** override existing Bootstrap content, such as variables and rules.
|
||||
|
||||
::
|
||||
|
||||
horizon/
|
||||
├── _styles.scss
|
||||
├── _variables.scss
|
||||
└── components/
|
||||
├── _component_0.scss
|
||||
├── _component_1.scss
|
||||
└── ...
|
||||
|
||||
- ``_styles.scss`` imports the SCSS defined for each component. It may also
|
||||
contain some minor styling overrides.
|
||||
- ``_variables.scss`` contains variable definitions that are specific to the
|
||||
horizon theme. This should **not** override any bootstrap variables, only
|
||||
define new ones. You can however, inherit bootstrap variables for reuse
|
||||
(and are encouraged to do so where possible).
|
||||
- The ``components`` directory contains styling for each individual component
|
||||
defined by Horizon, such as the sidebar or pie charts.
|
||||
|
||||
Adding new SCSS
|
||||
===============
|
||||
|
||||
To keep Horizon easily themable, there are several code design guidelines that
|
||||
should be adhered to:
|
||||
|
||||
- Reuse Bootstrap variables where possible. This allows themes to influence
|
||||
styling by simply overriding a few existing variables, instead of rewriting
|
||||
large chunks of the SCSS files.
|
||||
- If you are unable to use existing variables - such as for very specific
|
||||
functionality - keep the new rules as specific as possible to your component
|
||||
so they do not cause issues in unexpected places.
|
||||
- Check if existing components suit your use case. There may be existing
|
||||
components defined by Bootstrap or Horizon that can be reused, rather than
|
||||
writing new ones.
|
||||
|
||||
Theme Preview Panel
|
||||
===================
|
||||
|
||||
The Bootstrap Theme Preview panel contains examples of all stock Bootstrap
|
||||
markup with the currently applied theme, as well as source code for replicating
|
||||
them; click the ``</>`` symbol when hovering over a component.
|
||||
|
||||
To enable the Developer dashboard with the Theme Preview panel:
|
||||
|
||||
#. Set :ref:`DEBUG <debug_setting>` setting to ``True``.
|
||||
#. Copy ``_9001_developer.py`` and ``_9010_preview.py`` from
|
||||
``openstack_dashboard/contrib/developer/enabled/`` to
|
||||
``openstack_dashboard/local/enabled/``.
|
||||
#. Restart the web server.
|
||||
|
||||
Alternate Theme
|
||||
===============
|
||||
|
||||
A second theme is provided by default at
|
||||
``openstack_dashboard/themes/material/``. When adding new SCSS to horizon, you
|
||||
should check that it does not interfere with the Material theme. Images of how
|
||||
the Material theme should look can be found at https://bootswatch.com/paper/.
|
||||
This theme is now configured to run as the alternate theme within Horizon.
|
|
@ -1,388 +0,0 @@
|
|||
.. _topics-datatables:
|
||||
|
||||
======================
|
||||
DataTables Topic Guide
|
||||
======================
|
||||
|
||||
Horizon provides the :mod:`horizon.tables` module to provide
|
||||
a convenient, reusable API for building data-driven displays and interfaces.
|
||||
The core components of this API fall into three categories: ``DataTables``,
|
||||
``Actions``, and ``Class-based Views``.
|
||||
|
||||
.. seealso::
|
||||
|
||||
For a detailed API information check out the :ref:`ref-datatables`.
|
||||
|
||||
Tables
|
||||
======
|
||||
|
||||
The majority of interface in a dashboard-style interface ends up being
|
||||
tabular displays of the various resources the dashboard interacts with.
|
||||
The :class:`~horizon.tables.DataTable` class exists so you don't have to
|
||||
reinvent the wheel each time.
|
||||
|
||||
Creating your own tables
|
||||
------------------------
|
||||
|
||||
Creating a table is fairly simple:
|
||||
|
||||
#. Create a subclass of :class:`~horizon.tables.DataTable`.
|
||||
#. Define columns on it using :class:`~horizon.tables.Column`.
|
||||
#. Create an inner ``Meta`` class to contain the special options for
|
||||
this table.
|
||||
#. Define any actions for the table, and add them to
|
||||
:attr:`~horizon.tables.DataTableOptions.table_actions` or
|
||||
:attr:`~horizon.tables.DataTableOptions.row_actions`.
|
||||
|
||||
Examples of this can be found in any of the ``tables.py`` modules included
|
||||
in the reference modules under ``horizon.dashboards``.
|
||||
|
||||
Connecting a table to a view
|
||||
----------------------------
|
||||
|
||||
Once you've got your table set up the way you like it, the next step is to
|
||||
wire it up to a view. To make this as easy as possible Horizon provides the
|
||||
:class:`~horizon.tables.DataTableView` class-based view which can be subclassed
|
||||
to display your table with just a couple lines of code. At its simplest, it
|
||||
looks like this::
|
||||
|
||||
from horizon import tables
|
||||
from .tables import MyTable
|
||||
|
||||
|
||||
class MyTableView(tables.DataTableView):
|
||||
table_class = MyTable
|
||||
template_name = "my_app/my_table_view.html"
|
||||
|
||||
def get_data(self):
|
||||
return my_api.objects.list()
|
||||
|
||||
In the template you would just need to include the following to render the
|
||||
table::
|
||||
|
||||
{{ table.render }}
|
||||
|
||||
That's it! Easy, right?
|
||||
|
||||
Actions
|
||||
=======
|
||||
|
||||
Actions comprise any manipulations that might happen on the data in the table
|
||||
or the table itself. For example, this may be the standard object CRUD, linking
|
||||
to related views based on the object's id, filtering the data in the table,
|
||||
or fetching updated data when appropriate.
|
||||
|
||||
When actions get run
|
||||
--------------------
|
||||
|
||||
There are two points in the request-response cycle in which actions can
|
||||
take place; prior to data being loaded into the table, and after the data
|
||||
is loaded. When you're using one of the pre-built class-based views for
|
||||
working with your tables the pseudo-workflow looks like this:
|
||||
|
||||
#. The request enters view.
|
||||
#. The table class is instantiated without data.
|
||||
#. Any "preemptive" actions are checked to see if they should run.
|
||||
#. Data is fetched and loaded into the table.
|
||||
#. All other actions are checked to see if they should run.
|
||||
#. If none of the actions have caused an early exit from the view,
|
||||
the standard response from the view is returned (usually the
|
||||
rendered table).
|
||||
|
||||
The benefit of the multi-step table instantiation is that you can use
|
||||
preemptive actions which don't need access to the entire collection of data
|
||||
to save yourself on processing overhead, API calls, etc.
|
||||
|
||||
Basic actions
|
||||
-------------
|
||||
|
||||
At their simplest, there are three types of actions: actions which act on the
|
||||
data in the table, actions which link to related resources, and actions that
|
||||
alter which data is displayed. These correspond to
|
||||
:class:`~horizon.tables.Action`, :class:`~horizon.tables.LinkAction`, and
|
||||
:class:`~horizon.tables.FilterAction`.
|
||||
|
||||
Writing your own actions generally starts with subclassing one of those
|
||||
action classes and customizing the designated attributes and methods.
|
||||
|
||||
Shortcut actions
|
||||
----------------
|
||||
|
||||
There are several common tasks for which Horizon provides pre-built shortcut
|
||||
classes. These include :class:`~horizon.tables.BatchAction`, and
|
||||
:class:`~horizon.tables.DeleteAction`. Each of these abstracts away nearly
|
||||
all of the boilerplate associated with writing these types of actions and
|
||||
provides consistent error handling, logging, and user-facing interaction.
|
||||
|
||||
It is worth noting that ``BatchAction`` and ``DeleteAction`` are extensions
|
||||
of the standard ``Action`` class. Some ``BatchAction`` or ``DeleteAction``
|
||||
classes may cause some unrecoverable results, like deleted images or
|
||||
unrecoverable instances. It may be helpful to specify specific help_text to
|
||||
explain the concern to the user, such as "Deleted images are not recoverable".
|
||||
|
||||
Preemptive actions
|
||||
------------------
|
||||
|
||||
Action classes which have their :attr:`~horizon.tables.Action.preempt`
|
||||
attribute set to ``True`` will be evaluated before any data is loaded into
|
||||
the table. As such, you must be careful not to rely on any table methods that
|
||||
require data, such as :meth:`~horizon.tables.DataTable.get_object_display` or
|
||||
:meth:`~horizon.tables.DataTable.get_object_by_id`. The advantage of preemptive
|
||||
actions is that you can avoid having to do all the processing, API calls, etc.
|
||||
associated with loading data into the table for actions which don't require
|
||||
access to that information.
|
||||
|
||||
Policy checks on actions
|
||||
------------------------
|
||||
|
||||
The :attr:`~horizon.tables.Action.policy_rules` attribute, when set, will
|
||||
validate access to the action using the policy rules specified. The attribute
|
||||
is a list of scope/rule pairs. Where the scope is the service type defining
|
||||
the rule and the rule is a rule from the corresponding service policy.json
|
||||
file. The format of :attr:`horizon.tables.Action.policy_rules` looks like::
|
||||
|
||||
(("identity", "identity:get_user"),)
|
||||
|
||||
Multiple checks can be made for the same action by merely adding more tuples
|
||||
to the list. The policy check will use information stored in the session
|
||||
about the user and the result of
|
||||
:meth:`~horizon.tables.Action.get_policy_target` (which can be overridden in
|
||||
the derived action class) to determine if the user
|
||||
can execute the action. If the user does not have access to the action, the
|
||||
action is not added to the table.
|
||||
|
||||
If :attr:`~horizon.tables.Action.policy_rules` is not set, no policy checks
|
||||
will be made to determine if the action should be visible and will be
|
||||
displayed solely based on the result of
|
||||
:meth:`~horizon.tables.Action.allowed`.
|
||||
|
||||
For more information on policy based Role Based Access Control see
|
||||
:ref:`topics-policy`.
|
||||
|
||||
Table Cell filters (decorators)
|
||||
===============================
|
||||
|
||||
DataTable displays lists of objects in rows and object attributes in cell.
|
||||
How should we proceed, if we want to decorate some column, e.g. if we have
|
||||
column ``memory`` which returns a number e.g. 1024, and we want to show
|
||||
something like 1024.00 GB inside table?
|
||||
|
||||
Decorator pattern
|
||||
-----------------
|
||||
|
||||
The clear anti-pattern is defining the new attributes on object like
|
||||
``ram_float_format_2_gb`` or to tweak a DataTable in any way for displaying
|
||||
purposes.
|
||||
|
||||
The cleanest way is to use ``filters``. Filters are decorators, following GOF
|
||||
``Decorator pattern``. This way ``DataTable logic`` and ``displayed object
|
||||
logic`` are correctly separated from ``presentation logic`` of the object
|
||||
inside of the various tables. And therefore the filters are reusable in all
|
||||
tables.
|
||||
|
||||
Filter function
|
||||
---------------
|
||||
|
||||
Horizon DatablesTable takes a tuple of pointers to filter functions
|
||||
or anonymous lambda functions. When displaying a ``Cell``, ``DataTable``
|
||||
takes ``Column`` filter functions from left to right, using the returned value
|
||||
of the previous function as a parameter of the following function. Then
|
||||
displaying the returned value of the last filter function.
|
||||
|
||||
A valid filter function takes one parameter and returns the decorated value.
|
||||
So e.g. these are valid filter functions ::
|
||||
|
||||
# Filter function.
|
||||
def add_unit(v):
|
||||
return str(v) + " GB"
|
||||
|
||||
# Or filter lambda function.
|
||||
lambda v: str(v) + " GB"
|
||||
|
||||
# This is also a valid definition of course, although for the change of the
|
||||
# unit parameter, function has to be wrapped by lambda
|
||||
# (e.g. floatformat function example below).
|
||||
def add_unit(v, unit="GB"):
|
||||
return str(v) + " " + unit
|
||||
|
||||
Using filters in DataTable column
|
||||
---------------------------------
|
||||
|
||||
DataTable takes tuple of filter functions, so e.g. this is valid decorating
|
||||
of a value with float format and with unit ::
|
||||
|
||||
ram = tables.Column(
|
||||
"ram",
|
||||
verbose_name=_('Memory'),
|
||||
filters=(lambda v: floatformat(v, 2),
|
||||
add_unit))
|
||||
|
||||
It always takes tuple, so using only one filter would look like this ::
|
||||
|
||||
filters=(lambda v: floatformat(v, 2),)
|
||||
|
||||
The decorated parameter doesn't have to be only a string or number, it can
|
||||
be anything e.g. list or an object. So decorating of object, that has
|
||||
attributes value and unit would look like this ::
|
||||
|
||||
ram = tables.Column(
|
||||
"ram",
|
||||
verbose_name=_('Memory'),
|
||||
filters=(lambda x: getattr(x, 'value', '') +
|
||||
" " + getattr(x, 'unit', ''),))
|
||||
|
||||
Available filters
|
||||
-----------------
|
||||
|
||||
There are a load of filters, that can be used, defined in django already:
|
||||
https://github.com/django/django/blob/master/django/template/defaultfilters.py
|
||||
|
||||
So it's enough to just import and use them, e.g. ::
|
||||
|
||||
from django.template import defaultfilters as filters
|
||||
|
||||
# code omitted
|
||||
filters=(filters.yesno, filters.capfirst)
|
||||
|
||||
|
||||
from django.template.defaultfilters import timesince
|
||||
from django.template.defaultfilters import title
|
||||
|
||||
# code omitted
|
||||
filters=(parse_isotime, timesince)
|
||||
|
||||
|
||||
Inline editing
|
||||
==============
|
||||
|
||||
Table cells can be easily upgraded with in-line editing. With use of
|
||||
django.form.Field, we are able to run validations of the field and correctly
|
||||
parse the data. The updating process is fully encapsulated into table
|
||||
functionality, communication with the server goes through AJAX in JSON format.
|
||||
The javascript wrapper for inline editing allows each table cell that has
|
||||
in-line editing available to:
|
||||
|
||||
#. Refresh itself with new data from the server.
|
||||
#. Display in edit mode.
|
||||
#. Send changed data to server.
|
||||
#. Display validation errors.
|
||||
|
||||
There are basically 3 things that need to be defined in the table in order
|
||||
to enable in-line editing.
|
||||
|
||||
Fetching the row data
|
||||
---------------------
|
||||
|
||||
Defining an ``get_data`` method in a class inherited from ``tables.Row``.
|
||||
This method takes care of fetching the row data. This class has to be then
|
||||
defined in the table Meta class as ``row_class = UpdateRow``.
|
||||
|
||||
Example::
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
# this method is also used for automatic update of the row
|
||||
ajax = True
|
||||
|
||||
def get_data(self, request, project_id):
|
||||
# getting all data of all row cells
|
||||
project_info = api.keystone.tenant_get(request, project_id,
|
||||
admin=True)
|
||||
return project_info
|
||||
|
||||
Updating changed cell data (DEPRECATED)
|
||||
---------------------------------------
|
||||
|
||||
Define an ``update_cell`` method in the class inherited from
|
||||
``tables.UpdateAction``. This method takes care of saving the data of the
|
||||
table cell. There can be one class for every cell thanks to the
|
||||
``cell_name`` parameter. This class is then defined in tables column as
|
||||
``update_action=UpdateCell``, so each column can have its own updating
|
||||
method.
|
||||
|
||||
Example::
|
||||
|
||||
class UpdateCell(tables.UpdateAction):
|
||||
def allowed(self, request, project, cell):
|
||||
# Determines whether given cell or row will be inline editable
|
||||
# for signed in user.
|
||||
return api.keystone.keystone_can_edit_project()
|
||||
|
||||
def update_cell(self, request, project_id, cell_name, new_cell_value):
|
||||
# in-line update project info
|
||||
try:
|
||||
project_obj = datum
|
||||
# updating changed value by new value
|
||||
setattr(project_obj, cell_name, new_cell_value)
|
||||
|
||||
# sending new attributes back to API
|
||||
api.keystone.tenant_update(
|
||||
request,
|
||||
project_id,
|
||||
name=project_obj.name,
|
||||
description=project_obj.description,
|
||||
enabled=project_obj.enabled)
|
||||
|
||||
except Conflict:
|
||||
# Validation error for naming conflict, raised when user
|
||||
# choose the existing name. We will raise a
|
||||
# ValidationError, that will be sent back to the client
|
||||
# browser and shown inside of the table cell.
|
||||
message = _("This name is already taken.")
|
||||
raise ValidationError(message)
|
||||
except:
|
||||
# Other exception of the API just goes through standard
|
||||
# channel
|
||||
exceptions.handle(request, ignore=True)
|
||||
return False
|
||||
return True
|
||||
|
||||
Defining a form_field for each Column that we want to be in-line edited.
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Form field should be ``django.form.Field`` instance, so we can use django
|
||||
validations and parsing of the values sent by POST (in example validation
|
||||
``required=True`` and correct parsing of the checkbox value from the POST
|
||||
data).
|
||||
|
||||
Form field can be also ``django.form.Widget`` class, if we need to just
|
||||
display the form widget in the table and we don't need Field functionality.
|
||||
|
||||
Then connecting ``UpdateRow`` and ``UpdateCell`` classes to the table.
|
||||
|
||||
Example::
|
||||
|
||||
class TenantsTable(tables.DataTable):
|
||||
# Adding html text input for inline editing, with required validation.
|
||||
# HTML form input will have a class attribute tenant-name-input, we
|
||||
# can define here any HTML attribute we need.
|
||||
name = tables.Column('name', verbose_name=_('Name'),
|
||||
form_field=forms.CharField(required=True),
|
||||
form_field_attributes={'class':'tenant-name-input'},
|
||||
update_action=UpdateCell)
|
||||
|
||||
# Adding html textarea without required validation.
|
||||
description = tables.Column(lambda obj: getattr(obj, 'description', None),
|
||||
verbose_name=_('Description'),
|
||||
form_field=forms.CharField(
|
||||
widget=forms.Textarea(),
|
||||
required=False),
|
||||
update_action=UpdateCell)
|
||||
|
||||
# Id will not be inline edited.
|
||||
id = tables.Column('id', verbose_name=_('Project ID'))
|
||||
|
||||
# Adding html checkbox, that will be shown inside of the table cell with
|
||||
# label
|
||||
enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True,
|
||||
form_field=forms.BooleanField(
|
||||
label=_('Enabled'),
|
||||
required=False),
|
||||
update_action=UpdateCell)
|
||||
|
||||
class Meta(object):
|
||||
name = "tenants"
|
||||
verbose_name = _("Projects")
|
||||
# Connection to UpdateRow, so table can fetch row data based on
|
||||
# their primary key.
|
||||
row_class = UpdateRow
|
||||
|
|
@ -1,318 +0,0 @@
|
|||
.. _topics-testing:
|
||||
|
||||
================
|
||||
Testing Overview
|
||||
================
|
||||
|
||||
Having good tests in place is absolutely critical for ensuring a stable,
|
||||
maintainable codebase. Hopefully that doesn't need any more explanation.
|
||||
|
||||
However, what defines a "good" test is not always obvious, and there are
|
||||
a lot of common pitfalls that can easily shoot your test suite in the
|
||||
foot.
|
||||
|
||||
If you already know everything about testing but are fed up with trying to
|
||||
debug why a specific test failed, you can skip the intro and jump
|
||||
straight to :ref:`debugging_unit_tests`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Angular specific testing <javascript_testing>
|
||||
|
||||
An overview of testing
|
||||
======================
|
||||
|
||||
There are three main types of tests, each with their associated pros and cons:
|
||||
|
||||
Unit tests
|
||||
----------
|
||||
|
||||
These are isolated, stand-alone tests with no external dependencies. They are
|
||||
written from the perspective of "knowing the code", and test the assumptions
|
||||
of the codebase and the developer.
|
||||
|
||||
Pros:
|
||||
|
||||
* Generally lightweight and fast.
|
||||
* Can be run anywhere, anytime since they have no external dependencies.
|
||||
|
||||
Cons:
|
||||
|
||||
* Easy to be lax in writing them, or lazy in constructing them.
|
||||
* Can't test interactions with live external services.
|
||||
|
||||
Functional tests
|
||||
----------------
|
||||
|
||||
These are generally also isolated tests, though sometimes they may interact
|
||||
with other services running locally. The key difference between functional
|
||||
tests and unit tests, however, is that functional tests are written from the
|
||||
perspective of the user (who knows nothing about the code) and only knows
|
||||
what they put in and what they get back. Essentially this is a higher-level
|
||||
testing of "does the result match the spec?".
|
||||
|
||||
Pros:
|
||||
|
||||
* Ensures that your code *always* meets the stated functional requirements.
|
||||
* Verifies things from an "end user" perspective, which helps to ensure
|
||||
a high-quality experience.
|
||||
* Designing your code with a functional testing perspective in mind helps
|
||||
keep a higher-level viewpoint in mind.
|
||||
|
||||
Cons:
|
||||
|
||||
* Requires an additional layer of thinking to define functional requirements
|
||||
in terms of inputs and outputs.
|
||||
* Often requires writing a separate set of tests and/or using a different
|
||||
testing framework from your unit tests.
|
||||
* Doesn't offer any insight into the quality or status of the underlying code,
|
||||
only verifies that it works or it doesn't.
|
||||
|
||||
Integration Tests
|
||||
-----------------
|
||||
|
||||
This layer of testing involves testing all of the components that your
|
||||
codebase interacts with or relies on in conjunction. This is equivalent to
|
||||
"live" testing, but in a repeatable manner.
|
||||
|
||||
Pros:
|
||||
|
||||
* Catches *many* bugs that unit and functional tests will not.
|
||||
* Doesn't rely on assumptions about the inputs and outputs.
|
||||
* Will warn you when changes in external components break your code.
|
||||
* Will take screenshot of the current page on test fail for easy debug
|
||||
|
||||
Cons:
|
||||
|
||||
* Difficult and time-consuming to create a repeatable test environment.
|
||||
* Did I mention that setting it up is a pain?
|
||||
|
||||
Screenshot directory could be set through horizon.conf file, default value:
|
||||
``./integration_tests_screenshots``
|
||||
|
||||
So what should I write?
|
||||
-----------------------
|
||||
|
||||
A few simple guidelines:
|
||||
|
||||
#. Every bug fix should have a regression test. Period.
|
||||
|
||||
#. When writing a new feature, think about writing unit tests to verify
|
||||
the behavior step-by-step as you write the feature. Every time you'd
|
||||
go to run your code by hand and verify it manually, think "could I
|
||||
write a test to do this instead?". That way when the feature is done
|
||||
and you're ready to commit it you've already got a whole set of tests
|
||||
that are more thorough than anything you'd write after the fact.
|
||||
|
||||
#. Write tests that hit every view in your application. Even if they
|
||||
don't assert a single thing about the code, it tells you that your
|
||||
users aren't getting fatal errors just by interacting with your code.
|
||||
|
||||
What makes a good unit test?
|
||||
============================
|
||||
|
||||
Limiting our focus just to unit tests, there are a number of things you can
|
||||
do to make your unit tests as useful, maintainable, and unburdensome as
|
||||
possible.
|
||||
|
||||
Test data
|
||||
---------
|
||||
|
||||
Use a single, consistent set of test data. Grow it over time, but do everything
|
||||
you can not to fragment it. It quickly becomes unmaintainable and perniciously
|
||||
out-of-sync with reality.
|
||||
|
||||
Make your test data as accurate to reality as possible. Supply *all* the
|
||||
attributes of an object, provide objects in all the various states you may want
|
||||
to test.
|
||||
|
||||
If you do the first suggestion above *first* it makes the second one far less
|
||||
painful. Write once, use everywhere.
|
||||
|
||||
To make your life even easier, if your codebase doesn't have a built-in
|
||||
ORM-like function to manage your test data you can consider building (or
|
||||
borrowing) one yourself. Being able to do simple retrieval queries on your
|
||||
test data is incredibly valuable.
|
||||
|
||||
Mocking
|
||||
-------
|
||||
|
||||
Mocking is the practice of providing stand-ins for objects or pieces of code
|
||||
you don't need to test. While convenient, they should be used with *extreme*
|
||||
caution.
|
||||
|
||||
Why? Because overuse of mocks can rapidly land you in a situation where you're
|
||||
not testing any real code. All you've done is verified that your mocking
|
||||
framework returns what you tell it to. This problem can be very tricky to
|
||||
recognize, since you may be mocking things in ``setUp`` methods, other modules,
|
||||
etc.
|
||||
|
||||
A good rule of thumb is to mock as close to the source as possible. If you have
|
||||
a function call that calls an external API in a view , mock out the external
|
||||
API, not the whole function. If you mock the whole function you've suddenly
|
||||
lost test coverage for an entire chunk of code *inside* your codebase. Cut the
|
||||
ties cleanly right where your system ends and the external world begins.
|
||||
|
||||
Similarly, don't mock return values when you could construct a real return
|
||||
value of the correct type with the correct attributes. You're just adding
|
||||
another point of potential failure by exercising your mocking framework instead
|
||||
of real code. Following the suggestions for testing above will make this a lot
|
||||
less burdensome.
|
||||
|
||||
Assertions and verification
|
||||
---------------------------
|
||||
|
||||
Think long and hard about what you really want to verify in your unit test. In
|
||||
particular, think about what custom logic your code executes.
|
||||
|
||||
A common pitfall is to take a known test object, pass it through your code,
|
||||
and then verify the properties of that object on the output. This is all well
|
||||
and good, except if you're verifying properties that were untouched by your
|
||||
code. What you want to check are the pieces that were *changed*, *added*, or
|
||||
*removed*. Don't check the object's id attribute unless you have reason to
|
||||
suspect it's not the object you started with. But if you added a new attribute
|
||||
to it, be damn sure you verify that came out right.
|
||||
|
||||
It's also very common to avoid testing things you really care about because
|
||||
it's more difficult. Verifying that the proper messages were displayed to the
|
||||
user after an action, testing for form errors, making sure exception handling
|
||||
is tested... these types of things aren't always easy, but they're extremely
|
||||
necessary.
|
||||
|
||||
To that end, Horizon includes several custom assertions to make these tasks
|
||||
easier. :meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors`,
|
||||
:meth:`~horizon.test.helpers.TestCase.assertMessageCount`, and
|
||||
:meth:`~horizon.test.helpers.TestCase.assertNoMessages` all exist for exactly
|
||||
these purposes. Moreover, they provide useful output when things go wrong so
|
||||
you're not left scratching your head wondering why your view test didn't
|
||||
redirect as expected when you posted a form.
|
||||
|
||||
.. _debugging_unit_tests:
|
||||
|
||||
Debugging Unit Tests
|
||||
====================
|
||||
|
||||
Tips and tricks
|
||||
---------------
|
||||
|
||||
#. Use :meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors`
|
||||
immediately after your ``client.post`` call for tests that handle form views.
|
||||
This will immediately fail if your form POST failed due to a validation error
|
||||
and tell you what the error was.
|
||||
|
||||
#. Use :meth:`~horizon.test.helpers.TestCase.assertMessageCount` and
|
||||
:meth:`~horizon.test.helpers.TestCase.assertNoMessages` when a piece of code
|
||||
is failing inexplicably. Since the core error handlers attach user-facing
|
||||
error messages (and since the core logging is silenced during test runs)
|
||||
these methods give you the dual benefit of verifying the output you expect
|
||||
while clearly showing you the problematic error message if they fail.
|
||||
|
||||
#. Use Python's ``pdb`` module liberally. Many people don't realize it works
|
||||
just as well in a test case as it does in a live view. Simply inserting
|
||||
``import pdb; pdb.set_trace()`` anywhere in your codebase will drop the
|
||||
interpreter into an interactive shell so you can explore your test
|
||||
environment and see which of your assumptions about the code isn't,
|
||||
in fact, flawlessly correct.
|
||||
|
||||
#. If the error is in the Selenium test suite, you're likely getting very little
|
||||
information about the error. To increase the information provided to you,
|
||||
edit ``horizon/test/settings.py`` to set ``DEBUG = True`` and set the logging
|
||||
level to 'DEBUG' for the default 'test' logger. Also, add a logger config
|
||||
for Django::
|
||||
|
||||
},
|
||||
'loggers': {
|
||||
+ 'django': {
|
||||
+ 'handlers': ['test'],
|
||||
+ 'propagate': False,
|
||||
+ },
|
||||
'django.db.backends': {
|
||||
|
||||
Coverage reports
|
||||
----------------
|
||||
|
||||
It is possible for tests to fail on your patch due to the npm-run-test not
|
||||
passing the minimum threshold. This is not necessarily related directly to the
|
||||
functions in the patch that have failed, but more that there are not enough
|
||||
tests across horizon that are related to your patch.
|
||||
|
||||
The coverage reports may be found in the 'cover' directory. There's a
|
||||
subdirectory for horizon and openstack_dashboard, and then under a directory
|
||||
for the browser used to run the tests you should find an ``index.html``. This
|
||||
can then be viewed to see the coverage details.
|
||||
|
||||
In this scenario you may need to submit a secondary patch to address test
|
||||
coverage for another function within horizon to ensure tests rise above the
|
||||
coverage threshold and your original patch can pass the necessary tests.
|
||||
|
||||
Common pitfalls
|
||||
---------------
|
||||
|
||||
There are a number of typical (and non-obvious) ways to break the unit tests.
|
||||
Some common things to look for:
|
||||
|
||||
#. Make sure you stub out the method exactly as it's called in the code
|
||||
being tested. For example, if your real code calls
|
||||
``api.keystone.tenant_get``, stubbing out ``api.tenant_get`` (available
|
||||
for legacy reasons) will fail.
|
||||
|
||||
#. When defining the expected input to a stubbed call, make sure the
|
||||
arguments are *identical*, this includes ``str`` vs. ``int`` differences.
|
||||
|
||||
#. Make sure your test data are completely in line with the expected inputs.
|
||||
Again, ``str`` vs. ``int`` or missing properties on test objects will
|
||||
kill your tests.
|
||||
|
||||
#. Make sure there's nothing amiss in your templates (particularly the
|
||||
``{% url %}`` tag and its arguments). This often comes up when refactoring
|
||||
views or renaming context variables. It can easily result in errors that
|
||||
you might not stumble across while clicking around the development server.
|
||||
|
||||
#. Make sure you're not redirecting to views that no longer exist, e.g.
|
||||
the ``index`` view for a panel that got combined (such as instances &
|
||||
volumes).
|
||||
|
||||
#. Make sure your mock calls are in order before calling ``mox.ReplayAll``.
|
||||
The order matters.
|
||||
|
||||
#. Make sure you repeat any stubbed out method calls that happen more than
|
||||
once. They don't automatically repeat, you have to explicitly define them.
|
||||
While this is a nuisance, it makes you acutely aware of how many API
|
||||
calls are involved in a particular function.
|
||||
|
||||
Understanding the output from ``mox``
|
||||
-------------------------------------
|
||||
|
||||
Horizon uses ``mox`` as its mocking framework of choice, and while it
|
||||
offers many nice features, its output when a test fails can be quite
|
||||
mysterious.
|
||||
|
||||
Unexpected Method Call
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This occurs when you stubbed out a piece of code, and it was subsequently
|
||||
called in a way that you didn't specify it would be. There are two reasons
|
||||
this tends to come up:
|
||||
|
||||
#. You defined the expected call, but a subtle difference crept in. This
|
||||
may be a string versus integer difference, a string versus unicode
|
||||
difference, a slightly off date/time, or passing a name instead of an id.
|
||||
|
||||
#. The method is actually being called *multiple times*. Since mox uses
|
||||
a call stack internally, it simply pops off the expected method calls to
|
||||
verify them. That means once a call is used once, it's gone. An easy way
|
||||
to see if this is the case is simply to copy and paste your method call a
|
||||
second time to see if the error changes. If it does, that means your method
|
||||
is being called more times than you think it is.
|
||||
|
||||
Expected Method Never Called
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This one is the opposite of the unexpected method call. This one means you
|
||||
told mox to expect a call and it didn't happen. This is almost always the
|
||||
result of an error in the conditions of the test. Using the
|
||||
:meth:`~openstack_dashboard.test.helpers.TestCase.assertNoFormErrors` and
|
||||
:meth:`~horizon.test.helpers.TestCase.assertMessageCount` will make it readily
|
||||
apparent what the problem is in the majority of cases. If not, then use ``pdb``
|
||||
and start interrupting the code flow to see where things are getting off track.
|
|
@ -1,279 +0,0 @@
|
|||
======================
|
||||
Translation in Horizon
|
||||
======================
|
||||
|
||||
What is the point of translating my code?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You introduced an awesome piece of code and revel in your glorious
|
||||
accomplishment. Suddenly your world comes crashing down when a core hands you
|
||||
a -1 because your code is not translated. What gives?
|
||||
|
||||
If you are writing software for a global audience, you must ensure that it is
|
||||
translated so that other people around the world are able to use it. Adding
|
||||
translation to your code is not that hard and a requirement for horizon.
|
||||
|
||||
If you are interested in contributing translations, you may want to investigate
|
||||
`Zanata <https://translate.openstack.org>`_ and the
|
||||
`upstream translations <http://docs.openstack.org/developer/i18n/>`_.
|
||||
You can visit the internationalization project IRC channel **#openstack-i18n**,
|
||||
if you need further assistance.
|
||||
|
||||
Overview and Architecture
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can skip this section if you are only interested in learning how to use
|
||||
translation. This section explains the two main components to translation:
|
||||
message extraction and message substitution. We will briefly go over what each
|
||||
one does for translation as a whole.
|
||||
|
||||
Message Extraction
|
||||
------------------
|
||||
|
||||
.. The source can be found at:
|
||||
https://drive.google.com/open?id=0B5nlaOV3OEj5MTNMdG9WV1RiVEU
|
||||
|
||||
.. figure:: ../../images/message_extraction.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
:alt: Message extraction diagram
|
||||
|
||||
Message extraction is the process of collecting translatable strings from the
|
||||
code. The diagram above shows the flow of how messages are extracted and then
|
||||
translated. Lets break this up into steps we can follow:
|
||||
|
||||
1. The first step is to mark untranslated strings so that the extractor is able
|
||||
to locate them. Refer to the guide below on how to use translation and what
|
||||
these markers look like.
|
||||
|
||||
2. Once marked, we can then run ``tox -e manage -- extract_messages``, which
|
||||
searches the codebase for these markers and extracts them into a Portable
|
||||
Object Template (POT) file. In horizon, we extract from both the ``horizon``
|
||||
folder and the ``openstack_dashboard`` folder. We use the AngularJS extractor
|
||||
for JavaScript and HTML files and the Django extractor for Python and Django
|
||||
templates; both extractors are Babel plugins.
|
||||
|
||||
3. To update the .po files, you can run ``tox -e manage -- update_catalog`` to
|
||||
update the .po file for every language, or you can specify a specific
|
||||
language to update like this: ``tox -e manage -- update_catalog de``. This
|
||||
is useful if you want to add a few extra translatabale strings for a
|
||||
downstream customisation.
|
||||
|
||||
.. Note ::
|
||||
|
||||
When pushing code upstream, the only requirement is to mark the strings
|
||||
correctly. All creation of POT and PO files is handled by a daily upstream
|
||||
job. Further information can be found in the
|
||||
`translation infrastructure documentation
|
||||
<http://docs.openstack.org/developer/i18n/infra.html>`_.
|
||||
|
||||
Message Substitution
|
||||
--------------------
|
||||
|
||||
.. The source can be found at:
|
||||
https://drive.google.com/open?id=0B5nlaOV3OEj5UHZCNmFGT0lPQVU
|
||||
|
||||
.. figure:: ../../images/message_substitution.png
|
||||
:width: 80%
|
||||
:align: center
|
||||
:alt: Message substitution diagram
|
||||
|
||||
Message substitution is not the reverse process of message extraction. The
|
||||
process is entirely different. Lets walk through this process.
|
||||
|
||||
* Remember those markers we talked about earlier? Most of them are functions
|
||||
like gettext or one of its variants. This allows the function to serve a dual
|
||||
purpose - acting as a marker and also as a replacer.
|
||||
|
||||
* In order for translation to work properly, we need to know the user’s locale.
|
||||
In horizon, the user can specify the locale using the Settings panel. Once we
|
||||
know the locale, we know which Portable Object (PO) file to use. The PO file
|
||||
is the file we received from translators in the message extraction process.
|
||||
The gettext functions that we wrapped our code around are then able to
|
||||
replace the untranslated strings with the translated one by using the
|
||||
untranslated string as the message id.
|
||||
|
||||
* For client-side translation, Django embeds a corresponding Django message
|
||||
catalog. Javascript code on the client can use this catalog to do string
|
||||
replacement similar to how server-side translation works.
|
||||
|
||||
If you are setting up a project and need to know how to make it translatable,
|
||||
please refer to `this guide
|
||||
<http://docs.openstack.org/infra/manual/creators.html#enabling-translation-infrastructure>`_.
|
||||
|
||||
.. _making_strings_translatable:
|
||||
|
||||
Making strings translatable
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To make your strings translatable, you need to mark it so that horizon can
|
||||
locate and extract it into a POT file. When a user from another locale visits
|
||||
your page, your string is replaced with the correct translated version.
|
||||
|
||||
In Django
|
||||
---------
|
||||
|
||||
To translate a string, simply wrap one of the gettext variants around the
|
||||
string. The examples below show you how to do translation for various
|
||||
scenarios, such as interpolation, contextual markers and translation comments.
|
||||
|
||||
::
|
||||
|
||||
from django.utils.translation import pgettext
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ungettext
|
||||
|
||||
|
||||
class IndexView(request):
|
||||
|
||||
# Single example
|
||||
_("Images")
|
||||
|
||||
# Plural example
|
||||
ungettext(
|
||||
'there is %(count)d object',
|
||||
'there are %(count)d objects',
|
||||
count) % { 'count': count }
|
||||
|
||||
# Interpolated example
|
||||
mood = ‘wonderful’
|
||||
output = _('Today is %(mood)s.') % mood
|
||||
|
||||
# Contextual markers
|
||||
pgettext("the month name", "May")
|
||||
|
||||
# Translators: This message appears as a comment for translators!
|
||||
ugettext("Welcome translators.")
|
||||
|
||||
.. Note ::
|
||||
|
||||
In the example above, we imported ``ugettext`` as ``_``. This is a common
|
||||
alias for gettext or any of its variants. In Django, you have to explicitly
|
||||
spell it out with the import statement.
|
||||
|
||||
In Django templates
|
||||
-------------------
|
||||
|
||||
To use translation in your template, make sure you load the i18n module. To
|
||||
translate a line of text, use the ``trans`` template tag. If you need to
|
||||
translate a block of text, use the ``blocktrans`` template tag.
|
||||
|
||||
Sometimes, it is helpful to provide some context via the ``comment`` template
|
||||
tag. There a number of other tags and filters at your disposal should you need
|
||||
to use them. For more information, see the
|
||||
`Django docs <https://docs.djangoproject.com/en/1.8/topics/i18n/translation/>`_
|
||||
|
||||
::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}
|
||||
{% trans "Images" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
{% comment %}Translators: Images is an OpenStack resource{% endcomment %}
|
||||
{% blocktrans with amount=images.length %}
|
||||
There are {{ amount }} images available for display.
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
||||
|
||||
In JavaScript
|
||||
-------------
|
||||
|
||||
The Django message catalog is injected into the front-end. The gettext function
|
||||
is available as a global function so you can just use it directly. If you are
|
||||
writing AngularJS code, we prefer that you use the gettext service, which is
|
||||
essentially a wrapper around the gettext function.
|
||||
|
||||
::
|
||||
|
||||
Angular
|
||||
.module(…)
|
||||
.controller(myCtrl);
|
||||
|
||||
myCtrl.$inject = [‘horizon.framework.util.i18n.gettext’];
|
||||
function myCtrl(gettext) {
|
||||
var translated = gettext(‘Images’);
|
||||
}
|
||||
|
||||
.. warning ::
|
||||
|
||||
For localization in AngularJS files, use the
|
||||
AngularJS service ``horizon.framework.util.i18n.gettext``. Ensure that the
|
||||
injected dependency is named ``gettext`` or ``nggettext``. If you do not do this,
|
||||
message extraction will not work properly!
|
||||
|
||||
|
||||
In AngularJS templates
|
||||
-----------------------
|
||||
|
||||
To use translation in your AngularJS template, use the translate tag or the
|
||||
translate filter. Note that we are using
|
||||
`angular-gettext <https://angular-gettext.rocketeer.be/>`_
|
||||
for message substitution but not for message extraction.
|
||||
|
||||
::
|
||||
|
||||
<translate>Directive example</translate>
|
||||
<div translate>Attribute example</div>
|
||||
<div translate>Interpolated {{example}}</div>
|
||||
<span>{$ ‘Filter example’|translate $}</span>
|
||||
|
||||
<span translate>
|
||||
This <em>is</em> a <strong>bad</strong> example
|
||||
because it contains HTML and makes it harder to translate.
|
||||
However, it will still translate.
|
||||
</span>
|
||||
|
||||
.. Note ::
|
||||
|
||||
The annotations in the example above are guaranteed to work. However, not all of
|
||||
the angular-gettext annotations are supported because we wrote our own custom
|
||||
babel extractor. If you need support for the annotations, ask on IRC in the
|
||||
#openstack-horizon room or report a bug. Also note that you should avoid embedding
|
||||
HTML fragments in your texts because it makes it harder to translate. Use your
|
||||
best judgement if you absolutely need to include HTML.
|
||||
|
||||
.. _pseudo_translation:
|
||||
|
||||
Pseudo translation tool
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The pseudo translation tool can be used to verify that code is ready to be
|
||||
translated. The pseudo tool replaces a language's translation with a complete,
|
||||
fake translation. Then you can verify that your code properly displays fake
|
||||
translations to validate that your code is ready for translation.
|
||||
|
||||
Running the pseudo translation tool
|
||||
-----------------------------------
|
||||
|
||||
#. Make sure your .pot files are up to date:
|
||||
``tox -e manage -- extract_messages``
|
||||
#. Run the pseudo tool to create pseudo translations. For example, to replace
|
||||
the German translation with a pseudo translation:
|
||||
``tox -e manage -- update_catalog de --pseudo``
|
||||
#. Compile the catalog: ``tox -e manage -- compilemessages``
|
||||
#. Run your development server.
|
||||
#. Log in and change to the language you pseudo translated.
|
||||
|
||||
It should look weird. More specifically, the translatable segments are going
|
||||
to start and end with a bracket and they are going to have some added
|
||||
characters. For example, "Log In" will become "[~Log In~您好яшçあ]"
|
||||
This is useful because you can inspect for the following, and consider if your
|
||||
code is working like it should:
|
||||
|
||||
* If you see a string in English it's not translatable. Should it be?
|
||||
* If you see brackets next to each other that might be concatenation. Concatenation
|
||||
can make quality translations difficult or impossible. See
|
||||
`"Use string formatting variables, never perform string concatenation"
|
||||
<https://wiki.openstack.org/wiki/I18n/TranslatableStrings#Use_string_formating_variables.2C_never_perform_string_concatenation>`_
|
||||
for additional information.
|
||||
* If there is unexpected wrapping/truncation there might not be enough
|
||||
space for translations.
|
||||
* If you see a string in the proper translated language, it comes from an
|
||||
external source. (That's not bad, just sometimes useful to know)
|
||||
* If you get new crashes, there is probably a bug.
|
||||
|
||||
Don't forget to remove any pseudo translated ``.pot`` or ``.po`` files.
|
||||
Those should not be submitted for review.
|
|
@ -1,136 +0,0 @@
|
|||
.. _topics-workflows:
|
||||
|
||||
======================
|
||||
Workflows Topic Guide
|
||||
======================
|
||||
|
||||
One of the most challenging aspects of building a compelling user experience
|
||||
is crafting complex multi-part workflows. Horizon's ``workflows`` module
|
||||
aims to bring that capability within everyday reach.
|
||||
|
||||
.. seealso::
|
||||
|
||||
For detailed API information refer to the :ref:`ref-workflows`.
|
||||
|
||||
Workflows
|
||||
=========
|
||||
|
||||
Workflows are complex forms with tabs, each workflow must consist of classes
|
||||
extending the :class:`~horizon.workflows.Workflow`,
|
||||
:class:`~horizon.workflows.Step` and :class:`~horizon.workflows.Action`
|
||||
|
||||
Complex example of a workflow
|
||||
------------------------------
|
||||
|
||||
The following is a complex example of how data is exchanged between
|
||||
urls, views, workflows and templates:
|
||||
|
||||
#. In ``urls.py``, we have the named parameter. E.g. ``resource_class_id``. ::
|
||||
|
||||
RESOURCE_CLASS = r'^(?P<resource_class_id>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = [
|
||||
url(RESOURCE_CLASS % 'update', UpdateView.as_view(), name='update')
|
||||
]
|
||||
|
||||
#. In ``views.py``, we pass data to the template and to the action(form)
|
||||
(action can also pass data to the ``get_context_data`` method and to the
|
||||
template). ::
|
||||
|
||||
class UpdateView(workflows.WorkflowView):
|
||||
workflow_class = UpdateResourceClass
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||
# Data from URL are always in self.kwargs, here we pass the data
|
||||
# to the template.
|
||||
context["resource_class_id"] = self.kwargs['resource_class_id']
|
||||
# Data contributed by Workflow's Steps are in the
|
||||
# context['workflow'].context list. We can use that in the
|
||||
# template too.
|
||||
return context
|
||||
|
||||
def _get_object(self, *args, **kwargs):
|
||||
# Data from URL are always in self.kwargs, we can use them here
|
||||
# to load our object of interest.
|
||||
resource_class_id = self.kwargs['resource_class_id']
|
||||
# Code omitted, this method should return some object obtained
|
||||
# from API.
|
||||
|
||||
def get_initial(self):
|
||||
resource_class = self._get_object()
|
||||
# This data will be available in the Action's methods and
|
||||
# Workflow's handle method.
|
||||
# But only if the steps will depend on them.
|
||||
return {'resource_class_id': resource_class.id,
|
||||
'name': resource_class.name,
|
||||
'service_type': resource_class.service_type}
|
||||
|
||||
#. In ``workflows.py`` we process the data, it is just more complex django
|
||||
form. ::
|
||||
|
||||
class ResourcesAction(workflows.Action):
|
||||
# The name field will be automatically available in all action's
|
||||
# methods.
|
||||
# If we want this field to be used in the another Step or Workflow,
|
||||
# it has to be contributed by this step, then depend on in another
|
||||
# step.
|
||||
name = forms.CharField(max_length=255,
|
||||
label=_("Testing Name"),
|
||||
help_text="",
|
||||
required=True)
|
||||
|
||||
def handle(self, request, data):
|
||||
pass
|
||||
# If we want to use some data from the URL, the Action's step
|
||||
# has to depend on them. It's then available in
|
||||
# self.initial['resource_class_id'] or data['resource_class_id'].
|
||||
# In other words, resource_class_id has to be passed by view's
|
||||
# get_initial and listed in step's depends_on list.
|
||||
|
||||
# We can also use here the data from the other steps. If we want
|
||||
# the data from the other step, the step needs to contribute the
|
||||
# data and the steps needs to be ordered properly.
|
||||
|
||||
class UpdateResources(workflows.Step):
|
||||
action_class = ResourcesAction
|
||||
# This passes data from Workflow context to action methods
|
||||
# (handle, clean). Workflow context consists of URL data and data
|
||||
# contributed by other steps.
|
||||
depends_on = ("resource_class_id",)
|
||||
|
||||
# By contributing, the data on these indexes will become available to
|
||||
# Workflow and to other Steps (if they will depend on them). Notice,
|
||||
# that the resources_object_ids key has to be manually added in
|
||||
# contribute method first.
|
||||
contributes = ("resources_object_ids", "name")
|
||||
|
||||
def contribute(self, data, context):
|
||||
# We can obtain the http request from workflow.
|
||||
request = self.workflow.request
|
||||
if data:
|
||||
# Only fields defined in Action are automatically
|
||||
# available for contribution. If we want to contribute
|
||||
# something else, We need to override the contribute method
|
||||
# and manually add it to the dictionary.
|
||||
context["resources_object_ids"] =\
|
||||
request.POST.getlist("resources_object_ids")
|
||||
|
||||
# We have to merge new context with the passed data or let
|
||||
# the superclass do this.
|
||||
context.update(data)
|
||||
return context
|
||||
|
||||
class UpdateResourceClass(workflows.Workflow):
|
||||
default_steps = (UpdateResources,)
|
||||
|
||||
def handle(self, request, data):
|
||||
pass
|
||||
# This method is called as last (after all Action's handle
|
||||
# methods). All data that are listed in step's 'contributes='
|
||||
# and 'depends_on=' are available here.
|
||||
# It can be easier to have the saving logic only here if steps
|
||||
# are heavily connected or complex.
|
||||
|
||||
# data["resources_object_ids"], data["name"] and
|
||||
# data["resources_class_id"] are available here.
|
|
@ -1,596 +0,0 @@
|
|||
.. _tutorials-dashboard:
|
||||
|
||||
============================================
|
||||
Tutorial: Building a Dashboard using Horizon
|
||||
============================================
|
||||
|
||||
This tutorial covers how to use the various components in horizon to build
|
||||
an example dashboard and a panel with a tab which has a table containing data
|
||||
from the back end.
|
||||
|
||||
As an example, we'll create a new ``My Dashboard`` dashboard with a ``My Panel``
|
||||
panel that has an ``Instances Tab`` tab. The tab has a table which contains the
|
||||
data pulled by the Nova instances API.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
This tutorial assumes you have either a ``devstack`` or
|
||||
``openstack`` environment up and running. There are a variety of
|
||||
other resources which may be helpful to read first. For example,
|
||||
you may want to start with the :ref:`quickstart` or the `Django
|
||||
tutorial`_.
|
||||
|
||||
.. _Django tutorial: https://docs.djangoproject.com/en/dev/intro/tutorial01/
|
||||
|
||||
|
||||
Creating a dashboard
|
||||
====================
|
||||
|
||||
The quick version
|
||||
-----------------
|
||||
|
||||
Horizon provides a custom management command to create a typical base
|
||||
dashboard structure for you. Run the following commands in your Horizon root
|
||||
directory. It generates most of the boilerplate code you need::
|
||||
|
||||
$ mkdir openstack_dashboard/dashboards/mydashboard
|
||||
|
||||
$ tox -e manage -- startdash mydashboard \
|
||||
--target openstack_dashboard/dashboards/mydashboard
|
||||
|
||||
$ mkdir openstack_dashboard/dashboards/mydashboard/mypanel
|
||||
|
||||
$ tox -e manage -- startpanel mypanel \
|
||||
--dashboard=openstack_dashboard.dashboards.mydashboard \
|
||||
--target=openstack_dashboard/dashboards/mydashboard/mypanel
|
||||
|
||||
|
||||
You will notice that the directory ``mydashboard`` gets automatically
|
||||
populated with the files related to a dashboard and the ``mypanel`` directory
|
||||
gets automatically populated with the files related to a panel.
|
||||
|
||||
|
||||
Structure
|
||||
---------
|
||||
If you use the ``tree mydashboard`` command to list the ``mydashboard``
|
||||
directory in ``openstack_dashboard/dashboards`` , you will see a directory
|
||||
structure that looks like the following::
|
||||
|
||||
mydashboard
|
||||
├── dashboard.py
|
||||
├── dashboard.pyc
|
||||
├── __init__.py
|
||||
├── __init__.pyc
|
||||
├── mypanel
|
||||
│ ├── __init__.py
|
||||
│ ├── panel.py
|
||||
│ ├── templates
|
||||
│ │ └── mypanel
|
||||
│ │ └── index.html
|
||||
│ ├── tests.py
|
||||
│ ├── urls.py
|
||||
│ └── views.py
|
||||
├── static
|
||||
│ └── mydashboard
|
||||
│ ├── css
|
||||
│ │ └── mydashboard.css
|
||||
│ └── js
|
||||
│ └── mydashboard.js
|
||||
└── templates
|
||||
└── mydashboard
|
||||
└── base.html
|
||||
|
||||
|
||||
For this tutorial, we will not deal with the static directory, or the
|
||||
``tests.py`` file. Leave them as they are.
|
||||
|
||||
With the rest of the files and directories in place, we can move on to add our
|
||||
own dashboard.
|
||||
|
||||
|
||||
Defining a dashboard
|
||||
--------------------
|
||||
|
||||
Open the ``dashboard.py`` file. You will notice the following code has been
|
||||
automatically generated::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Mydashboard(horizon.Dashboard):
|
||||
name = _("Mydashboard")
|
||||
slug = "mydashboard"
|
||||
panels = () # Add your panels here.
|
||||
default_panel = '' # Specify the slug of the dashboard's default panel.
|
||||
|
||||
|
||||
horizon.register(Mydashboard)
|
||||
|
||||
|
||||
If you want the dashboard name to be something else, you can change the ``name``
|
||||
attribute in the ``dashboard.py`` file . For example, you can change it
|
||||
to be ``My Dashboard`` ::
|
||||
|
||||
name = _("My Dashboard")
|
||||
|
||||
|
||||
A dashboard class will usually contain a ``name`` attribute (the display name of
|
||||
the dashboard), a ``slug`` attribute (the internal name that could be referenced
|
||||
by other components), a list of panels, default panel, etc. We will cover how
|
||||
to add a panel in the next section.
|
||||
|
||||
|
||||
Creating a panel
|
||||
================
|
||||
|
||||
We'll create a panel and call it ``My Panel``.
|
||||
|
||||
Structure
|
||||
---------
|
||||
|
||||
As described above, the ``mypanel`` directory under
|
||||
``openstack_dashboard/dashboards/mydashboard`` should look like the following::
|
||||
|
||||
mypanel
|
||||
├── __init__.py
|
||||
├── models.py
|
||||
├── panel.py
|
||||
├── templates
|
||||
│ └── mypanel
|
||||
│ └── index.html
|
||||
├── tests.py
|
||||
├── urls.py
|
||||
└── views.py
|
||||
|
||||
|
||||
Defining a panel
|
||||
----------------
|
||||
|
||||
The ``panel.py`` file referenced above has a special meaning.
|
||||
Within a dashboard, any module name listed in the ``panels`` attribute on the
|
||||
dashboard class will be auto-discovered by looking for the ``panel.py`` file in
|
||||
a corresponding directory (the details are a bit magical, but have been
|
||||
thoroughly vetted in Django's admin codebase).
|
||||
|
||||
Open the ``panel.py`` file, you will have the following auto-generated code::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard import dashboard
|
||||
|
||||
|
||||
class Mypanel(horizon.Panel):
|
||||
name = _("Mypanel")
|
||||
slug = "mypanel"
|
||||
|
||||
|
||||
dashboard.Mydashboard.register(Mypanel)
|
||||
|
||||
|
||||
If you want the panel name to be something else, you can change the ``name``
|
||||
attribute in the ``panel.py`` file . For example, you can change it to be
|
||||
``My Panel``::
|
||||
|
||||
name = _("My Panel")
|
||||
|
||||
|
||||
Open the ``dashboard.py`` file again, insert the following code above the
|
||||
``Mydashboard`` class. This code defines the ``Mygroup`` class and adds a panel
|
||||
called ``mypanel``::
|
||||
|
||||
class Mygroup(horizon.PanelGroup):
|
||||
slug = "mygroup"
|
||||
name = _("My Group")
|
||||
panels = ('mypanel',)
|
||||
|
||||
|
||||
Modify the ``Mydashboard`` class to include ``Mygroup`` and add ``mypanel`` as
|
||||
the default panel::
|
||||
|
||||
class Mydashboard(horizon.Dashboard):
|
||||
name = _("My Dashboard")
|
||||
slug = "mydashboard"
|
||||
panels = (Mygroup,) # Add your panels here.
|
||||
default_panel = 'mypanel' # Specify the slug of the default panel.
|
||||
|
||||
|
||||
The completed ``dashboard.py`` file should look like
|
||||
the following::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Mygroup(horizon.PanelGroup):
|
||||
slug = "mygroup"
|
||||
name = _("My Group")
|
||||
panels = ('mypanel',)
|
||||
|
||||
|
||||
class Mydashboard(horizon.Dashboard):
|
||||
name = _("My Dashboard")
|
||||
slug = "mydashboard"
|
||||
panels = (Mygroup,) # Add your panels here.
|
||||
default_panel = 'mypanel' # Specify the slug of the default panel.
|
||||
|
||||
|
||||
horizon.register(Mydashboard)
|
||||
|
||||
|
||||
|
||||
Tables, Tabs, and Views
|
||||
-----------------------
|
||||
|
||||
We'll start with the table, combine that with the tabs, and then build our
|
||||
view from the pieces.
|
||||
|
||||
Defining a table
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Horizon provides a :class:`~horizon.forms.SelfHandlingForm`
|
||||
:class:`~horizon.tables.DataTable` class which simplifies the vast majority of
|
||||
displaying data to an end-user. We're just going to skim the surface here, but
|
||||
it has a tremendous number of capabilities. Create a ``tables.py`` file under
|
||||
the ``mypanel`` directory and add the following code::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
name = tables.Column("name", verbose_name=_("Name"))
|
||||
status = tables.Column("status", verbose_name=_("Status"))
|
||||
zone = tables.Column('availability_zone',
|
||||
verbose_name=_("Availability Zone"))
|
||||
image_name = tables.Column('image_name', verbose_name=_("Image Name"))
|
||||
|
||||
class Meta(object):
|
||||
name = "instances"
|
||||
verbose_name = _("Instances")
|
||||
|
||||
|
||||
There are several things going on here... we created a table subclass,
|
||||
and defined four columns that we want to retrieve data and display.
|
||||
Each of those columns defines what attribute it accesses on the instance object
|
||||
as the first argument, and since we like to make everything translatable,
|
||||
we give each column a ``verbose_name`` that's marked for translation.
|
||||
|
||||
Lastly, we added a ``Meta`` class which indicates the meta object that describes
|
||||
the ``instances`` table.
|
||||
|
||||
.. note::
|
||||
|
||||
This is a slight simplification from the reality of how the instance
|
||||
object is actually structured. In reality, accessing other attributes
|
||||
requires an additional step.
|
||||
|
||||
Adding actions to a table
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Horizon provides three types of basic action classes which can be taken
|
||||
on a table's data:
|
||||
|
||||
- :class:`~horizon.tables.Action`
|
||||
- :class:`~horizon.tables.LinkAction`
|
||||
- :class:`~horizon.tables.FilterAction`
|
||||
|
||||
|
||||
There are also additional actions which are extensions of the basic Action
|
||||
classes:
|
||||
|
||||
- :class:`~horizon.tables.BatchAction`
|
||||
- :class:`~horizon.tables.DeleteAction`
|
||||
- :class:`~horizon.tables.UpdateAction` **DEPRECATED**
|
||||
- :class:`~horizon.tables.FixedFilterAction`
|
||||
|
||||
|
||||
|
||||
Now let's create and add a filter action to the table. To do so, we will need
|
||||
to edit the ``tables.py`` file used above. To add a filter action which will
|
||||
only show rows which contain the string entered in the filter field, we
|
||||
must first define the action::
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
The action specified above will default the ``filter_type`` to be ``"query"``.
|
||||
This means that the filter will use the client side table sorter.
|
||||
|
||||
Then, we add that action to the table actions for our table.::
|
||||
|
||||
class InstancesTable:
|
||||
class Meta(object):
|
||||
table_actions = (MyFilterAction,)
|
||||
|
||||
|
||||
The completed ``tables.py`` file should look like the following::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
name = tables.Column('name', \
|
||||
verbose_name=_("Name"))
|
||||
status = tables.Column('status', \
|
||||
verbose_name=_("Status"))
|
||||
zone = tables.Column('availability_zone', \
|
||||
verbose_name=_("Availability Zone"))
|
||||
image_name = tables.Column('image_name', \
|
||||
verbose_name=_("Image Name"))
|
||||
|
||||
class Meta(object):
|
||||
name = "instances"
|
||||
verbose_name = _("Instances")
|
||||
table_actions = (MyFilterAction,)
|
||||
|
||||
|
||||
Defining tabs
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
So we have a table, ready to receive our data. We could go straight to a view
|
||||
from here, but in this case we're also going to use horizon's
|
||||
:class:`~horizon.tabs.TabGroup` class.
|
||||
|
||||
Create a ``tabs.py`` file under the ``mypanel`` directory. Let's make a tab
|
||||
group which has one tab. The completed code should look like the following::
|
||||
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel import tables
|
||||
|
||||
|
||||
class InstanceTab(tabs.TableTab):
|
||||
name = _("Instances Tab")
|
||||
slug = "instances_tab"
|
||||
table_classes = (tables.InstancesTable,)
|
||||
template_name = ("horizon/common/_detail_table.html")
|
||||
preload = False
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._has_more
|
||||
|
||||
def get_instances_data(self):
|
||||
try:
|
||||
marker = self.request.GET.get(
|
||||
tables.InstancesTable._meta.pagination_param, None)
|
||||
|
||||
instances, self._has_more = api.nova.server_list(
|
||||
self.request,
|
||||
search_opts={'marker': marker, 'paginate': True})
|
||||
|
||||
return instances
|
||||
except Exception:
|
||||
self._has_more = False
|
||||
error_message = _('Unable to get instances')
|
||||
exceptions.handle(self.request, error_message)
|
||||
|
||||
return []
|
||||
|
||||
class MypanelTabs(tabs.TabGroup):
|
||||
slug = "mypanel_tabs"
|
||||
tabs = (InstanceTab,)
|
||||
sticky = True
|
||||
|
||||
|
||||
This tab gets a little more complicated. The tab handles data tables (and
|
||||
all their associated features), and it also uses the ``preload`` attribute to
|
||||
specify that this tab shouldn't be loaded by default. It will instead be loaded
|
||||
via AJAX when someone clicks on it, saving us on API calls in the vast majority
|
||||
of cases.
|
||||
|
||||
Additionally, the displaying of the table is handled by a reusable template,
|
||||
``horizon/common/_detail_table.html``. Some simple pagination code was added
|
||||
to handle large instance lists.
|
||||
|
||||
Lastly, this code introduces the concept of error handling in horizon.
|
||||
The :func:`horizon.exceptions.handle` function is a centralized error
|
||||
handling mechanism that takes all the guess-work and inconsistency out of
|
||||
dealing with exceptions from the API. Use it everywhere.
|
||||
|
||||
Tying it together in a view
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are lots of pre-built class-based views in horizon. We try to provide
|
||||
the starting points for all the common combinations of components.
|
||||
|
||||
Open the ``views.py`` file, the auto-generated code is like the following::
|
||||
|
||||
from horizon import views
|
||||
|
||||
|
||||
class IndexView(views.APIView):
|
||||
# A very simple class-based view...
|
||||
template_name = 'mydashboard/mypanel/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
In this case we want a starting view type that works with both tabs and
|
||||
tables... that'd be the :class:`~horizon.tabs.TabbedTableView` class. It takes
|
||||
the best of the dynamic delayed-loading capabilities tab groups provide and
|
||||
mixes in the actions and AJAX-updating that tables are capable of with almost
|
||||
no work on the user's end. Change ``views.APIView`` to be
|
||||
``tabs.TabbedTableView`` and add ``MypanelTabs`` as the tab group class in the
|
||||
``IndexView`` class::
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = mydashboard_tabs.MypanelTabs
|
||||
|
||||
|
||||
After importing the proper package, the completed ``views.py`` file now looks
|
||||
like the following::
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel \
|
||||
import tabs as mydashboard_tabs
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = mydashboard_tabs.MypanelTabs
|
||||
template_name = 'mydashboard/mypanel/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
URLs
|
||||
----
|
||||
The auto-generated ``urls.py`` file is like::
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
]
|
||||
|
||||
|
||||
The template
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Open the ``index.html`` file in the ``mydashboard/mypanel/templates/mypanel``
|
||||
directory, the auto-generated code is like the following::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Mypanel" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Mypanel") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
The ``main`` block must be modified to insert the following code::
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
If you want to change the title of the ``index.html`` file to be something else,
|
||||
you can change it. For example, change it to be ``My Panel`` in the
|
||||
``block title`` section. If you want the ``title`` in the ``block page_header``
|
||||
section to be something else, you can change it. For example, change it to be
|
||||
``My Panel``. The updated code could be like::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "My Panel" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("My Panel") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
This gives us a custom page title, a header, and renders our tab group provided
|
||||
by the view.
|
||||
|
||||
With all our code in place, the only thing left to do is to integrate it into
|
||||
our OpenStack Dashboard site.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
For more information about Django views, URLs and templates, please refer
|
||||
to the `Django documentation`_.
|
||||
|
||||
.. _Django documentation: https://docs.djangoproject.com/en/dev/
|
||||
|
||||
|
||||
Enable and show the dashboard
|
||||
=============================
|
||||
|
||||
In order to make ``My Dashboard`` show up along with the existing dashboards
|
||||
like ``Project`` or ``Admin`` on horizon, you need to create a file called
|
||||
``_50_mydashboard.py`` under ``openstack_dashboard/enabled`` and add the
|
||||
following::
|
||||
|
||||
# The name of the dashboard to be added to HORIZON['dashboards']. Required.
|
||||
DASHBOARD = 'mydashboard'
|
||||
|
||||
# If set to True, this dashboard will not be added to the settings.
|
||||
DISABLED = False
|
||||
|
||||
# A list of applications to be added to INSTALLED_APPS.
|
||||
ADD_INSTALLED_APPS = [
|
||||
'openstack_dashboard.dashboards.mydashboard',
|
||||
]
|
||||
|
||||
|
||||
Run and check the dashboard
|
||||
===========================
|
||||
|
||||
Everything is in place, now run ``Horizon`` on the different port::
|
||||
|
||||
$ tox -e runserver -- 0:9000
|
||||
|
||||
Go to ``http://<your server>:9000`` using a browser. After login as an admin
|
||||
you should be able see ``My Dashboard`` shows up at the left side on horizon.
|
||||
Click it, ``My Group`` will expand with ``My Panel``. Click on ``My Panel``,
|
||||
the right side panel will display an ``Instances Tab`` which has an
|
||||
``Instances`` table.
|
||||
|
||||
If you don't see any instance data, you haven't created any instances yet. Go to
|
||||
dashboard ``Project`` -> ``Images``, select a small image, for example,
|
||||
``cirros-0.3.1-x86_64-uec`` , click ``Launch`` and enter an ``Instance Name``,
|
||||
click the button ``Launch``. It should create an instance if the OpenStack or
|
||||
devstack is correctly set up. Once the creation of an instance is successful, go
|
||||
to ``My Dashboard`` again to check the data.
|
||||
|
||||
|
||||
Adding a complex action to a table
|
||||
==================================
|
||||
|
||||
For a more detailed look into adding a table action, one that requires forms for
|
||||
gathering data, you can walk through :ref:`tutorials-table-actions` tutorial.
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
What you've learned here is the fundamentals of how to write interfaces for
|
||||
your own project based on the components horizon provides.
|
||||
|
||||
If you have feedback on how this tutorial could be improved, please feel free
|
||||
to submit a bug against ``Horizon`` in
|
||||
`launchpad <https://bugs.launchpad.net/horizon>`__.
|
|
@ -1,13 +0,0 @@
|
|||
=========
|
||||
Tutorials
|
||||
=========
|
||||
|
||||
Detailed tutorials to help you get started.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
plugin
|
||||
dashboard
|
||||
table_actions
|
||||
workflow_extend
|
|
@ -1,510 +0,0 @@
|
|||
.. _tutorials-plugin:
|
||||
|
||||
======================================
|
||||
Tutorial: Creating an Horizon Plugin
|
||||
======================================
|
||||
|
||||
Why should I package my code as a plugin?
|
||||
=========================================
|
||||
|
||||
We highly encourage that you write and maintain your code using our plugin
|
||||
architecture. A plugin by definition means the ability to be connected. In
|
||||
practical terms, plugins are a way to extend and add to the functionality that
|
||||
already exists. You can control its content and progress at a rate independent
|
||||
of Horizon. If you write and package your code as a plugin, it will continue to
|
||||
work in future releases.
|
||||
|
||||
Writing your code as a plugin also modularizes your code making it easier to
|
||||
translate and test. This also makes it easier for deployers to consume your code
|
||||
allowing selective enablement of features. We are currently using this pattern
|
||||
internally for our dashboards.
|
||||
|
||||
Creating the Plugin
|
||||
===================
|
||||
|
||||
This tutorial assumes you have a basic understanding of Python, HTML,
|
||||
JavaScript. Knowledge of AngularJS is optional but recommended if you are
|
||||
attempting to create an Angular plugin.
|
||||
|
||||
Name of your repository
|
||||
-----------------------
|
||||
|
||||
Needless to say, it is important to choose a meaningful repository name.
|
||||
|
||||
In addition, if you plan to support translation on your dashboard plugin,
|
||||
it is recommended to choose a name like ``xxxx-dashboard``
|
||||
(or ``xxxx-ui``. ``xxxx-horizon``). The OpenStack CI infra script
|
||||
considers a repository with these suffixes as Django project.
|
||||
|
||||
Types of Plugins that add content
|
||||
---------------------------------
|
||||
|
||||
The file structure for your plugin type will be different depending on your
|
||||
needs. Your plugin can be categorized into two types:
|
||||
|
||||
* Plugins that create new panels or dashboards
|
||||
* Plugins that modify existing workflows, actions, etc... (Angular only)
|
||||
|
||||
We will cover the basics of working with panels for both Python and Angular.
|
||||
If you are interested in creating a new panel, follow the steps below.
|
||||
|
||||
.. Note ::
|
||||
|
||||
This tutorial shows you how to create a new panel. If you are
|
||||
interested in creating a new dashboard plugin, use the file
|
||||
structure from :ref:`tutorials-dashboard` instead.
|
||||
|
||||
File Structure
|
||||
--------------
|
||||
Below is a skeleton of what your plugin should look like.::
|
||||
|
||||
myplugin
|
||||
│
|
||||
├── myplugin
|
||||
│ ├── __init__.py
|
||||
│ │
|
||||
│ ├── enabled
|
||||
│ │ └──_31000_myplugin.py
|
||||
│ │
|
||||
│ ├── api
|
||||
│ │ ├──__init__.py
|
||||
│ │ ├── my_rest_api.py
|
||||
│ │ └── myservice.py
|
||||
│ │
|
||||
│ ├── content
|
||||
│ │ ├──__init__.py
|
||||
│ │ └── mypanel
|
||||
│ │ ├── __init__.py
|
||||
│ │ ├── panel.py
|
||||
│ │ ├── tests.py
|
||||
│ │ ├── urls.py
|
||||
│ │ ├── views.py
|
||||
│ │ └── templates
|
||||
│ │ └── mypanel
|
||||
│ │ └── index.html
|
||||
│ │
|
||||
│ └── static
|
||||
│ | └── dashboard
|
||||
│ | └── identity
|
||||
│ | └── myplugin
|
||||
│ | └── mypanel
|
||||
│ | ├── mypanel.html
|
||||
│ | ├── mypanel.js
|
||||
│ | └── mypanel.scss
|
||||
│ │
|
||||
│ └── locale
|
||||
│ └── <lang>
|
||||
│ └── LC_MESSAGES
|
||||
│ ├── django.po
|
||||
│ └── djangojs.po
|
||||
│
|
||||
├── setup.py
|
||||
├── setup.cfg
|
||||
├── LICENSE
|
||||
├── MANIFEST.in
|
||||
├── README.rst
|
||||
├── babel-django.cfg
|
||||
└── babel-djangojs.cfg
|
||||
|
||||
If you are creating a Python plugin, you may ignore the ``static`` folder. Most
|
||||
of the classes you need are provided for in Python. If you intend on adding
|
||||
custom front-end logic, you will need to include additional JavaScript here.
|
||||
|
||||
An AngularJS plugin is a collection of JavaScript files or static resources.
|
||||
Because it runs entirely in your browser, we need to place all of our static
|
||||
resources inside the ``static`` folder. This ensures that the Django static
|
||||
collector picks it up and distributes it to the browser correctly.
|
||||
|
||||
The Enabled File
|
||||
----------------
|
||||
|
||||
The enabled folder contains the configuration file(s) that registers your
|
||||
plugin with Horizon. The file is prefixed with an alpha-numeric string that
|
||||
determines the load order of your plugin. For more information on what you can
|
||||
include in this file, see pluggable settings in :ref:`install-settings`.
|
||||
|
||||
_31000_myplugin.py::
|
||||
|
||||
# The name of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'mypanel'
|
||||
|
||||
# The name of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'identity'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'myplugin.content.mypanel.panel.MyPanel'
|
||||
|
||||
# A list of applications to be prepended to INSTALLED_APPS
|
||||
ADD_INSTALLED_APPS = ['myplugin']
|
||||
|
||||
# A list of AngularJS modules to be loaded when Angular bootstraps.
|
||||
ADD_ANGULAR_MODULES = ['horizon.dashboard.identity.myplugin.mypanel']
|
||||
|
||||
# Automatically discover static resources in installed apps
|
||||
AUTO_DISCOVER_STATIC_FILES = True
|
||||
|
||||
# A list of js files to be included in the compressed set of files
|
||||
ADD_JS_FILES = []
|
||||
|
||||
# A list of scss files to be included in the compressed set of files
|
||||
ADD_SCSS_FILES = ['dashboard/identity/myplugin/mypanel/mypanel.scss']
|
||||
|
||||
.. Note ::
|
||||
|
||||
Currently, AUTO_DISCOVER_STATIC_FILES = True will only discover JavaScript files,
|
||||
not SCSS files.
|
||||
|
||||
my_rest_api.py
|
||||
--------------
|
||||
|
||||
This file will likely be necessary if creating a plugin using Angular. Your
|
||||
plugin will need to communicate with a new service or require new interactions
|
||||
with a service already supported by Horizon. In this particular example, the
|
||||
plugin will augment the support for the already supported Identity service,
|
||||
Keystone. This file serves to define new REST interfaces for the plugin's
|
||||
client-side to communicate with Horizon. Typically, the REST interfaces here
|
||||
make calls into ``myservice.py``.
|
||||
|
||||
This file is unnecessary in a purely Django based plugin, or if your Angular
|
||||
based plugin is relying on CORS support in the desired service. For more
|
||||
information on CORS, see
|
||||
`http://docs.openstack.org/admin-guide/cross_project_cors.html`
|
||||
|
||||
myservice.py
|
||||
------------
|
||||
|
||||
This file will likely be necessary if creating a Django or Angular driven
|
||||
plugin. This file is intended to act as a convenient location for interacting
|
||||
with the new service this plugin is supporting. While interactions with the
|
||||
service can be handled in the ``views.py``, isolating the logic is an
|
||||
established pattern in Horizon.
|
||||
|
||||
panel.py
|
||||
--------
|
||||
|
||||
We define a panel where our plugin's content will reside in. This is currently a
|
||||
necessity even for Angular plugins. The slug is the panel's unique identifier
|
||||
and is often use as part of the URL. Make sure that it matches what you have in
|
||||
your enabled file.::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import horizon
|
||||
|
||||
|
||||
class MyPanel(horizon.Panel):
|
||||
name = _("My Panel")
|
||||
slug = "mypanel"
|
||||
|
||||
tests.py
|
||||
--------
|
||||
|
||||
Write some tests for the Django portion of your plugin and place them here.
|
||||
|
||||
urls.py
|
||||
-------
|
||||
|
||||
Now that we have a panel, we need to provide a URL so that users can visit our
|
||||
new panel! This URL generally will point to a view.::
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from myplugin.content.mypanel import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
]
|
||||
|
||||
views.py
|
||||
--------
|
||||
|
||||
Because rendering is done client-side, all our view needs is to reference some
|
||||
HTML page. If you are writing a Python plugin, this view can be much more
|
||||
complex. Refer to the topic guides for more details.::
|
||||
|
||||
from django.views import generic
|
||||
|
||||
|
||||
class IndexView(generic.TemplateView):
|
||||
template_name = 'identity/mypanel/index.html'
|
||||
|
||||
index.html
|
||||
----------
|
||||
|
||||
The index HTML is where rendering occurs. In this example, we are only using
|
||||
Django. If you are interested in using Angular directives instead, read the
|
||||
AngularJS section below.::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "My plugin" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_domain_page_header.html"
|
||||
with title=_("My Panel") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
Hello world!
|
||||
{% endblock %}
|
||||
|
||||
At this point, you have a very basic plugin. Note that new templates are
|
||||
required to extend base.html. Including base.html is important for a number of
|
||||
reasons. It is the template that contains all of your static resources along
|
||||
with any functionality external to your panel (things like navigation, context
|
||||
selection, etc...). As of this moment, this is also true for Angular plugins.
|
||||
|
||||
MANIFEST.in
|
||||
-----------
|
||||
This file is responsible for listing the paths you want included in your tar.::
|
||||
|
||||
include setup.py
|
||||
|
||||
recursive-include myplugin *.js *.html *.scss
|
||||
|
||||
|
||||
setup.py
|
||||
--------
|
||||
::
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=1.8'],
|
||||
pbr=True)
|
||||
|
||||
setup.cfg
|
||||
---------
|
||||
::
|
||||
|
||||
[metadata]
|
||||
name = myplugin
|
||||
summary = A panel plugin for OpenStack Dashboard
|
||||
description-file =
|
||||
README.rst
|
||||
author = myname
|
||||
author_email = myemail
|
||||
home-page = http://docs.openstack.org/developer/horizon/
|
||||
classifiers = [
|
||||
Environment :: OpenStack
|
||||
Framework :: Django
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
myplugin
|
||||
|
||||
AngularJS Plugin
|
||||
================
|
||||
|
||||
If you have no plans to add AngularJS to your plugin, you may skip this section.
|
||||
In the tutorial below, we will show you how to customize your panel using
|
||||
Angular.
|
||||
|
||||
index.html
|
||||
----------
|
||||
|
||||
The index HTML is where rendering occurs and serves as an entry point for
|
||||
Angular. This is where we start to diverge from the traditional Python plugin.
|
||||
In this example, we use a Django template as the glue to our Angular template.
|
||||
Why are we going through a Django template for an Angular plugin? Long story
|
||||
short, ``base.html`` contains the navigation piece that we still need for each
|
||||
panel.
|
||||
|
||||
::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "My panel" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
<hz-page-header
|
||||
header="{$ 'My panel' | translate $}"
|
||||
description="{$ 'My custom panel!' | translate $}">
|
||||
</hz-page-header>
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<ng-include
|
||||
src="'{{ STATIC_URL }}dashboard/identity/myplugin/mypanel/mypanel.html'">
|
||||
</ng-include>
|
||||
{% endblock %}
|
||||
|
||||
This template contains both Django and AngularJS code. Angular is denoted by
|
||||
{$..$} while Django is denoted by {{..}} and {%..%}. This template gets
|
||||
processed twice, once by Django on the server-side and once more by Angular on
|
||||
the client-side. This means that the expressions in {{..}} and {%..%} are
|
||||
substituted with values by the time it reaches your Angular template.
|
||||
|
||||
What you chose to include in ``block main`` is entirely up to you. Since you are
|
||||
creating an Angular plugin, we recommend that you keep everything in this
|
||||
section Angular. Do not mix Python code in here! If you find yourself passing in
|
||||
Python data, do it via our REST services instead.
|
||||
|
||||
Remember to always use ``STATIC_URL`` when referencing your static resources.
|
||||
This ensures that changes to the static path in settings will continue to serve
|
||||
your static resources properly.
|
||||
|
||||
.. Note ::
|
||||
|
||||
Angular's directives are prefixed with ng. Similarly, Horizon's directives
|
||||
are prefixed with hz. You can think of them as namespaces.
|
||||
|
||||
mypanel.js
|
||||
-----------
|
||||
|
||||
Your controller is the glue between the model and the view. In this example, we
|
||||
are going to give it some fake data to render. To load more complex data,
|
||||
consider using the $http service.
|
||||
|
||||
::
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.dashboard.identity.myplugin.mypanel', [])
|
||||
.controller('horizon.dashboard.identity.myPluginController',
|
||||
myPluginController);
|
||||
|
||||
myPluginController.$inject = [ '$http' ];
|
||||
|
||||
function myPluginController($http) {
|
||||
var ctrl = this;
|
||||
ctrl.items = [
|
||||
{ name: 'abc', id: 123 },
|
||||
{ name: 'efg', id: 345 },
|
||||
{ name: 'hij', id: 678 }
|
||||
];
|
||||
}
|
||||
})();
|
||||
|
||||
This is a basic example where we mocked the data. For exercise, load your data
|
||||
using the ``$http`` service.
|
||||
|
||||
mypanel.html
|
||||
-------------
|
||||
|
||||
This is our view. In this example, we are looping through the list of items
|
||||
provided by the controller and displaying the name and id. The important thing
|
||||
to note is the reference to our controller using the ``ng-controller``
|
||||
directive.
|
||||
|
||||
::
|
||||
|
||||
<div ng-controller="horizon.dashboard.identity.myPluginController as ctrl">
|
||||
<div>Loading data from your controller:</div>
|
||||
<ul>
|
||||
<li ng-repeat="item in ctrl.items">
|
||||
<span class="c1">{$ item.name $}</span>
|
||||
<span class="c2">{$ item.id $}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
mypanel.scss
|
||||
-------------
|
||||
|
||||
You can choose to customize your panel by providing your own scss.
|
||||
Be sure to include it in your enabled file via the ``ADD_SCSS_FILES`` setting.
|
||||
|
||||
Translation Support
|
||||
===================
|
||||
|
||||
A general instruction on how to enable translation support is described in
|
||||
the Infrastructure User Manual [#]_.
|
||||
|
||||
This section describes topics specific to Horizon plugins.
|
||||
|
||||
ADD_INSTALLED_APPS
|
||||
------------------
|
||||
|
||||
Be sure to include ``<modulename>`` (``myplugin`` in this example)
|
||||
in ``ADD_INSTALLED_APPS`` in the corresponding ``enabled`` file.
|
||||
|
||||
* If you are preparing a new plugin, you will use ``<modulename>``
|
||||
as ``INSTALLED_APPS`` in most cases as suggested in this tutorial.
|
||||
This is good and there is nothing more to do.
|
||||
* If for some reason your plugin needs to register other python modules
|
||||
to ``ADD_INSTALLED_APPS``, ensure that you include its ``<modulename>``
|
||||
additionally.
|
||||
|
||||
This comes from the combination of the following two reasons.
|
||||
|
||||
* Django looks for translation message catalogs from each path specified in
|
||||
``INSTALLED_APPS`` [#]_.
|
||||
* OpenStack infra scripts assumes translation message catalogs are placed
|
||||
under ``<modulename>/locale`` (for example ``myplugin/locale``).
|
||||
|
||||
.. [#] http://docs.openstack.org/infra/manual/creators.html#enabling-translation-infrastructure
|
||||
.. [#] https://docs.djangoproject.com/es/1.9/topics/i18n/translation/#how-django-discovers-translations
|
||||
|
||||
myplugin/locale
|
||||
---------------
|
||||
|
||||
Translated message catalog files (PO files) are placed under this
|
||||
directory.
|
||||
|
||||
babel-django.cfg, babel-djangojs.cfg
|
||||
------------------------------------
|
||||
|
||||
These files are used to extract messages by ``pybabel``:
|
||||
``babel-django.cfg`` for python code and template files, and
|
||||
``babel-djangojs.cfg`` for JavaScript files.
|
||||
|
||||
They are required to enable translation support by OpenStack CI infra.
|
||||
If they do not exist, the translation jobs will skip processing for
|
||||
your project.
|
||||
|
||||
Installing Your Plugin
|
||||
======================
|
||||
|
||||
Now that you have a complete plugin, it is time to install and test it. The
|
||||
instructions below assume that you have a working plugin.
|
||||
|
||||
* ``plugin`` is the location of your plugin
|
||||
* ``horizon`` is the location of horizon
|
||||
* ``package`` is the complete name of your packaged plugin
|
||||
|
||||
1. Run "cd ``plugin`` & python setup.py sdist"
|
||||
2. Run "cp -rv enabled ``horizon``/openstack_dashboard/local/"
|
||||
3. Run "``horizon``/tools/with_venv.sh pip install dist/``package``.tar.gz"
|
||||
4. Restart Apache or your Django test server
|
||||
|
||||
.. Note ::
|
||||
|
||||
Step 3 installs your package into the Horizon's virtual environment. You can
|
||||
install your plugin without using ``with_venv.sh`` and ``pip``. The package
|
||||
would simply be installed in the ``PYTHON_PATH`` of the system instead.
|
||||
|
||||
If you are able to hit the URL pattern in ``urls.py`` in your browser, you have
|
||||
successfully deployed your plugin! For plugins that do not have a URL, check
|
||||
that your static resources are loaded using the browser inspector.
|
||||
|
||||
Assuming you implemented ``my_rest_api.py``, you can use a REST client to hit
|
||||
the url directly and test it. There should be many REST clients available on
|
||||
your web browser.
|
||||
|
||||
Note that you may need to rebuild your virtual environment if your plugin is not
|
||||
showing up properly. If your plugin does not show up properly, check your
|
||||
``.venv`` folder to make sure the plugin's content is as you expect.
|
||||
|
||||
.. Note ::
|
||||
|
||||
To uninstall, use ``pip uninstall``. You will also need to remove the enabled
|
||||
file from the ``local/enabled`` folder.
|
|
@ -1,302 +0,0 @@
|
|||
.. _tutorials-table-actions:
|
||||
|
||||
============================================
|
||||
Tutorial: Adding a complex action to a table
|
||||
============================================
|
||||
|
||||
This tutorial covers how to add a more complex action to a table, one that
|
||||
requires an action and form definitions, as well as changes to the view, urls,
|
||||
and table.
|
||||
|
||||
This tutorial assumes you have already completed
|
||||
:ref:`tutorials-dashboard`. If not, please do so now as we will be
|
||||
modifying the files created there.
|
||||
|
||||
This action will create a snapshot of the instance. When the action is taken,
|
||||
it will display a form that will allow the user to enter a snapshot name,
|
||||
and will create that snapshot when the form is closed using the ``Create
|
||||
snapshot`` button.
|
||||
|
||||
Defining the view
|
||||
=================
|
||||
|
||||
To define the view, we must create a view class, along with the template
|
||||
(``HTML``) file and the form class for that view.
|
||||
|
||||
The template file
|
||||
-----------------
|
||||
The template file contains the HTML that will be used to show the view.
|
||||
|
||||
Create a ``create_snapshot.html`` file under the ``mypanel/templates/mypanel``
|
||||
directory and add the following code::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Snapshot" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Create a Snapshot") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'mydashboard/mypanel/_create_snapshot.html' %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
As you can see, the main body will be defined in ``_create_snapshot.html``,
|
||||
so we must also create that file under the ``mypanel/templates/mypanel``
|
||||
directory. It should contain the following code::
|
||||
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-body-right %}
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>{% trans "Snapshots preserve the disk state of a running instance." %}</p>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
The form
|
||||
--------
|
||||
|
||||
Horizon provides a :class:`~horizon.forms.base.SelfHandlingForm` class which
|
||||
simplifies some of the details involved in creating a form. Our form will
|
||||
derive from this class, adding a character field to allow the user to specify
|
||||
a name for the snapshot, and handling the successful closure of the form by
|
||||
calling the nova api to create the snapshot.
|
||||
|
||||
Create the ``forms.py`` file under the ``mypanel`` directory and add the
|
||||
following::
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
|
||||
class CreateSnapshot(forms.SelfHandlingForm):
|
||||
instance_id = forms.CharField(label=_("Instance ID"),
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
name = forms.CharField(max_length=255, label=_("Snapshot Name"))
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
snapshot = api.nova.snapshot_create(request,
|
||||
data['instance_id'],
|
||||
data['name'])
|
||||
return snapshot
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to create snapshot.'))
|
||||
|
||||
|
||||
The view
|
||||
--------
|
||||
|
||||
Now, the view will tie together the template and the form. Horizon provides a
|
||||
:class:`~horizon.forms.views.ModalFormView` class which simplifies the creation
|
||||
of a view that will contain a modal form.
|
||||
|
||||
Open the ``views.py`` file under the ``mypanel`` directory and add the code
|
||||
for the CreateSnapshotView and the necessary imports. The complete
|
||||
file should now look something like this::
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tabs
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel \
|
||||
import forms as project_forms
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel \
|
||||
import tabs as mydashboard_tabs
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = mydashboard_tabs.MypanelTabs
|
||||
# A very simple class-based view...
|
||||
template_name = 'mydashboard/mypanel/index.html'
|
||||
|
||||
def get_data(self, request, context, *args, **kwargs):
|
||||
# Add data to the context here...
|
||||
return context
|
||||
|
||||
|
||||
class CreateSnapshotView(forms.ModalFormView):
|
||||
form_class = project_forms.CreateSnapshot
|
||||
template_name = 'mydashboard/mypanel/create_snapshot.html'
|
||||
success_url = reverse_lazy("horizon:project:images:index")
|
||||
modal_id = "create_snapshot_modal"
|
||||
modal_header = _("Create Snapshot")
|
||||
submit_label = _("Create Snapshot")
|
||||
submit_url = "horizon:mydashboard:mypanel:create_snapshot"
|
||||
|
||||
@memoized.memoized_method
|
||||
def get_object(self):
|
||||
try:
|
||||
return api.nova.server_get(self.request,
|
||||
self.kwargs["instance_id"])
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve instance."))
|
||||
|
||||
def get_initial(self):
|
||||
return {"instance_id": self.kwargs["instance_id"]}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CreateSnapshotView, self).get_context_data(**kwargs)
|
||||
instance_id = self.kwargs['instance_id']
|
||||
context['instance_id'] = instance_id
|
||||
context['instance'] = self.get_object()
|
||||
context['submit_url'] = reverse(self.submit_url, args=[instance_id])
|
||||
return context
|
||||
|
||||
|
||||
Adding the url
|
||||
==============
|
||||
|
||||
We must add the url for our new view. Open the ``urls.py`` file under
|
||||
the ``mypanel`` directory and add the following as a new url pattern::
|
||||
|
||||
url(r'^(?P<instance_id>[^/]+)/create_snapshot/$',
|
||||
views.CreateSnapshotView.as_view(),
|
||||
name='create_snapshot'),
|
||||
|
||||
The complete ``urls.py`` file should look like this::
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.mydashboard.mypanel import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$',
|
||||
views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<instance_id>[^/]+)/create_snapshot/$',
|
||||
views.CreateSnapshotView.as_view(),
|
||||
name='create_snapshot'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
Define the action
|
||||
=================
|
||||
|
||||
Horizon provides a :class:`~horizon.tables.LinkAction` class which simplifies
|
||||
adding an action which can be used to display another view.
|
||||
|
||||
We will add a link action to the table that will be accessible from each row
|
||||
in the table. The action will use the view defined above to create a snapshot
|
||||
of the instance represented by the row in the table.
|
||||
|
||||
To do this, we must edit the ``tables.py`` file under the ``mypanel`` directory
|
||||
and add the following::
|
||||
|
||||
def is_deleting(instance):
|
||||
task_state = getattr(instance, "OS-EXT-STS:task_state", None)
|
||||
if not task_state:
|
||||
return False
|
||||
return task_state.lower() == "deleting"
|
||||
|
||||
|
||||
class CreateSnapshotAction(tables.LinkAction):
|
||||
name = "snapshot"
|
||||
verbose_name = _("Create Snapshot")
|
||||
url = "horizon:mydashboard:mypanel:create_snapshot"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "camera"
|
||||
|
||||
# This action should be disabled if the instance
|
||||
# is not active, or the instance is being deleted
|
||||
def allowed(self, request, instance=None):
|
||||
return instance.status in ("ACTIVE") \
|
||||
and not is_deleting(instance)
|
||||
|
||||
|
||||
We must also add our new action as a row action for the table::
|
||||
|
||||
row_actions = (CreateSnapshotAction,)
|
||||
|
||||
|
||||
The complete ``tables.py`` file should look like this::
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
|
||||
|
||||
def is_deleting(instance):
|
||||
task_state = getattr(instance, "OS-EXT-STS:task_state", None)
|
||||
if not task_state:
|
||||
return False
|
||||
return task_state.lower() == "deleting"
|
||||
|
||||
|
||||
class CreateSnapshotAction(tables.LinkAction):
|
||||
name = "snapshot"
|
||||
verbose_name = _("Create Snapshot")
|
||||
url = "horizon:mydashboard:mypanel:create_snapshot"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "camera"
|
||||
|
||||
def allowed(self, request, instance=None):
|
||||
return instance.status in ("ACTIVE") \
|
||||
and not is_deleting(instance)
|
||||
|
||||
|
||||
class MyFilterAction(tables.FilterAction):
|
||||
name = "myfilter"
|
||||
|
||||
|
||||
class InstancesTable(tables.DataTable):
|
||||
name = tables.Column("name", verbose_name=_("Name"))
|
||||
status = tables.Column("status", verbose_name=_("Status"))
|
||||
zone = tables.Column('availability_zone', verbose_name=_("Availability Zone"))
|
||||
image_name = tables.Column('image_name', verbose_name=_("Image Name"))
|
||||
|
||||
class Meta(object):
|
||||
name = "instances"
|
||||
verbose_name = _("Instances")
|
||||
table_actions = (MyFilterAction,)
|
||||
row_actions = (CreateSnapshotAction,)
|
||||
|
||||
|
||||
Run and check the dashboard
|
||||
===========================
|
||||
|
||||
We must once again run horizon to verify our dashboard is working::
|
||||
|
||||
$ tox -e runserver -- 0:9000
|
||||
|
||||
|
||||
Go to ``http://<your server>:9000`` using a browser. After login as an admin,
|
||||
display ``My Panel`` to see the ``Instances`` table. For every ``ACTIVE``
|
||||
instance in the table, there will be a ``Create Snapshot`` action on the row.
|
||||
Click on ``Create Snapshot``, enter a snapshot name in the form that is shown,
|
||||
then click to close the form. The ``Project Images`` view should be shown with
|
||||
the new snapshot added to the table.
|
||||
|
||||
|
||||
Conclusion
|
||||
==========
|
||||
|
||||
What you've learned here is the fundamentals of how to add a table action that
|
||||
requires a form for data entry. This can easily be expanded from creating a
|
||||
snapshot to other API calls that require more complex forms to gather the
|
||||
necessary information.
|
||||
|
||||
If you have feedback on how this tutorial could be improved, please feel free
|
||||
to submit a bug against ``Horizon`` in
|
||||
`launchpad <https://bugs.launchpad.net/horizon>`__.
|
|
@ -1,207 +0,0 @@
|
|||
Extending an AngularJS Workflow
|
||||
===============================
|
||||
|
||||
A workflow extends the ``extensibleService``. This means that all workflows
|
||||
inherit properties and methods provided by the ``extensibleService``. Extending
|
||||
a workflow allows you to add your own steps, remove existing steps, and inject
|
||||
custom data handling logic. Refer to inline documentation on what those
|
||||
properties and methods are.
|
||||
|
||||
We highly recommend that you complete the
|
||||
:ref:`tutorials-plugin` if you have not done so already.
|
||||
If you do not know how to package and install a plugin, the rest of this
|
||||
tutorial will not make sense! In this tutorial, we will examine an existing
|
||||
workflow and how we can extend it as a plugin.
|
||||
|
||||
.. Note ::
|
||||
|
||||
Although this tutorial focuses on extending a workflow, the steps here
|
||||
can easily be adapted to extend any service that inherited the
|
||||
``extensibleService``. Examples of other extensible points include
|
||||
table columns and table actions.
|
||||
|
||||
File Structure
|
||||
--------------
|
||||
|
||||
Remember that the goal of this tutorial is to inject our custom step into an
|
||||
**existing** workflow. All of the files we are interested in reside in the
|
||||
``static`` folder.
|
||||
|
||||
::
|
||||
|
||||
myplugin
|
||||
│
|
||||
├── enabled
|
||||
│ └── _31000_myplugin.py
|
||||
│
|
||||
└── static
|
||||
└── horizon
|
||||
└── app
|
||||
└── core
|
||||
└── images
|
||||
├── plugins
|
||||
│ └── myplugin.module.js
|
||||
│
|
||||
└── steps
|
||||
└── mystep
|
||||
├── mystep.controller.js
|
||||
├── mystep.help.html
|
||||
└── mystep.html
|
||||
|
||||
myplugin.module.js
|
||||
------------------
|
||||
|
||||
This is the entry point into our plugin. We hook into an existing module via the
|
||||
run block which is executed after the module has been initialized. All we need
|
||||
to do is inject it as a dependency and then use the methods provided in the
|
||||
extensible service to override or modify steps. In this example, we are going to
|
||||
prepend our custom step so that it will show up as the first step in the wizard.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.images')
|
||||
.run(myPlugin);
|
||||
|
||||
myPlugin.$inject = [
|
||||
'horizon.app.core.images.basePath',
|
||||
'horizon.app.core.images.workflows.create-volume.service'
|
||||
];
|
||||
|
||||
function myPlugin(basePath, workflow) {
|
||||
var customStep = {
|
||||
id: 'mypluginstep',
|
||||
title: gettext('My Step'),
|
||||
templateUrl: basePath + 'steps/mystep/mystep.html',
|
||||
helpUrl: basePath + 'steps/mystep/mystep.help.html',
|
||||
formName: 'myStepForm'
|
||||
};
|
||||
workflow.prepend(customStep);
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
.. Note ::
|
||||
|
||||
Replace ``horizon.app.core.images.workflows.create-volume.service`` with
|
||||
the workflow you intend to augment.
|
||||
|
||||
mystep.controller.js
|
||||
--------------------
|
||||
|
||||
It is important to note that the scope is the glue between our controllers,
|
||||
this is how we are propagating events from one controller to another. We can
|
||||
propagate events upward using the $emit method and propagate events downward
|
||||
using the $broadcast method.
|
||||
|
||||
Using the $on method, we can listen to events generated within the scope. In
|
||||
this manner, actions we completed in the wizard are visually reflected in the
|
||||
table even though they are two completely different widgets. Similarly, you can
|
||||
share data between steps in your workflow as long as they share the same parent
|
||||
scope.
|
||||
|
||||
In this example, we are listening for events generated by the wizard and the
|
||||
user panel. We also emit a custom event that other controllers can register to
|
||||
when favorite color changes.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('horizon.app.core.images')
|
||||
.controller('horizon.app.core.images.steps.myStepController',
|
||||
myStepController);
|
||||
|
||||
myStepController.$inject = [
|
||||
'$scope',
|
||||
'horizon.framework.widgets.wizard.events',
|
||||
'horizon.app.core.images.events'
|
||||
];
|
||||
|
||||
function myStepController($scope, wizardEvents, imageEvents) {
|
||||
|
||||
var ctrl = this;
|
||||
ctrl.favoriteColor = 'red';
|
||||
|
||||
///////////////////////////
|
||||
|
||||
$scope.$on(wizardEvents.ON_SWITCH, function(e, args) {
|
||||
console.info('Wizard is switching step!');
|
||||
console.info(args);
|
||||
});
|
||||
|
||||
$scope.$on(wizardEvents.BEFORE_SUBMIT, function() {
|
||||
console.info('About to submit!');
|
||||
});
|
||||
|
||||
$scope.$on(imageEvents.VOLUME_CHANGED, function(event, newVolume) {
|
||||
console.info(newVolume);
|
||||
});
|
||||
|
||||
///////////////////////////
|
||||
|
||||
$scope.$watchCollection(getFavoriteColor, watchFavoriteColor);
|
||||
|
||||
function getFavoriteColor() {
|
||||
return ctrl.favoriteColor;
|
||||
}
|
||||
|
||||
function watchFavoriteColor(newColor, oldColor) {
|
||||
if (newColor != oldColor) {
|
||||
$scope.$emit('mystep.favoriteColor', newColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
mystep.help.html
|
||||
----------------
|
||||
|
||||
In this tutorial, we will leave this file blank. Include additional information
|
||||
here if your step requires it. Otherwise, remove the file and the ``helpUrl``
|
||||
property from your step.
|
||||
|
||||
mystep.html
|
||||
-----------
|
||||
|
||||
This file contains contents you want to display to the user. We will provide a
|
||||
simple example of a step that asks for your favorite color. The most important
|
||||
thing to note here is the reference to our controller via the ``ng-controller``
|
||||
directive. This is essentially the link to our controller.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div ng-controller="horizon.app.core.images.steps.myStepController as ctrl">
|
||||
<h1 translate>Blue Plugin</h1>
|
||||
<div class="content">
|
||||
<div class="subtitle" translate>My custom step</div>
|
||||
<div translate style="margin-bottom:1em;">
|
||||
Place your custom content here!
|
||||
</div>
|
||||
<div class="selected-source clearfix">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-8">
|
||||
<div class="form-group required">
|
||||
<label class="control-label" translate>Favorite color</label>
|
||||
<input type="text" class="form-control"
|
||||
ng-model="ctrl.favoriteColor"
|
||||
placeholder="{$ 'Enter your favorite color'|translate $}">
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- row -->
|
||||
</div><!-- clearfix -->
|
||||
</div><!-- content -->
|
||||
</div><!-- controller -->
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
Now that we have completed our plugin, lets package it and test that it works.
|
||||
If you need a refresher, take a look at the installation section in
|
||||
:ref:`tutorials-plugin`.
|
|
@ -1,26 +0,0 @@
|
|||
.. _glossary:
|
||||
|
||||
========
|
||||
Glossary
|
||||
========
|
||||
|
||||
Horizon
|
||||
|
||||
The OpenStack dashboard project. Also the name of the top-level
|
||||
Python object which handles registration for the app.
|
||||
|
||||
Dashboard
|
||||
|
||||
A Python class representing a top-level navigation item (e.g. "project")
|
||||
which provides a consistent API for Horizon-compatible applications.
|
||||
|
||||
Panel
|
||||
|
||||
A Python class representing a sub-navigation item (e.g. "instances")
|
||||
which contains all the necessary logic (views, forms, tests, etc.) for
|
||||
that interface.
|
||||
|
||||
Project
|
||||
|
||||
Used in user-facing text in place of the term "Tenant" which is Keystone's
|
||||
word.
|
Binary file not shown.
Before Width: | Height: | Size: 99 KiB |
Binary file not shown.
Before Width: | Height: | Size: 96 KiB |
|
@ -1,72 +0,0 @@
|
|||
..
|
||||
Copyright 2012 OpenStack Foundation
|
||||
All Rights Reserved.
|
||||
|
||||
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.
|
||||
|
||||
========================================
|
||||
Horizon: The OpenStack Dashboard Project
|
||||
========================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Horizon is the canonical implementation of `OpenStack's Dashboard
|
||||
<https://github.com/openstack/horizon>`_, which provides a web based user
|
||||
interface to OpenStack services including Nova, Swift, Keystone, etc.
|
||||
|
||||
For a more in-depth look at Horizon and its architecture, see the
|
||||
:ref:`contributor-intro`.
|
||||
|
||||
To learn what you need to know to get going, see the :ref:`quickstart`.
|
||||
|
||||
Using Horizon
|
||||
=============
|
||||
|
||||
How to use Horizon in your own projects.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install/index
|
||||
configuration/index
|
||||
User Documentation <user/index>
|
||||
admin/index
|
||||
|
||||
Contributor Docs
|
||||
================
|
||||
|
||||
For those wishing to develop Horizon itself, or go in-depth with building
|
||||
your own :class:`~horizon.Dashboard` or :class:`~horizon.Panel` classes,
|
||||
the following documentation is provided.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
contributor/index
|
||||
|
||||
Release Notes
|
||||
=============
|
||||
|
||||
See http://docs.openstack.org/releasenotes/horizon/.
|
||||
|
||||
Information
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
glossary
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
|
@ -1,241 +0,0 @@
|
|||
===================
|
||||
Manual installation
|
||||
===================
|
||||
|
||||
This page covers the basic installation of horizon in a production
|
||||
environment. If you are looking for a developer environment, see
|
||||
:ref:`quickstart`.
|
||||
|
||||
.. _system-requirements-label:
|
||||
|
||||
System Requirements
|
||||
===================
|
||||
|
||||
* Python 2.7
|
||||
* Django 1.8
|
||||
* An accessible `keystone <https://docs.openstack.org/developer/keystone>`_ endpoint
|
||||
|
||||
* All other services are optional.
|
||||
Horizon supports the following services as of the Pike release.
|
||||
If the keystone endpoint for a service is configured,
|
||||
horizon detects it and enables its support automatically.
|
||||
|
||||
* `cinder <https://docs.openstack.org/developer/cinder>`_: Block Storage
|
||||
* `glance <https://docs.openstack.org/developer/glance>`_: Image Management
|
||||
* `heat <https://docs.openstack.org/developer/heat>`_: Orchestration
|
||||
* `neutron <https://docs.openstack.org/developer/neutron>`_: Networking
|
||||
* `nova <https://docs.openstack.org/developer/nova>`_: Compute
|
||||
* `swift <https://docs.openstack.org/developer/swift>`_: Object Storage
|
||||
* Horizon also supports many other OpenStack services via plugins. For more
|
||||
information, see the :ref:`install-plugin-registry`.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
.. note::
|
||||
|
||||
In the commands below, substitute "<release>" for your version of choice,
|
||||
such as "ocata" or "pike".
|
||||
|
||||
#. Clone Horizon
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ git clone https://git.openstack.org/openstack/horizon -b stable/<release> --depth=1
|
||||
$ cd horizon
|
||||
|
||||
#. Install the horizon python module into your system
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo pip install -c http://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/<release> .
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
This section contains a small summary of the critical settings required to run
|
||||
horizon. For more details, please refer to :ref:`install-settings`.
|
||||
|
||||
Settings
|
||||
--------
|
||||
|
||||
Create ``openstack_dashboard/local/local_settings.py``. It is usually a good
|
||||
idea to copy ``openstack_dashboard/local/local_settings.py.example`` and
|
||||
edit it. As a minimum, the follow settings will need to be modified:
|
||||
|
||||
``DEBUG``
|
||||
Set to ``False``
|
||||
``ALLOWED_HOSTS``
|
||||
Set to your domain name(s)
|
||||
``OPENSTACK_HOST``
|
||||
Set to the IP of your Keystone endpoint. You may also
|
||||
need to alter ``OPENSTACK_KEYSTONE_URL``
|
||||
|
||||
.. note::
|
||||
|
||||
The following steps in the "Configuration" section are optional, but highly
|
||||
recommended in production.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
Compile translation message catalogs for internationalization. This step is
|
||||
not required if you do not need to support languages other than US English.
|
||||
GNU ``gettext`` tool is required to compile message catalogs.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt-get install gettext
|
||||
$ ./manage.py compilemessages
|
||||
|
||||
Static Assets
|
||||
-------------
|
||||
|
||||
Compress your static files by adding ``COMPRESS_OFFLINE = True`` to your
|
||||
``local_settings.py``, then run the following commands
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./manage.py collectstatic
|
||||
$ ./manage.py compress
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
Horizons uses Django's logging configuration mechanism, which can be customized
|
||||
by altering the ``LOGGING`` dictionary in ``local_settings.py``. By default,
|
||||
Horizon's logging example sets the log level to ``INFO``.
|
||||
|
||||
Horizon also uses a number of 3rd-party clients which log separately. The
|
||||
log level for these can still be controlled through Horizon's ``LOGGING``
|
||||
config, however behaviors may vary beyond Horizon's control.
|
||||
|
||||
For more information regarding configuring logging in Horizon, please
|
||||
read the `Django logging directive`_ and the `Python logging directive`_
|
||||
documentation. Horizon is built on Python and Django.
|
||||
|
||||
.. _Django logging directive: https://docs.djangoproject.com/en/dev/topics/logging
|
||||
.. _Python logging directive: http://docs.python.org/2/library/logging.html
|
||||
|
||||
Session Storage
|
||||
---------------
|
||||
|
||||
Horizon uses `Django's sessions framework`_ for handling session data. There
|
||||
are numerous session backends available, which are selected through the
|
||||
``SESSION_ENGINE`` setting in your ``local_settings.py`` file.
|
||||
|
||||
.. _Django's sessions framework: https://docs.djangoproject.com/en/dev/topics/http/sessions/
|
||||
|
||||
Memcached
|
||||
~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
CACHES = {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache'
|
||||
'LOCATION': 'my_memcached_host:11211',
|
||||
}
|
||||
|
||||
External caching using an application such as memcached offers persistence
|
||||
and shared storage, and can be very useful for small-scale deployment and/or
|
||||
development. However, for distributed and high-availability scenarios
|
||||
memcached has inherent problems which are beyond the scope of this
|
||||
documentation.
|
||||
|
||||
Requirements:
|
||||
|
||||
* Memcached service running and accessible
|
||||
* Python memcached module installed
|
||||
|
||||
Database
|
||||
~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.core.cache.backends.db.DatabaseCache'
|
||||
DATABASES = {
|
||||
'default': {
|
||||
# Database configuration here
|
||||
}
|
||||
}
|
||||
|
||||
Database-backed sessions are scalable (using an appropriate database strategy),
|
||||
persistent, and can be made high-concurrency and highly-available.
|
||||
|
||||
The downside to this approach is that database-backed sessions are one of the
|
||||
slower session storages, and incur a high overhead under heavy usage. Proper
|
||||
configuration of your database deployment can also be a substantial
|
||||
undertaking and is far beyond the scope of this documentation.
|
||||
|
||||
Cached Database
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
To mitigate the performance issues of database queries, you can also consider
|
||||
using Django's ``cached_db`` session backend which utilizes both your database
|
||||
and caching infrastructure to perform write-through caching and efficient
|
||||
retrieval. You can enable this hybrid setting by configuring both your database
|
||||
and cache as discussed above and then using
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
|
||||
|
||||
Deployment
|
||||
==========
|
||||
|
||||
#. Set up a web server with WSGI support. For example, install Apache web
|
||||
server on Ubuntu
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt-get install apache2 libapache2-mod-wsgi
|
||||
|
||||
You can either use the provided ``openstack_dashboard/wsgi/django.wsgi`` or
|
||||
generate a ``openstack_dashboard/wsgi/horizon.wsgi`` file with the following
|
||||
command (which detects if you use a virtual environment or not to
|
||||
automatically build an adapted WSGI file)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./manage.py make_web_conf --wsgi
|
||||
|
||||
Then configure the web server to host OpenStack dashboard via WSGI.
|
||||
For apache2 web server, you may need to create
|
||||
``/etc/apache2/sites-available/horizon.conf``.
|
||||
The template in DevStack is a good example of the file.
|
||||
http://git.openstack.org/cgit/openstack-dev/devstack/tree/files/apache-horizon.template
|
||||
Or, if you previously generated an ``openstack_dashboard/wsgi/horizon.wsgi``
|
||||
you can automatically generate an apache configuration file
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./manage.py make_web_conf --apache > /etc/apache2/sites-available/horizon.conf
|
||||
|
||||
Same as above but if you want SSL support
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./manage.py make_web_conf --apache --ssl --sslkey=/path/to/ssl/key --sslcert=/path/to/ssl/cert > /etc/apache2/sites-available/horizon.conf
|
||||
|
||||
By default the apache configuration will launch a number of apache processes
|
||||
equal to the number of CPUs + 1 of the machine on which you launch the
|
||||
``make_web_conf`` command. If the target machine is not the same or if you
|
||||
want to specify the number of processes, add the ``--processes`` option
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./manage.py make_web_conf --apache --processes 10 > /etc/apache2/sites-available/horizon.conf
|
||||
|
||||
#. Enable the above configuration and restart the web server
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo a2ensite horizon
|
||||
$ sudo service apache2 restart
|
||||
|
||||
Next Steps
|
||||
==========
|
||||
|
||||
* :ref:`install-settings` lists the available settings for horizon.
|
||||
* :ref:`install-customizing` describes how to customize horizon.
|
|
@ -1,47 +0,0 @@
|
|||
==================
|
||||
Installation Guide
|
||||
==================
|
||||
|
||||
This section describes how to install and configure the dashboard
|
||||
on the controller node.
|
||||
|
||||
The only core service required by the dashboard is the Identity service.
|
||||
You can use the dashboard in combination with other services, such as
|
||||
Image service, Compute, and Networking. You can also use the dashboard
|
||||
in environments with stand-alone services such as Object Storage.
|
||||
|
||||
.. note::
|
||||
|
||||
This section assumes proper installation, configuration, and operation
|
||||
of the Identity service using the Apache HTTP server and Memcached
|
||||
service.
|
||||
|
||||
Installing from Packages
|
||||
========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
install-*
|
||||
verify-*
|
||||
next-steps
|
||||
|
||||
Installing from Source
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
from-source.rst
|
||||
|
||||
Horizon plugins
|
||||
===============
|
||||
|
||||
There are a number of horizon plugins for various useful features. You can get
|
||||
dashboard supports for them by installing corresponding horizon plugins.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
plugin-registry.rst
|
|
@ -1,212 +0,0 @@
|
|||
================================
|
||||
Install and configure for Debian
|
||||
================================
|
||||
|
||||
This section describes how to install and configure the dashboard
|
||||
on the controller node.
|
||||
|
||||
The only core service required by the dashboard is the Identity service.
|
||||
You can use the dashboard in combination with other services, such as
|
||||
Image service, Compute, and Networking. You can also use the dashboard
|
||||
in environments with stand-alone services such as Object Storage.
|
||||
|
||||
.. note::
|
||||
|
||||
This section assumes proper installation, configuration, and operation
|
||||
of the Identity service using the Apache HTTP server and Memcached
|
||||
service.
|
||||
|
||||
Install and configure components
|
||||
--------------------------------
|
||||
|
||||
.. include:: note_configuration_vary_by_distribution.txt
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1. Install the packages:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# apt install openstack-dashboard-apache
|
||||
|
||||
.. end
|
||||
|
||||
2. Respond to prompts for web server configuration.
|
||||
|
||||
.. note::
|
||||
|
||||
The automatic configuration process generates a self-signed
|
||||
SSL certificate. Consider obtaining an official certificate
|
||||
for production environments.
|
||||
|
||||
.. note::
|
||||
|
||||
There are two modes of installation. One using ``/horizon`` as the URL,
|
||||
keeping your default vhost and only adding an Alias directive: this is
|
||||
the default. The other mode will remove the default Apache vhost and install
|
||||
the dashboard on the webroot. It was the only available option
|
||||
before the Liberty release. If you prefer to set the Apache configuration
|
||||
manually, install the ``openstack-dashboard`` package instead of
|
||||
``openstack-dashboard-apache``.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. Edit the
|
||||
``/etc/openstack-dashboard/local_settings.py``
|
||||
file and complete the following actions:
|
||||
|
||||
* Configure the dashboard to use OpenStack services on the
|
||||
``controller`` node:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_HOST = "controller"
|
||||
|
||||
.. end
|
||||
|
||||
* In the Dashboard configuration section, allow your hosts to access
|
||||
Dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
ALLOWED_HOSTS = ['one.example.com', 'two.example.com']
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
- Do not edit the ``ALLOWED_HOSTS`` parameter under the Ubuntu
|
||||
configuration section.
|
||||
- ``ALLOWED_HOSTS`` can also be ``['*']`` to accept all hosts. This
|
||||
may be useful for development work, but is potentially insecure
|
||||
and should not be used in production. See the
|
||||
`Django documentation
|
||||
<https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts>`_
|
||||
for further information.
|
||||
|
||||
* Configure the ``memcached`` session storage service:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION': 'controller:11211',
|
||||
}
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
Comment out any other session storage configuration.
|
||||
|
||||
* Enable the Identity API version 3:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
|
||||
|
||||
.. end
|
||||
|
||||
* Enable support for domains:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
|
||||
.. end
|
||||
|
||||
* Configure API versions:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_API_VERSIONS = {
|
||||
"identity": 3,
|
||||
"image": 2,
|
||||
"volume": 2,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``Default`` as the default domain for users that you create
|
||||
via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "Default"
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``user`` as the default role for
|
||||
users that you create via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "user"
|
||||
|
||||
.. end
|
||||
|
||||
* If you chose networking option 1, disable support for layer-3
|
||||
networking services:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_NEUTRON_NETWORK = {
|
||||
...
|
||||
'enable_router': False,
|
||||
'enable_quotas': False,
|
||||
'enable_ipv6': False,
|
||||
'enable_distributed_router': False,
|
||||
'enable_ha_router': False,
|
||||
'enable_lb': False,
|
||||
'enable_firewall': False,
|
||||
'enable_vpn': False,
|
||||
'enable_fip_topology_check': False,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Optionally, configure the time zone:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
TIME_ZONE = "TIME_ZONE"
|
||||
|
||||
.. end
|
||||
|
||||
Replace ``TIME_ZONE`` with an appropriate time zone identifier.
|
||||
For more information, see the `list of time zones
|
||||
<https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>`__.
|
||||
|
||||
|
||||
Finalize installation
|
||||
---------------------
|
||||
|
||||
|
||||
* Reload the web server configuration:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# service apache2 reload
|
||||
|
||||
.. end
|
||||
|
||||
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
============================================================
|
||||
Install and configure for openSUSE and SUSE Linux Enterprise
|
||||
============================================================
|
||||
|
||||
This section describes how to install and configure the dashboard
|
||||
on the controller node.
|
||||
|
||||
The only core service required by the dashboard is the Identity service.
|
||||
You can use the dashboard in combination with other services, such as
|
||||
Image service, Compute, and Networking. You can also use the dashboard
|
||||
in environments with stand-alone services such as Object Storage.
|
||||
|
||||
.. note::
|
||||
|
||||
This section assumes proper installation, configuration, and operation
|
||||
of the Identity service using the Apache HTTP server and Memcached
|
||||
service.
|
||||
|
||||
Install and configure components
|
||||
--------------------------------
|
||||
|
||||
.. include:: note_configuration_vary_by_distribution.txt
|
||||
|
||||
|
||||
1. Install the packages:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# zypper install openstack-dashboard
|
||||
|
||||
.. end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. Configure the web server:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cp /etc/apache2/conf.d/openstack-dashboard.conf.sample \
|
||||
/etc/apache2/conf.d/openstack-dashboard.conf
|
||||
# a2enmod rewrite
|
||||
|
||||
.. end
|
||||
|
||||
3. Edit the
|
||||
``/srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py``
|
||||
file and complete the following actions:
|
||||
|
||||
* Configure the dashboard to use OpenStack services on the
|
||||
``controller`` node:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_HOST = "controller"
|
||||
|
||||
.. end
|
||||
|
||||
* Allow your hosts to access the dashboard:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
ALLOWED_HOSTS = ['one.example.com', 'two.example.com']
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
``ALLOWED_HOSTS`` can also be ``['*']`` to accept all hosts. This may be
|
||||
useful for development work, but is potentially insecure and should
|
||||
not be used in production. See `Django documentation
|
||||
<https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts>`_
|
||||
for further information.
|
||||
|
||||
* Configure the ``memcached`` session storage service:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION': 'controller:11211',
|
||||
}
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
Comment out any other session storage configuration.
|
||||
|
||||
* Enable the Identity API version 3:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
|
||||
|
||||
.. end
|
||||
|
||||
* Enable support for domains:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
|
||||
.. end
|
||||
|
||||
* Configure API versions:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_API_VERSIONS = {
|
||||
"identity": 3,
|
||||
"image": 2,
|
||||
"volume": 2,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``Default`` as the default domain for users that you create
|
||||
via the dashboard:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "Default"
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``user`` as the default role for
|
||||
users that you create via the dashboard:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "user"
|
||||
|
||||
.. end
|
||||
|
||||
* If you chose networking option 1, disable support for layer-3
|
||||
networking services:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_NEUTRON_NETWORK = {
|
||||
...
|
||||
'enable_router': False,
|
||||
'enable_quotas': False,
|
||||
'enable_distributed_router': False,
|
||||
'enable_ha_router': False,
|
||||
'enable_lb': False,
|
||||
'enable_firewall': False,
|
||||
'enable_vpn': False,
|
||||
'enable_fip_topology_check': False,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Optionally, configure the time zone:
|
||||
|
||||
.. path /srv/www/openstack-dashboard/openstack_dashboard/local/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
TIME_ZONE = "TIME_ZONE"
|
||||
|
||||
.. end
|
||||
|
||||
Replace ``TIME_ZONE`` with an appropriate time zone identifier.
|
||||
For more information, see the `list of time zones
|
||||
<https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>`__.
|
||||
|
||||
|
||||
|
||||
|
||||
Finalize installation
|
||||
---------------------
|
||||
|
||||
|
||||
|
||||
* Restart the web server and session storage service:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# systemctl restart apache2.service memcached.service
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
The ``systemctl restart`` command starts each service if
|
||||
not currently running.
|
||||
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
=============================================================
|
||||
Install and configure for Red Hat Enterprise Linux and CentOS
|
||||
=============================================================
|
||||
|
||||
This section describes how to install and configure the dashboard
|
||||
on the controller node.
|
||||
|
||||
The only core service required by the dashboard is the Identity service.
|
||||
You can use the dashboard in combination with other services, such as
|
||||
Image service, Compute, and Networking. You can also use the dashboard
|
||||
in environments with stand-alone services such as Object Storage.
|
||||
|
||||
.. note::
|
||||
|
||||
This section assumes proper installation, configuration, and operation
|
||||
of the Identity service using the Apache HTTP server and Memcached
|
||||
service.
|
||||
|
||||
Install and configure components
|
||||
--------------------------------
|
||||
|
||||
.. include:: note_configuration_vary_by_distribution.txt
|
||||
|
||||
|
||||
|
||||
1. Install the packages:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# yum install openstack-dashboard
|
||||
|
||||
.. end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. Edit the
|
||||
``/etc/openstack-dashboard/local_settings``
|
||||
file and complete the following actions:
|
||||
|
||||
* Configure the dashboard to use OpenStack services on the
|
||||
``controller`` node:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_HOST = "controller"
|
||||
|
||||
.. end
|
||||
|
||||
* Allow your hosts to access the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
ALLOWED_HOSTS = ['one.example.com', 'two.example.com']
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
ALLOWED_HOSTS can also be ['*'] to accept all hosts. This may be
|
||||
useful for development work, but is potentially insecure and should
|
||||
not be used in production. See
|
||||
https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
|
||||
for further information.
|
||||
|
||||
* Configure the ``memcached`` session storage service:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION': 'controller:11211',
|
||||
}
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
Comment out any other session storage configuration.
|
||||
|
||||
* Enable the Identity API version 3:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
|
||||
|
||||
.. end
|
||||
|
||||
* Enable support for domains:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
|
||||
.. end
|
||||
|
||||
* Configure API versions:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_API_VERSIONS = {
|
||||
"identity": 3,
|
||||
"image": 2,
|
||||
"volume": 2,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``Default`` as the default domain for users that you create
|
||||
via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "Default"
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``user`` as the default role for
|
||||
users that you create via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "user"
|
||||
|
||||
.. end
|
||||
|
||||
* If you chose networking option 1, disable support for layer-3
|
||||
networking services:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_NEUTRON_NETWORK = {
|
||||
...
|
||||
'enable_router': False,
|
||||
'enable_quotas': False,
|
||||
'enable_distributed_router': False,
|
||||
'enable_ha_router': False,
|
||||
'enable_lb': False,
|
||||
'enable_firewall': False,
|
||||
'enable_vpn': False,
|
||||
'enable_fip_topology_check': False,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Optionally, configure the time zone:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings
|
||||
.. code-block:: python
|
||||
|
||||
TIME_ZONE = "TIME_ZONE"
|
||||
|
||||
.. end
|
||||
|
||||
Replace ``TIME_ZONE`` with an appropriate time zone identifier.
|
||||
For more information, see the `list of time zones
|
||||
<https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>`__.
|
||||
|
||||
|
||||
|
||||
Finalize installation
|
||||
---------------------
|
||||
|
||||
|
||||
|
||||
|
||||
* Restart the web server and session storage service:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# systemctl restart httpd.service memcached.service
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
The ``systemctl restart`` command starts each service if
|
||||
not currently running.
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
================================
|
||||
Install and configure for Ubuntu
|
||||
================================
|
||||
|
||||
This section describes how to install and configure the dashboard
|
||||
on the controller node.
|
||||
|
||||
The only core service required by the dashboard is the Identity service.
|
||||
You can use the dashboard in combination with other services, such as
|
||||
Image service, Compute, and Networking. You can also use the dashboard
|
||||
in environments with stand-alone services such as Object Storage.
|
||||
|
||||
.. note::
|
||||
|
||||
This section assumes proper installation, configuration, and operation
|
||||
of the Identity service using the Apache HTTP server and Memcached
|
||||
service.
|
||||
|
||||
Install and configure components
|
||||
--------------------------------
|
||||
|
||||
.. include:: note_configuration_vary_by_distribution.txt
|
||||
|
||||
|
||||
|
||||
|
||||
1. Install the packages:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# apt install openstack-dashboard
|
||||
|
||||
.. end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2. Edit the
|
||||
``/etc/openstack-dashboard/local_settings.py``
|
||||
file and complete the following actions:
|
||||
|
||||
* Configure the dashboard to use OpenStack services on the
|
||||
``controller`` node:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_HOST = "controller"
|
||||
|
||||
.. end
|
||||
|
||||
* In the Dashboard configuration section, allow your hosts to access
|
||||
Dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
ALLOWED_HOSTS = ['one.example.com', 'two.example.com']
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
- Do not edit the ``ALLOWED_HOSTS`` parameter under the Ubuntu
|
||||
configuration section.
|
||||
- ``ALLOWED_HOSTS`` can also be ``['*']`` to accept all hosts. This
|
||||
may be useful for development work, but is potentially insecure
|
||||
and should not be used in production. See the
|
||||
`Django documentation
|
||||
<https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts>`_
|
||||
for further information.
|
||||
|
||||
* Configure the ``memcached`` session storage service:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'LOCATION': 'controller:11211',
|
||||
}
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
.. note::
|
||||
|
||||
Comment out any other session storage configuration.
|
||||
|
||||
* Enable the Identity API version 3:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST
|
||||
|
||||
.. end
|
||||
|
||||
* Enable support for domains:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
|
||||
.. end
|
||||
|
||||
* Configure API versions:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_API_VERSIONS = {
|
||||
"identity": 3,
|
||||
"image": 2,
|
||||
"volume": 2,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``Default`` as the default domain for users that you create
|
||||
via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "Default"
|
||||
|
||||
.. end
|
||||
|
||||
* Configure ``user`` as the default role for
|
||||
users that you create via the dashboard:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "user"
|
||||
|
||||
.. end
|
||||
|
||||
* If you chose networking option 1, disable support for layer-3
|
||||
networking services:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
OPENSTACK_NEUTRON_NETWORK = {
|
||||
...
|
||||
'enable_router': False,
|
||||
'enable_quotas': False,
|
||||
'enable_ipv6': False,
|
||||
'enable_distributed_router': False,
|
||||
'enable_ha_router': False,
|
||||
'enable_lb': False,
|
||||
'enable_firewall': False,
|
||||
'enable_vpn': False,
|
||||
'enable_fip_topology_check': False,
|
||||
}
|
||||
|
||||
.. end
|
||||
|
||||
* Optionally, configure the time zone:
|
||||
|
||||
.. path /etc/openstack-dashboard/local_settings.py
|
||||
.. code-block:: python
|
||||
|
||||
TIME_ZONE = "TIME_ZONE"
|
||||
|
||||
.. end
|
||||
|
||||
Replace ``TIME_ZONE`` with an appropriate time zone identifier.
|
||||
For more information, see the `list of time zones
|
||||
<https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>`__.
|
||||
|
||||
|
||||
Finalize installation
|
||||
---------------------
|
||||
|
||||
|
||||
* Reload the web server configuration:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# service apache2 reload
|
||||
|
||||
.. end
|
||||
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
==========
|
||||
Next steps
|
||||
==========
|
||||
|
||||
Your OpenStack environment now includes the dashboard.
|
||||
|
||||
After you install and configure the dashboard, you can
|
||||
complete the following tasks:
|
||||
|
||||
* Provide users with a public IP address, a username, and a password
|
||||
so they can access the dashboard through a web browser. In case of
|
||||
any SSL certificate connection problems, point the server
|
||||
IP address to a domain name, and give users access.
|
||||
|
||||
* Customize your dashboard. See section
|
||||
`Customize and configure the Dashboard
|
||||
<https://docs.openstack.org/admin-guide/dashboard-customize-configure.html>`__.
|
||||
|
||||
* Set up session storage. See
|
||||
`Set up session storage for the dashboard
|
||||
<https://docs.openstack.org/admin-guide/dashboard-sessions.html>`__.
|
||||
|
||||
* To use the VNC client with the dashboard, the browser
|
||||
must support HTML5 Canvas and HTML5 WebSockets.
|
||||
|
||||
For details about browsers that support noVNC, see
|
||||
`README
|
||||
<https://github.com/kanaka/noVNC/blob/master/README.md>`__
|
||||
and `browser support
|
||||
<https://github.com/kanaka/noVNC/wiki/Browser-support>`__.
|
|
@ -1,7 +0,0 @@
|
|||
.. note::
|
||||
|
||||
Default configuration files vary by distribution. You might need
|
||||
to add these sections and options rather than modifying existing
|
||||
sections and options. Also, an ellipsis (``...``) in the configuration
|
||||
snippets indicates potential default configuration options that you
|
||||
should retain.
|
|
@ -1,123 +0,0 @@
|
|||
.. _install-plugin-registry:
|
||||
|
||||
===============
|
||||
Plugin Registry
|
||||
===============
|
||||
|
||||
.. note::
|
||||
|
||||
Currently, Horizon plugins are responsible for their own compatibility.
|
||||
Check the individual repos for information on support.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 20 40 40
|
||||
|
||||
* - Plugin
|
||||
- URL
|
||||
- Launchpad
|
||||
* - Astara Dashboard
|
||||
- https://github.com/openstack/astara-horizon
|
||||
- https://launchpad.net/astara
|
||||
* - App Catalog UI
|
||||
- https://github.com/openstack/app-catalog-ui
|
||||
- https://launchpad.net/app-catalog
|
||||
* - BGPVPN Dashboard
|
||||
- https://github.com/openstack/networking-bgpvpn
|
||||
- https://launchpad.net/bgpvpn
|
||||
* - Blazar Dashboard
|
||||
- https://github.com/openstack/blazar-dashboard
|
||||
- https://launchpad.net/blazar
|
||||
* - Cerberus Dashboard
|
||||
- https://github.com/openstack/cerberus-dashboard
|
||||
- https://launchpad.net/cerberus
|
||||
* - Cisco UI
|
||||
- http://github.com/openstack/horizon-cisco-ui
|
||||
- https://launchpad.net/horizon-cisco-ui
|
||||
* - Cloudkitty Dashboard
|
||||
- https://github.com/openstack/cloudkitty-dashboard
|
||||
- https://launchpad.net/cloudkitty
|
||||
* - Congress Dashboard
|
||||
- https://github.com/openstack/congress-dashboard
|
||||
- https://launchpad.net/congress
|
||||
* - Cue Dashboard
|
||||
- https://github.com/openstack/cue-dashboard
|
||||
- https://launchpad.net/cue-dashboard
|
||||
* - Designate Dashboard
|
||||
- https://github.com/openstack/designate-dashboard
|
||||
- https://launchpad.net/designate-dashboard
|
||||
* - Group Based Policy UI
|
||||
- https://github.com/openstack/group-based-policy-ui
|
||||
- https://launchpad.net/group-based-policy-ui
|
||||
* - Freezer Web UI
|
||||
- https://github.com/openstack/freezer-web-ui
|
||||
- https://launchpad.net/freezer
|
||||
* - Ironic UI
|
||||
- https://github.com/openstack/ironic-ui
|
||||
- https://launchpad.net/ironic-ui
|
||||
* - Karbor Dashboard
|
||||
- https://github.com/openstack/karbor-dashboard
|
||||
- https://launchpad.net/karbor-dashboard
|
||||
* - Magnum UI
|
||||
- http://github.com/openstack/magnum-ui
|
||||
- https://launchpad.net/magnum-ui
|
||||
* - Manila UI
|
||||
- http://github.com/openstack/manila-ui
|
||||
- https://launchpad.net/manila-ui
|
||||
* - Mistral Dashboard
|
||||
- https://github.com/openstack/mistral-dashboard
|
||||
- https://launchpad.net/mistral
|
||||
* - Monasca UI
|
||||
- https://github.com/openstack/monasca-ui
|
||||
- https://launchpad.net/monasca
|
||||
* - Murano Dashboard
|
||||
- http://github.com/openstack/murano-dashboard
|
||||
- http://launchpad.net/murano
|
||||
* - Neutron FWaaS Dashboard
|
||||
- https://github.com/openstack/neutron-fwaas-dashboard
|
||||
- https://launchpad.net/neutron-fwaas-dashboard
|
||||
* - Neutron LBaaS Dashboard
|
||||
- https://github.com/openstack/neutron-lbaas-dashboard
|
||||
- http://launchpad.net/octavia
|
||||
* - Neutron VPNaaS Dashboard
|
||||
- https://github.com/openstack/neutron-vpnaas-dashboard
|
||||
- https://launchpad.net/neutron-vpnaas-dashboard
|
||||
* - Octavia Dashboard
|
||||
- https://github.com/openstack/octavia-dashboard
|
||||
- https://launchpad.net/octavia
|
||||
* - Sahara Dashboard
|
||||
- https://github.com/openstack/sahara-dashboard
|
||||
- https://launchpad.net/sahara
|
||||
* - Searchlight UI
|
||||
- https://github.com/openstack/searchlight-ui
|
||||
- https://launchpad.net/searchlight
|
||||
* - Senlin Dashboard
|
||||
- https://github.com/openstack/senlin-dashboard
|
||||
- http://launchpad.net/senlin-dashboard
|
||||
* - Solum Dashboard
|
||||
- https://github.com/openstack/solum-dashboard
|
||||
- https://launchpad.net/solum
|
||||
* - Sticks Dashboard
|
||||
- https://github.com/openstack/sticks-dashboard
|
||||
- https://wiki.openstack.org/wiki/Sticks
|
||||
* - Tacker UI
|
||||
- https://github.com/openstack/tacker-horizon
|
||||
- https://launchpad.net/tacker
|
||||
* - TripleO UI
|
||||
- https://github.com/openstack/tripleo-ui/
|
||||
- https://launchpad.net/tripleo
|
||||
* - Trove Dashboard
|
||||
- https://github.com/openstack/trove-dashboard
|
||||
- https://launchpad.net/trove-dashboard
|
||||
* - Vitrage Dashboard
|
||||
- http://github.com/openstack/vitrage-dashboard
|
||||
- https://launchpad.net/vitrage-dashboard
|
||||
* - Watcher Dashboard
|
||||
- http://github.com/openstack/watcher-dashboard
|
||||
- https://launchpad.net/watcher-dashboard
|
||||
* - Zaqar UI
|
||||
- http://github.com/openstack/zaqar-ui
|
||||
- https://launchpad.net/zaqar-ui
|
||||
* - Zun UI
|
||||
- https://github.com/openstack/zun-ui
|
||||
- https://launchpad.net/zun-ui
|
|
@ -1,11 +0,0 @@
|
|||
===========================
|
||||
Verify operation for Debian
|
||||
===========================
|
||||
|
||||
Verify operation of the dashboard.
|
||||
|
||||
Access the dashboard using a web browser at
|
||||
``http://controller/``.
|
||||
|
||||
Authenticate using ``admin`` or ``demo`` user
|
||||
and ``default`` domain credentials.
|
|
@ -1,11 +0,0 @@
|
|||
=======================================================
|
||||
Verify operation for openSUSE and SUSE Linux Enterprise
|
||||
=======================================================
|
||||
|
||||
Verify operation of the dashboard.
|
||||
|
||||
Access the dashboard using a web browser at
|
||||
``http://controller/``.
|
||||
|
||||
Authenticate using ``admin`` or ``demo`` user
|
||||
and ``default`` domain credentials.
|
|
@ -1,11 +0,0 @@
|
|||
========================================================
|
||||
Verify operation for Red Hat Enterprise Linux and CentOS
|
||||
========================================================
|
||||
|
||||
Verify operation of the dashboard.
|
||||
|
||||
Access the dashboard using a web browser at
|
||||
``http://controller/dashboard``.
|
||||
|
||||
Authenticate using ``admin`` or ``demo`` user
|
||||
and ``default`` domain credentials.
|
|
@ -1,11 +0,0 @@
|
|||
===========================
|
||||
Verify operation for Ubuntu
|
||||
===========================
|
||||
|
||||
Verify operation of the dashboard.
|
||||
|
||||
Access the dashboard using a web browser at
|
||||
``http://controller/horizon``.
|
||||
|
||||
Authenticate using ``admin`` or ``demo`` user
|
||||
and ``default`` domain credentials.
|
|
@ -1,100 +0,0 @@
|
|||
==================
|
||||
Supported Browsers
|
||||
==================
|
||||
|
||||
Horizon is primarily tested and supported on the latest version of Firefox,
|
||||
the latest version of Chrome, and IE9+. Issues related to Safari and Opera will
|
||||
also be considered.
|
||||
|
||||
This page aims to informally document what that means for different releases,
|
||||
everyone is warmly encouraged to update this page based on the versions they've
|
||||
tested with.
|
||||
|
||||
Legend:
|
||||
|
||||
- Very good: Very well tested, should work as expected
|
||||
- Good: Moderately tested, should look nice and work fine, maybe a few visual
|
||||
hiccups
|
||||
- Poor: Doesn't look good
|
||||
- Broken: Essential functionality not working (link to bug in the notes)
|
||||
- No: Not supported
|
||||
|
||||
Kilo
|
||||
====
|
||||
|
||||
+--------------------+--------------------+------------------------------+
|
||||
| | Status | Notes |
|
||||
+====================+====================+==============================+
|
||||
|Firefox |Very good |31+. (Earlier versions?) |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Firefox ESR |Very good |31+ |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Chrome |Very good |43.0.2357.81 |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 11 |Good? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 10 |Good? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 9 |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 8 and below |Not supported. | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Safari |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Opera |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|
||||
Juno
|
||||
====
|
||||
|
||||
+--------------------+--------------------+------------------------------+
|
||||
| | Status | Notes |
|
||||
+====================+====================+==============================+
|
||||
|Firefox |Very good |31+. (Earlier versions?) |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Firefox ESR |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Chrome |Very good |Versions? |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 11 |Good? |`Open IE Bugs`_. |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 10 |Good? |`Open IE Bugs`_. |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 9 |? |`Open IE Bugs`_. |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|IE 8 and below |Not supported. |No. |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Safari |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|Opera |? | |
|
||||
+--------------------+--------------------+------------------------------+
|
||||
|
||||
.. _Open IE Bugs: https://bugs.launchpad.net/horizon/+bugs?field.tag=ie
|
||||
|
||||
Icehouse
|
||||
========
|
||||
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
| | Status | Notes |
|
||||
+====================+=================+======================================+
|
||||
|Firefox |Very good |Versions? |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|Firefox ESR |Very good |Windows 24.7.0 ESR |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|Chrome |Very good |Windows Version 36, RHEL version 27.0 |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|Chromium |Very good |version 31.0 |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|IE 11 |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|IE 10 |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|IE 9 |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|IE 8 and below |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|Safari |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|Opera |? | |
|
||||
+--------------------+-----------------+--------------------------------------+
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
===========================================
|
||||
Configure access and security for instances
|
||||
===========================================
|
||||
|
||||
Before you launch an instance, you should add security group rules to
|
||||
enable users to ping and use SSH to connect to the instance. Security
|
||||
groups are sets of IP filter rules that define networking access and are
|
||||
applied to all instances within a project. To do so, you either add
|
||||
rules to the default security group :ref:`security_groups_add_rule`
|
||||
or add a new security group with rules.
|
||||
|
||||
Key pairs are SSH credentials that are injected into an instance when it
|
||||
is launched. To use key pair injection, the image that the instance is
|
||||
based on must contain the ``cloud-init`` package. Each project should
|
||||
have at least one key pair. For more information, see the section
|
||||
:ref:`keypair_add`.
|
||||
|
||||
If you have generated a key pair with an external tool, you can import
|
||||
it into OpenStack. The key pair can be used for multiple instances that
|
||||
belong to a project. For more information, see the section
|
||||
:ref:`dashboard_import_keypair`.
|
||||
|
||||
.. note::
|
||||
|
||||
A key pair belongs to an individual user, not to a project.
|
||||
To share a key pair across multiple users, each user
|
||||
needs to import that key pair.
|
||||
|
||||
When an instance is created in OpenStack, it is automatically assigned a
|
||||
fixed IP address in the network to which the instance is assigned. This
|
||||
IP address is permanently associated with the instance until the
|
||||
instance is terminated. However, in addition to the fixed IP address, a
|
||||
floating IP address can also be attached to an instance. Unlike fixed IP
|
||||
addresses, floating IP addresses are able to have their associations
|
||||
modified at any time, regardless of the state of the instances involved.
|
||||
|
||||
.. _security_groups_add_rule:
|
||||
|
||||
Add a rule to the default security group
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This procedure enables SSH and ICMP (ping) access to instances. The
|
||||
rules apply to all instances within a given project, and should be set
|
||||
for every project unless there is a reason to prohibit SSH or ICMP
|
||||
access to the instances.
|
||||
|
||||
This procedure can be adjusted as necessary to add additional security
|
||||
group rules to a project, if your cloud requires them.
|
||||
|
||||
.. note::
|
||||
|
||||
When adding a rule, you must specify the protocol used with the
|
||||
destination port or source port.
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Compute` tab and
|
||||
click :guilabel:`Access & Security` category. The
|
||||
:guilabel:`Security Groups` tab shows the security groups that are
|
||||
available for this project.
|
||||
|
||||
#. Select the default security group and click :guilabel:`Manage Rules`.
|
||||
|
||||
#. To allow SSH access, click :guilabel:`Add Rule`.
|
||||
|
||||
#. In the :guilabel:`Add Rule` dialog box, enter the following values:
|
||||
|
||||
* **Rule**: ``SSH``
|
||||
* **Remote**: ``CIDR``
|
||||
* **CIDR**: ``0.0.0.0/0``
|
||||
|
||||
.. note::
|
||||
|
||||
To accept requests from a particular range of IP
|
||||
addresses, specify the IP address block in the
|
||||
:guilabel:`CIDR` box.
|
||||
|
||||
#. Click :guilabel:`Add`.
|
||||
|
||||
Instances will now have SSH port 22 open for requests from any IP
|
||||
address.
|
||||
|
||||
#. To add an ICMP rule, click :guilabel:`Add Rule`.
|
||||
|
||||
#. In the :guilabel:`Add Rule` dialog box, enter the following values:
|
||||
|
||||
* **Rule**: ``All ICMP``
|
||||
* **Direction**: ``Ingress``
|
||||
* **Remote**: ``CIDR``
|
||||
* **CIDR**: ``0.0.0.0/0``
|
||||
|
||||
#. Click :guilabel:`Add`.
|
||||
|
||||
Instances will now accept all incoming ICMP packets.
|
||||
|
||||
.. _keypair_add:
|
||||
|
||||
Add a key pair
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Create at least one key pair for each project.
|
||||
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Compute` tab and
|
||||
click :guilabel:`Access & Security` category.
|
||||
|
||||
#. Click the :guilabel:`Key Pairs` tab, which shows the key pairs that
|
||||
are available for this project.
|
||||
|
||||
#. Click :guilabel:`Create Key Pair`.
|
||||
|
||||
#. In the :guilabel:`Create Key Pair` dialog box, enter a name for your
|
||||
key pair, and click :guilabel:`Create Key Pair`.
|
||||
|
||||
#. Respond to the prompt to download the key pair.
|
||||
|
||||
.. _dashboard_import_keypair:
|
||||
|
||||
Import a key pair
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Compute` tab and
|
||||
click :guilabel:`Access & Security` category.
|
||||
|
||||
#. Click the :guilabel:`Key Pairs` tab, which shows the key pairs that
|
||||
are available for this project.
|
||||
|
||||
#. Click :guilabel:`Import Key Pair`.
|
||||
|
||||
#. In the :guilabel:`Import Key Pair` dialog box, enter the name of your
|
||||
key pair, copy the public key into the :guilabel:`Public Key` box,
|
||||
and then click :guilabel:`Import Key Pair`.
|
||||
|
||||
#. Save the ``*.pem`` file locally.
|
||||
|
||||
#. To change its permissions so that only you can read and write to the
|
||||
file, run the following command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ chmod 0600 yourPrivateKey.pem
|
||||
|
||||
.. note::
|
||||
|
||||
If you are using the Dashboard from a Windows computer, use PuTTYgen
|
||||
to load the ``*.pem`` file and convert and save it as ``*.ppk``. For
|
||||
more information see the `WinSCP web page for
|
||||
PuTTYgen <http://winscp.net/eng/docs/ui_puttygen>`__.
|
||||
|
||||
#. To make the key pair known to SSH, run the :command:`ssh-add` command.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ssh-add yourPrivateKey.pem
|
||||
|
||||
The Compute database registers the public key of the key pair.
|
||||
|
||||
The Dashboard lists the key pair on the :guilabel:`Access & Security` tab.
|
||||
|
||||
Allocate a floating IP address to an instance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When an instance is created in OpenStack, it is automatically assigned a
|
||||
fixed IP address in the network to which the instance is assigned. This
|
||||
IP address is permanently associated with the instance until the
|
||||
instance is terminated.
|
||||
|
||||
However, in addition to the fixed IP address, a floating IP address can
|
||||
also be attached to an instance. Unlike fixed IP addresses, floating IP
|
||||
addresses can have their associations modified at any time, regardless
|
||||
of the state of the instances involved. This procedure details the
|
||||
reservation of a floating IP address from an existing pool of addresses
|
||||
and the association of that address with a specific instance.
|
||||
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Compute` tab and
|
||||
click :guilabel:`Access & Security` category.
|
||||
|
||||
#. Click the :guilabel:`Floating IPs` tab, which shows the floating IP
|
||||
addresses allocated to instances.
|
||||
|
||||
#. Click :guilabel:`Allocate IP To Project`.
|
||||
|
||||
#. Choose the pool from which to pick the IP address.
|
||||
|
||||
#. Click :guilabel:`Allocate IP`.
|
||||
|
||||
#. In the :guilabel:`Floating IPs` list, click :guilabel:`Associate`.
|
||||
|
||||
#. In the :guilabel:`Manage Floating IP Associations` dialog box,
|
||||
choose the following options:
|
||||
|
||||
- The :guilabel:`IP Address` field is filled automatically,
|
||||
but you can add a new IP address by clicking the
|
||||
:guilabel:`+` button.
|
||||
|
||||
- In the :guilabel:`Port to be associated` field, select a port
|
||||
from the list.
|
||||
|
||||
The list shows all the instances with their fixed IP addresses.
|
||||
|
||||
#. Click :guilabel:`Associate`.
|
||||
|
||||
.. note::
|
||||
|
||||
To disassociate an IP address from an instance, click the
|
||||
:guilabel:`Disassociate` button.
|
||||
|
||||
To release the floating IP address back into the floating IP pool, click
|
||||
the :guilabel:`Release Floating IP` option in the :guilabel:`Actions` column.
|
|
@ -1,146 +0,0 @@
|
|||
==========================
|
||||
Create and manage networks
|
||||
==========================
|
||||
|
||||
The OpenStack Networking service provides a scalable system for managing
|
||||
the network connectivity within an OpenStack cloud deployment. It can
|
||||
easily and quickly react to changing network needs (for example,
|
||||
creating and assigning new IP addresses).
|
||||
|
||||
Networking in OpenStack is complex. This section provides the basic
|
||||
instructions for creating a network and a router. For detailed
|
||||
information about managing networks, refer to the `OpenStack
|
||||
Administrator
|
||||
Guide <https://docs.openstack.org/admin-guide/networking.html>`__.
|
||||
|
||||
Create a network
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Network` tab and
|
||||
click :guilabel:`Networks` category.
|
||||
|
||||
#. Click :guilabel:`Create Network`.
|
||||
|
||||
#. In the :guilabel:`Create Network` dialog box, specify the following values.
|
||||
|
||||
:guilabel:`Network` tab
|
||||
|
||||
:guilabel:`Network Name`: Specify a name to identify the network.
|
||||
|
||||
:guilabel:`Shared`: Share the network with other projects. Non admin users
|
||||
are not allowed to set shared option.
|
||||
|
||||
:guilabel:`Admin State`: The state to start the network in.
|
||||
|
||||
:guilabel:`Create Subnet`: Select this check box to create a subnet
|
||||
|
||||
You do not have to specify a subnet when you create a network, but if
|
||||
you do not specify a subnet, the network can not be attached to an instance.
|
||||
|
||||
:guilabel:`Subnet` tab
|
||||
|
||||
:guilabel:`Subnet Name`: Specify a name for the subnet.
|
||||
|
||||
:guilabel:`Network Address`: Specify the IP address for the subnet.
|
||||
|
||||
:guilabel:`IP Version`: Select IPv4 or IPv6.
|
||||
|
||||
:guilabel:`Gateway IP`: Specify an IP address for a specific gateway. This
|
||||
parameter is optional.
|
||||
|
||||
:guilabel:`Disable Gateway`: Select this check box to disable a gateway IP
|
||||
address.
|
||||
|
||||
:guilabel:`Subnet Details` tab
|
||||
|
||||
:guilabel:`Enable DHCP`: Select this check box to enable DHCP.
|
||||
|
||||
:guilabel:`Allocation Pools`: Specify IP address pools.
|
||||
|
||||
:guilabel:`DNS Name Servers`: Specify a name for the DNS server.
|
||||
|
||||
:guilabel:`Host Routes`: Specify the IP address of host routes.
|
||||
|
||||
#. Click :guilabel:`Create`.
|
||||
|
||||
The dashboard shows the network on the :guilabel:`Networks` tab.
|
||||
|
||||
Create a router
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Network` tab and
|
||||
click :guilabel:`Routers` category.
|
||||
|
||||
#. Click :guilabel:`Create Router`.
|
||||
|
||||
#. In the :guilabel:`Create Router` dialog box, specify a name for the router
|
||||
and :guilabel:`External Network`, and click :guilabel:`Create Router`.
|
||||
|
||||
The new router is now displayed in the :guilabel:`Routers` tab.
|
||||
|
||||
#. To connect a private network to the newly created router, perform the
|
||||
following steps:
|
||||
|
||||
A) On the :guilabel:`Routers` tab, click the name of the router.
|
||||
|
||||
B) On the :guilabel:`Router Details` page, click the :guilabel:`Interfaces`
|
||||
tab, then click :guilabel:`Add Interface`.
|
||||
|
||||
C) In the :guilabel:`Add Interface` dialog box, select a :guilabel:`Subnet`.
|
||||
|
||||
Optionally, in the :guilabel:`Add Interface` dialog box, set an
|
||||
:guilabel:`IP Address` for the router interface for the selected subnet.
|
||||
|
||||
If you choose not to set the :guilabel:`IP Address` value, then by
|
||||
default OpenStack Networking uses the first host IP address in the
|
||||
subnet.
|
||||
|
||||
The :guilabel:`Router Name` and :guilabel:`Router ID` fields are
|
||||
automatically updated.
|
||||
|
||||
#. Click :guilabel:`Add Interface`.
|
||||
|
||||
You have successfully created the router. You can view the new topology
|
||||
from the :guilabel:`Network Topology` tab.
|
||||
|
||||
Create a port
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. warning::
|
||||
|
||||
Creating and managing ports requires administrator privileges.
|
||||
Contact an administrator before adding or changing ports.
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. Select the appropriate project from the drop-down menu at the top left.
|
||||
|
||||
#. On the :guilabel:`Admin` tab, click :guilabel:`Networks` category.
|
||||
|
||||
#. Click on the :guilabel:`Network Name` of the network in which the port
|
||||
has to be created.
|
||||
|
||||
#. In the :guilabel:`Create Port` dialog box, specify the following values.
|
||||
|
||||
:guilabel:`Name`: Specify name to identify the port.
|
||||
|
||||
:guilabel:`Device ID`: Device ID attached to the port.
|
||||
|
||||
:guilabel:`Device Owner`: Device owner attached to the port.
|
||||
|
||||
:guilabel:`Binding Host`: The ID of the host where the port is allocated.
|
||||
|
||||
:guilabel:`Binding VNIC Type`: Select the VNIC type that is bound to the
|
||||
neutron port.
|
||||
|
||||
#. Click :guilabel:`Create Port`.
|
||||
|
||||
The new port is now displayed in the :guilabel:`Ports` list.
|
|
@ -1,216 +0,0 @@
|
|||
===========================
|
||||
Create and manage databases
|
||||
===========================
|
||||
|
||||
The Database service provides scalable and reliable cloud provisioning
|
||||
functionality for both relational and non-relational database engines.
|
||||
Users can quickly and easily use database features without the burden of
|
||||
handling complex administrative tasks.
|
||||
|
||||
.. _dashboard_create_db_instance:
|
||||
|
||||
Create a database instance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Prerequisites.** Before you create a database instance, you need to
|
||||
configure a default datastore and make sure you have an appropriate
|
||||
flavor for the type of database instance you want.
|
||||
|
||||
#. **Configure a default datastore.**
|
||||
|
||||
Because the dashboard does not let you choose a specific datastore to
|
||||
use with an instance, you need to configure a default datastore. The
|
||||
dashboard then uses the default datastore to create the instance.
|
||||
|
||||
#. Add the following line to ``/etc/trove/trove.conf``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
default_datastore = DATASTORE_NAME
|
||||
|
||||
Replace ``DATASTORE_NAME`` with the name that the administrative
|
||||
user set when issuing the :command:`trove-manage` command to create the
|
||||
datastore. You can use the trove :command:`datastore-list` command to
|
||||
display the datastores that are available in your environment.
|
||||
|
||||
For example, if your MySQL data store name is set to ``mysql``,
|
||||
your entry would look like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
default_datastore = mysql
|
||||
|
||||
#. Restart Database services on the controller node:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# service trove-api restart
|
||||
# service trove-taskmanager restart
|
||||
# service trove-conductor restart
|
||||
|
||||
#. **Verify flavor.**
|
||||
|
||||
Make sure an appropriate flavor exists for the type of
|
||||
database instance you want.
|
||||
|
||||
**Create database instance.** Once you have configured a default
|
||||
datastore and verified that you have an appropriate flavor, you can
|
||||
create a database instance.
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. From the CURRENT PROJECT on the :guilabel:`Project` tab, select the
|
||||
appropriate project.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Database` tab and
|
||||
click :guilabel:`Instances` category. This lists the instances that
|
||||
already exist in your environment.
|
||||
|
||||
#. Click :guilabel:`Launch Instance`.
|
||||
|
||||
#. In the :guilabel:`Launch Database` dialog box, specify the following values.
|
||||
|
||||
Details
|
||||
|
||||
:guilabel:`Database Name`: Specify a name for the database instance.
|
||||
|
||||
:guilabel:`Flavor`: Select an appropriate flavor for the instance.
|
||||
|
||||
:guilabel:`Volume Size`: Select a volume size. Volume size is expressed in
|
||||
GB.
|
||||
|
||||
:guilabel:`Initialize Databases`: Initial Database
|
||||
|
||||
Optionally provide a comma separated list of databases to create, for
|
||||
example:
|
||||
|
||||
``database1``, ``database2``, ``database3``
|
||||
|
||||
:guilabel:`Initial Admin User`: Create an initial admin user. This user will
|
||||
have access to all the databases you create.
|
||||
|
||||
:guilabel:`Password`: Specify a password associated with the initial admin
|
||||
user you just named.
|
||||
|
||||
:guilabel:`Host`: Optionally, allow the user to connect only from this host.
|
||||
If you do not specify a host, this user will be allowed to connect from
|
||||
anywhere.
|
||||
|
||||
#. Click the :guilabel:`Launch` button. The new database instance appears in
|
||||
the databases list.
|
||||
|
||||
Backup and restore a database
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can use Database services to backup a database and store the backup
|
||||
artifact in the Object Storage service. Later on, if the original
|
||||
database is damaged, you can use the backup artifact to restore the
|
||||
database. The restore process creates a database instance.
|
||||
|
||||
This example shows you how to back up and restore a MySQL database.
|
||||
|
||||
To backup the database instance
|
||||
-------------------------------
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. From the CURRENT PROJECT on the :guilabel:`Project` tab, select the
|
||||
appropriate project.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Database` tab and
|
||||
click :guilabel:`Instances` category. This displays the existing
|
||||
instances in your system.
|
||||
|
||||
#. Click :guilabel:`Create Backup`.
|
||||
|
||||
#. In the :guilabel:`Backup Database` dialog box, specify the following
|
||||
values:
|
||||
|
||||
Name
|
||||
|
||||
Specify a name for the backup.
|
||||
|
||||
Database Instance
|
||||
|
||||
Select the instance you want to back up.
|
||||
|
||||
#. Click :guilabel:`Backup`. The new backup appears in the backup list.
|
||||
|
||||
To restore a database instance
|
||||
------------------------------
|
||||
|
||||
Now assume that your original database instance is damaged and you
|
||||
need to restore it. You do the restore by using your backup to create
|
||||
a new database instance.
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. From the CURRENT PROJECT on the :guilabel:`Project` tab, select the
|
||||
appropriate project.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Database` tab and
|
||||
click :guilabel:`Backups` category. This lists the available backups.
|
||||
|
||||
#. Check the backup you want to use and click :guilabel:`Restore Backup`.
|
||||
|
||||
#. In the :guilabel:`Launch Database` dialog box, specify the values you
|
||||
want for the new database instance.
|
||||
|
||||
#. Click the :guilabel:`Restore From Database` tab and make sure that this
|
||||
new instance is based on the correct backup.
|
||||
|
||||
#. Click :guilabel:`Launch`.
|
||||
|
||||
The new instance appears in the database instances list.
|
||||
|
||||
Update a database instance
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can change various characteristics of a database instance,
|
||||
such as its volume size and flavor.
|
||||
|
||||
To change the volume size of an instance
|
||||
----------------------------------------
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. From the CURRENT PROJECT on the :guilabel:`Project` tab, select the
|
||||
appropriate project.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Database` tab and
|
||||
click :guilabel:`Instances` category. This displays the existing
|
||||
instances in your system.
|
||||
|
||||
#. Check the instance you want to work with.
|
||||
In the :guilabel:`Actions` column, expand the drop down menu
|
||||
and select :guilabel:`Resize Volume`.
|
||||
|
||||
#. In the :guilabel:`Resize Database Volume` dialog box,
|
||||
fill in the :guilabel:`New Size` field with an integer indicating
|
||||
the new size you want for the instance. Express the size in GB, and
|
||||
note that the new size must be larger than the current size.
|
||||
|
||||
#. Click :guilabel:`Resize Database Volume`.
|
||||
|
||||
To change the flavor of an instance
|
||||
-----------------------------------
|
||||
|
||||
#. Log in to the dashboard.
|
||||
|
||||
#. From the CURRENT PROJECT on the :guilabel:`Project` tab, select the
|
||||
appropriate project.
|
||||
|
||||
#. On the :guilabel:`Project` tab, open the :guilabel:`Database` tab and
|
||||
click :guilabel:`Instances` category. This displays the existing
|
||||
instances in your system.
|
||||
|
||||
#. Check the instance you want to work with. In the
|
||||
:guilabel:`Actions` column, expand the drop down menu and
|
||||
select :guilabel:`Resize Instance`.
|
||||
|
||||
#. In the :guilabel:`Resize Database Instance` dialog box,
|
||||
expand the drop down menu in the :guilabel:`New Flavor` field.
|
||||
Select the new flavor you want for the instance.
|
||||
|
||||
#. Click :guilabel:`Resize Database Instance`.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue