From 64f76b55a95eb396d203e3bc63f83501339b1fca Mon Sep 17 00:00:00 2001 From: luyao Date: Wed, 20 Jul 2016 20:07:44 +0800 Subject: [PATCH] remove doc file and modify install/uninstall/upgrade shell about python-django-horizon-doc Change-Id: Ief6d7e2f4805b704919b40a58e8a2bf54c444b71 Signed-off-by: luyao --- code/horizon/doc/Makefile | 153 -- code/horizon/doc/source/conf.py | 438 ------ code/horizon/doc/source/contributing.rst | 559 ------- code/horizon/doc/source/faq.rst | 45 - code/horizon/doc/source/glossary.rst | 24 - code/horizon/doc/source/index.rst | 132 -- code/horizon/doc/source/intro.rst | 121 -- code/horizon/doc/source/quickstart.rst | 314 ---- .../doc/source/ref/context_processors.rst | 6 - code/horizon/doc/source/ref/decorators.rst | 6 - code/horizon/doc/source/ref/exceptions.rst | 6 - code/horizon/doc/source/ref/forms.rst | 98 -- code/horizon/doc/source/ref/horizon.rst | 45 - code/horizon/doc/source/ref/local_conf.rst | 70 - code/horizon/doc/source/ref/middleware.rst | 6 - code/horizon/doc/source/ref/run_tests.rst | 263 ---- code/horizon/doc/source/ref/tables.rst | 104 -- code/horizon/doc/source/ref/tabs.rst | 45 - code/horizon/doc/source/ref/test.rst | 25 - code/horizon/doc/source/ref/workflows.rst | 38 - code/horizon/doc/source/releases/2012_1.rst | 148 -- code/horizon/doc/source/releases/2012_2.rst | 159 -- code/horizon/doc/source/releases/2013_1.rst | 274 ---- code/horizon/doc/source/releases/2013_2.rst | 254 --- code/horizon/doc/source/releases/2014_1.rst | 190 --- code/horizon/doc/source/releases/2014_2.rst | 180 --- code/horizon/doc/source/testing.rst | 59 - .../horizon/doc/source/topics/customizing.rst | 283 ---- code/horizon/doc/source/topics/deployment.rst | 227 --- code/horizon/doc/source/topics/install.rst | 100 -- code/horizon/doc/source/topics/policy.rst | 148 -- code/horizon/doc/source/topics/settings.rst | 1383 ----------------- .../doc/source/topics/table_actions.rst | 300 ---- code/horizon/doc/source/topics/tables.rst | 387 ----- code/horizon/doc/source/topics/testing.rst | 276 ---- code/horizon/doc/source/topics/tutorial.rst | 629 -------- code/horizon/doc/source/topics/workflows.rst | 134 -- tools/setup/install/install_interface.sh | 2 +- tools/setup/uninstall/uninstall_interface.sh | 2 +- tools/setup/upgrade/upgrade_func.sh | 2 +- 40 files changed, 3 insertions(+), 7632 deletions(-) delete mode 100644 code/horizon/doc/Makefile delete mode 100755 code/horizon/doc/source/conf.py delete mode 100644 code/horizon/doc/source/contributing.rst delete mode 100644 code/horizon/doc/source/faq.rst delete mode 100644 code/horizon/doc/source/glossary.rst delete mode 100644 code/horizon/doc/source/index.rst delete mode 100644 code/horizon/doc/source/intro.rst delete mode 100644 code/horizon/doc/source/quickstart.rst delete mode 100644 code/horizon/doc/source/ref/context_processors.rst delete mode 100644 code/horizon/doc/source/ref/decorators.rst delete mode 100644 code/horizon/doc/source/ref/exceptions.rst delete mode 100644 code/horizon/doc/source/ref/forms.rst delete mode 100644 code/horizon/doc/source/ref/horizon.rst delete mode 100644 code/horizon/doc/source/ref/local_conf.rst delete mode 100644 code/horizon/doc/source/ref/middleware.rst delete mode 100644 code/horizon/doc/source/ref/run_tests.rst delete mode 100644 code/horizon/doc/source/ref/tables.rst delete mode 100644 code/horizon/doc/source/ref/tabs.rst delete mode 100644 code/horizon/doc/source/ref/test.rst delete mode 100644 code/horizon/doc/source/ref/workflows.rst delete mode 100644 code/horizon/doc/source/releases/2012_1.rst delete mode 100644 code/horizon/doc/source/releases/2012_2.rst delete mode 100644 code/horizon/doc/source/releases/2013_1.rst delete mode 100644 code/horizon/doc/source/releases/2013_2.rst delete mode 100644 code/horizon/doc/source/releases/2014_1.rst delete mode 100644 code/horizon/doc/source/releases/2014_2.rst delete mode 100644 code/horizon/doc/source/testing.rst delete mode 100644 code/horizon/doc/source/topics/customizing.rst delete mode 100644 code/horizon/doc/source/topics/deployment.rst delete mode 100644 code/horizon/doc/source/topics/install.rst delete mode 100644 code/horizon/doc/source/topics/policy.rst delete mode 100644 code/horizon/doc/source/topics/settings.rst delete mode 100644 code/horizon/doc/source/topics/table_actions.rst delete mode 100644 code/horizon/doc/source/topics/tables.rst delete mode 100644 code/horizon/doc/source/topics/testing.rst delete mode 100644 code/horizon/doc/source/topics/tutorial.rst delete mode 100644 code/horizon/doc/source/topics/workflows.rst diff --git a/code/horizon/doc/Makefile b/code/horizon/doc/Makefile deleted file mode 100644 index 986ad3df..00000000 --- a/code/horizon/doc/Makefile +++ /dev/null @@ -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 ' where 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." diff --git a/code/horizon/doc/source/conf.py b/code/horizon/doc/source/conf.py deleted file mode 100755 index ecf7eccf..00000000 --- a/code/horizon/doc/source/conf.py +++ /dev/null @@ -1,438 +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 horizon.version -import os -import sys - -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') - - -def write_autodoc_index(): - - def find_autodoc_modules(module_name, sourcedir): - """returns a list of modules in the SOURCE directory.""" - modlist = [] - os.chdir(os.path.join(sourcedir, module_name)) - print("SEARCHING %s" % sourcedir) - for root, dirs, files in os.walk("."): - for filename in files: - if filename == 'tests.py': - continue - if filename.endswith(".py"): - # remove the pieces of the root - elements = root.split(os.path.sep) - # replace the leading "." with the module name - elements[0] = module_name - # and get the base module name - base, extension = os.path.splitext(filename) - if not (base == "__init__"): - elements.append(base) - result = ".".join(elements) - # print result - modlist.append(result) - return modlist - - RSTDIR = os.path.abspath(os.path.join(BASE_DIR, "sourcecode")) - SRCS = [('horizon', ROOT), - ('openstack_dashboard', ROOT)] - - EXCLUDED_MODULES = ('horizon.test', - 'openstack_dashboard.enabled', - 'openstack_dashboard.test', - 'openstack_dashboard.openstack.common', - ) - CURRENT_SOURCES = {} - - if not(os.path.exists(RSTDIR)): - os.mkdir(RSTDIR) - CURRENT_SOURCES[RSTDIR] = ['autoindex.rst'] - - INDEXOUT = open(os.path.join(RSTDIR, "autoindex.rst"), "w") - INDEXOUT.write(""" -================= -Source Code Index -================= - -.. contents:: - :depth: 1 - :local: - -""") - - for modulename, path in SRCS: - sys.stdout.write("Generating source documentation for %s\n" % - modulename) - INDEXOUT.write("\n%s\n" % modulename.capitalize()) - INDEXOUT.write("%s\n" % ("=" * len(modulename),)) - INDEXOUT.write(".. toctree::\n") - INDEXOUT.write(" :maxdepth: 1\n") - INDEXOUT.write("\n") - - MOD_DIR = os.path.join(RSTDIR, modulename) - CURRENT_SOURCES[MOD_DIR] = [] - if not(os.path.exists(MOD_DIR)): - os.mkdir(MOD_DIR) - for module in find_autodoc_modules(modulename, path): - if any([module.startswith(exclude) for exclude - in EXCLUDED_MODULES]): - print("Excluded module %s." % module) - continue - mod_path = os.path.join(path, *module.split(".")) - generated_file = os.path.join(MOD_DIR, "%s.rst" % module) - - INDEXOUT.write(" %s/%s\n" % (modulename, module)) - - # Find the __init__.py module if this is a directory - if os.path.isdir(mod_path): - source_file = ".".join((os.path.join(mod_path, "__init__"), - "py",)) - else: - source_file = ".".join((os.path.join(mod_path), "py")) - - CURRENT_SOURCES[MOD_DIR].append("%s.rst" % module) - # Only generate a new file if the source has changed or we don't - # have a doc file to begin with. - if not os.access(generated_file, os.F_OK) or ( - os.stat(generated_file).st_mtime < - os.stat(source_file).st_mtime): - print("Module %s updated, generating new documentation." - % module) - FILEOUT = open(generated_file, "w") - header = "The :mod:`%s` Module" % module - FILEOUT.write("%s\n" % ("=" * len(header),)) - FILEOUT.write("%s\n" % header) - FILEOUT.write("%s\n" % ("=" * len(header),)) - FILEOUT.write(".. automodule:: %s\n" % module) - FILEOUT.write(" :members:\n") - FILEOUT.write(" :undoc-members:\n") - FILEOUT.write(" :show-inheritance:\n") - FILEOUT.write(" :noindex:\n") - FILEOUT.close() - - INDEXOUT.close() - - # Delete auto-generated .rst files for sources which no longer exist - for directory, subdirs, files in list(os.walk(RSTDIR)): - for old_file in files: - if old_file not in CURRENT_SOURCES.get(directory, []): - print("Removing outdated file for %s" % old_file) - os.remove(os.path.join(directory, old_file)) - - -write_autodoc_index() - -# 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.pngmath', - 'sphinx.ext.viewcode', - 'oslosphinx', - ] - -# 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 = [] - -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 = '_theme' - -# 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 -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' -git_cmd = "git log --pretty=format:'%ad, commit %h' --date=local -n1" -html_last_updated_fmt = os.popen(git_cmd).read() - -# 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 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 diff --git a/code/horizon/doc/source/contributing.rst b/code/horizon/doc/source/contributing.rst deleted file mode 100644 index e473c682..00000000 --- a/code/horizon/doc/source/contributing.rst +++ /dev/null @@ -1,559 +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 :doc:`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. - -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. 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 pass: ``./run_tests.sh`` -* Make sure PEP8 is clean: ``./run_tests.sh --pep8`` -* Make sure your code is ready for translation: ``./run_tests.sh --pseudo de`` See the Translatability section below for details. -* 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! - -.. _`OpenStack Contributor License Agreement`: http://wiki.openstack.org/CLA -.. _`OpenStack Contributors`: https://launchpad.net/~openstack-cla -.. _`Horizon Developers`: https://launchpad.net/~horizon -.. _`instructions for setting up git-review`: http://docs.openstack.org/infra/manual/developers.html#development-workflow - -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. - -Translatability -=============== -Horizon gets translated into multiple languages. 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 English po file is up to date: ``./run_tests.sh --makemessages`` -#. Run the pseudo tool to create pseudo translations. For example, to replace the German translation with a pseudo translation: ``./run_tests.sh --pseudo de`` -#. Compile the catalog: ``./run_tests.sh --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 - 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 cleanup any pseudo translated po files. Those don't get merged! - -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 ``./run_tests.sh --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 9 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: - - .. code :: - - (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: - - .. code :: - - angular.forEach(objectToIterateOver, function(value, key) { - // loop logic - }); - - jQuery: - - .. code :: - - $.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 `` - {% 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 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:: - - - -or you can paste your script directly in the file, being sure to use -appropriate tags:: - - - - -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 just after the default Horizon meta tags. diff --git a/code/horizon/doc/source/topics/deployment.rst b/code/horizon/doc/source/topics/deployment.rst deleted file mode 100644 index 9665e086..00000000 --- a/code/horizon/doc/source/topics/deployment.rst +++ /dev/null @@ -1,227 +0,0 @@ -================= -Deploying Horizon -================= - -This guide aims to cover some common questions, concerns and pitfalls you -may encounter when deploying Horizon in a production environment. - -.. seealso:: :doc:`settings` - -.. note:: - - The Service Catalog returned by the Identity Service after a user - has successfully authenticated determines the dashboards and panels - that will be available within the OpenStack Dashboard. If you are not - seeing a particular service you expected (e.g. Object Storage/Swift or - Networking/Neutron) make sure your Service Catalog is configured correctly. - - Prior to the Essex release of Horizon these features were controlled by - individual settings in the ``local_settings.py`` file. This code has been - long-since removed and those pre-Essex settings have no impact now. - -Configure Your Identity Service Host -==================================== - -The one thing you *must* do in order to run Horizon is to specify the -host for your OpenStack Identity Service endpoint. To do this, set the value -of the ``OPENSTACK_HOST`` settings in your ``local_settings.py`` file. - -Logging -======= - -Logging is an important concern for production deployments, and the intricacies -of good logging configuration go far beyond what can be covered here. However -there are a few points worth noting about the logging included with Horizon, -how to customize it, and where other components may take over: - -* Horizon's logging uses Django's logging configuration mechanism, which - can be customized in your ``local_settings.py`` file through the - ``LOGGING`` dictionary. -* Horizon's default logging example sets the log level to ``"INFO"``, which is - a reasonable choice for production deployments. For development, however, - you may want to change the log level to ``"DEBUG"``. -* 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/1.5/topics/logging -.. _Python logging directive: http://docs.python.org/2/library/logging.html - -.. warning:: - - At this time there is `a known bug in python-keystoneclient`_ where it will - log the complete request body of any request sent to Keystone through it - (including logging passwords in plain text) when the log level is set to - ``"DEBUG"``. If this behavior is not desired, make sure your log level is - ``"INFO"`` or higher. - -.. _a known bug in python-keystoneclient: https://bugs.launchpad.net/keystone/+bug/1004114 - -File Uploads -============ - -Horizon allows users to upload files via their web browser to other OpenStack -services such as Glance and Swift. Files uploaded through this mechanism are -first stored on the Horizon server before being forwarded on - files are not -uploaded directly or streamed as Horizon receives them. As Horizon itself does -not impose any restrictions on the size of file uploads, production deployments -will want to consider configuring their server hosting the Horizon application -to enforce such a limit to prevent large uploads exhausting system resources -and disrupting services. Deployments using Apache2 can use the -`LimitRequestBody directive`_ to achieve this. - -Uploads to the Glance image store service tend to be particularly large - in -the order of hundreds of megabytes to multiple gigabytes. Deployments are able -to disable local image uploads through Horizon by setting -``HORIZON_IMAGES_ALLOW_UPLOAD`` to ``False`` in your ``local_settings.py`` -file. - -.. note:: - This will not disable image creation altogether, as this setting does not - affect images created by specifying an image location (URL) as the image source. - - - .. _LimitRequestBody directive: http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestbody - -Session Storage -=============== - -Horizon uses `Django's sessions framework`_ for handling user session data; -however that's not the end of the story. There are numerous session backends -available, which are controlled through the ``SESSION_ENGINE`` setting in -your ``local_settings.py`` file. What follows is a quick discussion of the -pros and cons of each of the common options as they pertain to deploying -Horizon specifically. - -.. _Django's sessions framework: https://docs.djangoproject.com/en/dev/topics/http/sessions/ - -Local Memory Cache ------------------- - -Enabled by:: - - SESSION_ENGINE = 'django.contrib.sessions.backends.cache' - CACHES = { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache' - } - -Local memory storage is the quickest and easiest session backend to set up, -as it has no external dependencies whatsoever. However, it has two significant -drawbacks: - - * No shared storage across processes or workers. - * No persistence after a process terminates. - -The local memory backend 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. For better options, read on. - -Memcached ---------- - -Enabled by:: - - 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. - -Memcached is an extremely fast and efficient cache backend for cases where it -fits the deployment need. But it's not appropriate for all scenarios. - -Requirements: - - * Memcached service running and accessible. - * Python memcached module installed. - -Database --------- - -Enabled by:: - - 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:: - - SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" - -Cookies -------- - -If you're using Django 1.4 or later, a new session backend is available to you -which avoids server load and scaling problems: the ``signed_cookies`` backend! - -This backend stores session data in a cookie which is stored by the -user's browser. The backend 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 session engine are that it doesn't require any 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 which can be stored. - -For a thorough discussion of the security implications of this session backend, -please read the `Django documentation on cookie-based sessions`_. - -.. _Django documentation on cookie-based sessions: https://docs.djangoproject.com/en/dev/topics/http/sessions/#using-cookie-based-sessions - -Secure Site Recommendations ---------------------------- - -When implementing Horizon for public usage, with the website served through -HTTPS, it is recommended that the following settings are applied. - -To help protect the session cookies from `cross-site scripting`_, add the -following to ``local_settings.py``:: - - CSRF_COOKIE_SECURE = True - SESSION_COOKIE_SECURE = True - -Note that the CSRF_COOKIE_SECURE option is only available from Django 1.4. It -does no harm to have the setting in earlier versions, but it does not take effect. - -You can also disable `browser autocompletion`_ for the authentication form by -modifying the ``HORIZON_CONFIG`` dictionary in ``local_settings.py`` by adding -the key ``password_autocomplete`` with the value ``off`` as shown here:: - - HORIZON_CONFIG = { - ... - 'password_autocomplete': 'off', - } - -.. _cross-site scripting: https://www.owasp.org/index.php/HttpOnly -.. _browser autocompletion: https://wiki.mozilla.org/The_autocomplete_attribute_and_web_documents_using_XHTML diff --git a/code/horizon/doc/source/topics/install.rst b/code/horizon/doc/source/topics/install.rst deleted file mode 100644 index c3aa53b5..00000000 --- a/code/horizon/doc/source/topics/install.rst +++ /dev/null @@ -1,100 +0,0 @@ -================== -Installing Horizon -================== - -This page covers the basic installation of Horizon. - -.. _system-requirements-label: - -System Requirements -=================== - -* Python 2.7 -* Django 1.6 (1.4 and 1.5 are supported too) -* Minimum required set of running OpenStack services are: - - * Nova - * Keystone - * Glance - * Neutron (unless nova-network is used) - -* All other services are optional. - Horizon supports the following services in Juno release. - If Keystone endpoint for a service is configured, - Horizon detects it and enables its support automatically. - - * Swift - * Cinder - * Heat - * Ceilometer - * Trove - * Sahara - -Installation -============ - -1. Compile translation message catalogs for internationalization. - This step is not required if you do not need to support languages - other than English. GNU ``gettext`` tool is required to compile - message catalogs:: - - $ sudo apt-get install gettext - $ ./run_tests.sh --compilemessages - - This command compiles translation message catalogs within Python - virtualenv named ``.venv``. After this step, you can remove - ``.venv`` directory safely. - -2. Install Horizon python module into your system. Run the following - in the top directory:: - - $ sudo pip install . - -3. 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. - At least we need to customize the following variables in this file. - - * ``ALLOWED_HOSTS`` (unless ``DEBUG`` is ``True``) - * ``OPENSTACK_KEYSTONE_URL`` - - For more details, please refer to :doc:`deployment` and :doc:`settings`. - -4. Optional: Django has a Compressor feature that performs many enhancements - for the delivery of static files, including standardization and - minification/uglification. This processing can be run either online or - offline (pre-processed). Letting the compression process occur at runtime - will incur processing and memory use when the resources are first requested; - doing it ahead of time removes those runtime penalties. - - If you want the static files to be processed before server runtime, you'll - need to configure your local_settings.py to specify - ``COMPRESS_OFFLINE = True``, then run the following commands:: - - $ ./manage.py collectstatic - $ ./manage.py compress - -5. Set up a web server with WSGI support. - It is optional but recommended in production deployments. - For example, install Apache web server on Ubuntu:: - - $ sudo apt-get install apache2 libapache2-mod-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 - -6. Finally, enable the above configuration and restart the web server:: - - $ sudo a2ensite horizon - $ sudo service apache2 restart - -Next Steps -========== - -* :doc:`deployment` covers some common questions, concerns and pitfalls you - may encounter when deploying Horizon in a production environment. -* :doc:`settings` lists the available settings for Horizon. -* :doc:`customizing` describes how to customizing Horizon as you want. diff --git a/code/horizon/doc/source/topics/policy.rst b/code/horizon/doc/source/topics/policy.rst deleted file mode 100644 index ed549be2..00000000 --- a/code/horizon/doc/source/topics/policy.rst +++ /dev/null @@ -1,148 +0,0 @@ -============================================================ -Horizon Policy Enforcement (RBAC: Role Based Access Control) -============================================================ - -Introduction -============ - -Horizon's policy enforcement builds on the oslo-incubator policy engine. -The basis of which is ``openstack_dashboard/openstack/common/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. - - -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 ` 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`. - -.. _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. diff --git a/code/horizon/doc/source/topics/settings.rst b/code/horizon/doc/source/topics/settings.rst deleted file mode 100644 index d612d2da..00000000 --- a/code/horizon/doc/source/topics/settings.rst +++ /dev/null @@ -1,1383 +0,0 @@ -========================== -Settings and Configuration -========================== - -Introduction -============ - -Horizon's settings tend to fall into three categories: - -* Horizon configuration options (contained in the ``HORIZON_CONFIG`` dict) - which are not OpenStack-specific and pertain only to the core framework. -* OpenStack-related settings which pertain to other projects/services and - are generally prefixed with ``OPENSTACK_`` in the settings file. -* Django settings (including common plugins like ``django-compressor``) which - can be (and should be) read about in their respective documentation. - -What follows is an overview of the Horizon and OpenStack-specific settings -and a few notes on the Django-related settings. - -.. note:: - - Prior to the Essex release of Horizon there were settings which controlled - whether features such as Object Storage/Swift or Networking/Neutron would be - enabled in the OpenStack Dashboard. This code has long since been removed - and those pre-Essex settings have no impact now. - - In Essex and later, the Service Catalog returned by the Identity Service - after a user has successfully authenticated determines the dashboards and - panels that will be available within the OpenStack Dashboard. If you are not - seeing a particular service you expected make sure your Service Catalog is - configured correctly. - -Horizon Settings -================ - -The following options are available in order to configure/customize the -behavior of your Horizon installation. All of them are contained in the -``HORIZON_CONFIG`` dictionary. - -.. _dashboards: - -``dashboards`` --------------- - -.. warning:: - - In OpenStack Dashboard configuration, we suggest **NOT** to use this - setting. Please specify the order of dashboard using the - :ref:`pluggable-settings-label`. - - Both the pluggable dashboard mechanism (OpenStack Dashboard default) and - this setting ``dashboard`` configure the order of dashboards and - the setting ``dashboard`` precedes the pluggable dashboard mechanism. - Specifying the order in two places may cause confusion. - Please use this parameter only when the pluggable config is not used. - -.. versionadded:: 2012.1(Essex) - -Default: ``None`` - -Horizon Dashboards are automatically discovered in the following way: - -* By adding a configuration file to the ``openstack_dashboard/local/enabled`` - directory (for more information see :ref:`pluggable-settings-label`). - This is the default way in OpenStack Dashboard. -* By traversing Django's list of - `INSTALLED_APPS `_ - and importing any files that have the name ``"dashboard.py"`` and include - code to register themselves as a Horizon dashboard. - -By default, dashboards defined by ``openstack_dashboard/local/enabled`` are -displayed first in the alphabetical order of the config files, and then the -remaining dashboards discovered by traversing INSTALLED_APPS are displayed -in the alphabetical order. - -If a list of ``dashboard`` slugs is provided in this setting, the supplied -ordering is applied to the list of discovered dashboards. If the list of -dashboard slugs is shorter than the number of discovered dashboards, the -remaining dashboards are appended in the default order described above. - -The dashboards listed must be in a Python module which -is included in the ``INSTALLED_APPS`` list and on the Python path. - -``default_dashboard`` ---------------------- - -.. warning:: - - In OpenStack Dashboard configuration, we suggest **NOT** to use this - setting. Please specify the order of dashboard using the - :ref:`pluggable-settings-label`. - - The default dashboard can be configured via both the pluggable - dashboard mechanism (OpenStack Dashboard default) and this setting - ``default_dashboard``, and if both are specified, the setting - by the pluggable dashboard mechanism will be used. - Specifying the default dashboard in two places may cause confusion. - Please use this parameter only when the pluggable config is not used. - -.. versionadded:: 2012.1(Essex) - -Default: ``None`` - -The slug of the dashboard which should act as the first-run/fallback dashboard -whenever a user logs in or is otherwise redirected to an ambiguous location. - -``user_home`` -------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``settings.LOGIN_REDIRECT_URL`` - -This can be either a literal URL path (such as the default), or Python's -dotted string notation representing a function which will evaluate what URL -a user should be redirected to based on the attributes of that user. - -``ajax_queue_limit`` --------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``10`` - -The maximum number of simultaneous AJAX connections the dashboard may try -to make. This is particularly relevant when monitoring a large number of -instances, volumes, etc. which are all actively trying to update/change state. - -``ajax_poll_interval`` ----------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``2500`` - -How frequently resources in transition states should be polled for updates, -expressed in milliseconds. - -``auto_fade_alerts`` --------------------- - -.. versionadded:: 2013.2(Havana) - -Defaults: ``{'delay': [3000], 'fade_duration': [1500], 'types': []}`` - -If provided, will auto-fade the alert types specified. Valid alert types -include: ['alert-success', 'alert-info', 'alert-warning', 'alert-error'] -Can also define the delay before the alert fades and the fade out duration. - -``help_url`` ------------- - -.. versionadded:: 2012.2(Folsom) - -Default: ``None`` - -If provided, a "Help" link will be displayed in the site header which links -to the value of this settings (ideally a URL containing help information). - -``exceptions`` --------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``{'unauthorized': [], 'not_found': [], 'recoverable': []}`` - -A dictionary containing classes of exceptions which Horizon's centralized -exception handling should be aware of. Based on these exception categories, -Horizon will handle the exception and display a message to the user. - -``modal_backdrop`` ------------------- - -.. versionadded:: 2014.2(Kilo) - -Default: ``"static"`` - -Controls how bootstrap backdrop element outside of modals looks and feels. -Valid values are ``"true"`` (show backdrop element outside the modal, close -the modal after clicking on backdrop), ``"false"`` (do not show backdrop -element, do not close the modal after clicking outside of it) and ``"static"`` -(show backdrop element outside the modal, do not close the modal after -clicking on backdrop). - -``disable_password_reveal`` ---------------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``False`` - -Setting this to True will disable the reveal button for password fields, -including on the login form. - -``password_validator`` ----------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``{'regex': '.*', 'help_text': _("Password is not accepted")}`` - -A dictionary containing a regular expression which will be used for password -validation and help text which will be displayed if the password does not -pass validation. The help text should describe the password requirements if -there are any. - -This setting allows you to set rules for passwords if your organization -requires them. - -``password_autocomplete`` -------------------------- - -.. versionadded:: 2013.1(Grizzly) - -Default: ``"off"`` - -Controls whether browser autocompletion should be enabled on the login form. -Valid values are ``"on"`` and ``"off"``. - -``simple_ip_management`` ------------------------- - -.. versionadded:: 2013.1(Grizzly) - -Default: ``True`` - -Enable or disable simplified floating IP address management. - -"Simple" floating IP address management means that the user does not ever have -to select the specific IP addresses they wish to use, and the process of -allocating an IP and assigning it to an instance is one-click. - -The "advanced" floating IP management allows users to select the floating IP -pool from which the IP should be allocated and to select a specific IP address -when associating one with an instance. - -.. note:: - - Currently "simple" floating IP address management is not compatible with - Neutron. There are two reasons for this. First, Neutron does not support - the default floating IP pool at the moment. Second, a Neutron floating IP - can be associated with each VIF and we need to check whether there is only - one VIF for an instance to enable simple association support. - -``angular_modules`` -------------------------- - -Default: ``[]`` - -A list of AngularJS modules to be loaded when Angular bootstraps. These modules -are added as dependencies on the root Horizon application ``hz``. - -``js_files`` -------------------------- - -Default: ``[]`` - -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 -``angular_modules`` and therefore need to be include in every page. - -``js_spec_files`` -------------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``[]`` - -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. - -OpenStack Settings (Partial) -============================ - -The following settings inform the OpenStack Dashboard of information about the -other OpenStack projects which are part of this cloud and control the behavior -of specific dashboards, panels, API calls, etc. - -Most of the following settings are defined in - ``openstack_dashboard/local/local_settings.py``, which should be copied from - ``openstack_dashboard/local/local_settings.py.example``. - -``AUTHENTICATION_URLS`` ------------------------ - -.. versionadded:: 2015.1(Kilo) - -Default: ``['openstack_auth.urls']`` - -A list of modules from which to collate authentication URLs from. The default -option adds URLs from the django-openstack-auth module however others will be -required for additional authentication mechanisms. - - -``API_RESULT_LIMIT`` --------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``1000`` - -The maximum number of objects (e.g. Swift objects or Glance images) to display -on a single page before providing a paging element (a "more" link) to paginate -results. - -``API_RESULT_PAGE_SIZE`` ------------------------- - -.. versionadded:: 2012.2(Folsom) - -Default: ``20`` - -Similar to ``API_RESULT_LIMIT``. This setting controls the number of items -to be shown per page if API pagination support for this exists. - - -``AVAILABLE_REGIONS`` ---------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``None`` - -A list of tuples which define multiple regions. The tuple format is -``('http://{{ keystone_host }}:5000/v2.0', '{{ region_name }}')``. If any regions -are specified the login form will have a dropdown selector for authenticating -to the appropriate region, and there will be a region switcher dropdown in -the site header when logged in. - -If you do not have multiple regions you should use the ``OPENSTACK_HOST`` and -``OPENSTACK_KEYSTONE_URL`` settings instead. - - -``CONSOLE_TYPE`` ----------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``"AUTO"`` - -This setting specifies the type of in-browser console used to access the -VMs. -Valid values are ``"AUTO"``(default), ``"VNC"``, ``"SPICE"``, ``"RDP"``, -``"SERIAL"``, and ``None``. -``None`` deactivates the in-browser console and is available in version -2014.2(Juno). -``"SERIAL"`` is available since 2015.1(Kilo). - - -``SWIFT_FILE_TRANSFER_CHUNK_SIZE`` ----------------------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``512 * 1024`` - -This setting specifies the size of the chunk (in bytes) for downloading objects -from Swift. Do not make it very large (higher than several dozens of Megabytes, -exact number depends on your connection speed), otherwise you may encounter -socket timeout. The default value is 524288 bytes (or 512 Kilobytes). - - -``INSTANCE_LOG_LENGTH`` ------------------------ - -.. versionadded:: 2015.1(Kilo) - -Default: ``35`` - -This setting enables you to change the default number of lines displayed for -the log of an instance. -Valid value must be a positive integer. - - -``CREATE_INSTANCE_FLAVOR_SORT`` -------------------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``{'key':'ram'}`` - -When launching a new instance the default flavor is sorted by RAM usage in -ascending order. -You can customize the sort order by: id, name, ram, disk and vcpus. -Additionally, you can insert any custom callback function. You can also -provide a flag for reverse sort. -See the description in local_settings.py.example for more information. - -This example sorts flavors by vcpus in descending order:: - - CREATE_INSTANCE_FLAVOR_SORT = { - 'key':'vcpus', - 'reverse': True, - } - -``CUSTOM_THEME_PATH`` ---------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``"static/themes/default"`` - -This setting allows Horizon to use a custom theme. The theme folder -should contains one _variables.scss file and one _styles.scss file. -_variables.scss contains all the bootstrap and horizon specific variables -which are used to style the GUI. Whereas _styles.scss contains extra styling. -For example themes, see: /horizon/openstack_dashboard/static/themes/ - -``DROPDOWN_MAX_ITEMS`` ----------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``30`` - -This setting sets the maximum number of items displayed in a dropdown. -Dropdowns that limit based on this value need to support a way to observe -the entire list. - -``ENFORCE_PASSWORD_CHECK`` --------------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``False`` - -This setting will display an 'Admin Password' field on the Change Password -form to verify that it is indeed the admin logged-in who wants to change -the password. - -``IMAGES_LIST_FILTER_TENANTS`` ------------------------------- - -.. versionadded:: 2013.1(Grizzly) - -Default: ``None`` - -A list of dictionaries to add optional categories to the image fixed filters -in the Images panel, based on project ownership. - -Each dictionary should contain a `tenant` attribute with the project -id, and optionally a `text` attribute specifying the category name, and -an `icon` attribute that displays an icon in the filter button. The -icon names are based on the default icon theme provided by Bootstrap. - -Example: ``[{'text': 'Official', 'tenant': '27d0058849da47c896d205e2fc25a5e8', 'icon': 'icon-ok'}]`` - -.. note:: - - Since the Kilo release, the Bootstrap icon library (e.g. 'icon-ok') has - been replaced with Font Awesome (e.g. 'fa-check'). - - -``IMAGE_RESERVED_CUSTOM_PROPERTIES`` ------------------------------------- - -.. versionadded:: 2014.2(Juno) - -Default: ``[]`` - -A list of image custom property keys that should not be displayed in the -Update Metadata tree. - -This setting can be used in the case where a separate panel is used for -managing a custom property or if a certain custom property should never be -edited. - -``OPENSTACK_API_VERSIONS`` --------------------------- - -.. versionadded:: 2013.2(Havana) - -Default:: - - { - "data-processing": 1.1, - "identity": 2.0, - "volume": 2 - } - -Overrides for OpenStack API versions. Use this setting to force the -OpenStack dashboard to use a specific API version for a given service API. - -.. note:: - - The version should be formatted as it appears in the URL for the - service API. For example, the identity service APIs have inconsistent - use of the decimal point, so valid options would be "2.0" or "3". - For example, - OPENSTACK_API_VERSIONS = { - "data-processing": 1.1, - "identity": 3, - "volume": 2 - } - - -``OPENSTACK_ENABLE_PASSWORD_RETRIEVE`` --------------------------------------- - -.. versionadded:: 2014.1(Icehouse) - -Default: ``"False"`` - -When set, enables the instance action "Retrieve password" allowing password retrieval -from metadata service. - - -``OPENSTACK_ENDPOINT_TYPE`` ---------------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``"publicURL"`` - -A string which specifies the endpoint type to use for the endpoints in the -Keystone service catalog. The default value for all services except for identity is ``"publicURL"`` . The default value for the identity service is ``"internalURL"``. - - -``OPENSTACK_HOST`` ------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``"127.0.0.1"`` - -The hostname of the Keystone server used for authentication if you only have -one region. This is often the *only* setting that needs to be set for a -basic deployment. - -.. _hypervisor-settings-label: - -``OPENSTACK_HYPERVISOR_FEATURES`` ---------------------------------- - -.. versionadded:: 2012.2(Folsom) - -Default:: - - { - 'can_set_mount_point': False, - 'can_set_password': False - } - -A dictionary containing settings which can be used to identify the -capabilities of the hypervisor for Nova. - -The Xen Hypervisor has the ability to set the mount point for volumes attached -to instances (other Hypervisors currently do not). Setting -``can_set_mount_point`` to ``True`` will add the option to set the mount point -from the UI. - -Setting ``can_set_password`` to ``True`` will enable the option to set -an administrator password when launching or rebuilding an instance. - - -``OPENSTACK_IMAGE_BACKEND`` ---------------------------- - -.. versionadded:: 2013.2(Havana) - -Default:: - - { - 'image_formats': [ - ('', _('Select format')), - ('aki', _('AKI - Amazon Kernel Image')), - ('ami', _('AMI - Amazon Machine Image')), - ('ari', _('ARI - Amazon Ramdisk Image')), - ('iso', _('ISO - Optical Disk Image')), - ('qcow2', _('QCOW2 - QEMU Emulator')), - ('raw', _('Raw')), - ('vdi', _('VDI')), - ('vhd', _('VHD')), - ('vmdk', _('VMDK')) - ] - } - -Used to customize features related to the image service, such as the list of -supported image formats. - - -``IMAGE_CUSTOM_PROPERTY_TITLES`` --------------------------------- - -.. versionadded:: 2014.1(Icehouse) - -Default:: - - { - "architecture": _("Architecture"), - "kernel_id": _("Kernel ID"), - "ramdisk_id": _("Ramdisk ID"), - "image_state": _("Euca2ools state"), - "project_id": _("Project ID"), - "image_type": _("Image Type") - } - -Used to customize the titles for image custom property attributes that -appear on image detail pages. - - -``HORIZON_IMAGES_ALLOW_UPLOAD`` --------------------------------- - -.. versionadded:: 2013.1(Grizzly) - -Default: ``True`` - -If set to ``False``, this setting disables *local* uploads to prevent filling -up the disk on the dashboard server since uploads to the Glance image store -service tend to be particularly large - in the order of hundreds of megabytes -to multiple gigabytes. - -.. note:: - - This will not disable image creation altogether, as this setting does not - affect images created by specifying an image location (URL) as the image source. - - -``OPENSTACK_KEYSTONE_BACKEND`` ------------------------------- - -.. versionadded:: 2012.1(Essex) - -Default: ``{'name': 'native', 'can_edit_user': True, 'can_edit_project': True}`` - -A dictionary containing settings which 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`` and ``can_edit_project`` to ``False`` and name to ``"ldap"``. - - -``OPENSTACK_KEYSTONE_DEFAULT_DOMAIN`` -------------------------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``"Default"`` - -Overrides the default domain used when running on single-domain model -with Keystone V3. All entities will be created in the default domain. - - -``OPENSTACK_KEYSTONE_DEFAULT_ROLE`` ------------------------------------ - -.. versionadded:: 2011.3(Diablo) - -Default: ``"_member_"`` - -The name of the role which will be assigned to a user when added to a project. -This name must correspond to a role name in Keystone. - - -``OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT`` ------------------------------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``False`` - -Set this to True if running on multi-domain model. When this is enabled, it -will require user to enter the Domain name in addition to username for login. - - -``OPENSTACK_KEYSTONE_URL`` --------------------------- - -.. versionadded:: 2011.3(Diablo) - -Default: ``"http://%s:5000/v2.0" % OPENSTACK_HOST`` - -The full URL for the Keystone endpoint used for authentication. Unless you -are using HTTPS, running your Keystone server on a nonstandard port, or using -a nonstandard URL scheme you shouldn't need to touch this setting. - - -``WEBSSO_ENABLED`` ------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``False`` - -Enables keystone web single-sign-on if set to True. For this feature to work, -make sure that you are using Keystone V3 and Django OpenStack Auth V1.2.0 or -later. - - -``WEBSSO_INITIAL_CHOICE`` -------------------------- - -.. versionadded:: 2015.1(Kilo) - -Default: ``"credentials"`` - -Determines the default authentication mechanism. When user lands on the login -page, this is the first choice they will see. - - -``WEBSSO_CHOICES`` ------------------- - -.. versionadded:: 2015.1(Kilo) - -Default:: - - ( - ("credentials", _("Keystone Credentials")), - ("oidc", _("OpenID Connect")), - ("saml2", _("Security Assertion Markup Language")) - ) - -This is the list of authentication mechanisms available to the user. It includes -Keystone federation protocols such as OpenID Connect and SAML. The list of -choices is completely configurable, so as long as the id remains intact. Do not -remove the credentials mechanism unless you are sure. Once removed, even admins -will have no way to log into the system via the dashboard. - - -``OPENSTACK_CINDER_FEATURES`` ------------------------------ - -.. versionadded:: 2014.2(Juno) - -Default: ``{'enable_backup': False}`` - -A dictionary of settings which can be used to enable optional services provided -by cinder. Currently only the backup service is available. - - -``OPENSTACK_NEUTRON_NETWORK`` ------------------------------ - -.. versionadded:: 2013.1(Grizzly) - -Default:: - - { - 'enable_router': True, - 'enable_distributed_router': False, - 'enable_ha_router': False, - 'enable_lb': True, - 'enable_quotas': False, - 'enable_firewall': True, - 'enable_vpn': True, - 'profile_support': None, - 'supported_provider_types': ["*"], - 'supported_vnic_types': ["*"], - 'segmentation_id_range': {} - } - -A dictionary of settings which can be used to enable optional services provided -by Neutron and configure Neutron specific features. The following options are -available. - -``enable_router``: - -.. versionadded:: 2014.2(Juno) - -Default: ``True`` - -Enable (True) or disable (False) the panels and menus related -to router and Floating IP features. This option only affects -when Neutron is enabled. If your Neutron deployment has no support for -Layer-3 features, or you do not wish to provide the Layer-3 -features through the Dashboard, this should be set to ``False``. - -``enable_distributed_router``: - -.. versionadded:: 2014.2(Juno) - -Default: ``False`` - -Enable or disable Neutron distributed virtual router (DVR) feature in -the Router panel. For the DVR feature to be enabled, this option needs -to be set to True and your Neutron deployment must support DVR. Even -when your Neutron plugin (like ML2 plugin) supports DVR feature, DVR -feature depends on l3-agent configuration, so deployers should set this -option appropriately depending on your deployment. - -``enable_ha_router``: - -.. versionadded:: 2014.2(Juno) - -Default: ``False`` - -Enable or disable HA (High Availability) mode in Neutron virtual router -in the Router panel. For the HA router mode to be enabled, this option needs -to be set to True and your Neutron deployment must support HA router mode. -Even when your Neutron plugin (like ML2 plugin) supports HA router mode, -the feature depends on l3-agent configuration, so deployers should set this -option appropriately depending on your deployment. - -``enable_lb``: - -.. versionadded:: 2013.1(Grizzly) - -(Deprecated) - -Default: ``True`` - -Enables the load balancer panel. The load balancer panel will be enabled when -this option is True and your Neutron deployment supports LBaaS. If you want -to disable load balancer panel even when your Neutron supports LBaaS, set it to False. - -This option is now marked as "deprecated" and will be removed in Kilo or later release. -The load balancer panel is now enabled only when LBaaS feature is available in Neutron -and this option is no longer needed. We suggest not to use this option to disable the -load balancer panel from now on. - -``enable_quotas``: - -Default: ``False`` - -Enable support for Neutron quotas feature. To make this feature work -appropriately, you need to use Neutron plugins with quotas extension support -and quota_driver should be DbQuotaDriver (default config). - -``enable_firewall``: - -(Deprecated) - -Default: ``True`` - -Enables the firewall panel. firewall panel will be enabled when this -option is True and your Neutron deployment supports FWaaS. If you want -to disable firewall panel even when your Neutron supports FWaaS, set -it to False. - -This option is now marked as "deprecated" and will be removed in -Kilo or later release. The firewall panel is now enabled only -when FWaaS feature is available in Neutron and this option is no -longer needed. We suggest not to use this option to disable the -firewall panel from now on. - -``enable_vpn``: - -(Deprecated) - -Default: ``True`` - -Enables the VPN panel. VPN panel will be enabled when this option is True -and your Neutron deployment supports VPNaaS. If you want to disable -VPN panel even when your Neutron supports VPNaaS, set it to False. - -This option is now marked as "deprecated" and will be removed in -Kilo or later release. The VPN panel is now enabled only -when VPNaaS feature is available in Neutron and this option is no -longer needed. We suggest not to use this option to disable the -VPN panel from now on. - -``profile_support``: - -Default: ``None`` - -This option specifies a type of network port profile support. Currently the -available value is either ``None`` or ``"cisco"``. ``None`` means to disable -port profile support. ``cisco`` can be used with Neutron Cisco plugins. - -``supported_provider_types``: - -.. versionadded:: 2014.2(Juno) - -Default: ``["*"]`` - -For use with the provider network extension. Use this to explicitly set which -provider network types are supported. Only the network types in this list will -be available to choose from when creating a network. Network types include -local, flat, vlan, gre, and vxlan. By default all provider network types will -be available to choose from. - -Example: ``['local', 'flat', 'gre']`` - -``supported_vnic_types``: - -.. versionadded:: 2015.1(Kilo) - -Default ``['*']`` - -For use with the port binding extension. Use this to explicitly set which VNIC -types are supported; only those listed will be shown when creating or editing -a port. VNIC types include normal, direct and macvtap. By default all VNIC -types will be available to choose from. - -Example ``['normal', 'direct']`` - -``segmentation_id_range``: - -.. versionadded:: 2014.2(Juno) - -Default: ``{}`` - -For use with the provider network extension. This is a dictionary where each -key is a provider network type and each value is a list containing two numbers. -The first number is the minimum segmentation ID that is valid. The second -number is the maximum segmentation ID. Pertains only to the vlan, gre, and -vxlan network types. By default this option is not provided and each minimum -and maximum value will be the default for the provider network type. - -Example: ``{'vlan': [1024, 2048], 'gre': [4094, 65536]}`` - - -``OPENSTACK_SSL_CACERT`` ------------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``None`` - -When unset or set to ``None`` the default CA certificate on the system is used -for SSL verification. - -When set with the path to a custom CA certificate file, this overrides use of -the default system CA certificate. This custom certificate is used to verify all -connections to openstack services when making API calls. - - -``OPENSTACK_SSL_NO_VERIFY`` ---------------------------- - -.. versionadded:: 2012.2(Folsom) - -Default: ``False`` - -Disable SSL certificate checks in the OpenStack clients (useful for self-signed -certificates). - - -``OPENSTACK_TOKEN_HASH_ALGORITHM`` ----------------------------------- - -.. versionadded:: 2014.2(Juno) - -Default: ``"md5"`` - -The hash algorithm to use for authentication tokens. This must match the hash -algorithm that the identity (Keystone) server and the auth_token middleware -are using. Allowed values are the algorithms supported by Python's hashlib -library. - - -``POLICY_FILES`` ----------------- - -.. versionadded:: 2013.2(Havana) - -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 ``POLICY_FILES_PATH``, -they should be included here too. - - -``POLICY_FILES_PATH`` ---------------------- - -.. versionadded:: 2013.2(Havana) - -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. - -``SESSION_TIMEOUT`` -------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``"1800"`` - -Specifies the timespan in seconds inactivity, until a user is considered as - logged out. - -``SAHARA_AUTO_IP_ALLOCATION_ENABLED`` -------------------------------------- - -Default: ``False`` - -This setting notifies the Data Processing (Sahara) system whether or not -automatic IP allocation is enabled. You would want to set this to True -if you were running Nova Networking with auto_assign_floating_ip = True. - - -``TROVE_ADD_USER_PERMS`` and ``TROVE_ADD_DATABASE_PERMS`` ---------------------------------------------------------- - -.. versionadded:: 2013.2(Havana) - -Default: ``[]`` - -Trove user and database extension support. By default, support for -creating users and databases on database instances is turned on. -To disable these extensions set the permission to something -unusable such as ``[!]``. - - -``WEBROOT`` ------------ - -.. versionadded:: 2015.1(Kilo) - -Default: ``"/"`` - -Specifies the location where the access to the dashboard is configured in -the web server. - -For example, if you're accessing the Dashboard via -https:///dashboard, you would set this to ``"/dashboard/"``. - -Additionally, setting the ``"$webroot"`` SCSS variable is required. You -can change this directly in -``"openstack_dasbboard/static/dashboard/scss/_variables.scss"`` or in the -``"_variables.scss"`` file in your custom theme. For more information on -custom themes, see: ``"CUSTOM_THEME_PATH"``. - -For your convenience, a custom theme for only setting the web root has been -provided see: ``"/horizon/openstack_dashboard/static/themes/webroot"`` - -.. note:: - - Additional settings may be required in the config files of your webserver - of choice. For example to make ``"/dashboard/"`` the web root in apache, - the ``"sites-available/horizon.conf"`` requires a couple of additional - aliases set:: - - Alias /dashboard/static %HORIZON_DIR%/static - - Alias /dashboard/media %HORIZON_DIR%/openstack_dashboard/static - - -Django Settings (Partial) -========================= - -.. warning:: - - This is not meant to be anywhere near a complete list of settings for - Django. You should always consult the upstream documentation, especially - with regards to deployment considerations and security best-practices. - -There are a few key settings you should be aware of for development and the -most basic of deployments. Further recommendations can be found in the -Deploying Horizon section of this documentation. - -``ALLOWED_HOSTS`` ------------------ - -.. versionadded:: 2013.2(Havana) - -Default: ``['localhost']`` - -This list should contain names (or IP addresses) of the host -running the dashboard; if it's being accessed via name, the -DNS name (and probably short-name) should be added, if it's accessed via -IP address, that should be added. The setting may contain more than one entry. - -.. note:: - - ALLOWED_HOSTS is required for versions of Django 1.5 and newer. - If Horizon is running in production (DEBUG is False), set this - with the list of host/domain names that the application can serve. - For more information see: - https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts - -``DEBUG`` and ``TEMPLATE_DEBUG`` --------------------------------- - -.. versionadded:: 2011.2(Cactus) - -Default: ``True`` - -Controls whether unhandled exceptions should generate a generic 500 response -or present the user with a pretty-formatted debug information page. - -This setting should **always** be set to ``False`` for production deployments -as the debug page can display sensitive information to users and attackers -alike. - -``SECRET_KEY`` --------------- - -.. versionadded:: 2012.1(Essex) - -This should absolutely be set to a unique (and secret) value for your -deployment. Unless you are running a load-balancer with multiple Horizon -installations behind it, each Horizon instance should have a unique secret key. - -.. note:: - - Setting a custom secret key: - You can either set it to a specific value or you can let Horizon generate a - default secret key that is unique on this machine, regardless of the - amount of Python WSGI workers (if used behind Apache+mod_wsgi). However, there - may be situations where you would want to set this explicitly, e.g. when - multiple dashboard instances are distributed on different machines (usually - behind a load-balancer). Either you have to make sure that a session gets all - requests routed to the same dashboard instance or you set the same SECRET_KEY - for all of them. - - -From horizon.utils import secret_key:: - - SECRET_KEY = secret_key.generate_or_read_from_file( - os.path.join(LOCAL_PATH, '.secret_key_store')) - -The ``local_settings.py.example`` file includes a quick-and-easy way to -generate a secret key for a single installation. - - -``SECURE_PROXY_SSL_HEADER``, ``CSRF_COOKIE_SECURE`` and ``SESSION_COOKIE_SECURE`` ---------------------------------------------------------------------------------- - -.. versionadded:: 2013.1(Grizzly) - -These three settings should be configured if you are deploying Horizon with -SSL. The values indicated in the default ``local_settings.py.example`` file -are generally safe to use. - -When CSRF_COOKIE_SECURE or SESSION_COOKIE_SECURE are set to True, these attributes -help protect the session cookies from cross-site scripting. - -``ADD_INSTALLED_APPS`` ----------------------- - -.. versionadded:: 2015.1(Kilo) - -A list of Django applications to be prepended to the ``INSTALLED_APPS`` -setting. Allows extending the list of installed applications without having -to override it completely. - - -.. _pluggable-settings-label: - -Pluggable Settings -================================= -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. - -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 ``hz``. - -``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. - - -``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 the Router dashboard locally, create a file -``openstack_dashboard/local/enabled/_40_router.py`` with the following -content:: - - DASHBOARD = 'router' - 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' - diff --git a/code/horizon/doc/source/topics/table_actions.rst b/code/horizon/doc/source/topics/table_actions.rst deleted file mode 100644 index 30d17255..00000000 --- a/code/horizon/doc/source/topics/table_actions.rst +++ /dev/null @@ -1,300 +0,0 @@ -============================================ -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 :doc:`Building a Dashboard using -Horizon `. 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 %} -

