aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonan Amicel <ronan.amicel@gmail.com>2012-09-14 00:36:10 +0200
committerRonan Amicel <ronan.amicel@gmail.com>2012-09-21 02:25:51 +0200
commitcdda68e8e24f6390e5d443ab3c44f7b6bc1a35da (patch)
tree49a5c24d90695e954c21e0e76f6abd87049e1064
parentRemove unused parameter (diff)
downloadfabtools-cdda68e8e24f6390e5d443ab3c44f7b6bc1a35da.tar.xz
Improve documentation
-rw-r--r--.gitignore1
-rw-r--r--README.rst56
-rw-r--r--docs/CHANGELOG.rst (renamed from CHANGELOG.rst)0
-rw-r--r--docs/Makefile153
-rw-r--r--docs/api/cron.rst7
-rw-r--r--docs/api/deb.rst9
-rw-r--r--docs/api/files.rst9
-rw-r--r--docs/api/index.rst21
-rw-r--r--docs/api/mysql.rst20
-rw-r--r--docs/api/network.rst7
-rw-r--r--docs/api/openvz.rst38
-rw-r--r--docs/api/postgres.rst20
-rw-r--r--docs/api/python.rst26
-rw-r--r--docs/api/python_distribute.rst9
-rw-r--r--docs/api/require/deb.rst22
-rw-r--r--docs/api/require/files.rst9
-rw-r--r--docs/api/require/index.rst20
-rw-r--r--docs/api/require/mysql.rst9
-rw-r--r--docs/api/require/nginx.rst7
-rw-r--r--docs/api/require/openvz.rst9
-rw-r--r--docs/api/require/postfix.rst7
-rw-r--r--docs/api/require/postgres.rst9
-rw-r--r--docs/api/require/python.rst22
-rw-r--r--docs/api/require/redis.rst7
-rw-r--r--docs/api/require/service.rst9
-rw-r--r--docs/api/require/shorewall.rst9
-rw-r--r--docs/api/require/supervisor.rst9
-rw-r--r--docs/api/require/system.rst18
-rw-r--r--docs/api/require/users.rst9
-rw-r--r--docs/api/service.rst9
-rw-r--r--docs/api/shorewall.rst26
-rw-r--r--docs/api/supervisor.rst22
-rw-r--r--docs/api/system.rst23
-rw-r--r--docs/api/user.rst9
-rw-r--r--docs/api/vagrant.rst9
-rw-r--r--docs/conf.py248
-rw-r--r--docs/index.rst41
-rw-r--r--docs/tests.rst41
-rw-r--r--fabtools/cron.py37
-rw-r--r--fabtools/deb.py86
-rw-r--r--fabtools/files.py32
-rw-r--r--fabtools/mysql.py36
-rw-r--r--fabtools/network.py31
-rw-r--r--fabtools/openvz/__init__.py12
-rw-r--r--fabtools/openvz/container.py55
-rw-r--r--fabtools/openvz/contextmanager.py26
-rw-r--r--fabtools/openvz/operations.py62
-rw-r--r--fabtools/postgres.py32
-rw-r--r--fabtools/python.py65
-rw-r--r--fabtools/python_distribute.py42
-rw-r--r--fabtools/require/deb.py68
-rw-r--r--fabtools/require/files.py61
-rw-r--r--fabtools/require/mysql.py38
-rw-r--r--fabtools/require/nginx.py64
-rw-r--r--fabtools/require/openvz.py64
-rw-r--r--fabtools/require/postfix.py20
-rw-r--r--fabtools/require/postgres.py30
-rw-r--r--fabtools/require/python.py51
-rw-r--r--fabtools/require/redis.py35
-rw-r--r--fabtools/require/service.py38
-rw-r--r--fabtools/require/shorewall.py27
-rw-r--r--fabtools/require/supervisor.py41
-rw-r--r--fabtools/require/system.py13
-rw-r--r--fabtools/require/users.py22
-rw-r--r--fabtools/service.py50
-rw-r--r--fabtools/shorewall.py44
-rw-r--r--fabtools/supervisor.py20
-rw-r--r--fabtools/system.py30
-rw-r--r--fabtools/user.py15
-rw-r--r--fabtools/vagrant.py32
70 files changed, 2026 insertions, 232 deletions
diff --git a/.gitignore b/.gitignore
index 303a84a..20bbe1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
bin/
build/
dist/
+docs/_build/
include/
lib/
man/
diff --git a/README.rst b/README.rst
index d1dcc73..942568d 100644
--- a/README.rst
+++ b/README.rst
@@ -1,29 +1,29 @@
-Introduction
-============
+About
+=====
``fabtools`` includes useful functions to help you write your `Fabric <http://fabfile.org/>`_ files.
``fabtools`` makes it easier to manage system users, packages, databases, etc.
-``fabtools`` includes a number of low-level actions, as well as a higher level interface called ``require``.
+``fabtools`` includes a number of low-level actions, as well as a higher level interface called ``fabtools.require``.
-Using ``require`` allows you to use a more declarative style, similar to Chef or Puppet.
+Using ``fabtools.require`` allows you to use a more declarative style, similar to Chef or Puppet.
Installing
==========
-To install the latest release from PyPI::
+To install the latest release from `PyPI <http://pypi.python.org/pypi/fabtools>`_::
$ pip install fabtools
-To install the latest development version from GitHub::
+To install the latest development version from `GitHub <https://github.com/ronnix/fabtools>`_::
$ pip install git+git://github.com/ronnix/fabtools.git
Example
=======
-Here is an example fabfile using ``fabtools``::
+Here is an example ``fabfile.py`` using ``fabtools``::
from fabric.api import *
from fabtools import require
@@ -76,45 +76,3 @@ Supported targets
* Ubuntu 11.04
* Ubuntu 11.10
* Ubuntu 12.04 LTS
-
-Tests
-=====
-
-Running tests
--------------
-
-If you're using Python 2.7, you can launch the tests using the built-in `unittest <http://docs.python.org/library/unittest.html>`_ runner::
-
- $ python -m unittest discover
-
-If you're using Python 2.5 or 2.6, you'll need to install `unittest2 <http://pypi.python.org/pypi/unittest2>`_, and use the provided runner::
-
- $ pip install unittest2
- $ unit2 discover
-
-Or you can run the tests on all supported Python versions using `tox <http://pypi.python.org/pypi/tox>`_, which will take care of everything::
-
- $ pip install tox
- $ tox
-
-Unit tests
-----------
-
-Running unit tests requires the `mock <http://pypi.python.org/pypi/mock/>`_ library.
-
-Functional tests
-----------------
-
-Running functional tests requires `Vagrant <http://vagrantup.com/>`_ to launch virtual machines,
-against which all the tests will be run.
-
-If Vagrant is not installed, the functional tests will be skipped automatically.
-
-If Vagrant is installed, the default is to run the tests on all available base boxes.
-You can specify which base boxes should be used by setting the VAGRANT_BOXES environment variable::
-
- $ VAGRANT_BOXES='ubuntu_10_04 ubuntu_12_04' tox -e py27
-
-You can also use this to manually disable functional tests::
-
- $ VAGRANT_BOXES='' tox
diff --git a/CHANGELOG.rst b/docs/CHANGELOG.rst
index bf5f728..bf5f728 100644
--- a/CHANGELOG.rst
+++ b/docs/CHANGELOG.rst
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..0d9e14c
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,153 @@
+# 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) .
+# 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 gettext
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/fabtools.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/fabtools.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/fabtools"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/fabtools"
+ @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/docs/api/cron.rst b/docs/api/cron.rst
new file mode 100644
index 0000000..13f7a25
--- /dev/null
+++ b/docs/api/cron.rst
@@ -0,0 +1,7 @@
+.. _cron_module:
+
+:mod:`fabtools.cron`
+--------------------
+
+.. automodule:: fabtools.cron
+ :members:
diff --git a/docs/api/deb.rst b/docs/api/deb.rst
new file mode 100644
index 0000000..ad47133
--- /dev/null
+++ b/docs/api/deb.rst
@@ -0,0 +1,9 @@
+.. _deb_module:
+
+:mod:`fabtools.deb`
+-------------------
+
+.. automodule:: fabtools.deb
+ :members:
+
+ .. seealso:: :ref:`require_deb_module`
diff --git a/docs/api/files.rst b/docs/api/files.rst
new file mode 100644
index 0000000..4c82a47
--- /dev/null
+++ b/docs/api/files.rst
@@ -0,0 +1,9 @@
+.. _files_module:
+
+:mod:`fabtools.files`
+---------------------
+
+.. automodule:: fabtools.files
+ :members:
+
+ .. seealso:: :ref:`require_files_module`
diff --git a/docs/api/index.rst b/docs/api/index.rst
new file mode 100644
index 0000000..3084c04
--- /dev/null
+++ b/docs/api/index.rst
@@ -0,0 +1,21 @@
+fabtools
+========
+
+.. toctree::
+ :maxdepth: 1
+
+ cron
+ deb
+ files
+ mysql
+ network
+ openvz
+ postgres
+ python
+ python_distribute
+ service
+ shorewall
+ supervisor
+ system
+ user
+ vagrant
diff --git a/docs/api/mysql.rst b/docs/api/mysql.rst
new file mode 100644
index 0000000..fd1ab4f
--- /dev/null
+++ b/docs/api/mysql.rst
@@ -0,0 +1,20 @@
+.. _mysql_module:
+
+:mod:`fabtools.mysql`
+---------------------
+
+.. automodule:: fabtools.mysql
+
+ .. seealso:: :ref:`require_mysql_module`
+
+ Manage users
+ ~~~~~~~~~~~~
+
+ .. autofunction:: user_exists
+ .. autofunction:: create_user
+
+ Manage databases
+ ~~~~~~~~~~~~~~~~
+
+ .. autofunction:: database_exists
+ .. autofunction:: create_database
diff --git a/docs/api/network.rst b/docs/api/network.rst
new file mode 100644
index 0000000..4132935
--- /dev/null
+++ b/docs/api/network.rst
@@ -0,0 +1,7 @@
+.. _network_module:
+
+:mod:`fabtools.network`
+-----------------------
+
+.. automodule:: fabtools.network
+ :members:
diff --git a/docs/api/openvz.rst b/docs/api/openvz.rst
new file mode 100644
index 0000000..9420494
--- /dev/null
+++ b/docs/api/openvz.rst
@@ -0,0 +1,38 @@
+.. _openvz_module:
+
+:mod:`fabtools.openvz`
+----------------------
+
+.. automodule:: fabtools.openvz
+
+ .. seealso:: :ref:`require_openvz_module`
+
+ Manage templates
+ ~~~~~~~~~~~~~~~~
+
+ .. autofunction:: download_template
+
+ Manage containers
+ ~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: exists
+ .. autofunction:: create
+ .. autofunction:: set
+ .. autofunction:: status
+ .. autofunction:: start
+ .. autofunction:: stop
+ .. autofunction:: restart
+ .. autofunction:: destroy
+
+
+ Run commands inside a container
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: exec2
+ .. autofunction:: guest
+
+ Container class
+ ~~~~~~~~~~~~~~~
+
+ .. autoclass:: fabtools.openvz.container.Container
+ :members:
diff --git a/docs/api/postgres.rst b/docs/api/postgres.rst
new file mode 100644
index 0000000..cc4ff32
--- /dev/null
+++ b/docs/api/postgres.rst
@@ -0,0 +1,20 @@
+.. _postgres_module:
+
+:mod:`fabtools.postgres`
+------------------------
+
+.. automodule:: fabtools.postgres
+
+ .. seealso:: :ref:`require_postgres_module`
+
+ Manage users
+ ~~~~~~~~~~~~
+
+ .. autofunction:: user_exists
+ .. autofunction:: create_user
+
+ Manage databases
+ ~~~~~~~~~~~~~~~~
+
+ .. autofunction:: database_exists
+ .. autofunction:: create_database
diff --git a/docs/api/python.rst b/docs/api/python.rst
new file mode 100644
index 0000000..0938741
--- /dev/null
+++ b/docs/api/python.rst
@@ -0,0 +1,26 @@
+.. _python_module:
+
+:mod:`fabtools.python`
+----------------------
+
+.. automodule:: fabtools.python
+
+ .. seealso:: :ref:`python_distribute_module` and :ref:`require_python_module`
+
+ Virtual environments
+ ~~~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: virtualenv
+
+ Installing pip
+ ~~~~~~~~~~~~~~
+
+ .. autofunction:: is_pip_installed
+ .. autofunction:: install_pip
+
+ Installing packages
+ ~~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: is_installed
+ .. autofunction:: install
+ .. autofunction:: install_requirements
diff --git a/docs/api/python_distribute.rst b/docs/api/python_distribute.rst
new file mode 100644
index 0000000..8f00478
--- /dev/null
+++ b/docs/api/python_distribute.rst
@@ -0,0 +1,9 @@
+.. _python_distribute_module:
+
+:mod:`fabtools.python_distribute`
+---------------------------------
+
+.. automodule:: fabtools.python_distribute
+ :members:
+
+ .. seealso:: :ref:`python_module` and :ref:`require_python_module`
diff --git a/docs/api/require/deb.rst b/docs/api/require/deb.rst
new file mode 100644
index 0000000..6d5a9c4
--- /dev/null
+++ b/docs/api/require/deb.rst
@@ -0,0 +1,22 @@
+.. _require_deb_module:
+
+:mod:`fabtools.require.deb`
+---------------------------
+
+.. automodule:: fabtools.require.deb
+
+ .. seealso:: :ref:`deb_module`
+
+ Repositories
+ ~~~~~~~~~~~~
+
+ .. autofunction:: source
+ .. autofunction:: ppa
+
+ Packages
+ ~~~~~~~~
+
+ .. autofunction:: package
+ .. autofunction:: packages
+ .. autofunction:: nopackage
+ .. autofunction:: nopackages
diff --git a/docs/api/require/files.rst b/docs/api/require/files.rst
new file mode 100644
index 0000000..07dd030
--- /dev/null
+++ b/docs/api/require/files.rst
@@ -0,0 +1,9 @@
+.. _require_files_module:
+
+:mod:`fabtools.require.files`
+---------------------------
+
+.. automodule:: fabtools.require.files
+ :members:
+
+ .. seealso:: :ref:`files_module`
diff --git a/docs/api/require/index.rst b/docs/api/require/index.rst
new file mode 100644
index 0000000..cd516f0
--- /dev/null
+++ b/docs/api/require/index.rst
@@ -0,0 +1,20 @@
+fabtools.require
+================
+
+.. toctree::
+ :maxdepth: 1
+
+ deb
+ files
+ mysql
+ nginx
+ openvz
+ postfix
+ postgres
+ python
+ redis
+ service
+ shorewall
+ supervisor
+ system
+ users
diff --git a/docs/api/require/mysql.rst b/docs/api/require/mysql.rst
new file mode 100644
index 0000000..b8278e9
--- /dev/null
+++ b/docs/api/require/mysql.rst
@@ -0,0 +1,9 @@
+.. _require_mysql_module:
+
+:mod:`fabtools.require.mysql`
+-----------------------------
+
+.. automodule:: fabtools.require.mysql
+ :members:
+
+ .. seealso:: :ref:`mysql_module`
diff --git a/docs/api/require/nginx.rst b/docs/api/require/nginx.rst
new file mode 100644
index 0000000..0739ae2
--- /dev/null
+++ b/docs/api/require/nginx.rst
@@ -0,0 +1,7 @@
+.. _require_nginx_module:
+
+:mod:`fabtools.require.nginx`
+-----------------------------
+
+.. automodule:: fabtools.require.nginx
+ :members:
diff --git a/docs/api/require/openvz.rst b/docs/api/require/openvz.rst
new file mode 100644
index 0000000..3773eb5
--- /dev/null
+++ b/docs/api/require/openvz.rst
@@ -0,0 +1,9 @@
+.. _require_openvz_module:
+
+:mod:`fabtools.require.openvz`
+------------------------------
+
+.. automodule:: fabtools.require.openvz
+ :members:
+
+ .. seealso:: :ref:`openvz_module`
diff --git a/docs/api/require/postfix.rst b/docs/api/require/postfix.rst
new file mode 100644
index 0000000..0b01221
--- /dev/null
+++ b/docs/api/require/postfix.rst
@@ -0,0 +1,7 @@
+.. _require_postfix_module:
+
+:mod:`fabtools.require.postfix`
+-------------------------------
+
+.. automodule:: fabtools.require.postfix
+ :members:
diff --git a/docs/api/require/postgres.rst b/docs/api/require/postgres.rst
new file mode 100644
index 0000000..410342d
--- /dev/null
+++ b/docs/api/require/postgres.rst
@@ -0,0 +1,9 @@
+.. _require_postgres_module:
+
+:mod:`fabtools.require.postgres`
+--------------------------------
+
+.. automodule:: fabtools.require.postgres
+ :members:
+
+ .. seealso:: :ref:`postgres_module`
diff --git a/docs/api/require/python.rst b/docs/api/require/python.rst
new file mode 100644
index 0000000..1175eec
--- /dev/null
+++ b/docs/api/require/python.rst
@@ -0,0 +1,22 @@
+.. _require_python_module:
+
+:mod:`fabtools.require.python`
+------------------------------
+
+.. automodule:: fabtools.require.python
+
+ .. seealso:: :ref:`python_module`
+
+ Virtual environments
+ ~~~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: virtualenv
+
+ Installing packages
+ ~~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: package
+ .. autofunction:: packages
+ .. autofunction:: requirements
+ .. autofunction:: pip
+ .. autofunction:: distribute
diff --git a/docs/api/require/redis.rst b/docs/api/require/redis.rst
new file mode 100644
index 0000000..6e97c97
--- /dev/null
+++ b/docs/api/require/redis.rst
@@ -0,0 +1,7 @@
+.. _require_redis_module:
+
+:mod:`fabtools.require.redis`
+-----------------------------
+
+.. automodule:: fabtools.require.redis
+ :members:
diff --git a/docs/api/require/service.rst b/docs/api/require/service.rst
new file mode 100644
index 0000000..f223134
--- /dev/null
+++ b/docs/api/require/service.rst
@@ -0,0 +1,9 @@
+.. _require_service_module:
+
+:mod:`fabtools.require.service`
+-------------------------------
+
+.. automodule:: fabtools.require.service
+ :members:
+
+ .. seealso:: :ref:`service_module`
diff --git a/docs/api/require/shorewall.rst b/docs/api/require/shorewall.rst
new file mode 100644
index 0000000..a5888e1
--- /dev/null
+++ b/docs/api/require/shorewall.rst
@@ -0,0 +1,9 @@
+.. _require_shorewall_module:
+
+:mod:`fabtools.require.shorewall`
+---------------------------------
+
+.. automodule:: fabtools.require.shorewall
+ :members:
+
+ .. seealso:: :ref:`shorewall_module`
diff --git a/docs/api/require/supervisor.rst b/docs/api/require/supervisor.rst
new file mode 100644
index 0000000..e8cfa6f
--- /dev/null
+++ b/docs/api/require/supervisor.rst
@@ -0,0 +1,9 @@
+.. _require_supervisor_module:
+
+:mod:`fabtools.require.supervisor`
+----------------------------------
+
+.. automodule:: fabtools.require.supervisor
+ :members:
+
+ .. seealso:: :ref:`supervisor_module`
diff --git a/docs/api/require/system.rst b/docs/api/require/system.rst
new file mode 100644
index 0000000..d0d80f4
--- /dev/null
+++ b/docs/api/require/system.rst
@@ -0,0 +1,18 @@
+.. _require_system_module:
+
+:mod:`fabtools.require.system`
+------------------------------
+
+.. automodule:: fabtools.require.system
+
+ .. seealso:: :ref:`system_module`
+
+ .. autofunction:: hostname
+ .. autofunction:: sysctl
+
+ Locales
+ ~~~~~~~
+
+ .. autofunction:: default_locale
+ .. autofunction:: locale
+ .. autofunction:: locales
diff --git a/docs/api/require/users.rst b/docs/api/require/users.rst
new file mode 100644
index 0000000..7513113
--- /dev/null
+++ b/docs/api/require/users.rst
@@ -0,0 +1,9 @@
+.. _require_users_module:
+
+:mod:`fabtools.require.users`
+-----------------------------
+
+.. automodule:: fabtools.require.users
+ :members:
+
+ .. seealso:: :ref:`user_module`
diff --git a/docs/api/service.rst b/docs/api/service.rst
new file mode 100644
index 0000000..4dcb229
--- /dev/null
+++ b/docs/api/service.rst
@@ -0,0 +1,9 @@
+.. _service_module:
+
+:mod:`fabtools.service`
+-----------------------
+
+.. automodule:: fabtools.service
+ :members:
+
+ .. seealso:: :ref:`require_service_module`
diff --git a/docs/api/shorewall.rst b/docs/api/shorewall.rst
new file mode 100644
index 0000000..67a914c
--- /dev/null
+++ b/docs/api/shorewall.rst
@@ -0,0 +1,26 @@
+.. _shorewall_module:
+
+:mod:`fabtools.shorewall`
+------------------------
+
+.. automodule:: fabtools.shorewall
+
+ .. seealso:: :ref:`require_shorewall_module`
+
+ Firewall status
+ ~~~~~~~~~~~~~~~
+
+ .. autofunction:: status
+ .. autofunction:: is_started
+ .. autofunction:: is_stopped
+
+ Firewall rules
+ ~~~~~~~~~~~~~~
+
+ .. autofunction:: rule
+ .. autofunction:: hosts
+ .. autofunction:: Ping
+ .. autofunction:: SSH
+ .. autofunction:: HTTP
+ .. autofunction:: HTTPS
+ .. autofunction:: SMTP
diff --git a/docs/api/supervisor.rst b/docs/api/supervisor.rst
new file mode 100644
index 0000000..4e585ce
--- /dev/null
+++ b/docs/api/supervisor.rst
@@ -0,0 +1,22 @@
+.. _supervisor_module:
+
+:mod:`fabtools.supervisor`
+--------------------------
+
+.. automodule:: fabtools.supervisor
+
+ .. seealso:: :ref:`require_supervisor_module`
+
+ Manage supervisord
+ ~~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: reload_config
+ .. autofunction:: update_config
+
+ Manage processes
+ ~~~~~~~~~~~~~~~~
+
+ .. autofunction:: process_status
+ .. autofunction:: start_process
+ .. autofunction:: stop_process
+ .. autofunction:: restart_process
diff --git a/docs/api/system.rst b/docs/api/system.rst
new file mode 100644
index 0000000..9c7d11e
--- /dev/null
+++ b/docs/api/system.rst
@@ -0,0 +1,23 @@
+.. _system_module:
+
+:mod:`fabtools.system`
+----------------------
+
+.. automodule:: fabtools.system
+
+ Hostname
+ ~~~~~~~~
+
+ .. autofunction:: get_hostname
+ .. autofunction:: set_hostname
+
+ Kernel parameters
+ ~~~~~~~~~~~~~~~~~
+
+ .. autofunction:: get_sysctl
+ .. autofunction:: set_sysctl
+
+ Locales
+ ~~~~~~~
+
+ .. autofunction:: supported_locales
diff --git a/docs/api/user.rst b/docs/api/user.rst
new file mode 100644
index 0000000..c072861
--- /dev/null
+++ b/docs/api/user.rst
@@ -0,0 +1,9 @@
+.. _user_module:
+
+:mod:`fabtools.user`
+--------------------
+
+.. automodule:: fabtools.user
+ :members:
+
+ .. seealso:: :ref:`require_users_module`
diff --git a/docs/api/vagrant.rst b/docs/api/vagrant.rst
new file mode 100644
index 0000000..83a213c
--- /dev/null
+++ b/docs/api/vagrant.rst
@@ -0,0 +1,9 @@
+.. _vagrant_module:
+
+:mod:`fabtools.vagrant`
+-----------------------
+
+.. automodule:: fabtools.vagrant
+ :members:
+
+ .. autofunction:: vagrant
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..412b671
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,248 @@
+# -*- coding: utf-8 -*-
+#
+# fabtools documentation build configuration file, created by
+# sphinx-quickstart on Thu Sep 13 17:22:32 2012.
+#
+# 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.
+
+import sys, os
+
+# 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.viewcode',
+]
+
+# Preserve source order in API documentation
+autodoc_member_order = 'bysource'
+
+# 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'fabtools'
+copyright = u'2012, Ronan Amicel'
+
+# 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.5'
+# The full version, including alpha/beta/rc tags.
+release = '0.5'
+
+# 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 = ['_build']
+
+# 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 = []
+
+
+# -- 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 = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'fabtoolsdoc'
+
+
+# -- 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', 'fabtools.tex', u'fabtools Documentation',
+ u'Ronan Amicel', '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', 'fabtools', u'fabtools Documentation',
+ [u'Ronan Amicel'], 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', 'fabtools', u'fabtools Documentation',
+ u'Ronan Amicel', 'fabtools', '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'
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..0500072
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,41 @@
+======================================
+ Welcome to fabtools's documentation!
+======================================
+
+.. include:: ../README.rst
+
+
+API Documentation
+=================
+
+.. toctree::
+ :maxdepth: 1
+
+ api/index
+ api/require/index
+
+
+Changelog
+=========
+
+.. toctree::
+ :maxdepth: 1
+
+ CHANGELOG
+
+
+Development
+===========
+
+.. toctree::
+ :maxdepth: 1
+
+ tests
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/tests.rst b/docs/tests.rst
new file mode 100644
index 0000000..98588ed
--- /dev/null
+++ b/docs/tests.rst
@@ -0,0 +1,41 @@
+Tests
+=====
+
+Running tests
+-------------
+
+If you're using Python 2.7, you can launch the tests using the built-in `unittest <http://docs.python.org/library/unittest.html>`_ runner::
+
+ $ python -m unittest discover
+
+If you're using Python 2.5 or 2.6, you'll need to install `unittest2 <http://pypi.python.org/pypi/unittest2>`_, and use the provided runner::
+
+ $ pip install unittest2
+ $ unit2 discover
+
+Or you can run the tests on all supported Python versions using `tox <http://pypi.python.org/pypi/tox>`_, which will take care of everything::
+
+ $ pip install tox
+ $ tox
+
+Unit tests
+----------
+
+Running unit tests requires the `mock <http://pypi.python.org/pypi/mock/>`_ library.
+
+Functional tests
+----------------
+
+Running functional tests requires `Vagrant <http://vagrantup.com/>`_ to launch virtual machines,
+against which all the tests will be run.
+
+If Vagrant is not installed, the functional tests will be skipped automatically.
+
+If Vagrant is installed, the default is to run the tests on all available base boxes.
+You can specify which base boxes should be used by setting the VAGRANT_BOXES environment variable::
+
+ $ VAGRANT_BOXES='ubuntu_10_04 ubuntu_12_04' tox -e py27
+
+You can also use this to manually disable functional tests::
+
+ $ VAGRANT_BOXES='' tox
diff --git a/fabtools/cron.py b/fabtools/cron.py
index cfe0f3b..bf11455 100644
--- a/fabtools/cron.py
+++ b/fabtools/cron.py
@@ -1,5 +1,9 @@
"""
-Fabric tools for managing crontab tasks
+Cron tasks
+==========
+
+This module provides tools to manage periodic tasks using cron.
+
"""
from __future__ import with_statement
@@ -11,7 +15,26 @@ from fabtools.files import upload_template
def add_task(name, timespec, user, command):
"""
- Add a cron task
+ Add a cron task.
+
+ The *command* will be run as *user* periodically.
+
+ You can use any valid `crontab(5)`_ *timespec*, including the
+ ``@hourly``, ``@daily``, ``@weekly``, ``@monthly`` and ``@yearly``
+ shortcuts.
+
+ Examples::
+
+ from fabtools.cron import add_task
+
+ # Run every month
+ add_task('cleanup', '@monthly', 'alice', '/home/alice/bin/cleanup.sh')
+
+ # Run every tuesday and friday at 5:30am
+ add_task('reindex', '30 5 * * 2,4', 'bob', '/home/bob/bin/reindex.sh')
+
+ .. _crontab(5): http://manpages.debian.net/cgi-bin/man.cgi?query=crontab&sektion=5
+
"""
with NamedTemporaryFile() as script:
script.write('%(timespec)s %(user)s %(command)s\n' % locals())
@@ -25,6 +48,14 @@ def add_task(name, timespec, user, command):
def add_daily(name, user, command):
"""
- Add a cron task to run daily
+ Shortcut to add a daily cron task.
+
+ Example::
+
+ import fabtools
+
+ # Run every day
+ fabtools.cron.add_daily('backup', 'root', '/usr/local/bin/backup.sh')
+
"""
add_task(name, '@daily', user, command)
diff --git a/fabtools/deb.py b/fabtools/deb.py
index 7708724..fec3374 100644
--- a/fabtools/deb.py
+++ b/fabtools/deb.py
@@ -1,5 +1,10 @@
"""
-Fabric tools for managing Debian/Ubuntu packages
+Debian packages
+===============
+
+This module provides tools to manage Debian/Ubuntu packages
+and repositories.
+
"""
from __future__ import with_statement
@@ -11,7 +16,7 @@ MANAGER = 'apt-get'
def update_index(quiet=True):
"""
- Quietly update package index
+ Update APT package definitions.
"""
options = "-q -q" if quiet else ""
sudo("%s %s update" % (MANAGER, options))
@@ -19,7 +24,7 @@ def update_index(quiet=True):
def upgrade(safe=True):
"""
- Upgrade all packages
+ Upgrade all packages.
"""
manager = MANAGER
cmds = {'apt-get': {False: 'dist-upgrade', True: 'upgrade'},
@@ -30,7 +35,7 @@ def upgrade(safe=True):
def is_installed(pkg_name):
"""
- Check if .deb package is installed
+ Check if a package is installed.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = run("dpkg -s %(pkg_name)s" % locals())
@@ -44,7 +49,26 @@ def is_installed(pkg_name):
def install(packages, update=False, options=None):
"""
- Install .deb package(s)
+ Install one or more packages.
+
+ If *update* is ``True``, the package definitions will be updated
+ first, using :py:func:`~fabtools.deb.update_index`.
+
+ Extra *options* may be passed to ``apt-get`` if necessary.
+
+ Example::
+
+ import fabtools
+
+ # Update index, then install a single package
+ fabtools.deb.install('build-essential', update=True)
+
+ # Install multiple packages
+ fabtools.deb.install([
+ 'python-dev',
+ 'libxml2-dev',
+ ])
+
"""
manager = MANAGER
if update:
@@ -60,7 +84,12 @@ def install(packages, update=False, options=None):
def uninstall(packages, purge=False, options=None):
"""
- Remove .deb package(s)
+ Remove one or more packages.
+
+ If *purge* is ``True``, the package configuration files will be
+ removed from the system.
+
+ Extra *options* may be passed to ``apt-get`` if necessary.
"""
manager = MANAGER
command = "purge" if purge else "remove"
@@ -75,7 +104,21 @@ def uninstall(packages, purge=False, options=None):
def preseed_package(pkg_name, preseed):
"""
- Enable unattended package installation by preseeding debconf parameters
+ Enable unattended package installation by preseeding ``debconf``
+ parameters.
+
+ Example::
+
+ import fabtools
+
+ # Unattended install of Postfix mail server
+ fabtools.deb.preseed_package('postfix', {
+ 'postfix/main_mailer_type': ('select', 'Internet Site'),
+ 'postfix/mailname': ('string', 'example.com'),
+ 'postfix/destinations': ('string', 'example.com, localhost.localdomain, localhost'),
+ })
+ fabtools.deb.install('postfix')
+
"""
for q_name, _ in preseed.items():
q_type, q_answer = _
@@ -84,8 +127,9 @@ def preseed_package(pkg_name, preseed):
def get_selections():
"""
- Get the state of dkpg selections
- Returns dict with state => [packages]
+ Get the state of ``dkpg`` selections.
+
+ Returns a dict with state => [packages].
"""
with settings(hide('stdout')):
res = sudo('dpkg --get-selections')
@@ -98,7 +142,15 @@ def get_selections():
def distrib_codename():
"""
- Get the codename of the distrib
+ Get the codename of the distrib.
+
+ Example::
+
+ from fabtools.deb import distrib_codename
+
+ if distrib_codename() == 'precise':
+ print('Ubuntu 12.04 LTS')
+
"""
with settings(hide('running', 'stdout')):
return run('lsb_release --codename --short')
@@ -106,7 +158,19 @@ def distrib_codename():
def add_apt_key(filename, update=True):
"""
- Add an APT key
+ Trust packages signed with this public key.
+
+ Example::
+
+ import fabtools
+
+ # Download 3rd party APT public key
+ if not fabtools.is_file('rabbitmq-signing-key-public.asc'):
+ run('wget http://www.rabbitmq.com/rabbitmq-signing-key-public.asc')
+
+ # Tell APT to trust that key
+ fabtools.deb.add_apt_key('rabbitmq-signing-key-public.asc')
+
"""
sudo('apt-key add %(filename)s' % locals())
if update:
diff --git a/fabtools/files.py b/fabtools/files.py
index 9177ad6..c90eca8 100644
--- a/fabtools/files.py
+++ b/fabtools/files.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing files and directories
+Files and directories
+=====================
"""
from __future__ import with_statement
@@ -11,7 +12,7 @@ from fabric.contrib.files import upload_template as _upload_template
def is_file(path, use_sudo=False):
"""
- Check if a path exists, and is a file
+ Check if a path exists, and is a file.
"""
func = use_sudo and sudo or run
with settings(hide('running', 'warnings'), warn_only=True):
@@ -20,7 +21,7 @@ def is_file(path, use_sudo=False):
def is_dir(path, use_sudo=False):
"""
- Check if a path exists, and is a directory
+ Check if a path exists, and is a directory.
"""
func = use_sudo and sudo or run
with settings(hide('running', 'warnings'), warn_only=True):
@@ -29,7 +30,7 @@ def is_dir(path, use_sudo=False):
def is_link(path, use_sudo=False):
"""
- Check if a path exists, and is a symbolic link
+ Check if a path exists, and is a symbolic link.
"""
func = use_sudo and sudo or run
with settings(hide('running', 'warnings'), warn_only=True):
@@ -39,7 +40,7 @@ def is_link(path, use_sudo=False):
def upload_template(filename, template, context=None, use_sudo=False,
user="root", mkdir=False, chown=False):
"""
- Upload a template file
+ Upload a template file.
"""
if mkdir:
d = os.path.dirname(filename)
@@ -55,7 +56,7 @@ def upload_template(filename, template, context=None, use_sudo=False,
def md5sum(filename, use_sudo=False):
"""
- Compute MD5 sum
+ Compute the MD5 sum of a file.
"""
func = use_sudo and sudo or run
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
@@ -68,37 +69,46 @@ def md5sum(filename, use_sudo=False):
class watch(object):
"""
- Context manager to watch for changes to the contents of some files
+ Context manager to watch for changes to the contents of some files.
- The 'filenames' argument can be either a string (single filename)
+ The *filenames* argument can be either a string (single filename)
or a list (multiple filenames).
- You can read the 'changed' attribute at the end of the block to
+ You can read the *changed* attribute at the end of the block to
check if the contents of any of the watched files has changed.
- You can also provide a callback that will be called at the end of
+ You can also provide a *callback* that will be called at the end of
the block if the contents of any of the watched files has changed.
Example using an explicit check::
from fabric.contrib.files import comment, uncomment
+
from fabtools.files import watch
from fabtools.services import restart
+
+ # Edit configuration file
with watch('/etc/daemon.conf') as config:
uncomment('/etc/daemon.conf', 'someoption')
comment('/etc/daemon.conf', 'otheroption')
+
+ # Restart daemon if needed
if config.changed:
restart('daemon')
- Example using a callback::
+ Same example using a callback::
from functools import partial
+
from fabric.contrib.files import comment, uncomment
+
from fabtools.files import watch
from fabtools.services import restart
+
with watch('/etc/daemon.conf', callback=partial(restart, 'daemon')):
uncomment('/etc/daemon.conf', 'someoption')
comment('/etc/daemon.conf', 'otheroption')
+
"""
def __init__(self, filenames, callback=None, use_sudo=False):
diff --git a/fabtools/mysql.py b/fabtools/mysql.py
index e3d19d1..4e43255 100644
--- a/fabtools/mysql.py
+++ b/fabtools/mysql.py
@@ -1,5 +1,9 @@
"""
-Fabric tools for managing MySQL users and databases
+MySQL users and databases
+=========================
+
+This module provides tools for creating MySQL users and databases.
+
"""
from __future__ import with_statement
@@ -8,14 +12,14 @@ from fabric.api import *
def prompt_password(user='root'):
"""
- Ask MySQL password interactively
+ Ask MySQL password interactively.
"""
return prompt('Please enter password for MySQL user "%s":' % user)
def _query(query, use_sudo=True, **kwargs):
"""
- Run a MySQL query
+ Run a MySQL query.
"""
func = use_sudo and sudo or run
@@ -33,7 +37,7 @@ def _query(query, use_sudo=True, **kwargs):
def user_exists(name, **kwargs):
"""
- Check if a MySQL user exists
+ Check if a MySQL user exists.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = _query("use mysql; SELECT User FROM user WHERE User = '%(name)s';" % {
@@ -44,7 +48,16 @@ def user_exists(name, **kwargs):
def create_user(name, password, host='localhost', **kwargs):
"""
- Create a MySQL user
+ Create a MySQL user.
+
+ Example::
+
+ import fabtools
+
+ # Create DB user if it does not exist
+ if not fabtools.mysql.user_exists('dbuser'):
+ fabtools.mysql.create_user('dbuser', password='somerandomstring')
+
"""
with settings(hide('running')):
_query("CREATE USER '%(name)s'@'%(host)s' IDENTIFIED BY '%(password)s';" % {
@@ -57,7 +70,7 @@ def create_user(name, password, host='localhost', **kwargs):
def database_exists(name, **kwargs):
"""
- Check if a MySQL database exists
+ Check if a MySQL database exists.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = _query("use mysql; SELECT Db FROM db WHERE Db = '%(name)s';" % {
@@ -69,7 +82,16 @@ def database_exists(name, **kwargs):
def create_database(name, owner=None, owner_host='localhost', charset='utf8', collate='utf8_general_ci', **kwargs):
"""
- Create a MySQL database
+ Create a MySQL database.
+
+ Example::
+
+ import fabtools
+
+ # Create DB if it does not exist
+ if not fabtools.mysql.database_exists('myapp'):
+ fabtools.mysql.create_database('myapp', owner='dbuser')
+
"""
with settings(hide('running')):
diff --git a/fabtools/network.py b/fabtools/network.py
index 0ade487..76a4766 100644
--- a/fabtools/network.py
+++ b/fabtools/network.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing network
+Network
+=======
"""
from __future__ import with_statement
@@ -8,11 +9,7 @@ from fabric.api import *
def interfaces():
"""
- Get the list of network interfaces
-
- >>> fabtools.network.interfaces()
- ['eth0', 'eth1', 'lo']
-
+ Get the list of network interfaces.
"""
with settings(hide('running', 'stdout')):
res = run('/sbin/ifconfig -s')
@@ -21,10 +18,15 @@ def interfaces():
def address(interface):
"""
- Get the IPv4 address assigned to an interface
+ Get the IPv4 address assigned to an interface.
+
+ Example::
- >>> fabtools.network.address('lo')
- '127.0.0.1'
+ import fabtools
+
+ # Print all configured IP addresses
+ for interface in fabtools.network.interfaces():
+ print(fabtools.network.address(interface))
"""
with settings(hide('running', 'stdout')):
@@ -34,10 +36,15 @@ def address(interface):
def nameservers():
"""
- Get the list of nameserver addresses
+ Get the list of nameserver addresses.
+
+ Example::
+
+ import fabtools
- >>> fabtools.network.nameservers()
- ['208.67.222.222', '208.67.220.220']
+ # Check that all name servers are reachable
+ for ip in fabtools.network.nameservers():
+ run('ping -c1 %s' % ip)
"""
with settings(hide('running', 'stdout')):
diff --git a/fabtools/openvz/__init__.py b/fabtools/openvz/__init__.py
index 1dc5d40..9abe98e 100644
--- a/fabtools/openvz/__init__.py
+++ b/fabtools/openvz/__init__.py
@@ -1,2 +1,14 @@
+"""
+OpenVZ containers
+=================
+
+This module provides high-level tools for managing OpenVZ_ templates
+and containers.
+
+.. _OpenVZ: http://openvz.org/
+
+.. warning:: The remote host needs a patched kernel with OpenVZ support.
+
+"""
from fabtools.openvz.operations import *
from fabtools.openvz.contextmanager import guest
diff --git a/fabtools/openvz/container.py b/fabtools/openvz/container.py
index 2107dc2..a92af48 100644
--- a/fabtools/openvz/container.py
+++ b/fabtools/openvz/container.py
@@ -1,12 +1,13 @@
"""
-Fabric tools for managing OpenVZ containers
+OpenVZ containers
+=================
"""
from fabtools import openvz as vz
class Container(object):
"""
- Object-oriented interface to OpenVZ containers
+ Object-oriented interface to OpenVZ containers.
"""
def __init__(self, ctid):
@@ -19,31 +20,81 @@ class Container(object):
return None
def create(self, **kwargs):
+ """
+ Create the container.
+
+ Extra args are passed to :py:func:`fabtools.openvz.create`.
+ """
return vz.create(self.ctid, **kwargs)
def destroy(self):
+ """
+ Destroy the container.
+ """
return vz.destroy(self.ctid)
def set(self, **kwargs):
+ """
+ Set container parameters.
+
+ Extra args are passed to :py:func:`fabtools.openvz.set`.
+ """
return vz.set(self.ctid, **kwargs)
def start(self, **kwargs):
+ """
+ Start the container.
+
+ Extra args are passed to :py:func:`fabtools.openvz.start`.
+ """
return vz.start(self.ctid, **kwargs)
def stop(self, **kwargs):
+ """
+ Stop the container.
+
+ Extra args are passed to :py:func:`fabtools.openvz.stop`.
+ """
return vz.stop(self.ctid, **kwargs)
def restart(self, **kwargs):
+ """
+ Restart the container.
+
+ Extra args are passed to :py:func:`fabtools.openvz.restart`.
+ """
return vz.restart(self.ctid, **kwargs)
def status(self):
+ """
+ Get the container's status.
+ """
return vz.status(self.ctid)
def running(self):
+ """
+ Check if the container is running.
+ """
return vz.running(self.ctid)
def exists(self):
+ """
+ Check if the container exists.
+ """
return vz.exists(self.ctid)
def exec2(self, command):
+ """
+ Run a command inside the container.
+
+ ::
+
+ from fabtools.require.openvz import container
+
+ with container('foo') as ct:
+ res = ct.exec2('hostname')
+
+ .. warning:: the command will be run as **root**.
+
+ """
return vz.exec2(self.ctid, command)
diff --git a/fabtools/openvz/contextmanager.py b/fabtools/openvz/contextmanager.py
index 3b31cd2..98b3108 100644
--- a/fabtools/openvz/contextmanager.py
+++ b/fabtools/openvz/contextmanager.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing OpenVZ containers
+OpenVZ containers
+=================
"""
from __future__ import with_statement
@@ -34,11 +35,26 @@ import fabric.sftp
@contextmanager
def guest(name_or_ctid):
"""
- Context manager to run commands inside a guest container
+ Context manager to run commands inside a guest container.
- Warning: commands executed with run() will be run as root
- inside the container. Use sudo(command, user='foo') to run
- them as an unpriviledged user.
+ Supported basic operations are: `run`_, `sudo`_ and `put`_.
+
+ .. warning:: commands executed with ``run()`` will be run as
+ **root** inside the container.
+ Use ``sudo(command, user='foo')`` to run them as
+ an unpriviledged user.
+ Example::
+
+ from fabtools.openvz import guest
+
+ with guest('foo'):
+ run('hostname')
+ sudo('whoami', user='alice')
+ put('files/hello.txt')
+
+ .. _run: http://docs.fabfile.org/en/1.4.3/api/core/operations.html#fabric.operations.run
+ .. _sudo: http://docs.fabfile.org/en/1.4.3/api/core/operations.html#fabric.operations.sudo
+ .. _put: http://docs.fabfile.org/en/1.4.3/api/core/operations.html#fabric.operations.put
"""
# Monkey patch fabric operations
diff --git a/fabtools/openvz/operations.py b/fabtools/openvz/operations.py
index 743718c..2bfb1d4 100644
--- a/fabtools/openvz/operations.py
+++ b/fabtools/openvz/operations.py
@@ -1,7 +1,6 @@
"""
-Fabric tools for managing OpenVZ containers
-
-The remote host needs a patched kernel with OpenVZ support
+OpenVZ containers
+=================
"""
from __future__ import with_statement
@@ -11,7 +10,7 @@ from fabric.api import *
def create(ctid, ostemplate=None, config=None, private=None,
root=None, ipadd=None, hostname=None, **kwargs):
"""
- Create OpenVZ container
+ Create an OpenVZ container.
"""
return _vzctl('create', ctid, ostemplate=ostemplate, config=config,
private=private, root=root, ipadd=ipadd, hostname=hostname, **kwargs)
@@ -19,44 +18,47 @@ def create(ctid, ostemplate=None, config=None, private=None,
def destroy(ctid_or_name):
"""
- Destroy OpenVZ container
+ Destroy the container.
"""
return _vzctl('destroy', ctid_or_name)
def set(ctid_or_name, save=True, **kwargs):
"""
- Set OpenVZ container parameters
+ Set container parameters.
"""
return _vzctl('set', ctid_or_name, save=save, **kwargs)
def start(ctid_or_name, wait=False, force=False, **kwargs):
"""
- Start OpenVZ container
+ Start the container.
+
+ If *wait* is ``True``, wait until the container is up and running.
- Warning: wait=True is broken with vzctl 3.0.24 on Debian 6.0 (squeeze)
+ .. warning:: ``wait=True`` is broken with vzctl 3.0.24
+ on Debian 6.0 (*squeeze*)
"""
return _vzctl('start', ctid_or_name, wait=wait, force=force, **kwargs)
def stop(ctid_or_name, fast=False, **kwargs):
"""
- Stop OpenVZ container
+ Stop the container.
"""
return _vzctl('stop', ctid_or_name, fast=fast, **kwargs)
def restart(ctid_or_name, wait=True, force=False, fast=False, **kwargs):
"""
- Restart OpenVZ container
+ Restart the container.
"""
return _vzctl('restart', ctid_or_name, wait=wait, force=force, fast=fast, **kwargs)
def status(ctid_or_name):
"""
- Show status of OpenVZ container
+ Get the status of the container.
"""
with settings(warn_only=True):
return _vzctl('status', ctid_or_name)
@@ -64,14 +66,14 @@ def status(ctid_or_name):
def running(ctid_or_name):
"""
- Is the container running?
+ Check if the container is running.
"""
return status(ctid_or_name).split(' ')[4] == 'running'
def exists(ctid_or_name):
"""
- Does the container exist?
+ Check if the container exists.
"""
with settings(hide('running', 'stdout', 'warnings'), warn_only=True):
return status(ctid_or_name).succeeded
@@ -79,7 +81,16 @@ def exists(ctid_or_name):
def exec2(ctid_or_name, command):
"""
- Run command inside OpenVZ container
+ Run a command inside the container.
+
+ ::
+
+ import fabtools
+
+ res = fabtools.openvz.exec2('foo', 'hostname')
+
+ .. warning:: the command will be run as **root**.
+
"""
return sudo("vzctl exec2 %s '%s'" % (ctid_or_name, command))
@@ -105,7 +116,24 @@ def _expand_args(**kwargs):
def download_template(name=None, url=None):
"""
- Download an OpenVZ template
+ Download an OpenVZ template.
+
+ Example::
+
+ from fabtools.openvz import download_template
+
+ # Use custom OS template
+ download_template(url='http://example.com/templates/mybox.tar.gz')
+
+ If no *url* is provided, the OS template will be downloaded from the
+ `download.openvz.org <http://download.openvz.org/template/precreated/>`_
+ repository::
+
+ from fabtools.openvz import download_template
+
+ # Use OS template from http://download.openvz.org/template/precreated/
+ download_template('debian-6.0-x86_64')
+
"""
if url is None:
url = 'http://download.openvz.org/template/precreated/%s.tar.gz' % name
@@ -116,7 +144,7 @@ def download_template(name=None, url=None):
def list_ctids():
"""
- Get the list of currently used CTIDs
+ Get the list of currently used CTIDs.
"""
with settings(hide('running', 'stdout')):
res = sudo('vzlist -a -1')
@@ -125,7 +153,7 @@ def list_ctids():
def get_available_ctid():
"""
- Get an available CTID
+ Get an available CTID.
"""
current_ctids = list_ctids()
if current_ctids:
diff --git a/fabtools/postgres.py b/fabtools/postgres.py
index febd445..05f3a75 100644
--- a/fabtools/postgres.py
+++ b/fabtools/postgres.py
@@ -1,5 +1,9 @@
"""
-Fabric tools for managing PostgreSQL users and databases
+PostgreSQL users and databases
+==============================
+
+This module provides tools for creating PostgreSQL users and databases.
+
"""
from __future__ import with_statement
@@ -15,7 +19,7 @@ def _run_as_pg(command):
def user_exists(name):
"""
- Check if a PostgreSQL user exists
+ Check if a PostgreSQL user exists.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = _run_as_pg('''psql -t -A -c "SELECT COUNT(*) FROM pg_user WHERE usename = '%(name)s';"''' % locals())
@@ -24,14 +28,23 @@ def user_exists(name):
def create_user(name, password):
"""
- Create a PostgreSQL user
+ Create a PostgreSQL user.
+
+ Example::
+
+ import fabtools
+
+ # Create DB user if it does not exist
+ if not fabtools.postgres.user_exists('dbuser'):
+ fabtools.postgres.create_user('dbuser', password='somerandomstring')
+
"""
_run_as_pg('''psql -c "CREATE USER %(name)s WITH PASSWORD '%(password)s';"''' % locals())
def database_exists(name):
"""
- Check if a PostgreSQL database exists
+ Check if a PostgreSQL database exists.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
return _run_as_pg('''psql -d %(name)s -c ""''' % locals()).succeeded
@@ -39,7 +52,16 @@ def database_exists(name):
def create_database(name, owner, template='template0', encoding='UTF8', locale='en_US.UTF-8'):
"""
- Create a PostgreSQL database
+ Create a PostgreSQL database.
+
+ Example::
+
+ import fabtools
+
+ # Create DB if it does not exist
+ if not fabtools.postgres.database_exists('myapp'):
+ fabtools.postgres.create_database('myapp', owner='dbuser')
+
"""
_run_as_pg('''createdb --owner %(owner)s --template %(template)s --encoding=%(encoding)s\
--lc-ctype=%(locale)s --lc-collate=%(locale)s %(name)s''' % locals())
diff --git a/fabtools/python.py b/fabtools/python.py
index 88e874c..aa6b92a 100644
--- a/fabtools/python.py
+++ b/fabtools/python.py
@@ -1,5 +1,13 @@
"""
-Fabric tools for managing Python packages using pip
+Python environments and packages
+================================
+
+This module includes tools for using `virtual environments`_
+and installing packages using `pip`_.
+
+.. _virtual environments: http://www.virtualenv.org/
+.. _pip: http://www.pip-installer.org/
+
"""
from __future__ import with_statement
@@ -14,7 +22,9 @@ from fabric.utils import puts
def is_pip_installed(version=None):
"""
- Check if pip is installed
+ Check if `pip`_ is installed.
+
+ .. _pip: http://www.pip-installer.org/
"""
with settings(hide('running', 'warnings', 'stderr', 'stdout'), warn_only=True):
res = run('pip --version')
@@ -33,7 +43,17 @@ def is_pip_installed(version=None):
def install_pip():
"""
- Install pip
+ Install the latest version of `pip`_.
+
+ .. _pip: http://www.pip-installer.org/
+
+ ::
+
+ import fabtools
+
+ if not fabtools.python.is_pip_installed():
+ fabtools.python.install_pip()
+
"""
with cd('/tmp'):
run('curl --silent -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py')
@@ -42,7 +62,7 @@ def install_pip():
def is_installed(package):
"""
- Check if a Python package is installed
+ Check if a Python package is installed.
"""
options = []
options = ' '.join(options)
@@ -54,7 +74,20 @@ def is_installed(package):
def install(packages, upgrade=False, use_mirrors=True, use_sudo=False, user=None, download_cache=None):
"""
- Install Python packages
+ Install Python package(s) using `pip`_.
+
+ .. _pip: http://www.pip-installer.org/
+
+ Examples::
+
+ import fabtools
+
+ # Install a single package
+ fabtools.python.install('package', use_sudo=True)
+
+ # Install a list of packages
+ fabtools.python.install(['pkg1', 'pkg2'], use_sudo=True)
+
"""
if not isinstance(packages, basestring):
packages = ' '.join(packages)
@@ -75,7 +108,15 @@ def install(packages, upgrade=False, use_mirrors=True, use_sudo=False, user=None
def install_requirements(filename, upgrade=False, use_mirrors=True, use_sudo=False, user=None, download_cache=None):
"""
- Install Python packages from a pip requirements file
+ Install Python packages from a pip `requirements file`_.
+
+ ::
+
+ import fabtools
+
+ fabtools.python.install_requirements('project/requirements.txt')
+
+ .. _requirements file: http://www.pip-installer.org/en/latest/requirements.html
"""
options = []
if use_mirrors:
@@ -95,7 +136,17 @@ def install_requirements(filename, upgrade=False, use_mirrors=True, use_sudo=Fal
@contextmanager
def virtualenv(directory, local=False):
"""
- Context manager to activate a Python virtualenv
+ Context manager to activate an existing Python `virtual environment`_.
+
+ ::
+
+ from fabric.api import run
+ from fabtools.python import virtualenv
+
+ with virtualenv('/path/to/virtualenv'):
+ run('python -V')
+
+ .. _virtual environment: http://www.virtualenv.org/
"""
join = os.path.join if local else posixpath.join
with prefix('. "%s"' % join(directory, 'bin', 'activate')):
diff --git a/fabtools/python_distribute.py b/fabtools/python_distribute.py
index 0293e9c..f5aedb0 100644
--- a/fabtools/python_distribute.py
+++ b/fabtools/python_distribute.py
@@ -1,5 +1,12 @@
"""
-Fabric tools for managing Python packages using distribute
+Python packages
+===============
+
+This module provides tools for installing Python packages using
+the ``easy_install`` command provided by `distribute`_.
+
+.. _distribute: http://packages.python.org/distribute/
+
"""
from __future__ import with_statement
@@ -8,7 +15,9 @@ from fabric.api import *
def is_distribute_installed():
"""
- Check if distribute is installed
+ Check if `distribute`_ is installed.
+
+ .. _distribute: http://packages.python.org/distribute/
"""
with settings(hide('running', 'warnings', 'stderr', 'stdout'), warn_only=True):
res = run('easy_install --version')
@@ -17,7 +26,17 @@ def is_distribute_installed():
def install_distribute():
"""
- Install distribute
+ Install the latest version of `distribute`_.
+
+ .. _distribute: http://packages.python.org/distribute/
+
+ ::
+
+ from fabtools.python_distribute import *
+
+ if not is_distribute_installed():
+ install_distribute()
+
"""
with cd("/tmp"):
run("curl --silent -O http://python-distribute.org/distribute_setup.py")
@@ -26,7 +45,22 @@ def install_distribute():
def install(packages, upgrade=False, use_sudo=False):
"""
- Install Python packages with distribute
+ Install Python packages with ``easy_install``.
+
+ Examples::
+
+ import fabtools
+
+ # Install a single package
+ fabtools.python_distribute.install('package', use_sudo=True)
+
+ # Install a list of packages
+ fabtools.python_distribute.install(['pkg1', 'pkg2'], use_sudo=True)
+
+ .. note:: most of the time, you'll want to use
+ :py:func:`fabtools.python.install()` instead,
+ which uses ``pip`` to install packages.
+
"""
func = use_sudo and sudo or run
if not isinstance(packages, basestring):
diff --git a/fabtools/require/deb.py b/fabtools/require/deb.py
index 933aaf4..0dfde24 100644
--- a/fabtools/require/deb.py
+++ b/fabtools/require/deb.py
@@ -1,5 +1,10 @@
"""
-Idempotent API for managing Debian/Ubuntu packages
+Debian packages
+===============
+
+This module provides high-level tools for managing Debian/Ubuntu packages
+and repositories.
+
"""
from __future__ import with_statement
@@ -12,7 +17,15 @@ import fabtools.require
def source(name, uri, distribution, *components):
"""
- Require a package source
+ Require a package source.
+
+ ::
+
+ from fabtools import require
+
+ # Official MongoDB packages
+ require.deb.source('mongodb', 'http://downloads-distro.mongodb.org/repo/ubuntu-upstart', 'dist', '10gen')
+
"""
path = '/etc/apt/sources.list.d/%(name)s.list' % locals()
components = ' '.join(components)
@@ -26,7 +39,16 @@ def source(name, uri, distribution, *components):
def ppa(name):
"""
- Require a PPA
+ Require a `PPA`_ package source.
+
+ ::
+
+ from fabtools import require
+
+ # Node.js packages by Chris Lea
+ require.deb.ppa('ppa:chris-lea/node.js')
+
+ .. _PPA: https://help.launchpad.net/Packaging/PPA
"""
assert name.startswith('ppa:')
user, repo = name[4:].split('/', 2)
@@ -41,7 +63,13 @@ def ppa(name):
def package(pkg_name, update=False):
"""
- Require a deb package
+ Require a deb package to be installed.
+
+ ::
+
+ from fabtools import require
+
+ require.deb.package('foo')
"""
if not is_installed(pkg_name):
install(pkg_name, update)
@@ -49,7 +77,17 @@ def package(pkg_name, update=False):
def packages(pkg_list, update=False):
"""
- Require several deb packages
+ Require several deb packages to be installed.
+
+ ::
+
+ from fabtools import require
+
+ require.deb.packages([
+ 'foo',
+ 'bar',
+ 'baz',
+ ])
"""
pkg_list = [pkg for pkg in pkg_list if not is_installed(pkg)]
if pkg_list:
@@ -58,7 +96,13 @@ def packages(pkg_list, update=False):
def nopackage(pkg_name):
"""
- Require a deb package to be absent
+ Require a deb package to be uninstalled.
+
+ ::
+
+ from fabtools import require
+
+ require.deb.nopackage('apache2')
"""
if is_installed(pkg_name):
uninstall(pkg_name)
@@ -66,7 +110,17 @@ def nopackage(pkg_name):
def nopackages(pkg_list):
"""
- Require several deb packages to be absent
+ Require several deb packages to be uninstalled.
+
+ ::
+
+ from fabtools import require
+
+ require.deb.nopackages([
+ 'perl',
+ 'php5',
+ 'ruby',
+ ])
"""
pkg_list = [pkg for pkg in pkg_list if is_installed(pkg)]
if pkg_list:
diff --git a/fabtools/require/files.py b/fabtools/require/files.py
index 23bace3..637c529 100644
--- a/fabtools/require/files.py
+++ b/fabtools/require/files.py
@@ -1,5 +1,10 @@
"""
-Idempotent API for managing files and directories
+Files and directories
+=====================
+
+This module provides high-level tools for managing files and
+directories.
+
"""
from __future__ import with_statement
@@ -17,7 +22,17 @@ BLOCKSIZE = 2 ** 20 # 1MB
def directory(path, use_sudo=False, owner='', group='', mode=''):
"""
- Require a directory
+ Require a directory to exist.
+
+ ::
+
+ from fabtools import require
+
+ require.directory('/tmp/mydir', owner='alice')
+
+ .. note:: this function can be accessed directly from the
+ ``fabtools.require`` module for convenience.
+
"""
func = use_sudo and sudo or run
if not is_dir(path):
@@ -31,17 +46,39 @@ def directory(path, use_sudo=False, owner='', group='', mode=''):
def file(path=None, contents=None, source=None, url=None, md5=None,
use_sudo=False, owner=None, group='', mode=None, verify_remote=True):
"""
- Require a file
+ Require a file to exist and have specific contents and properties.
You can provide either:
- - contents: the required contents of the file
- - source: the filename of a local file to upload
- - url: the address of a file to download (path is optional)
-
- If verify_remote is True (the default), then an MD5 comparison will be used
- to check whether the remote file is the same as the source. If this is
- False, the file will be assumed to be the same if it is present. This is
- useful for very large files, where generating an MD5 sum may take a while.
+
+ - *contents*: the required contents of the file::
+
+ from fabtools import require
+
+ require.file('/tmp/hello.txt', contents='Hello, world')
+
+ - *source*: the local path of a file to upload::
+
+ from fabtools import require
+
+ require.file('/tmp/hello.txt', source='files/hello.txt')
+
+ - *url*: the URL of a file to download (*path* is then optional)::
+
+ from fabric.api import cd
+ from fabtools import require
+
+ with cd('tmp'):
+ require.file(url='http://example.com/files/hello.txt')
+
+ If *verify_remote* is ``True`` (the default), then an MD5 comparison
+ will be used to check whether the remote file is the same as the
+ source. If this is ``False``, the file will be assumed to be the
+ same if it is present. This is useful for very large files, where
+ generating an MD5 sum may take a while.
+
+ .. note:: this function can be accessed directly from the
+ ``fabtools.require`` module for convenience.
+
"""
func = use_sudo and sudo or run
@@ -109,7 +146,7 @@ def file(path=None, contents=None, source=None, url=None, md5=None,
def template_file(path=None, template_contents=None, template_source=None, context=None, **kwargs):
"""
- Require a file whose contents is defined by a template
+ Require a file whose contents is defined by a template.
"""
if template_contents is None:
with open(template_source) as template_file:
diff --git a/fabtools/require/mysql.py b/fabtools/require/mysql.py
index 3e0e978..bceaca8 100644
--- a/fabtools/require/mysql.py
+++ b/fabtools/require/mysql.py
@@ -1,5 +1,10 @@
"""
-Idempotent API for managing MySQL users and databases
+MySQL
+=====
+
+This module provides high-level tools for installing a MySQL server
+and creating MySQL users and databases.
+
"""
from __future__ import with_statement
@@ -11,7 +16,14 @@ from fabtools.require.service import started
def server(version=None, password=None):
"""
- Require a MySQL server
+ Require a MySQL server to be installed and running.
+
+ Example::
+
+ from fabtools import require
+
+ require.mysql.server(password='s3cr3t')
+
"""
if version:
pkg_name = 'mysql-server-%s' % version
@@ -35,7 +47,16 @@ def server(version=None, password=None):
def user(name, password, **kwargs):
"""
- Require a MySQL user
+ Require a MySQL user.
+
+ Extra arguments will be passed to :py:func:`fabtools.mysql.create_user`.
+
+ Example::
+
+ from fabtools import require
+
+ require.mysql.user('dbuser', 'somerandomstring')
+
"""
if not user_exists(name, **kwargs):
create_user(name, password, **kwargs)
@@ -43,7 +64,16 @@ def user(name, password, **kwargs):
def database(name, **kwargs):
"""
- Require a MySQL database
+ Require a MySQL database.
+
+ Extra arguments will be passed to :py:func:`fabtools.mysql.create_database`.
+
+ Example::
+
+ from fabtools import require
+
+ require.mysql.database('myapp', owner='dbuser')
+
"""
if not database_exists(name, **kwargs):
create_database(name, **kwargs)
diff --git a/fabtools/require/nginx.py b/fabtools/require/nginx.py
index 52ab307..cc24c62 100644
--- a/fabtools/require/nginx.py
+++ b/fabtools/require/nginx.py
@@ -1,5 +1,12 @@
"""
-Idempotent API for managing nginx sites
+Nginx
+=====
+
+This module provides high-level tools for installing the `nginx`_
+web server and managing the configuration of web sites.
+
+.. nginx: http://nginx.org/
+
"""
from __future__ import with_statement
@@ -13,7 +20,13 @@ from fabtools.require.service import started
def server():
"""
- Require an nginx server
+ Require nginx server to be installed and running.
+
+ ::
+
+ from fabtools import require
+
+ require.nginx.server()
"""
package('nginx')
started('nginx')
@@ -21,7 +34,31 @@ def server():
def site(server_name, template_contents=None, template_source=None, enabled=True, check_config=True, **kwargs):
"""
- Require an nginx site
+ Require an nginx site.
+
+ You must provide a template for the site configuration, either as a
+ string (*template_contents*) or as the path to a local template
+ file (*template_source*).
+
+ ::
+
+ from fabtools import require
+
+ CONFIG_TPL = '''
+ server {
+ listen %(port)d;
+ server_name %(server_name)s %(server_alias)s;
+ root %(docroot)s;
+ access_log /var/log/nginx/%(server_name)s.log;
+ }'''
+
+ require.nginx.site('example.com', template_contents=CONFIG_TPL,
+ port=80,
+ server_alias='www.example.com',
+ docroot='/var/www/mysite',
+ )
+
+ .. seealso:: :py:func:`fabtools.require.files.template_file`
"""
server()
@@ -79,6 +116,25 @@ server {
def proxied_site(server_name, enabled=True, **kwargs):
"""
- Require an nginx site for a proxied app
+ Require an nginx site for a proxied app.
+
+ This uses a predefined configuration template suitable for proxying
+ requests to a backend application server.
+
+ Required keyword arguments are:
+
+ - *port*: the port nginx should listen on
+ - *proxy_url*: URL of backend application server
+ - *docroot*: path to static files
+
+ ::
+
+ from fabtools import require
+
+ require.nginx.proxied_site('example.com',
+ port=80,
+ proxy_url='http://127.0.0.1:8080/',
+ docroot='/path/to/myapp/static',
+ )
"""
site(server_name, template_contents=PROXIED_SITE_TEMPLATE, enabled=enabled, **kwargs)
diff --git a/fabtools/require/openvz.py b/fabtools/require/openvz.py
index ab7d121..bf586c7 100644
--- a/fabtools/require/openvz.py
+++ b/fabtools/require/openvz.py
@@ -1,5 +1,14 @@
"""
-Idempotent API for managing OpenVZ containers
+OpenVZ containers
+=================
+
+This module provides high-level tools for managing OpenVZ_ templates
+and containers.
+
+.. _OpenVZ: http://openvz.org/
+
+.. warning:: The remote host needs a patched kernel with OpenVZ support.
+
"""
import os.path
@@ -10,7 +19,26 @@ from fabtools.openvz.container import Container
def template(name=None, url=None):
"""
- Require an OpenVZ template
+ Require an OpenVZ OS template.
+
+ If the OS template is not installed yet, it will be downloaded from
+ *url* using :py:func:`~fabtools.openvz.download_template()`::
+
+ from fabtools import require
+
+ # Use custom OS template
+ require.openvz.template(url='http://example.com/templates/mybox.tar.gz')
+
+ If no *url* is provided, :py:func:`~fabtools.openvz.download_template()`
+ will attempt to download the OS template from the
+ `download.openvz.org <http://download.openvz.org/template/precreated/>`_
+ repository::
+
+ from fabtools import require
+
+ # Use OS template from http://download.openvz.org/template/precreated/
+ require.openvz.template('debian-6.0-x86_64')
+
"""
if name is not None:
filename = '%s.tar.gz' % name
@@ -23,7 +51,37 @@ def template(name=None, url=None):
def container(name, ostemplate, **kwargs):
"""
- Require an OpenVZ container
+ Require an OpenVZ container.
+
+ If it does not exist, the container will be created using the
+ specified OS template
+ (see :py:func:`fabtools.require.openvz.template()`).
+
+ Extra args will be passed to :py:func:`fabtools.openvz.create()`::
+
+ from fabtools import require
+
+ require.openvz.container('foo', 'debian', ipadd='1.2.3.4')
+
+ This function returns a :py:class:`fabtools.openvz.Container`
+ object, that can be used to perform further operations::
+
+ from fabtools.require.openvz import container
+
+ ct = container('foo', 'debian')
+ ct.set('ipadd', '1.2.3.4')
+ ct.start()
+ ct.exec2('hostname')
+
+ This function can also be used as a context manager::
+
+ from fabtools.require.openvz import container
+
+ with container('foo', 'debian') as ct:
+ ct.set('ipadd', '1.2.3.4')
+ ct.start()
+ ct.exec2('hostname')
+
"""
if not openvz.exists(name):
ctid = openvz.get_available_ctid()
diff --git a/fabtools/require/postfix.py b/fabtools/require/postfix.py
index f5929ea..e7af201 100644
--- a/fabtools/require/postfix.py
+++ b/fabtools/require/postfix.py
@@ -1,5 +1,11 @@
"""
-Idempotent API for managing Postfix email server
+Postfix
+=======
+
+This module provides high-level tools for managing the Postfix_ email server.
+
+.. _Postfix: http://www.postfix.org/
+
"""
from fabric.api import *
from fabtools.deb import is_installed, preseed_package, install
@@ -8,7 +14,17 @@ from fabtools.require.service import started
def server(mailname):
"""
- Require a Postfix email server
+ Require a Postfix email server.
+
+ This makes sure that Postfix is installed and started.
+
+ ::
+
+ from fabtools import require
+
+ # Handle incoming email for our domain
+ require.postfix.server('example.com')
+
"""
# Ensure the package is installed
diff --git a/fabtools/require/postgres.py b/fabtools/require/postgres.py
index fa3d97d..6a0be8e 100644
--- a/fabtools/require/postgres.py
+++ b/fabtools/require/postgres.py
@@ -1,5 +1,6 @@
"""
-Idempotent API for managing PostgreSQL users and databases
+PostgreSQL users and databases
+==============================
"""
from __future__ import with_statement
@@ -13,7 +14,14 @@ from fabtools.require.service import started
def server(version=None):
"""
- Require a PostgreSQL server
+ Require a PostgreSQL server to be installed and running.
+
+ ::
+
+ from fabtools import require
+
+ require.postgres.server()
+
"""
if version:
pkg_name = 'postgresql-%s' % version
@@ -35,7 +43,14 @@ def server(version=None):
def user(name, password):
"""
- Require a PostgreSQL user
+ Require a PostgreSQL user.
+
+ ::
+
+ from fabtools import require
+
+ require.postgres.user('dbuser', password='somerandomstring')
+
"""
if not user_exists(name):
create_user(name, password)
@@ -44,7 +59,14 @@ def user(name, password):
def database(name, owner, template='template0', encoding='UTF8',
locale='en_US.UTF-8'):
"""
- Require a PostgreSQL database
+ Require a PostgreSQL database.
+
+ ::
+
+ from fabtools import require
+
+ require.postgres.database('myapp', owner='dbuser')
+
"""
if not database_exists(name):
create_database(name, owner, template=template, encoding=encoding,
diff --git a/fabtools/require/python.py b/fabtools/require/python.py
index 41ea0bf..cc47247 100644
--- a/fabtools/require/python.py
+++ b/fabtools/require/python.py
@@ -1,5 +1,13 @@
"""
-Idempotent API for managing Python packages
+Python environments and packages
+================================
+
+This module includes tools for using `virtual environments`_
+and installing packages using `pip`_.
+
+.. _virtual environments: http://www.virtualenv.org/
+.. _pip: http://www.pip-installer.org/
+
"""
import posixpath
@@ -11,7 +19,9 @@ from fabtools.require import deb
def distribute():
"""
- Require distribute
+ Require `distribute`_ to be installed.
+
+ .. _distribute: http://packages.python.org/distribute/
"""
deb.packages([
'curl',
@@ -23,7 +33,7 @@ def distribute():
def pip(version=None):
"""
- Require pip
+ Require `pip`_ to be installed.
"""
distribute()
if not is_pip_installed(version):
@@ -32,7 +42,24 @@ def pip(version=None):
def package(pkg_name, url=None, **kwargs):
"""
- Require a Python package
+ Require a Python package.
+
+ If the package is not installed, it will be installed
+ using the `pip installer`_.
+
+ ::
+
+ from fabtools.python import virtualenv
+ from fabtools import require
+
+ # Install package system-wide
+ require.python.package('foo', use_sudo=True)
+
+ # Install package in an existing virtual environment
+ with virtualenv('/path/to/venv'):
+ require.python.package('bar')
+
+ .. _pip installer: http://www.pip-installer.org/
"""
pip('1.1')
if not is_installed(pkg_name):
@@ -41,7 +68,7 @@ def package(pkg_name, url=None, **kwargs):
def packages(pkg_list, **kwargs):
"""
- Require several Python packages
+ Require several Python packages.
"""
pip('1.1')
pkg_list = [pkg for pkg in pkg_list if not is_installed(pkg)]
@@ -51,7 +78,9 @@ def packages(pkg_list, **kwargs):
def requirements(filename, **kwargs):
"""
- Require Python packages from a pip requirements file
+ Require Python packages from a pip `requirements file`_.
+
+ .. _requirements file: http://www.pip-installer.org/en/latest/requirements.html
"""
pip('1.1')
install_requirements(filename, **kwargs)
@@ -59,7 +88,15 @@ def requirements(filename, **kwargs):
def virtualenv(directory, system_site_packages=False, python=None, use_sudo=False, user=None):
"""
- Require a Python virtual environment
+ Require a Python `virtual environment`_.
+
+ ::
+
+ from fabtools import require
+
+ require.python.virtualenv('/path/to/venv')
+
+ .. _virtual environment: http://www.virtualenv.org/
"""
package('virtualenv', use_sudo=True)
if not is_file(posixpath.join(directory, 'bin', 'python')):
diff --git a/fabtools/require/redis.py b/fabtools/require/redis.py
index cc9041c..d57b042 100644
--- a/fabtools/require/redis.py
+++ b/fabtools/require/redis.py
@@ -1,5 +1,11 @@
"""
-Idempotent API for managing Redis instances
+Redis
+=====
+
+This module provides high-level tools for managing `Redis`_ instances.
+
+.. _Redis: http://redis.io/
+
"""
from __future__ import with_statement
@@ -20,7 +26,9 @@ BINARIES = [
def installed_from_source(version=VERSION):
"""
- Require Redis to be installed from source
+ Require Redis to be installed from source.
+
+ The compiled binaries will be installed in ``/opt/redis-{version}/``.
"""
from fabtools import require
@@ -50,9 +58,28 @@ def installed_from_source(version=VERSION):
def instance(name, version=VERSION, **kwargs):
"""
- Require a Redis instance to be running
+ Require a Redis instance to be running.
+
+ The instance will be managed using supervisord, as a process named
+ ``redis_{name}``, running as the ``redis`` user.
+
+ ::
+
+ from fabtools import require
+ from fabtools.supervisor import process_status
+
+ require.redis.installed_from_source()
+
+ require.redis.instance('db1', port='6379')
+ require.redis.instance('db2', port='6380')
+
+ print process_status('redis_db1')
+ print process_status('redis_db2')
+
+ .. seealso:: :ref:`supervisor_module` and
+ :ref:`require_supervisor_module`
+
- The instance will be managed using supervisord.
"""
from fabtools import require
diff --git a/fabtools/require/service.py b/fabtools/require/service.py
index b268972..75bcc15 100644
--- a/fabtools/require/service.py
+++ b/fabtools/require/service.py
@@ -1,21 +1,57 @@
"""
-Idempotent API for managing services
+System services
+===============
+
+This module provides high-level tools for managing system services.
+The underlying operations use the ``service`` command, allowing to
+support both `upstart`_ services and traditional SysV-style
+``/etc/init.d/`` scripts.
+
+.. _upstart: http://upstart.ubuntu.com/
+
"""
from fabric.api import *
from fabtools.service import *
def started(service):
+ """
+ Require a service to be started.
+
+ ::
+
+ from fabtools import require
+
+ require.service.started('foo')
+ """
if not is_running(service):
start(service)
def stopped(service):
+ """
+ Require a service to be stopped.
+
+ ::
+
+ from fabtools import require
+
+ require.service.stopped('foo')
+ """
if is_running(service):
stop(service)
def restarted(service):
+ """
+ Require a service to be restarted.
+
+ ::
+
+ from fabtools import require
+
+ require.service.restarted('foo')
+ """
if is_running(service):
restart(service)
else:
diff --git a/fabtools/require/shorewall.py b/fabtools/require/shorewall.py
index 51830d8..104548d 100644
--- a/fabtools/require/shorewall.py
+++ b/fabtools/require/shorewall.py
@@ -1,5 +1,6 @@
"""
-Idempotent API for managing shorewall
+Shorewall firewall
+==================
"""
from __future__ import with_statement
@@ -245,7 +246,25 @@ CONFIG_FILES = [
def firewall(zones=None, interfaces=None, policy=None, rules=None,
routestopped=None, masq=None):
"""
- Require a firewall
+ Ensure that a firewall is configured.
+
+ Example::
+
+ from fabtools.shorewall import *
+ from fabtools import require
+
+ # We need a firewall with some custom rules
+ require.shorewall.firewall(
+ rules=[
+ Ping(),
+ SSH(),
+ HTTP(),
+ HTTPS(),
+ SMTP(),
+ rule(port=1234, source=hosts(['example.com'])),
+ ]
+ )
+
"""
package('shorewall')
@@ -268,7 +287,7 @@ def firewall(zones=None, interfaces=None, policy=None, rules=None,
def started():
"""
- Ensure the firewall is started
+ Ensure that the firewall is started.
"""
if not is_started():
start('shorewall')
@@ -276,7 +295,7 @@ def started():
def stopped():
"""
- Ensure the firewall is stopped
+ Ensure that the firewall is stopped.
"""
if not is_stopped():
stop('shorewall')
diff --git a/fabtools/require/supervisor.py b/fabtools/require/supervisor.py
index 07b6553..fc1bb91 100644
--- a/fabtools/require/supervisor.py
+++ b/fabtools/require/supervisor.py
@@ -1,5 +1,12 @@
"""
-Idempotent API for managing supervisor processes
+Supervisor processes
+====================
+
+This module provides high-level tools for managing long-running
+processes using `supervisord`_.
+
+.. _supervisord: http://supervisord.org/
+
"""
from __future__ import with_statement
@@ -9,7 +16,37 @@ from fabtools.supervisor import update_config, process_status, start_process
def process(name, **kwargs):
"""
- Require a supervisor process
+ Require a supervisor process to be running.
+
+ Keyword arguments will be used to build the program configuration
+ file. Some useful arguments are:
+
+ - ``command``: complete command including arguments (**required**)
+ - ``directory``: absolute path to the working directory
+ - ``user``: run the process as this user
+ - ``stdout_logfile``: absolute path to the log file
+
+ You should refer to the `supervisord documentation`_ for the
+ complete list of allowed arguments.
+
+ .. note:: the default values for the following arguments differs from
+ the supervisord defaults:
+
+ - ``autorestart``: defaults to ``true``
+ - ``redirect_stderr``: defaults to ``true``
+
+ Example::
+
+ from fabtools import require
+
+ require.supervisor.process('myapp',
+ command='/path/to/venv/bin/myapp --config production.ini --someflag',
+ directory='/path/to/working/dir',
+ user='alice',
+ stdout_logfile='/path/to/logs/myapp.log',
+ )
+
+ .. _supervisord documentation: http://supervisord.org/configuration.html#program-x-section-values
"""
from fabtools import require
require.deb.package('supervisor')
diff --git a/fabtools/require/system.py b/fabtools/require/system.py
index d798fbd..bb1a651 100644
--- a/fabtools/require/system.py
+++ b/fabtools/require/system.py
@@ -1,5 +1,6 @@
"""
-Idempotent API for managing system settings
+System settings
+===============
"""
from __future__ import with_statement
@@ -16,7 +17,7 @@ from fabtools.system import (
def sysctl(key, value, persist=True):
"""
- Require a kernel parameter to have a specific value
+ Require a kernel parameter to have a specific value.
"""
if get_sysctl(key) != value:
set_sysctl(key, value)
@@ -34,7 +35,7 @@ def sysctl(key, value, persist=True):
def hostname(name):
"""
- Require the hostname to have a specific value
+ Require the hostname to have a specific value.
"""
if get_hostname() != name:
set_hostname(name)
@@ -42,7 +43,7 @@ def hostname(name):
def locales(names):
"""
- Require the list of locales to be available
+ Require the list of locales to be available.
"""
config_file = '/var/lib/locales/supported.d/local'
@@ -66,14 +67,14 @@ def locales(names):
def locale(name):
"""
- Require the locale to be available
+ Require the locale to be available.
"""
locales([name])
def default_locale(name):
"""
- Require the locale to be the default
+ Require the locale to be the default.
"""
from fabtools import require
diff --git a/fabtools/require/users.py b/fabtools/require/users.py
index 074ac27..6e1157c 100644
--- a/fabtools/require/users.py
+++ b/fabtools/require/users.py
@@ -1,5 +1,6 @@
"""
-Idempotent API for managing users
+System users
+============
"""
from fabtools.files import is_file
from fabtools.user import *
@@ -9,7 +10,18 @@ import fabtools.require
def user(name, home=None):
"""
- Require a user
+ Require a user.
+
+ ::
+
+ from fabtools import require
+
+ require.user('alice') # no home directory
+ require.user('bob', home='/home/bob')
+
+ .. note:: this function can be accessed directly from the
+ ``fabtools.require`` module for convenience.
+
"""
if not exists(name):
create(name, home=home)
@@ -19,7 +31,11 @@ def user(name, home=None):
def sudoer(username, hosts="ALL", operators="ALL", passwd=False, commands="ALL"):
"""
- Require sudo permissions for a given user
+ Require sudo permissions for a given user.
+
+ .. note:: this function can be accessed directly from the
+ ``fabtools.require`` module for convenience.
+
"""
tags = "PASSWD:" if passwd else "NOPASSWD:"
spec = "%(username)s %(hosts)s=(%(operators)s) %(tags)s %(commands)s" % locals()
diff --git a/fabtools/service.py b/fabtools/service.py
index 91a1235..faed919 100644
--- a/fabtools/service.py
+++ b/fabtools/service.py
@@ -1,8 +1,13 @@
"""
-Fabric tools for managing services
+System services
+===============
-This uses the ``service`` command, supporting both upstart services
+This module provides low-level tools for managing system services,
+using the ``service`` command. It supports both `upstart`_ services
and traditional SysV-style ``/etc/init.d/`` scripts.
+
+.. _upstart: http://upstart.ubuntu.com/
+
"""
from __future__ import with_statement
@@ -11,7 +16,14 @@ from fabric.api import *
def is_running(service):
"""
- Check if a service is running
+ Check if a service is running.
+
+ ::
+
+ import fabtools
+
+ if fabtools.service.is_running('foo'):
+ print "Service foo is running!"
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = sudo('service %(service)s status' % locals())
@@ -20,20 +32,46 @@ def is_running(service):
def start(service):
"""
- Start a service
+ Start a service.
+
+ ::
+
+ import fabtools
+
+ # Start service if it is not running
+ if not fabtools.service.is_running('foo'):
+ fabtools.service.start('foo')
"""
sudo('service %(service)s start' % locals())
def stop(service):
"""
- Stop a service
+ Stop a service.
+
+ ::
+
+ import fabtools
+
+ # Stop service if it is running
+ if fabtools.service.is_running('foo'):
+ fabtools.service.stop('foo')
"""
sudo('service %(service)s stop' % locals())
def restart(service):
"""
- Restart a service
+ Restart a service.
+
+ ::
+
+ import fabtools
+
+ # Start service, or restart it if it is already running
+ if fabtools.service.is_running('foo'):
+ fabtools.service.restart('foo')
+ else:
+ fabtools.service.start('foo')
"""
sudo('service %(service)s restart' % locals())
diff --git a/fabtools/shorewall.py b/fabtools/shorewall.py
index 16622f5..74bdcf3 100644
--- a/fabtools/shorewall.py
+++ b/fabtools/shorewall.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing shorewall
+Shorewall firewall
+==================
"""
from __future__ import with_statement
@@ -11,7 +12,7 @@ import re
def status():
"""
- Get the firewall status
+ Get the firewall status.
"""
with settings(hide('running', 'stdout', 'warnings'), warn_only=True):
res = sudo('shorewall status')
@@ -20,21 +21,21 @@ def status():
def is_started():
"""
- Returns True if shorewall is started
+ Check if the firewall is started.
"""
return status() == 'running'
def is_stopped():
"""
- Returns True if shorewall is stopped
+ Check if the firewall is stopped.
"""
return status() == 'stopped'
def hosts(hostnames, zone='net'):
"""
- Builds a host list suitable for use in a firewall rule
+ Builds a host list suitable for use in a firewall rule.
"""
addresses = [gethostbyname(name) for name in hostnames]
return "%s:%s" % (zone, ','.join(addresses))
@@ -42,7 +43,18 @@ def hosts(hostnames, zone='net'):
def rule(port, action='ACCEPT', source='net', dest='$FW', proto='tcp'):
"""
- Builds a firewall rule
+ Helper to build a firewall rule.
+
+ Examples::
+
+ from fabtools.shorewall import rule
+
+ # Rule to accept connections from example.com on port 1234
+ r1 = rule(port=1234, source=hosts(['example.com']))
+
+ # Rule to reject outgoing SMTP connections
+ r2 = rule(port=25, action='REJECT', source='$FW', dest='net')
+
"""
return {
'action': action,
@@ -55,34 +67,44 @@ def rule(port, action='ACCEPT', source='net', dest='$FW', proto='tcp'):
def Ping(**kwargs):
"""
- Build a firewall rule for ICMP pings
+ Helper to build a firewall rule for ICMP pings.
+
+ Extra args will be passed to :py:func:`~fabtools.shorewall.rule`.
"""
return rule(port=8, proto='icmp', **kwargs)
def SSH(port=22, **kwargs):
"""
- Build a firewall rule for SSH connections
+ Helper to build a firewall rule for SSH connections
+
+ Extra args will be passed to :py:func:`~fabtools.shorewall.rule`.
"""
return rule(port, **kwargs)
def HTTP(port=80, **kwargs):
"""
- Builds a firewall rule for HTTP connections
+ Helper to build a firewall rule for HTTP connections
+
+ Extra args will be passed to :py:func:`~fabtools.shorewall.rule`.
"""
return rule(port, **kwargs)
def HTTPS(port=443, **kwargs):
"""
- Builds a firewall rule for HTTPS connections
+ Helper to build a firewall rule for HTTPS connections
+
+ Extra args will be passed to :py:func:`~fabtools.shorewall.rule`.
"""
return rule(port, **kwargs)
def SMTP(port=25, **kwargs):
"""
- Builds a firewall rule for SMTP connections
+ Helper to build a firewall rule for SMTP connections
+
+ Extra args will be passed to :py:func:`~fabtools.shorewall.rule`.
"""
return rule(port, **kwargs)
diff --git a/fabtools/supervisor.py b/fabtools/supervisor.py
index bc13b2d..e7061a9 100644
--- a/fabtools/supervisor.py
+++ b/fabtools/supervisor.py
@@ -1,5 +1,12 @@
"""
-Fabric tools for managing supervisor processes
+Supervisor processes
+====================
+
+This module provides high-level tools for managing long-running
+processes using `supervisord`_.
+
+.. _supervisord: http://supervisord.org/
+
"""
from __future__ import with_statement
@@ -8,23 +15,24 @@ from fabric.api import *
def reload_config():
"""
- Reload supervisor configuration
+ Reload supervisor configuration.
"""
sudo("supervisorctl reload")
def update_config():
"""
- Reread and update supervisor job configurations. Less heavy-handed than
- a full reload, as it doesn't restart the backend supervisor process and
- all managed processes.
+ Reread and update supervisor job configurations.
+
+ Less heavy-handed than a full reload, as it doesn't restart the
+ backend supervisor process and all managed processes.
"""
sudo("supervisorctl update")
def process_status(name):
"""
- Get the status of a supervisor process
+ Get the status of a supervisor process.
"""
with settings(hide('running', 'stdout', 'stderr', 'warnings'), warn_only=True):
res = sudo("supervisorctl status %(name)s" % locals())
diff --git a/fabtools/system.py b/fabtools/system.py
index 9e233a5..0992c1f 100644
--- a/fabtools/system.py
+++ b/fabtools/system.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing system settings
+System settings
+===============
"""
from __future__ import with_statement
@@ -8,7 +9,7 @@ from fabric.api import *
def get_hostname():
"""
- Get the fully qualified hostname
+ Get the fully qualified hostname.
"""
with settings(hide('running', 'stdout')):
return run('hostname --fqdn')
@@ -16,7 +17,7 @@ def get_hostname():
def set_hostname(hostname, persist=True):
"""
- Set the hostname
+ Set the hostname.
"""
sudo('hostname %s' % hostname)
if persist:
@@ -25,7 +26,14 @@ def set_hostname(hostname, persist=True):
def get_sysctl(key):
"""
- Get a kernel parameter
+ Get a kernel parameter.
+
+ Example::
+
+ from fabtools.system import get_sysctl
+
+ print "Max number of open files:", get_sysctl('fs.file-max')
+
"""
with settings(hide('running', 'stdout')):
return sudo('/sbin/sysctl -n -e %(key)s' % locals())
@@ -33,16 +41,24 @@ def get_sysctl(key):
def set_sysctl(key, value):
"""
- Set a kernel parameter
+ Set a kernel parameter.
+
+ Example::
+
+ import fabtools
+
+ # Protect from SYN flooding attack
+ fabtools.system.set_sysctl('net.ipv4.tcp_syncookies', 1)
+
"""
sudo('/sbin/sysctl -n -e -w %(key)s=%(value)s' % locals())
def supported_locales():
"""
- Gets the list of supported locales
+ Gets the list of supported locales.
- Each locale is returned as a (locale, charset) tuple
+ Each locale is returned as a ``(locale, charset)`` tuple.
"""
with settings(hide('running', 'stdout')):
res = run('grep -v "^#" /usr/share/i18n/SUPPORTED')
diff --git a/fabtools/user.py b/fabtools/user.py
index 25d6ec4..de40148 100644
--- a/fabtools/user.py
+++ b/fabtools/user.py
@@ -1,5 +1,6 @@
"""
-Fabric tools for managing users
+Users
+=====
"""
from __future__ import with_statement
@@ -8,7 +9,7 @@ from fabric.api import *
def exists(name):
"""
- Check if user exists
+ Check if a user exists.
"""
with settings(hide('running', 'stdout', 'warnings'), warn_only=True):
return sudo('getent passwd %(name)s' % locals()).succeeded
@@ -16,7 +17,15 @@ def exists(name):
def create(name, home=None, shell=None, uid=None, gid=None, groups=None):
"""
- Create a new user
+ Create a new user.
+
+ Example::
+
+ import fabtools
+
+ if not fabtools.user.exists('alice'):
+ fabtools.user.create('alice', home='/home/alice')
+
"""
options = []
if gid:
diff --git a/fabtools/vagrant.py b/fabtools/vagrant.py
index db9d2d6..59d487f 100644
--- a/fabtools/vagrant.py
+++ b/fabtools/vagrant.py
@@ -1,3 +1,7 @@
+"""
+Vagrant helpers
+===============
+"""
from __future__ import with_statement
from fabric.api import *
@@ -5,7 +9,7 @@ from fabric.api import *
def ssh_config(name=''):
"""
- Get the SSH parameters for a vagrant VM
+ Get the SSH parameters for connecting to a vagrant VM.
"""
with settings(hide('running')):
output = local('vagrant ssh-config %s' % name, capture=True)
@@ -36,7 +40,21 @@ def _settings_dict(config):
@task
def vagrant(name=''):
"""
- Run the following tasks on a vagrant box
+ Run the following tasks on a vagrant box.
+
+ First, you need to import this task in your ``fabfile.py``::
+
+ from fabric.api import *
+ from fabtools.vagrant import vagrant
+
+ @task
+ def some_task():
+ run('echo hello')
+
+ Then you can easily run tasks on your current Vagrant box::
+
+ $ fab vagrant some_task
+
"""
config = ssh_config(name)
@@ -47,7 +65,15 @@ def vagrant(name=''):
def vagrant_settings(name='', *args, **kwargs):
"""
Context manager that sets a vagrant VM
- as the remote host
+ as the remote host.
+
+ Use this context manager inside a task to run commands
+ on your current Vagrant box::
+
+ from fabtools.vagrant import vagrant_settings
+
+ with vagrant_settings():
+ run('hostname')
"""
config = ssh_config(name)