merging new index and Sphinx fixes into master

This commit is contained in:
Leona
2015-11-16 11:14:40 -08:00
13 changed files with 943 additions and 174 deletions
+177
View File
@@ -0,0 +1,177 @@
Documentation Build Instructions
================================
ClearLinux Docs are written in :abbr:`ReStructuredText (ReST)` AKA ``.rst``, which
makes for easily-parsable, command-line readable, indexed and search-friendly
documentation and APIs.
Building with `Sphinx`_, however, requires a few prerequisites:
* `GNU make`_
* `Python`_
* `PIP`_
The instructions for installing these varies according to OS. On a basic
out-of-the-box Ubuntu-like OS (which usually has Python installed by default),
you might need something like:
.. codeblock:: shell-session
$ sudo apt-get install python-pip
$ sudo pip install -U sphinx sphinx-autobuild
.. codeblock:: shell-session
$ python -c 'print __import__("sphinx").__version__'
1.3.1
Dependencies fulfilled, let's now clone that gitlab repo:
.. codeblock:: shell-session
$ git clone git@clrgitlab.intel.com:clr-documentation/project-docs.git
Cloning into 'project-docs'...
remote: Counting objects: 631, done.
remote: Compressing objects: 100% (583/583), done.
remote: Total 631 (delta 349), reused 108 (delta 35)
Receiving objects: 100% (631/631), 2.10 MiB | 0 bytes/s, done.
Resolving deltas: 100% (349/349), done.
Checking connectivity... done.
Before running Sphinx, we need to correct some of the problems in the Gitlab repo.
Running :command:`make` straightaway from our clone won't work. We need to delete the
existing conf.py file and also rename the existing index file so it can generate a new one
with the correct parameters. These files in the Gitlab repo are remnant of a build on a Windows
box, and they don't quite work on Linux. These should be removed from ``master`` eventually.
.. codeblock:: shell-session
$ cd project-docs/
$ ls
make.bat Makefile source/
$ rm -rf Makefile make.bat
$ rm -rf source/conf.py
$ mv source/index.rst source/oldindex.rst
In the cloned source directory, we have all the .rst files we need to build the docs. We
run a native instance of :command:`sphinx-quickstart`. The program will run you through
a series of questions. The main things to be conscious of here:
* Tell it to use the existing :file:`source/` directory as the Root path for
the documentation; this is what it looks in, in order to generate the HTML
* It's better to tell it to **not** separate the source and build directories; the Sphinx
will generate *another* :file:`source/` directory, which can be confusing.
What follows here is a log from a successful :command:`sphinx-quickstart` build started from
within the :file:`project-docs/` directory. Blank answers indicate default used.
.. codeblock:: shell-session
$ sphinx-quickstart
Welcome to the Sphinx 1.3.1 quickstart utility.
Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).
Enter the root path for documentation.
> Root path for the documentation [.]: source/
You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.
> Separate source and build directories (y/n) [n]: n
Inside the root directory, two more directories will be created; "_templates"
for custom HTML templates and "_static" for custom stylesheets and other static
files. You can enter another prefix (such as ".") to replace the underscore.
> Name prefix for templates and static dir [_]:
The project name will occur in several places in the built documentation.
> Project name: ClearLinux Docs
> Author name(s): Intel OTC
Sphinx has the notion of a "version" and a "release" for the
software. Each version can have multiple releases. For example, for
Python the version is something like 2.5 or 3.0, while the release is
something like 2.5.1 or 3.0a1. If you don't need this dual structure,
just set both to the same value.
> Project version: 1.0.0
> Project release [1.0.0]: 1.0.0
If the documents are to be written in a language other than English,
you can select a language here by its language code. Sphinx will then
translate text that it generates into that language.
For a list of supported codes, see
http://sphinx-doc.org/config.html#confval-language.
> Project language [en]: en
The file name suffix for source files. Commonly, this is either ".txt"
or ".rst". Only files with this suffix are considered documents.
> Source file suffix [.rst]: .rst
One document is special in that it is considered the top node of the
"contents tree", that is, it is the root of the hierarchical structure
of the documents. Normally, this is "index", but if your "index"
document is a custom template, you can also set this to another filename.
> Name of your master document (without suffix) [index]:
Sphinx can also add configuration for epub output:
> Do you want to use the epub builder (y/n) [n]: n
Please indicate if you want to use one of the following Sphinx extensions:
> autodoc: automatically insert docstrings from modules (y/n) [n]: n
> doctest: automatically test code snippets in doctest blocks (y/n) [n]: n
> intersphinx: link between Sphinx documentation of different projects (y/n) [n]: n
> todo: write "todo" entries that can be shown or hidden on build (y/n) [n]: n
> coverage: checks for documentation coverage (y/n) [n]: n
> pngmath: include math, rendered as PNG images (y/n) [n]: n
> mathjax: include math, rendered in the browser by MathJax (y/n) [n]: y
> ifconfig: conditional inclusion of content based on config values (y/n) [n]: y
> viewcode: include links to the source code of documented Python objects (y/n) [n]: y
A Makefile and a Windows command file can be generated for you so that you
only have to run e.g. "make html" instead of invoking sphinx-build
directly.
> Create Makefile? (y/n) [y]: y
> Create Windows command file? (y/n) [n]: n
Creating file source/conf.py.
Creating file source/index.rst.
Creating file source/Makefile.
Finished: An initial directory structure has been created.
You should now populate your master file source/index.rst and create other documentation
source files. Use the Makefile to build the docs, like so:
make builder
where "builder" is one of the supported builders, e.g. html, latex or linkcheck.
Finally are we ready to run :command:`make`. Be sure to :command:`cd` to the :file:`source/`
directory before running :command:`make` .
.. codeblock:: make
$ make html
sphinx-build -b html -d _build/doctrees . _build/html
Running Sphinx v1.3.1
making output directory...
.
.
.
build succeeded, 9 warnings.
Build finished. The HTML pages are in _build/html.
Open one of these pages in a web browser to view the rendered documentation. You can copy the
contents of the oldindex.rst into the generated index file, re-run :command:`make`, to generate
the new HTML, and your local Table of Contents should index and update accordingly.
For extra help and tips for contributing documentation which will render beautifully on websites,
despite being written in .rst see: `Theming_Sphinx`_.
.. _GNU make: https://www.gnu.org/software/make/
.. _Python: https://www.python.org/
.. _PIP: https://pypi.python.org/pypi/pip/
.. _Theming Sphinx: https://github.com/otcshare/tcs-hub/blob/master/theming-sphinx.rst
+192
View File
@@ -0,0 +1,192 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@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 " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@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 " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of 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/ClearLinuxDocumentation.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ClearLinuxDocumentation.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/ClearLinuxDocumentation"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ClearLinuxDocumentation"
@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."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@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."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

