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:
Tony Breeds 2017-09-12 15:39:12 -06:00
parent 08089b01c4
commit c3f0f80247
2581 changed files with 14 additions and 514535 deletions

View File

@ -1 +0,0 @@
horizon/static/horizon/lib/**

View File

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

48
.gitignore vendored
View File

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

View File

@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack/horizon.git

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

14
README Normal file
View File

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

View File

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

View File

@ -1,6 +0,0 @@
[extractors]
django = django_babel.extract:extract_django
[python: **.py]
[django: **/templates/**.html]
[django: **/templates/**.csv]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
====================
View cloud resources
====================
.. toctree::
:maxdepth: 2
manage-services.rst
view-cloud-resources.rst

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +0,0 @@
===================
Configuration Guide
===================
.. toctree::
:maxdepth: 1
settings
pluggable_panels
customizing
themes
branding

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +0,0 @@
===========================
Contributor Documentation
===========================
.. toctree::
:maxdepth: 2
intro
quickstart
contributing
testing
tutorials/index
topics/index
ref/index
faq

View File

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

View File

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

View File

@ -1,6 +0,0 @@
==========================
Horizon Context Processors
==========================
.. automodule:: horizon.context_processors
:members:

View File

@ -1,6 +0,0 @@
==================
Horizon Decorators
==================
.. automodule:: horizon.decorators
:members:

View File

@ -1,6 +0,0 @@
==================
Horizon Exceptions
==================
.. automodule:: horizon.exceptions
:members:

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +0,0 @@
==================
Horizon Middleware
==================
HorizonMiddleware
-----------------
.. autoclass:: horizon.middleware.HorizonMiddleware
:members:
OperationLogMiddleware
----------------------
.. autoclass:: horizon.middleware.OperationLogMiddleware
:members:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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([]);
});
});
...
});
})();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +0,0 @@
=========
Tutorials
=========
Detailed tutorials to help you get started.
.. toctree::
:maxdepth: 1
plugin
dashboard
table_actions
workflow_extend

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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