{% trans "Description:" %}

-

{% trans "Snapshots preserve the disk state of a running instance." %}

- {% 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[^/]+)/create_snapshot/$', - views.CreateSnapshotView.as_view(), - name='create_snapshot'), - -The complete ``urls.py`` file should look like this:: - - from django.conf.urls import patterns - from django.conf.urls import url - - from openstack_dashboard.dashboards.mydashboard.mypanel import views - - - urlpatterns = patterns('', - url(r'^\?tab=mypanel_tabs_tab$', - views.IndexView.as_view(), name='mypanel_tabs'), - url(r'^(?P[^/]+)/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: - 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:: - - ./run_tests.sh --runserver 0.0.0.0:8877 - - -Go to ``http://:8877`` 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`_. - - .. _launchpad: https://bugs.launchpad.net/horizon diff --git a/code/horizon/doc/source/topics/tables.rst b/code/horizon/doc/source/topics/tables.rst deleted file mode 100644 index 2b8a1881..00000000 --- a/code/horizon/doc/source/topics/tables.rst +++ /dev/null @@ -1,387 +0,0 @@ -====================== -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 :doc:`DataTables Reference - Guide `. - -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: -:doc:`Horizon Policy Enforcement (RBAC: Role Based Access Control) `. - -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 javacript 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 mod. - #. 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 --------------------------- - -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: - name = "tenants" - verbose_name = _("Projects") - # Connection to UpdateRow, so table can fetch row data based on - # their primary key. - row_class = UpdateRow - diff --git a/code/horizon/doc/source/topics/testing.rst b/code/horizon/doc/source/topics/testing.rst deleted file mode 100644 index ba1fbd9e..00000000 --- a/code/horizon/doc/source/topics/testing.rst +++ /dev/null @@ -1,276 +0,0 @@ -=================== -Testing Topic Guide -=================== - -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`. - -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. - -Cons: - -* Difficult and time-consuming to create a repeatable test environment. -* Did I mention that setting it up is a pain? - -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. - -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. diff --git a/code/horizon/doc/source/topics/tutorial.rst b/code/horizon/doc/source/topics/tutorial.rst deleted file mode 100644 index 648dc275..00000000 --- a/code/horizon/doc/source/topics/tutorial.rst +++ /dev/null @@ -1,629 +0,0 @@ -============================================ -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 :doc:`Horizon quickstart guide ` or the - `Django tutorial`_. - - .. _Django tutorial: https://docs.djangoproject.com/en/1.6/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 at the same location -where the ``run_tests.sh`` file resides. It generates most of the boilerplate -code you need:: - - mkdir openstack_dashboard/dashboards/mydashboard - - ./run_tests.sh -m startdash mydashboard \ - --target openstack_dashboard/dashboards/mydashboard - - mkdir openstack_dashboard/dashboards/mydashboard/mypanel - - ./run_tests.sh -m 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 - ├── models.py - ├── mypanel - │   ├── __init__.py - │   ├── models.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, the ``models.py`` -file and 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: - 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` -- :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: - 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: - 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 patterns - from django.conf.urls import url - - from openstack_dashboard.dashboards.mydashboard.mypanel.views \ - import IndexView - - - urlpatterns = patterns( - '', - url(r'^$', IndexView.as_view(), name='index'), - ) - - -Adjust the import of ``IndexView`` to make the code readable:: - - from openstack_dashboard.dashboards.mydashboard.mypanel import views - - -Replace the existing ``url`` pattern with the following line:: - - url(r'^$', - views.IndexView.as_view(), name='index'), - - -The completed ``urls.py`` file should look like the following:: - - from django.conf.urls import patterns - from django.conf.urls import url - - from openstack_dashboard.dashboards.mydashboard.mypanel import views - - - urlpatterns = patterns('', - 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:: - -
-
- {{ tab_group.render }} -
-
- - -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 %} -
-
- {{ tab_group.render }} -
-
- {% 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/1.6/ - - -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:: - - ./run_tests.sh --runserver 0.0.0.0:8877 - - -Go to ``http://:8877`` 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, -``crioos-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 :doc:`Adding a complex action to a table -` 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`_. - - .. _launchpad: https://bugs.launchpad.net/horizon diff --git a/code/horizon/doc/source/topics/workflows.rst b/code/horizon/doc/source/topics/workflows.rst deleted file mode 100644 index f48d60e1..00000000 --- a/code/horizon/doc/source/topics/workflows.rst +++ /dev/null @@ -1,134 +0,0 @@ -====================== -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 a detailed API information check out the :doc:`Workflows Reference - Guide `. - -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 workflow ----------------------------- - -The following is a complex example of how data are exchanged between -urls, views, workflows and templates: - -#. In ``urls.py``, we have the named parameter. E.g. ``resource_class_id``. :: - - RESOURCE_CLASS = r'^(?P[^/]+)/%s$' - - urlpatterns = patterns( - '', - 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): - # 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. diff --git a/tools/setup/install/install_interface.sh b/tools/setup/install/install_interface.sh index a75465bf..ba95eb3d 100644 --- a/tools/setup/install/install_interface.sh +++ b/tools/setup/install/install_interface.sh @@ -69,7 +69,7 @@ function all_install install_rpm_by_yum "daisy" write_install_log "install daisy dashboard rpm" - install_rpm_by_daisy_yum "python-django-horizon-doc" + install_rpm_by_daisy_yum "python-django-horizon" install_rpm_by_yum "daisy-dashboard" write_install_log "install clustershell rpm" diff --git a/tools/setup/uninstall/uninstall_interface.sh b/tools/setup/uninstall/uninstall_interface.sh index 1cdfaa06..a26e8c0f 100755 --- a/tools/setup/uninstall/uninstall_interface.sh +++ b/tools/setup/uninstall/uninstall_interface.sh @@ -19,7 +19,7 @@ function uninstall_daisy # ֹͣз echo "stop all service..." stop_service_all - remove_rpms_by_yum "openstack-keystone python-django-horizon python-keystoneclient python-keystone python-keystonemiddleware python-django-horizon-doc daisy-dashboard" + remove_rpms_by_yum "openstack-keystone python-django-horizon python-keystoneclient python-keystone python-keystonemiddleware daisy-dashboard" remove_rpms_by_yum "daisy python-daisyclient python-daisy" remove_rpms_by_yum "openstack-ironic-api openstack-ironic-common openstack-ironic-conductor python-ironicclient" remove_rpms_by_yum "openstack-ironic-discoverd python-ironic-discoverd" diff --git a/tools/setup/upgrade/upgrade_func.sh b/tools/setup/upgrade/upgrade_func.sh index a0da7f42..83a8d4cd 100755 --- a/tools/setup/upgrade/upgrade_func.sh +++ b/tools/setup/upgrade/upgrade_func.sh @@ -32,7 +32,7 @@ function get_daisy_services python-ironic-discoverd pxe_server_install pxe_docker_install - python-django-horizon-doc + python-django-horizon daisy-dashboard " }