+1 -1
View File
@@ -20,6 +20,6 @@ administrator, independent of how many and which pieces of the upstream
open source projects are needed for this functionality. The diagram
below gives an overall picture of it.
.. image:: images\bundles_overview.png
.. image:: _static/images/bundles_overview.png
:align: center
:alt: confirmation
+23 -39
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# Clear Linux documentation build configuration file, created by
# sphinx-quickstart on Fri May 8 11:43:01 2015.
# ClearLinux Documentation documentation build configuration file, created by
# sphinx-quickstart on Fri Nov 13 12:23:35 2015.
#
# This file is execfile()d with the current directory set to its
# containing dir.
@@ -29,13 +29,7 @@ import shlex
# 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', 'breathe', 'sphinx.ext.todo'
#]
extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.todo'
]
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -52,18 +46,18 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Clear Linux* Project for Intel® Architecture'
copyright = u'2015, many'
author = u'many'
project = u'ClearLinux Documentation'
copyright = u'2015, Intel OTC'
author = u'Intel OTC'
# 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 = '0.1'
version = '1.0.1'
# The full version, including alpha/beta/rc tags.
release = '0.1'
release = '1.0.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -109,16 +103,12 @@ pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
rst_epilog = """
.. include:: /substitutions.rst
"""
# -- 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 = 'alabaster'
#html_theme = 'zephyr'
# 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
@@ -126,11 +116,11 @@ html_theme = 'alabaster'
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['./themes']
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = "Documentation for Clear Linux* Project for Intel(r) Architecture"
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
@@ -156,11 +146,11 @@ 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'
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants =
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
@@ -170,22 +160,22 @@ html_last_updated_fmt = '%b %d, %Y'
#html_additional_pages = {}
# If false, no module index is generated.
html_domain_indices = False
#html_domain_indices = True
# If false, no index is generated.
html_use_index = True
#html_use_index = True
# If true, the index is split into individual pages for each letter.
html_split_index = True
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink =
#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
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
@@ -210,7 +200,7 @@ html_show_copyright = True
#html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'clrdoc'
htmlhelp_basename = 'ClearLinuxDocumentationdoc'
# -- Options for LaTeX output ---------------------------------------------
@@ -232,8 +222,8 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'clr.tex', u'Documentation for Clear Linux* Project for Intel® Architecture',
u'many', 'manual'),
(master_doc, 'ClearLinuxDocumentation.tex', u'ClearLinux Documentation Documentation',
u'Intel OTC', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -262,7 +252,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'zephyr', u'Documentation for Clear Linux* Project for Intel® Architecture',
(master_doc, 'clearlinuxdocumentation', u'ClearLinux Documentation Documentation',
[author], 1)
]
@@ -276,8 +266,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'zephyr', u'Documentation for Clear Linux* Project for Intel® Architecture',
author, 'Zephyr', 'One line description of project.',
(master_doc, 'ClearLinuxDocumentation', u'ClearLinux Documentation Documentation',
author, 'ClearLinuxDocumentation', 'One line description of project.',
'Miscellaneous'),
]
@@ -292,9 +282,3 @@ texinfo_documents = [
# If true, do not generate a @detailmenu in the "Top" node's menu.
#texinfo_no_detailmenu = False
breathe_projects = {
"Zephyr": "xml",
"doc-examples": "xml"
}
breathe_default_project = "Zephyr"
+1 -1
View File
@@ -61,7 +61,7 @@ Wait for the final confirmation
This example shows ``clear-2190-installer.img.xz`` flashed to a 16GB USB drive
mounted on ``/sdc``.
.. image:: images/gs_confirmation_screen.png
.. image:: _static/images/gs_confirmation_screen.png
:align: center
:alt: confirmation
+28 -6
View File
@@ -1,12 +1,12 @@
.. _gs_running_clr_virtual:
Running in a virtualized environment
##########################################################################
####################################
The easiest way to get started running Clear Linux* OS for Intel® Architecture in a virtualized
environment is to download a recent KVM image from the `image
directory <http://download.clearlinux.org/image/>`_. This directory
contains an image file, the UEFI firmware helper and the KVM start
The easiest way to get started running Clear Linux* OS for Intel®
Architecture in a virtualized environment is to download a recent KVM image
from the `image directory <http://download.clearlinux.org/image/>`_. This
directory contains an image file, the UEFI firmware helper and the KVM start
helper script.
Starter script
@@ -15,9 +15,17 @@ Starter script
To start the image, run the ``start_qemu.sh`` script from
`here <http://download.clearlinux.org/image/start_qemu.sh>`_, or modify
the following script for your needs and run it from the command line
<<<<<<< HEAD
with ``$ script clr_image``::
#!/bin/bash
=======
with ``$ script clr_image``
.. code-block:: bash
#!/bin/bash
>>>>>>> staging
if [ $# -eq 0 ] ; then
echo "Please provide an image to emulate as first argument"
exit 1
@@ -39,7 +47,11 @@ with ``$ script clr_image``::
To run this script, connect from host via SSH. Take note that SSH is not
enabled by default. To enable it, log in through serial console with the
username ``root``. After setting the password, enable root login via SSH
<<<<<<< HEAD
by configuring ``/etc/ssh/sshd_config`` with this content::
=======
by configuring :file:`/etc/ssh/sshd_config` with this content::
>>>>>>> staging
PermitRootLogin yes
@@ -47,16 +59,26 @@ Now you may connect from host via SSH through 2223::
$ ssh -p 2223 root@localhost
<<<<<<< HEAD
Alternately, there are a few other ways to approach this.
=======
Alternatively, there are a few other ways to approach this.
>>>>>>> staging
- To run the script without modifying its permissions::
$ bash start_qemu.sh clr_image
- To run it as a background process::
<<<<<<< HEAD
$ `bash start_qemu.sh clr_image` &
=======
$ bash start_qemu.sh clr_image &
>>>>>>> staging
- If you'd like to run the script with execute permission::
$ chmod +x start_qemu.sh
@@ -64,4 +86,4 @@ Alternately, there are a few other ways to approach this.
- And to run it as a background process::
$ `./start_qemu.sh clr_image` &
$ ./start_qemu.sh clr_image &
+41 -7
View File
@@ -1,6 +1,22 @@
..
Clear Linux Project documentation master file
.. ClearLinux Documentation documentation master file, created by
sphinx-quickstart on Fri Nov 13 12:23:35 2015.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
<<<<<<< HEAD
=======
Welcome to ClearLinux Documentation
===================================
Contents
========
.. toctree::
:maxdepth: 2
>>>>>>> staging
Getting started
###############
@@ -14,6 +30,7 @@ Getting started
gs_running_clr_virtual
gs-clear-containers-gettting-started
Software update
###############
@@ -23,6 +40,7 @@ Software update
swupdate_about_sw_update
swupdate_how_to_run_the_updater
Bundles
#######
@@ -31,9 +49,11 @@ Bundles
bundles_overview
bundles_in_clr
table_view_bundles
OpenStack* implementation
#########################
OpenStack``*`` implementation
#############################
.. toctree::
:maxdepth: 1
@@ -54,13 +74,27 @@ OpenStack* implementation
openstack_orchestration
openstack_telemetry
openstack_object_storage
openstack_supporting-database
openstack_supporting-messaging
Index and search
################
License and Disclaimers
=======================
.. toctree::
disclaimers
documentation_license
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
<<<<<<< HEAD
.. include:: documentation_license.rst
=======
>>>>>>> staging
+441 -113
View File
@@ -1,59 +1,83 @@
<<<<<<< HEAD
Block Storage
############################################################
=======
.. _openstack_block_storage:
>>>>>>> staging
Clear Linux* OS for Intel® Architecture can be used with the
OpenStack Block Storage service (cinder) to add persistent storage
options to a virtual machine. Block Storage provides an infrastructure
for managing volumes and interacting with OpenStack Compute (nova) to
provide volumes for specific instances. These volumes can be easily
managed (types and snapshots) under Block Storage. Here's how to get
OpenStack Block Storage working with Clear Linux OS for Intel
Architecture:
Block Storage
########################
Installing and configuring the controller node
----------------------------------------------------
The OpenStack Block Storage service (cinder) adds persistent storage to
a virtual machine. Block Storage provides an infrastructure for managing
volumes, and interacts with OpenStack Compute to provide volumes for
instances. The service also enables management of volume snapshots, and
volume types.
The first step is to install and configure the Block Storage service,
code-named cinder, on the controller node. This service requires at
least one additional storage node that provides volumes to instances.
Install and configure controller node
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prerequisites:
~~~~~~~~~~~~~~~~~~
Before installing and configuring the Block Storage service, create a
database, service credentials, and an API endpoint. To create the
database, complete these steps:
This section describes how to install and configure the Block
Storage service, code-named cinder, on the controller node. This
service requires at least one additional storage node that provides
volumes to instances.
<<<<<<< HEAD
**Create a database:**
#. Use the database access client to connect to the database server as
the root user::
=======
Prerequisites:
--------------
$ mysql -u root -p
Before you install and configure the Block Storage service, you
>>>>>>> staging
#. To create the database, complete these steps:
<<<<<<< HEAD
#. Create the cinder database::
CREATE DATABASE cinder;
#. Grant proper access to the cinder database. Replace ``CINDER_DBPASS``
with a suitable password::
=======
* Use the database access client to connect to the database
server as the ``root`` user::
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' \
IDENTIFIED BY 'CINDER_DBPASS';
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' \
IDENTIFIED BY 'CINDER_DBPASS';
$ mysql -u root -p
#. Exit the database access client.
* Create the ``cinder`` database::
**Create service credentials:**
CREATE DATABASE cinder;
>>>>>>> staging
* Grant proper access to the ``cinder`` database::
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'localhost' \
IDENTIFIED BY 'CINDER_DBPASS';
GRANT ALL PRIVILEGES ON cinder.* TO 'cinder'@'%' \
IDENTIFIED BY 'CINDER_DBPASS';
Replace ``CINDER_DBPASS`` with a suitable password.
<<<<<<< HEAD
#. Now source the admin credentials to gain access to admin-only CLI
commands::
=======
* Exit the database access client.
$ source admin-openrc.sh
#. Source the ``admin`` credentials to gain access to admin-only
CLI commands::
>>>>>>> staging
$ source admin-openrc.sh
#. To create the service credentials, complete these steps:
<<<<<<< HEAD
* Create a cinder user::
$ openstack user create --password-prompt cinder
@@ -153,17 +177,186 @@ Block Storage controller components:
#. Install OpenStack Block Storage Controller bundle::
# clr_bundle_add openstack-block-storage-controller
=======
* Create a ``cinder`` user::
$ openstack user create --domain default --password-prompt cinder
User Password:
Repeat User Password:
+-----------+----------------------------------+
| Field | Value |
+-----------+----------------------------------+
| domain_id | default |
| enabled | True |
| id | bb279f8ffc444637af38811a5e1f0562 |
| name | cinder |
+-----------+----------------------------------+
* Add the ``admin`` role to the ``cinder`` user::
$ openstack role add --project service --user cinder admin
* Create the ``cinder`` and ``cinderv2`` service entities::
$ openstack service create --name cinder \
--description "OpenStack Block Storage" volume
+-------------+----------------------------------+
| Field | Value |
+-------------+----------------------------------+
| description | OpenStack Block Storage |
| enabled | True |
| id | ab3bbbef780845a1a283490d281e7fda |
| name | cinder |
| type | volume |
+-------------+----------------------------------+
$ openstack service create --name cinderv2 \
--description "OpenStack Block Storage" volumev2
+-------------+----------------------------------+
| Field | Value |
+-------------+----------------------------------+
| description | OpenStack Block Storage |
| enabled | True |
| id | eb9fd245bdbc414695952e93f29fe3ac |
| name | cinderv2 |
| type | volumev2 |
+-------------+----------------------------------+
.. note::
The Block Storage services requires two service entities.
#. Create the Block Storage service API endpoints::
$ openstack endpoint create --region RegionOne \
volume public http://controller:8776/v1/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | 03fa2c90153546c295bf30ca86b1344b |
| interface | public |
| region | RegionOne |
| region_id | RegionOne |
| service_id | ab3bbbef780845a1a283490d281e7fda |
| service_name | cinder |
| service_type | volume |
| url | http://controller:8776/v1/%(tenant_id)s |
+--------------+-----------------------------------------+
$ openstack endpoint create --region RegionOne \
volume internal http://controller:8776/v1/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | 94f684395d1b41068c70e4ecb11364b2 |
| interface | internal |
| region | RegionOne |
| region_id | RegionOne |
| service_id | ab3bbbef780845a1a283490d281e7fda |
| service_name | cinder |
| service_type | volume |
| url | http://controller:8776/v1/%(tenant_id)s |
+--------------+-----------------------------------------+
$ openstack endpoint create --region RegionOne \
volume admin http://controller:8776/v1/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | 4511c28a0f9840c78bacb25f10f62c98 |
| interface | admin |
| region | RegionOne |
| region_id | RegionOne |
| service_id | ab3bbbef780845a1a283490d281e7fda |
| service_name | cinder |
| service_type | volume |
| url | http://controller:8776/v1/%(tenant_id)s |
+--------------+-----------------------------------------+
$ openstack endpoint create --region RegionOne \
volumev2 public http://controller:8776/v2/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | 513e73819e14460fb904163f41ef3759 |
| interface | public |
| region | RegionOne |
| region_id | RegionOne |
| service_id | eb9fd245bdbc414695952e93f29fe3ac |
| service_name | cinderv2 |
| service_type | volumev2 |
| url | http://controller:8776/v2/%(tenant_id)s |
+--------------+-----------------------------------------+
$ openstack endpoint create --region RegionOne \
volumev2 internal http://controller:8776/v2/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | 6436a8a23d014cfdb69c586eff146a32 |
| interface | internal |
| region | RegionOne |
| region_id | RegionOne |
| service_id | eb9fd245bdbc414695952e93f29fe3ac |
| service_name | cinderv2 |
| service_type | volumev2 |
| url | http://controller:8776/v2/%(tenant_id)s |
+--------------+-----------------------------------------+
$ openstack endpoint create --region RegionOne \
volumev2 admin http://controller:8776/v2/%\(tenant_id\)s
+--------------+-----------------------------------------+
| Field | Value |
+--------------+-----------------------------------------+
| enabled | True |
| id | e652cf84dd334f359ae9b045a2c91d96 |
| interface | admin |
| region | RegionOne |
| region_id | RegionOne |
| service_id | eb9fd245bdbc414695952e93f29fe3ac |
| service_name | cinderv2 |
| service_type | volumev2 |
| url | http://controller:8776/v2/%(tenant_id)s |
+--------------+-----------------------------------------+
.. note::
The Block Storage services requires endpoints for each service
entity.
Install and configure components
--------------------------------
#. Install OpenStack Block Storage Controller bundle::
# swupd bundle-add openstack-block-storage-controller
# swupd verify --fix
>>>>>>> staging
#. Custom configurations will be located at ``/etc/cinder``.
* Create ``/etc/cinder`` directory::
<<<<<<< HEAD
mkdir /etc/cinder
=======
mkdir /etc/cinder
>>>>>>> staging
* Create empty cinder configuration file in
``/etc/cinder/cinder.conf``::
<<<<<<< HEAD
touch /etc/cinder/cinder.conf
=======
touch /etc/cinder/cinder.conf
>>>>>>> staging
#. Edit the ``/etc/cinder/cinder.conf`` file and complete the following
actions:
@@ -171,40 +364,50 @@ Block Storage controller components:
* In the ``[database]`` section, configure database access. Replace
``CINDER_DBPASS`` with the password you chose for the
database::
<<<<<<< HEAD
[database]
...
connection=mysql://cinder:CINDER_DBPASS@controller/cinder
=======
[database]
...
connection=mysql://cinder:CINDER_DBPASS@controller/cinder
>>>>>>> staging
* In the ``[DEFAULT]`` and ``[oslo_messaging_rabbit]`` section,
configure RabbitMQ message queue access. Replace ``RABBIT_PASS``
with the password you chose for the account in
RabbitMQ::
[DEFAULT]
...
rpc_backend = rabbit
...
[oslo_messaging_rabbit]
rabbit_host = controller
rabbit_userid = openstack
rabbit_password = RABBIT_PASS
[DEFAULT]
...
rpc_backend = rabbit
<<<<<<< HEAD
* In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections,
configure Identity service access. Replace ``CINDER_PASS`` with the
password you chose for the cinder user in the Identity
service::
=======
[oslo_messaging_rabbit]
...
rabbit_host = controller
rabbit_userid = openstack
rabbit_password = RABBIT_PASS
[DEFAULT]
...
auth_strategy = keystone
...
[keystone_authtoken]
auth_uri = http://controller:5000/v2.0
admin_tenant_name = service
admin_user = cinder
admin_password = CINDER_PASS
* In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections, configure
Identity service access. Replace ``CINDER_PASS`` with the password you
chose for the ``cinder`` user in the Identity service.::
>>>>>>> staging
[DEFAULT]
...
auth_strategy = keystone
<<<<<<< HEAD
* In the ``[DEFAULT]`` section, configure the ``my_ip`` option to
use the management interface IP address of the controller node::
@@ -217,54 +420,102 @@ Block Storage controller components:
#. Populate the Block Storage database::
# su -s /bin/sh -c "cinder-manage db sync" cinder
=======
[keystone_authtoken]
...
auth_uri = http://controller:5000
auth_url = http://controller:35357
auth_plugin = password
project_domain_id = default
user_domain_id = default
project_name = service
username = cinder
password = CINDER_PASS
Finalizing installation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* In the ``[DEFAULT]`` section, configure the ``my_ip`` option to
use the management interface IP address of the controller node::
[DEFAULT]
...
my_ip = 10.0.0.11
#. Populate the Block Storage database::
# su -s /bin/sh -c "cinder-manage db sync" cinder
Configure Compute to use Block Storage
--------------------------------------
* Edit the ``/etc/nova/nova.conf`` file and add the following
to it::
>>>>>>> staging
[cinder]
os_region_name = RegionOne
<<<<<<< HEAD
To finalize installation, enable and start the Block Storage services::
=======
Finalize installation
---------------------
# systemctl enable cinder-api cinder-scheduler
# systemctl start cinder-api cinder-scheduler
#. Restart the Compute API service::
>>>>>>> staging
Installing and configuring a storage node
----------------------------------------------
# systemctl restart uwsgi@nova-api.service
This section describes how to install and configure storage nodes for
the Block Storage service. For simplicity, this configuration references
one storage node with an empty local block storage device ``/dev/sdb``
(for physical device) or ``/dev/vda`` (for virtual machine) that
contains a suitable partition table with one partition ``/dev/sdb1``
occupying the entire device. The service provisions logical volumes on
this device using the LVM driver and provides them to instances via
iSCSI transport. You can follow these instructions with minor
modifications to horizontally scale your environment with additional
storage nodes.
#. Start the Block Storage services and configure them to start when
the system boots::
Install Block Storage volume components
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# systemctl enable cinder-api cinder-scheduler
# systemctl start cinder-api cinder-scheduler
Install and configure a storage node
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<<<<<<< HEAD
Install the packages::
=======
This section describes how to install and configure storage nodes
for the Block Storage service. For simplicity, this configuration
references one storage node with an empty local block storage device.
The instructions use ``/dev/sdb``, but you can substitute a different
value for your particular node.
# clr_bundle_add openstack-block-storage
The service provisions logical volumes on this device using the
LVM driver and provides them to instances via iSCSI transport.
You can follow these instructions with minor modifications to
horizontally scale your environment with additional storage nodes.
>>>>>>> staging
**Prerequisites:**
Prerequisites
-------------
You must configure the storage node before installing and configuring
the volume service on it. Similar to the controller node, the storage
node contains one network interface on the management network. The
storage node also needs an empty block storage device of suitable size
for your environment.
#. Install the openstack block storage bundle::
# swupd bundle-add openstack-block-storage
# swupd verify --fix
#. Create the LVM physical volume: ``/dev/sdb1`` If your system uses a
different device name, adjust these steps accordingly::
<<<<<<< HEAD
=======
# pvcreate /dev/sdb1
Physical volume "/dev/sdb1" successfully created
# pvcreate /dev/sdb1
Physical volume "/dev/sdb1" successfully created
>>>>>>> staging
#. Create the LVM volume group ``cinder-volumes``::
<<<<<<< HEAD
#. Create the LVM volume group ``cinder-volumes``::
# vgcreate cinder-volumes /dev/sdb1
Volume group "cinder-volumes" successfully created
=======
# vgcreate cinder-volumes /dev/sdb1
Volume group "cinder-volumes" successfully created
>>>>>>> staging
The Block Storage service creates logical volumes in this volume
group.
@@ -279,17 +530,21 @@ for your environment.
reconfigure LVM to scan only the devices that contain the
``cinder-volume`` volume group.
<<<<<<< HEAD
#. Edit the ``/etc/lvm/lvm.conf`` file
and complete the following action:
=======
>>>>>>> staging
* In the ``devices`` section, add a filter that accepts the
``/dev/sdb`` device and rejects all other devices::
devices {
filter = [ "a/sdb/", "r/.*/"]
}
devices {
filter = [ "a/sdb/", "r/.*/"]
}
**Configure Block Storage volume components:**
Install and configure components
--------------------------------
#. Edit the ``/etc/cinder/cinder.conf`` file and complete the following
actions:
@@ -298,73 +553,119 @@ for your environment.
``CINDER_DBPASS`` with the password you chose for the Block Storage
database::
[database]
...
[database]
...
connection = mysql://cinder:CINDER_DBPASS@controller/cinder
* In the ``[DEFAULT]`` and ``[oslo_messaging_rabbit]`` sections,
<<<<<<< HEAD
configure *RabbitMQ* message queue access. Replace ``RABBIT_PASS``
with the password you chose for the openstack account in
*RabbitMQ*::
=======
configure ``RabbitMQ`` message queue access. Replace ``RABBIT_PASS``
with the password you chose for the openstack account in
``RabbitMQ``::
[DEFAULT]
...
rpc_backend = rabbit
[oslo_messaging_rabbit]
...
rabbit_host = controller
rabbit_userid = openstack
rabbit_password = RABBIT_PASS
[DEFAULT]
...
rpc_backend = rabbit
>>>>>>> staging
[oslo_messaging_rabbit]
...
rabbit_host = controller
rabbit_userid = openstack
rabbit_password = RABBIT_PASS
* In the ``[DEFAULT]`` and ``[keystone_authtoken]`` sections,
configure Identity service access. Replace ``CINDER_PASS`` with the
password you chose for the cinder user in the Identity service::
<<<<<<< HEAD
=======
[DEFAULT]
...
auth_strategy = keystone
[keystone_authtoken]
...
auth_uri = http://controller:5000
identity_uri = http://controller:35357
admin_tenant_name = service
admin_user = cinder
admin_password = CINDER_PASS
[DEFAULT]
...
auth_strategy = keystone
>>>>>>> staging
[keystone_authtoken]
...
auth_uri = http://controller:5000
auth_url = http://controller:35357
auth_plugin = password
project_domain_id = default
user_domain_id = default
project_name = service
username = cinder
password = CINDER_PASS
* In the ``[DEFAULT]`` section, configure the ``my_ip`` option.
<<<<<<< HEAD
Replace *MANAGEMENT_INTERFACE_IP_ADDRESS* with the IP address
of the management network interface on your storage node,
typically 10.0.0.41 for the first node in the example
architecture::
=======
Replace ``MANAGEMENT_INTERFACE_IP_ADDRESS`` with the IP address
of the management network interface on your storage node,
typically 10.0.0.41 for the first node in the example
architecture::
[DEFAULT]
...
my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS
[DEFAULT]
...
my_ip = MANAGEMENT_INTERFACE_IP_ADDRESS
>>>>>>> staging
* In the ``[lvm]`` section, configure the LVM back end with the LVM
driver, ``cinder-volumes`` volume group, iSCSI protocol, and
appropriate iSCSI service::
[lvm]
...
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
volume_group = cinder-volumes
iscsi_protocol = iscsi
iscsi_helper = tgtadm
<<<<<<< HEAD
* In the ``[lvm]`` section, configure the LVM back end with the LVM
driver, ``cinder-volumes`` volume group, iSCSI protocol, and
appropriate iSCSI service::
=======
[lvm]
...
volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver
volume_group = cinder-volumes
iscsi_protocol = iscsi
iscsi_helper = tgtadm
* In the ``[DEFAULT]`` section, enable the LVM back end::
>>>>>>> staging
[DEFAULT]
...
enabled_backends = lvm
<<<<<<< HEAD
* In the ``[DEFAULT]`` section, enable the LVM back end::
[DEFAULT]
...
enabled_backends = lvm
* In the ``[DEFAULT]`` section, configure the location of the Image
service::
=======
* In the ``[DEFAULT]`` section, configure the location of the Image
service::
[DEFAULT]
...
glance_host = controller
[DEFAULT]
...
glance_host = controller
#. Let systemd set the correct permissions for files in ``/etc/cinder``::
# systemctl restart update-triggers.target
>>>>>>> staging
Finalize installation
---------------------
<<<<<<< HEAD
#. Let systemd set the correct permissions for files in ``/etc/cinder``::
# systemctl restart update-triggers.target
@@ -373,18 +674,45 @@ Finalizing installation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Restart the Block Storage volume service including its dependencies::
=======
#. Start the Block Storage volume service including its dependencies
and configure them to start when the system boots::
# systemctl enable iscsid tgtd cinder-volume
# systemctl start iscsid tgtd cinder-volume
# systemctl enable iscsid tgtd cinder-volume
# systemctl start iscsid tgtd cinder-volume
Configuring a compute node to use Block Storage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------------------------------------------
#. Perform the following steps to enable a compute node to work with
block storage::
>>>>>>> staging
# systemctl enable iscsid
# systemctl start iscsi-gen-initiatorname iscsid
Verify operation
~~~~~~~~~~~~~~~~
Verify operation of the Block Storage service.
<<<<<<< HEAD
Perform the following steps to enable a compute node to work with block
storage::
=======
#. Source the ``admin`` credentials to gain access to
admin-only CLI commands::
# systemctl enable iscsid
# systemctl start iscsi-gen-initiatorname
# systemctl start iscsid
$ source admin-openrc.sh
>>>>>>> staging
#. List service components to verify successful launch of each process::
$ cinder service-list
+------------------+------------+------+---------+-------+----------------------------+-----------------+
| Binary | Host | Zone | Status | State | Updated_at | Disabled Reason |
+------------------+------------+------+---------+-------+----------------------------+-----------------+
| cinder-scheduler | controller | nova | enabled | up | 2014-10-18T01:30:54.000000 | None |
| cinder-volume | block1@lvm | nova | enabled | up | 2014-10-18T01:30:57.000000 | None |
+------------------+------------+------+---------+-------+----------------------------+-----------------+
Next topic: :ref:`openstack_dashboard`.
+33 -6
View File
@@ -1,3 +1,4 @@
<<<<<<< HEAD
Dashboard
############################################################
@@ -8,22 +9,48 @@ cloud controller via OpenStack APIs.
Installation and configuration
------------------------------
=======
.. _openstack_dashboard:
Please note that the dashboard relies on functional core services in
`OpenStack MVP <openstack_installing_bundles.html>`__,
including identity, image service, compute, and either networking
(``neutron``) or legacy networking (``nova-network``).
Dashboard
####################
Environments with stand-alone services, such as Object Storage, cannot
use the dashboard.
The OpenStack Dashboard, also known as Horizon, is a web-based interface
for cloud administrators and users to manage various OpenStack resources
and services.
The Dashboard enables web-based interactions with the
OpenStack Compute cloud controller through the OpenStack APIs.
>>>>>>> staging
Installation and configuration
------------------------------
The dashboard relies on functional core services including
Identity, Image service, Compute, and either Networking (neutron)
or legacy networking (nova-network). Environments with
stand-alone services such as Object Storage cannot use the
dashboard.
To get started with OpenStack Dashboard services:
#. Install the OpenStack Dashboard bundle::
<<<<<<< HEAD
# clr_bundle_add openstack-dashboard
#. Enable and start the memcached service and the httpd server::
# systemctl enable httpd memcached
# systemctl restart httpd memcached
=======
# swupd bundle-add openstack-dashboard
# swupd verify --fix
#. Enable and start the dashboard socket and the Nginx server::
# systemctl enable nginx uwsgi@horizon.socket
# systemctl restart nginx uwsgi@horizon.socket
Next topic: :ref:`openstack_networking`.
>>>>>>> staging
@@ -1,7 +1,12 @@
.. _table_view_bundles:
Table View of Bundles
=====================
.. raw:: html
<head>
<title>Bundles in ClearLinux</title>
<style media="screen" type="text/css">
table { margin: 2em; border-collapse: collapse; }
td, th { padding: .5em; border: 1px #ccc solid; }