summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToshio Kuratomi <a.badger@gmail.com>2020-07-20 14:28:35 -0700
committerGitHub <noreply@github.com>2020-07-20 14:28:35 -0700
commit46b1a999c6f3994348ecea25ad5a9ad403eda252 (patch)
tree912414676f9b0109a5aa391f94e7d8881f919c68
parent3eba8b8792a8ae733e49c44b3183d3be03caa167 (diff)
downloadansible-46b1a999c6f3994348ecea25ad5a9ad403eda252.tar.gz
Collections docs generation backport (#70515)
* Build documentation for Ansible-2.10 (formerly known as ACD). Builds plugin docs from collections whose source is on galaxy The new command downloads collections from galaxy, then finds the plugins inside of them to get the documentation for those plugins. * Update the python syntax checks * docs builds can now require python 3.6+. * Move plugin formatter code out to an external tool, antsibull-docs. Collection owners want to be able to extract docs for their own websites as well. * The jinja2 filters, tests, and other support code have moved to antsibull * Remove document_plugins as that has now been integrated into antsibull-docs * Cleanup and bugfix to other build script code: * The Commands class needed to have its metaclass set for abstractmethod to work correctly * Fix lint issues in some command plugins * Add the docs/docsite/rst/collections to .gitignore as everything in that directory will be generated so we don't want any of it saved in the git repository * gitignore the build dir and remove edit docs link on module pages * Add docs/rst/collections as a directory to remove on make clean * Split the collections docs from the main docs * remove version and edit on github * remove version banner for just collections * clarify examples need collection keyword defined * Remove references to plugin documentation locations that no longer exist. * Perhaps the pages in plugins/*.rst should be deprecated altogether and their content moved? * If not, perhaps we want to rephrase and link into the collection documentation? * Or perhaps we want to link to the plugins which are present in collections/ansible/builtin? * Remove PYTHONPATH from the build-ansible calls One of the design goals of the build-ansible.py script was for it to automatically set its library path to include the checkout of ansible and the library of code to implement itself. Because it automatically includes the checkout of ansible, we don't need to set PYTHONPATH in the Makefile any longer. * Create a command to only build ansible-base plugin docs * When building docs for devel, only build the ansible-base docs for now. This is because antsibull needs support for building a "devel tree" of docs. This can be changed once that is implemented * When building docs for the sanity tests, only build the ansible-base plugin docs for now. Those are the docs which are in this repo so that seems appropriate for now. * Docs: User guide overhaul, part 5 (#70307) (cherry picked from commit db354c03002440bbcb286b4897307dbb981d02db) * Need to return any error code from running antsibull-docs (#70763) This way we fail early if there's a problem (cherry picked from commit 1e3989c9f7919cbcfe82733711e13b93c026c2d8) Co-authored-by: Alicia Cozine <879121+acozine@users.noreply.github.com>
-rw-r--r--.gitignore2
-rw-r--r--Makefile2
-rw-r--r--docs/docsite/Makefile75
-rw-r--r--docs/docsite/_themes/sphinx_rtd_theme/ansible_banner.html45
-rw-r--r--docs/docsite/_themes/sphinx_rtd_theme/ansible_versions.html2
-rw-r--r--docs/docsite/collection-plugins.yml17
-rw-r--r--docs/docsite/requirements.txt1
-rw-r--r--docs/docsite/rst/conf.py4
-rw-r--r--docs/docsite/rst/index.rst2
-rw-r--r--docs/docsite/rst/network/index.rst2
-rw-r--r--docs/docsite/rst/network/user_guide/network_best_practices_2.5.rst2
-rw-r--r--docs/docsite/rst/plugins/become.rst5
-rw-r--r--docs/docsite/rst/plugins/cache.rst5
-rw-r--r--docs/docsite/rst/plugins/callback.rst6
-rw-r--r--docs/docsite/rst/plugins/connection.rst6
-rw-r--r--docs/docsite/rst/plugins/inventory.rst5
-rw-r--r--docs/docsite/rst/plugins/lookup.rst5
-rw-r--r--docs/docsite/rst/plugins/shell.rst5
-rw-r--r--docs/docsite/rst/plugins/strategy.rst5
-rw-r--r--docs/docsite/rst/plugins/vars.rst5
-rw-r--r--docs/docsite/rst/reference_appendices/faq.rst2
-rw-r--r--docs/docsite/rst/reference_appendices/glossary.rst2
-rw-r--r--docs/docsite/rst/user_guide/become.rst2
-rw-r--r--docs/docsite/rst/user_guide/collections_using.rst3
-rw-r--r--docs/docsite/rst/user_guide/intro_inventory.rst4
-rw-r--r--docs/docsite/rst/user_guide/modules.rst3
-rw-r--r--docs/docsite/rst/user_guide/modules_support.rst62
-rw-r--r--docs/docsite/rst/user_guide/playbook_pathing.rst17
-rw-r--r--docs/docsite/rst/user_guide/playbooks_best_practices.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_conditionals.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_delegation.rst53
-rw-r--r--docs/docsite/rst/user_guide/playbooks_error_handling.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_filters.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_loops.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_reuse.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_reuse_includes.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_reuse_roles.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_special_topics.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_strategies.rst46
-rw-r--r--docs/docsite/rst/user_guide/playbooks_templating.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_tests.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_variables.rst2
-rw-r--r--docs/docsite/rst/user_guide/playbooks_vault.rst157
-rw-r--r--docs/docsite/rst/user_guide/plugin_filtering_config.rst17
-rw-r--r--docs/docsite/rst/user_guide/vault.rst539
-rw-r--r--docs/docsite/rst/user_guide/windows_dsc.rst2
-rw-r--r--docs/docsite/rst/user_guide/windows_faq.rst2
-rw-r--r--docs/docsite/rst/user_guide/windows_setup.rst2
-rw-r--r--docs/docsite/rst/user_guide/windows_usage.rst2
-rw-r--r--docs/docsite/rst/user_guide/windows_winrm.rst2
-rw-r--r--docs/templates/list_of_CATEGORY_modules.rst.j248
-rw-r--r--docs/templates/list_of_CATEGORY_plugins.rst.j236
-rw-r--r--docs/templates/modules_by_support.rst.j245
-rw-r--r--docs/templates/plugin.rst.j2442
-rw-r--r--docs/templates/plugin_deprecation_stub.rst.j218
-rw-r--r--docs/templates/plugins_by_category.rst.j29
-rw-r--r--docs/templates/plugins_by_support.rst.j215
-rwxr-xr-xhacking/build-ansible.py17
-rw-r--r--hacking/build_library/build_ansible/command_plugins/collection_meta.py10
-rw-r--r--hacking/build_library/build_ansible/command_plugins/docs_build.py164
-rw-r--r--hacking/build_library/build_ansible/command_plugins/plugin_formatter.py807
-rw-r--r--hacking/build_library/build_ansible/jinja2/__init__.py0
-rw-r--r--hacking/build_library/build_ansible/jinja2/filters.py100
-rwxr-xr-xtest/sanity/code-smell/docs-build.py2
-rw-r--r--test/sanity/code-smell/docs-build.requirements.txt1
-rw-r--r--test/sanity/ignore.txt3
67 files changed, 697 insertions, 2164 deletions
diff --git a/.gitignore b/.gitignore
index cdd3941dee..5590928817 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,8 @@ docs/docsite/rst/cli/ansible.rst
docs/docsite/rst/dev_guide/collections_galaxy_meta.rst
docs/docsite/rst/dev_guide/testing/sanity/index.rst.new
docs/docsite/rst/modules/*.rst
+docs/docsite/rst/collections/*.rst
+docs/docsite/rst/collections/*/*.rst
docs/docsite/rst/playbooks_directives.rst
docs/docsite/rst/plugins_by_category.rst
docs/docsite/rst/plugins/*/*.rst
diff --git a/Makefile b/Makefile
index 60934b2891..f4e9534819 100644
--- a/Makefile
+++ b/Makefile
@@ -275,7 +275,7 @@ linkcheckdocs:
.PHONY: generate_rst
generate_rst: lib/ansible/cli/*.py
mkdir -p ./docs/man/man1/ ; \
- PYTHONPATH=./lib $(GENERATE_CLI) --template-file=docs/templates/man.j2 --output-dir=docs/man/man1/ --output-format man lib/ansible/cli/*.py
+ $(GENERATE_CLI) --template-file=docs/templates/man.j2 --output-dir=docs/man/man1/ --output-format man lib/ansible/cli/*.py
docs: generate_rst
diff --git a/docs/docsite/Makefile b/docs/docsite/Makefile
index 3b0b512b24..e36f239227 100644
--- a/docs/docsite/Makefile
+++ b/docs/docsite/Makefile
@@ -1,6 +1,6 @@
OS := $(shell uname -s)
SITELIB = $(shell python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"):
-PLUGIN_FORMATTER=../../hacking/build-ansible.py document-plugins
+PLUGIN_FORMATTER=../../hacking/build-ansible.py docs-build
TESTING_FORMATTER=../bin/testing_formatter.sh
KEYWORD_DUMPER=../../hacking/build-ansible.py document-keywords
CONFIG_DUMPER=../../hacking/build-ansible.py document-config
@@ -12,23 +12,35 @@ else
CPUS ?= $(shell nproc)
endif
-# Sets the build output directory if it's not already specified
+# Sets the build output directory for the main docsite if it's not already specified
ifndef BUILDDIR
BUILDDIR = _build
endif
-MODULE_ARGS=
+# Backwards compat for separate VARS
+PLUGIN_ARGS=
ifdef MODULES
- MODULE_ARGS = -l $(MODULES)
+ifndef PLUGINS
+ PLUGIN_ARGS = -l $(MODULES)
+else
+ PLUGIN_ARGS = -l $(MODULES),$(PLUGINS)
endif
-
-PLUGIN_ARGS=
+else
ifdef PLUGINS
PLUGIN_ARGS = -l $(PLUGINS)
endif
+endif
+
DOC_PLUGINS ?= become cache callback cliconf connection httpapi inventory lookup netconf shell strategy vars
+PYTHON=python
+# fetch version from project release.py as single source-of-truth
+VERSION := $(shell $(PYTHON) ../../packaging/release/versionhelper/version_helper.py --raw || echo error)
+ifeq ($(findstring error,$(VERSION)), error)
+$(error "version_helper failed")
+endif
+
assertrst:
ifndef rst
$(error specify document or pattern with rst=somefile.rst)
@@ -38,7 +50,8 @@ all: docs
docs: htmldocs
-generate_rst: collections_meta config cli keywords modules plugins testing
+generate_rst: collections_meta config cli keywords plugins testing
+base_generate_rst: collections_meta config cli keywords base_plugins testing
htmldocs: generate_rst
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
@@ -46,9 +59,12 @@ htmldocs: generate_rst
singlehtmldocs: generate_rst
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
+base_singlehtmldocs: base_generate_rst
+ CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
+
linkcheckdocs: generate_rst
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx linkcheck
-
+
webdocs: docs
#TODO: leaving htmlout removal for those having older versions, should eventually be removed also
@@ -58,7 +74,7 @@ clean:
-rm -rf $(BUILDDIR)/html
-rm -rf htmlout
-rm -rf module_docs
- -rm -rf _build
+ -rm -rf $(BUILDDIR)
-rm -f .buildinfo
-rm -f objects.inv
-rm -rf *.doctrees
@@ -70,43 +86,44 @@ clean:
find . -type f \( -name "*~" -or -name "#*" \) -delete
find . -type f \( -name "*.swp" \) -delete
@echo "Cleaning up generated rst"
- rm -f rst/modules/*_by_category.rst
- rm -f rst/modules/list_of_*.rst
- rm -f rst/modules/*_maintained.rst
- rm -f rst/modules/*_module.rst
- rm -f rst/modules/*_plugin.rst
rm -f rst/playbooks_directives.rst
- rm -f rst/plugins/*/*.rst
rm -f rst/reference_appendices/config.rst
rm -f rst/reference_appendices/playbooks_keywords.rst
rm -f rst/dev_guide/collections_galaxy_meta.rst
rm -f rst/cli/*.rst
+ rm -rf rst/collections/*
+ @echo "Cleaning up legacy generated rst locations"
+ rm -rf rst/modules
+ rm -f rst/plugins/*/*.rst
.PHONY: docs clean
collections_meta: ../templates/collections_galaxy_meta.rst.j2
- PYTHONPATH=../../lib $(COLLECTION_DUMPER) --template-file=../templates/collections_galaxy_meta.rst.j2 --output-dir=rst/dev_guide/ ../../lib/ansible/galaxy/data/collections_galaxy_meta.yml
+ $(COLLECTION_DUMPER) --template-file=../templates/collections_galaxy_meta.rst.j2 --output-dir=rst/dev_guide/ ../../lib/ansible/galaxy/data/collections_galaxy_meta.yml
# TODO: make generate_man output dir cli option
cli:
mkdir -p rst/cli
- PYTHONPATH=../../lib $(GENERATE_CLI) --template-file=../templates/cli_rst.j2 --output-dir=rst/cli/ --output-format rst ../../lib/ansible/cli/*.py
+ $(GENERATE_CLI) --template-file=../templates/cli_rst.j2 --output-dir=rst/cli/ --output-format rst ../../lib/ansible/cli/*.py
keywords: ../templates/playbooks_keywords.rst.j2
- PYTHONPATH=../../lib $(KEYWORD_DUMPER) --template-dir=../templates --output-dir=rst/reference_appendices/ ./keyword_desc.yml
+ $(KEYWORD_DUMPER) --template-dir=../templates --output-dir=rst/reference_appendices/ ./keyword_desc.yml
config: ../templates/config.rst.j2
- PYTHONPATH=../../lib $(CONFIG_DUMPER) --template-file=../templates/config.rst.j2 --output-dir=rst/reference_appendices/ ../../lib/ansible/config/base.yml
-
-modules: ../templates/plugin.rst.j2
- PYTHONPATH=../../lib $(PLUGIN_FORMATTER) -t rst --template-dir=../templates --module-dir=../../lib/ansible/modules -o rst/modules/ $(MODULE_ARGS)
-
-plugins: ../templates/plugin.rst.j2
- @echo "looping over doc plugins"
- for plugin in $(DOC_PLUGINS); \
- do \
- PYTHONPATH=../../lib $(PLUGIN_FORMATTER) -t rst --plugin-type $$plugin --template-dir=../templates --module-dir=../../lib/ansible/plugins/$$plugin -o rst $(PLUGIN_ARGS); \
- done
+ $(CONFIG_DUMPER) --template-file=../templates/config.rst.j2 --output-dir=rst/reference_appendices/ ../../lib/ansible/config/base.yml
+
+# For now, if we're building on devel, just build base docs. In the future we'll want to build docs that
+# are the latest versions on galaxy (using a different antsibull-docs subcommand)
+plugins:
+ if expr match "$(VERSION)" '.*[.]dev[0-9]\+$$' &> /dev/null; then \
+ $(PLUGIN_FORMATTER) base -o rst $(PLUGIN_ARGS);\
+ else \
+ $(PLUGIN_FORMATTER) full -o rst $(PLUGIN_ARGS);\
+ fi
+
+# This only builds the plugin docs included with ansible-base
+base_plugins:
+ $(PLUGIN_FORMATTER) base -o rst $(PLUGIN_ARGS);\
testing:
$(TESTING_FORMATTER)
diff --git a/docs/docsite/_themes/sphinx_rtd_theme/ansible_banner.html b/docs/docsite/_themes/sphinx_rtd_theme/ansible_banner.html
index 7ca2bd4a0f..ba889e7186 100644
--- a/docs/docsite/_themes/sphinx_rtd_theme/ansible_banner.html
+++ b/docs/docsite/_themes/sphinx_rtd_theme/ansible_banner.html
@@ -10,26 +10,27 @@
element.appendChild(para);
document.write('</div>');
}
-
- // Create a banner if we're not the latest version
- current_url = window.location.href;
- if ((current_url.search("latest") > -1) || (current_url.search("/{{ latest_version }}/") > -1)) {
- // no banner for latest release
- } else if (current_url.search("devel") > -1) {
- document.write('<div id="banner_id" class="admonition caution">');
- para = document.createElement('p');
- banner_text=document.createTextNode("You are reading the *devel* version of the Ansible documentation - most module documentation is currently missing as the modules have moved to collections. Until docs catches up to this change, use the version selection to the left if you want module documentation or the latest stable release version. The *devel* version is not guaranteed stable.");
- para.appendChild(banner_text);
- element = document.getElementById('banner_id');
- element.appendChild(para);
- document.write('</div>');
- } else {
- document.write('<div id="banner_id" class="admonition caution">');
- para = document.createElement('p');
- banner_text=document.createTextNode("You are reading an older version of the Ansible documentation. Use the version selection to the left if you want the latest stable released version.");
- para.appendChild(banner_text);
- element = document.getElementById('banner_id');
- element.appendChild(para);
- document.write('</div>');
- }
+ {% if (not READTHEDOCS) and (available_versions is defined) %}
+ // Create a banner if we're not the latest version
+ current_url = window.location.href;
+ if ((current_url.search("latest") > -1) || (current_url.search("/{{ latest_version }}/") > -1)) {
+ // no banner for latest release
+ } else if (current_url.search("devel") > -1) {
+ document.write('<div id="banner_id" class="admonition caution">');
+ para = document.createElement('p');
+ banner_text=document.createTextNode("You are reading the *devel* version of the Ansible documentation - this version is not guaranteed stable. Use the version selection to the left if you want the latest stable released version.");
+ para.appendChild(banner_text);
+ element = document.getElementById('banner_id');
+ element.appendChild(para);
+ document.write('</div>');
+ } else {
+ document.write('<div id="banner_id" class="admonition caution">');
+ para = document.createElement('p');
+ banner_text=document.createTextNode("You are reading an older version of the Ansible documentation. Use the version selection to the left if you want the latest stable released version.");
+ para.appendChild(banner_text);
+ element = document.getElementById('banner_id');
+ element.appendChild(para);
+ document.write('</div>');
+ }
+ {% endif %}
</script>
diff --git a/docs/docsite/_themes/sphinx_rtd_theme/ansible_versions.html b/docs/docsite/_themes/sphinx_rtd_theme/ansible_versions.html
index 06a0271eda..3d3d7fc682 100644
--- a/docs/docsite/_themes/sphinx_rtd_theme/ansible_versions.html
+++ b/docs/docsite/_themes/sphinx_rtd_theme/ansible_versions.html
@@ -1,7 +1,7 @@
<!--- Based on https://github.com/rtfd/sphinx_rtd_theme/pull/438/files -->
{# Creates dropdown version selection in the top-left navigation. #}
<div class="version">
- {% if not READTHEDOCS %}
+ {% if (not READTHEDOCS) and (available_versions is defined) %}
<div class="version-dropdown">
<select class="version-list" id="version-list" onchange="javascript:location.href = this.value;">
<script> x = document.getElementById("version-list"); </script>
diff --git a/docs/docsite/collection-plugins.yml b/docs/docsite/collection-plugins.yml
new file mode 100644
index 0000000000..499274b40b
--- /dev/null
+++ b/docs/docsite/collection-plugins.yml
@@ -0,0 +1,17 @@
+# We also need an example of modules hosted in Automation Hub
+# We'll likely move to data hosted in botmeta instead of a standalone file but
+# we'll need all of these same details.
+module:
+ purefa_user:
+ source: 'https://galaxy.ansible.com/'
+ fqcn: 'purestorage.flasharray'
+ purefa_vg:
+ source: 'https://galaxy.ansible.com/'
+ fqcn: 'purestorage.flasharray'
+ gcp_compute_firewall_info:
+ source: 'https://galaxy.ansible.com/'
+ fqcn: 'google.cloud'
+module_utils:
+ purefa:
+ source: 'https://galaxy.ansible.com/'
+ fqcn: 'purestorage.flasharray'
diff --git a/docs/docsite/requirements.txt b/docs/docsite/requirements.txt
index 2672d01095..1290ac1df0 100644
--- a/docs/docsite/requirements.txt
+++ b/docs/docsite/requirements.txt
@@ -6,3 +6,4 @@ sphinx==2.1.2
sphinx-notfound-page
Pygments >= 2.4.0
straight.plugin # Needed for hacking/build-ansible.py which is the backend build script
+antsibull >= 0.15.0
diff --git a/docs/docsite/rst/conf.py b/docs/docsite/rst/conf.py
index e564808d7c..82916acfb3 100644
--- a/docs/docsite/rst/conf.py
+++ b/docs/docsite/rst/conf.py
@@ -277,6 +277,10 @@ autoclass_content = 'both'
intersphinx_mapping = {'python': ('https://docs.python.org/2/', (None, '../python2.inv')),
'python3': ('https://docs.python.org/3/', (None, '../python3.inv')),
'jinja2': ('http://jinja.palletsprojects.com/', (None, '../jinja2.inv')),
+ 'collections': ('https://docs.ansible.com/collections/',
+ (None, '../collections.inv',
+ 'http://docs.testing.ansible.com/collections/objects.inv',
+ '../_collections_build/html/objects.inv')),
'ansible_2_9': ('https://docs.ansible.com/ansible/2.9/', (None, '../ansible_2_9.inv')),
'ansible_2_8': ('https://docs.ansible.com/ansible/2.8/', (None, '../ansible_2_8.inv')),
'ansible_2_7': ('https://docs.ansible.com/ansible/2.7/', (None, '../ansible_2_7.inv')),
diff --git a/docs/docsite/rst/index.rst b/docs/docsite/rst/index.rst
index 0d2c5a6612..eea338480c 100644
--- a/docs/docsite/rst/index.rst
+++ b/docs/docsite/rst/index.rst
@@ -74,7 +74,7 @@ Ansible releases a new major release of Ansible approximately three to four time
:maxdepth: 1
:caption: Reference & Appendices
- ../modules/modules_by_category
+ collections/index
reference_appendices/playbooks_keywords
reference_appendices/common_return_values
reference_appendices/config
diff --git a/docs/docsite/rst/network/index.rst b/docs/docsite/rst/network/index.rst
index 9ae7a863f3..257563910c 100644
--- a/docs/docsite/rst/network/index.rst
+++ b/docs/docsite/rst/network/index.rst
@@ -10,7 +10,7 @@ Ansible Network modules extend the benefits of simple, powerful, agentless autom
If you're new to Ansible, or new to using Ansible for network management, start with :ref:`network_getting_started`. If you are already familiar with network automation with Ansible, see :ref:`network_advanced`.
-For documentation on using a particular network module, consult the :ref:`list of all network modules<network_modules>`. Some network modules are maintained by the Ansible community - here's a list of :ref:`network modules maintained by the Ansible Network Team<network_supported>`.
+For documentation on using a particular network module, consult the :ref:`list of all network modules<network_modules>`. Network modules for various hardware are supported by different teams including the hardware vendors themselves, volunteers from the Ansible community, and the Ansible Network Team.
.. toctree::
:maxdepth: 3
diff --git a/docs/docsite/rst/network/user_guide/network_best_practices_2.5.rst b/docs/docsite/rst/network/user_guide/network_best_practices_2.5.rst
index f1f4d7ad2c..5cb5ab209b 100644
--- a/docs/docsite/rst/network/user_guide/network_best_practices_2.5.rst
+++ b/docs/docsite/rst/network/user_guide/network_best_practices_2.5.rst
@@ -483,4 +483,4 @@ If you receive an connection error please double check the inventory and playboo
* :ref:`network_guide`
* :ref:`intro_inventory`
- * :ref:`Vault best practices <best_practices_for_variables_and_vaults>`
+ * :ref:`Keeping vaulted variables visible <tip_for_variables_and_vaults>`
diff --git a/docs/docsite/rst/plugins/become.rst b/docs/docsite/rst/plugins/become.rst
index 7bc181d06e..c4bbb7e8c6 100644
--- a/docs/docsite/rst/plugins/become.rst
+++ b/docs/docsite/rst/plugins/become.rst
@@ -47,11 +47,6 @@ Plugin List
You can use ``ansible-doc -t become -l`` to see the list of available plugins.
Use ``ansible-doc -t become <plugin name>`` to see specific documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- become/*
-
.. seealso::
:ref:`about_playbooks`
diff --git a/docs/docsite/rst/plugins/cache.rst b/docs/docsite/rst/plugins/cache.rst
index ce7e848743..a13c78db04 100644
--- a/docs/docsite/rst/plugins/cache.rst
+++ b/docs/docsite/rst/plugins/cache.rst
@@ -118,11 +118,6 @@ Plugin List
You can use ``ansible-doc -t cache -l`` to see the list of available plugins.
Use ``ansible-doc -t cache <plugin name>`` to see specific documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- cache/*
-
.. seealso::
:ref:`action_plugins`
diff --git a/docs/docsite/rst/plugins/callback.rst b/docs/docsite/rst/plugins/callback.rst
index 3160eb120f..a8de3de986 100644
--- a/docs/docsite/rst/plugins/callback.rst
+++ b/docs/docsite/rst/plugins/callback.rst
@@ -79,12 +79,6 @@ Plugin list
You can use ``ansible-doc -t callback -l`` to see the list of available plugins.
Use ``ansible-doc -t callback <plugin name>`` to see specific documents and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- callback/*
-
-
.. seealso::
:ref:`action_plugins`
diff --git a/docs/docsite/rst/plugins/connection.rst b/docs/docsite/rst/plugins/connection.rst
index 02d2ffb4c1..0417526ff2 100644
--- a/docs/docsite/rst/plugins/connection.rst
+++ b/docs/docsite/rst/plugins/connection.rst
@@ -58,12 +58,6 @@ You can use ``ansible-doc -t connection -l`` to see the list of available plugin
Use ``ansible-doc -t connection <plugin name>`` to see detailed documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- connection/*
-
-
.. seealso::
:ref:`Working with Playbooks<working_with_playbooks>`
diff --git a/docs/docsite/rst/plugins/inventory.rst b/docs/docsite/rst/plugins/inventory.rst
index a652f8dd3d..eda99bf3e7 100644
--- a/docs/docsite/rst/plugins/inventory.rst
+++ b/docs/docsite/rst/plugins/inventory.rst
@@ -162,11 +162,6 @@ Plugin List
You can use ``ansible-doc -t inventory -l`` to see the list of available plugins.
Use ``ansible-doc -t inventory <plugin name>`` to see plugin-specific documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- inventory/*
-
.. seealso::
:ref:`about_playbooks`
diff --git a/docs/docsite/rst/plugins/lookup.rst b/docs/docsite/rst/plugins/lookup.rst
index cb76fa26da..31183b15c4 100644
--- a/docs/docsite/rst/plugins/lookup.rst
+++ b/docs/docsite/rst/plugins/lookup.rst
@@ -138,11 +138,6 @@ Plugin list
You can use ``ansible-doc -t lookup -l`` to see the list of available plugins. Use ``ansible-doc -t lookup <plugin name>`` to see specific documents and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- lookup/*
-
.. seealso::
:ref:`about_playbooks`
diff --git a/docs/docsite/rst/plugins/shell.rst b/docs/docsite/rst/plugins/shell.rst
index 7caff1c637..b0846323e0 100644
--- a/docs/docsite/rst/plugins/shell.rst
+++ b/docs/docsite/rst/plugins/shell.rst
@@ -33,11 +33,6 @@ In this case, you will also want to update the :ref:`ansible_shell_executable <a
You can further control the settings for each plugin via other configuration options
detailed in the plugin themselves (linked below).
-.. toctree:: :maxdepth: 1
- :glob:
-
- shell/*
-
.. seealso::
:ref:`about_playbooks`
diff --git a/docs/docsite/rst/plugins/strategy.rst b/docs/docsite/rst/plugins/strategy.rst
index 5e7d6c99ac..e3623329a6 100644
--- a/docs/docsite/rst/plugins/strategy.rst
+++ b/docs/docsite/rst/plugins/strategy.rst
@@ -59,11 +59,6 @@ You can use ``ansible-doc -t strategy -l`` to see the list of available plugins.
Use ``ansible-doc -t strategy <plugin name>`` to see plugin-specific specific documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- strategy/*
-
.. seealso::
:ref:`about_playbooks`
diff --git a/docs/docsite/rst/plugins/vars.rst b/docs/docsite/rst/plugins/vars.rst
index d70706a3bd..c24bdb8116 100644
--- a/docs/docsite/rst/plugins/vars.rst
+++ b/docs/docsite/rst/plugins/vars.rst
@@ -57,11 +57,6 @@ You can use ``ansible-doc -t vars -l`` to see the list of available plugins.
Use ``ansible-doc -t vars <plugin name>`` to see specific plugin-specific documentation and examples.
-.. toctree:: :maxdepth: 1
- :glob:
-
- vars/*
-
.. seealso::
:ref:`action_plugins`
diff --git a/docs/docsite/rst/reference_appendices/faq.rst b/docs/docsite/rst/reference_appendices/faq.rst
index 4b86366be0..162e0c9678 100644
--- a/docs/docsite/rst/reference_appendices/faq.rst
+++ b/docs/docsite/rst/reference_appendices/faq.rst
@@ -698,7 +698,7 @@ Please see the section below for a link to IRC and the Google Group, where you c
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-project>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/reference_appendices/glossary.rst b/docs/docsite/rst/reference_appendices/glossary.rst
index e769d1c2aa..1e83a69ba3 100644
--- a/docs/docsite/rst/reference_appendices/glossary.rst
+++ b/docs/docsite/rst/reference_appendices/glossary.rst
@@ -494,7 +494,7 @@ when a term comes up on the mailing list.
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/become.rst b/docs/docsite/rst/user_guide/become.rst
index 8e44bad7a0..b7462d080a 100644
--- a/docs/docsite/rst/user_guide/become.rst
+++ b/docs/docsite/rst/user_guide/become.rst
@@ -234,7 +234,7 @@ For more information, see `this systemd issue
Become and network automation
=============================
-As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all :ref:`Ansible-maintained platforms<network_supported>` that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
+As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all Ansible-maintained network platforms that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
You must set the connection type to either ``connection: network_cli`` or ``connection: httpapi`` to use ``become`` for privilege escalation on network devices. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
diff --git a/docs/docsite/rst/user_guide/collections_using.rst b/docs/docsite/rst/user_guide/collections_using.rst
index cbd1962dfa..c3f8e6bb7b 100644
--- a/docs/docsite/rst/user_guide/collections_using.rst
+++ b/docs/docsite/rst/user_guide/collections_using.rst
@@ -5,7 +5,8 @@
Using collections
*****************
-Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins.
+Collections are a distribution format for Ansible content that can include playbooks, roles, modules, and plugins. As modules move from the core Ansible repository into collections, the module documentation will move to the `collections documentation page <https://docs.ansible.com/collections/>`_
+
You can install and use collections through `Ansible Galaxy <https://galaxy.ansible.com>`_.
* For details on how to *develop* collections see :ref:`developing_collections`.
diff --git a/docs/docsite/rst/user_guide/intro_inventory.rst b/docs/docsite/rst/user_guide/intro_inventory.rst
index 7d4317fc13..5675f6bf1f 100644
--- a/docs/docsite/rst/user_guide/intro_inventory.rst
+++ b/docs/docsite/rst/user_guide/intro_inventory.rst
@@ -543,7 +543,7 @@ ansible_port
ansible_user
The user name to use when connecting to the host
ansible_password
- The password to use to authenticate to the host (never store this variable in plain text; always use a vault. See :ref:`best_practices_for_variables_and_vaults`)
+ The password to use to authenticate to the host (never store this variable in plain text; always use a vault. See :ref:`tip_for_variables_and_vaults`)
Specific to the SSH connection:
@@ -575,7 +575,7 @@ ansible_become_method
ansible_become_user
Equivalent to ``ansible_sudo_user`` or ``ansible_su_user``, allows to set the user you become through privilege escalation
ansible_become_password
- Equivalent to ``ansible_sudo_password`` or ``ansible_su_password``, allows you to set the privilege escalation password (never store this variable in plain text; always use a vault. See :ref:`best_practices_for_variables_and_vaults`)
+ Equivalent to ``ansible_sudo_password`` or ``ansible_su_password``, allows you to set the privilege escalation password (never store this variable in plain text; always use a vault. See :ref:`tip_for_variables_and_vaults`)
ansible_become_exe
Equivalent to ``ansible_sudo_exe`` or ``ansible_su_exe``, allows you to set the executable for the escalation method selected
ansible_become_flags
diff --git a/docs/docsite/rst/user_guide/modules.rst b/docs/docsite/rst/user_guide/modules.rst
index 36bdc32303..70dac88459 100644
--- a/docs/docsite/rst/user_guide/modules.rst
+++ b/docs/docsite/rst/user_guide/modules.rst
@@ -7,9 +7,8 @@ Working With Modules
:maxdepth: 1
modules_intro
- ../reference_appendices/common_return_values
modules_support
- ../modules/modules_by_category
+ ../reference_appendices/common_return_values
Ansible ships with a number of modules (called the 'module library')
diff --git a/docs/docsite/rst/user_guide/modules_support.rst b/docs/docsite/rst/user_guide/modules_support.rst
index 75ddd71f19..f21d0c25bf 100644
--- a/docs/docsite/rst/user_guide/modules_support.rst
+++ b/docs/docsite/rst/user_guide/modules_support.rst
@@ -4,68 +4,66 @@
Module Maintenance & Support
****************************
+If you are using a module and you discover a bug, you may want to know where to report that bug, who is responsible for fixing it, and how you can track changes to the module. If you are a Red Hat subscriber, you may want to know whether you can get support for the issue you are facing.
+
+Starting in Ansible 2.10, most modules live in collections. The distribution method for each collection reflects the maintenance and support for the modules in that collection.
+
.. contents::
- :depth: 2
:local:
Maintenance
===========
-To clarify who maintains each included module, adding features and fixing bugs, each included module now has associated metadata that provides information about maintenance.
-
-Core
-----
-
-:ref:`Core Maintained<core_supported>` modules are maintained by the Ansible Engineering Team.
-These modules are integral to the basic foundations of the Ansible distribution.
+.. table::
+ :class: documentation-table
-Network
--------
+ ============================= ========================================== ==========================
+ Collection Code location Maintained by
+ ============================= ========================================== ==========================
+ ansible.builtin `ansible/ansible repo`_ on GitHub core team
-:ref:`Network Maintained<network_supported>` modules are are maintained by the Ansible Network Team. Please note there are additional networking modules that are categorized as Certified or Community not maintained by Ansible.
+ distributed on Galaxy various; follow ``repo`` link community or partners
+ distributed on Automation Hub various; follow ``repo`` link content team or partners
+ ============================= ========================================== ==========================
-Certified
----------
+.. _ansible/ansible repo: https://github.com/ansible/ansible/tree/devel/lib/ansible/modules
-`Certified <https://access.redhat.com/articles/3642632>`_ modules are maintained by Ansible Partners.
+Issue Reporting
+===============
-Community
----------
+If you find a bug that affects a plugin in the main Ansible repo:
-:ref:`Community Maintained<community_supported>` modules are submitted and maintained by the Ansible community. These modules are not maintained by Ansible, and are included as a convenience.
+ #. Confirm that you are running the latest stable version of Ansible or the devel branch.
+ #. Look at the `issue tracker in the Ansible repo <https://github.com/ansible/ansible/issues>`_ to see if an issue has already been filed.
+ #. Create an issue if one does not already exist. Include as much detail as you can about the behavior you discovered.
-Issue Reporting
-===============
+If you find a bug that affects a plugin in a Galaxy collection:
-If you believe you have found a bug in a module and are already running the latest stable or development version of Ansible, first look at the `issue tracker in the Ansible repo <https://github.com/ansible/ansible/issues>`_ to see if an issue has already been filed. If not, please file one.
+ #. Find the collection on Galaxy.
+ #. Find the issue tracker for the collection.
+ #. Look there to see if an issue has already been filed.
+ #. Create an issue if one does not already exist. Include as much detail as you can about the behavior you discovered.
-Should you have a question rather than a bug report, inquiries are welcome on the `ansible-project Google group <https://groups.google.com/forum/#%21forum/ansible-project>`_ or on Ansible's "#ansible" channel, located on irc.freenode.net.
+Some partner collections may be hosted in private repositories.
-For development-oriented topics, use the `ansible-devel Google group <https://groups.google.com/forum/#%21forum/ansible-devel>`_ or Ansible's #ansible and #ansible-devel channels, located on irc.freenode.net. You should also read the :ref:`Community Guide <ansible_community_guide>`, :ref:`Testing Ansible <developing_testing>`, and the :ref:`Developer Guide <developer_guide>`.
+If you are not sure whether the behavior you see is a bug, if you have questions, if you want to discuss development-oriented topics, or if you just want to get in touch, use one of our Google groups or IRC channels to :ref:`communicate with Ansiblers <communication>`.
-The modules are hosted on GitHub in a subdirectory of the `Ansible <https://github.com/ansible/ansible/tree/devel/lib/ansible/modules>`_ repo.
+If you find a bug that affects a module in an Automation Hub collection:
-NOTE: If you have a Red Hat Ansible Automation product subscription, please follow the standard issue reporting process via the `Red Hat Customer Portal <https://access.redhat.com/>`_.
+ #. If the collection offers an Issue Tracker link on Automation Hub, click there and open an issue on the collection repository. If it does not, follow the standard process for reporting issues on the `Red Hat Customer Portal <https://access.redhat.com/>`_. You must have a subscription to the Red Hat Ansible Automation Platform to create an issue on the portal.
Support
=======
-For more information on how included Ansible modules are supported by Red Hat,
-please refer to the following `knowledge base article <https://access.redhat.com/articles/3166901>`_ as well as other resources on the `Red Hat Customer Portal. <https://access.redhat.com/>`_
+All plugins that remain in ansible-base and all collections hosted in Automation Hub are supported by Red Hat. No other plugins or collections are supported by Red Hat. If you have a subscription to the Red Hat Ansible Automation Platform, you can find more information and resources on the `Red Hat Customer Portal. <https://access.redhat.com/>`_
.. seealso::
- :ref:`Module index<modules_by_category>`
- A complete list of all available modules.
:ref:`intro_adhoc`
Examples of using modules in /usr/bin/ansible
:ref:`working_with_playbooks`
Examples of using modules with /usr/bin/ansible-playbook
- :ref:`developing_modules`
- How to write your own modules
- `List of Ansible Certified Modules <https://access.redhat.com/articles/3642632>`_
- High level list of Ansible certified modules from Partners
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/playbook_pathing.rst b/docs/docsite/rst/user_guide/playbook_pathing.rst
index 7e58f792e7..7d59102f74 100644
--- a/docs/docsite/rst/user_guide/playbook_pathing.rst
+++ b/docs/docsite/rst/user_guide/playbook_pathing.rst
@@ -4,20 +4,21 @@
Search paths in Ansible
***********************
-Absolute paths are not an issue as they always have a known start, but relative paths ... well, they are relative.
+You can control the paths Ansible searches to find resources on your control node (including configuration, modules, roles, ssh keys, and more) as well as resources on the remote nodes you are managing. Use absolute paths to tell Ansible where to find resources whenever you can. However, absolute paths are not always practical. This page covers how Ansible interprets relative search paths, along with ways to troubleshoot when Ansible cannot find the resource you need.
+
+.. contents::
+ :local:
Config paths
============
-By default these should be relative to the config file, some are specifically relative to the 'cwd' or the playbook and should have this noted in their description. Things like ssh keys are left to use 'cwd' because it mirrors how the underlying tools would use it.
+By default these should be relative to the config file, some are specifically relative to the current working directory or the playbook and should have this noted in their description. Things like ssh keys are left to use the current working directory because it mirrors how the underlying tools would use it.
Task paths
==========
-Here things start getting complicated, there are 2 different scopes to consider, task evaluation (paths are all local, like in lookups) and task execution, which is normally on the remote, unless an action plugin is involved.
-
-Some tasks that require 'local' resources use action plugins (template and copy are examples of these), in which case the path is also local.
+Task paths include two different scopes: task evaluation and task execution. For task evaluation, all paths are local, like in lookups. For task execution, which usually happens on the remote nodes, local paths do not usually apply. However, if a task uses an action plugin, it uses a local path. The template and copy modules are examples of modules that use action plugins, and therefore use local paths.
The magic of 'local' paths
--------------------------
@@ -32,12 +33,10 @@ i.e ::
play search path is playdir/{files|vars|templates}/, playdir/.
-The current working directory (cwd) is not searched. If you see it, it just happens to coincide with one of the paths above.
-If you `include` a task file from a role, it will NOT trigger role behavior, this only happens when running as a role, `include_role` will work.
-A new variable `ansible_search_path` var will have the search path used, in order (but without the appended subdirs). Using 5 "v"s (`-vvvvv`) should show the detail of the search as it happens.
+By default, Ansible does not search the current working directory unless it happens to coincide with one of the paths above. If you `include` a task file from a role, it will NOT trigger role behavior, this only happens when running as a role, `include_role` will work. A new variable `ansible_search_path` var will have the search path used, in order (but without the appended subdirs). Using 5 "v"s (`-vvvvv`) should show the detail of the search as it happens.
As for includes, they try the path of the included file first and fall back to the play/role that includes them.
-.. note: The 'cwd' might vary depending on the connection plugin and if the action is local or remote. For the remote it is normally the directory on which the login shell puts the user. For local it is either the directory you executed ansible from or in some cases the playbook directory.
+.. note: The current working directory might vary depending on the connection plugin and if the action is local or remote. For the remote it is normally the directory on which the login shell puts the user. For local it is either the directory you executed ansible from or in some cases the playbook directory.
diff --git a/docs/docsite/rst/user_guide/playbooks_best_practices.rst b/docs/docsite/rst/user_guide/playbooks_best_practices.rst
index d480266344..775d390d46 100644
--- a/docs/docsite/rst/user_guide/playbooks_best_practices.rst
+++ b/docs/docsite/rst/user_guide/playbooks_best_practices.rst
@@ -70,7 +70,7 @@ Separate production and staging inventory
You can keep your production environment separate from development, test, and staging environments by using separate inventory files or directories for each environment. This way you pick with -i what you are targeting. Keeping all your environments in one file can lead to surprises!
-.. _best_practices_for_variables_and_vaults:
+.. _tip_for_variables_and_vaults:
Keep vaulted variables safely visible
-------------------------------------
diff --git a/docs/docsite/rst/user_guide/playbooks_conditionals.rst b/docs/docsite/rst/user_guide/playbooks_conditionals.rst
index 4999476c1a..0c5ce904ed 100644
--- a/docs/docsite/rst/user_guide/playbooks_conditionals.rst
+++ b/docs/docsite/rst/user_guide/playbooks_conditionals.rst
@@ -464,7 +464,7 @@ Possible values (sample, not complete list)::
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_delegation.rst b/docs/docsite/rst/user_guide/playbooks_delegation.rst
index 58e0179410..12d55d4ee0 100644
--- a/docs/docsite/rst/user_guide/playbooks_delegation.rst
+++ b/docs/docsite/rst/user_guide/playbooks_delegation.rst
@@ -1,9 +1,9 @@
.. _playbooks_delegation:
-Delegation and local actions
-============================
+Controlling where tasks run: delegation and local actions
+=========================================================
-By default Ansible executes all tasks on the machines that match the ``hosts`` line of your playbook. If you want to run some tasks on a different machine, you can use delegation. For example, when updating webservers, you might want to retrieve information from your database servers. In this scenario, your play would target the webservers group and you would delegate the database tasks to your dbservers group. With delegation, you can perform a task on one host on behalf of another, or execute tasks locally on behalf of remote hosts.
+By default Ansible gathers facts and executes all tasks on the machines that match the ``hosts`` line of your playbook. This page shows you how to delegate tasks to a different machine or group, delegate facts to specific machines or groups, or run an entire playbook locally. Using these approaches, you can manage inter-related environments precisely and efficiently. For example, when updating your webservers, you might need to remove them from a load-balanced pool temporarily. You cannot perform this task on the webservers themselves. By delegating the task to localhost, you keep all the tasks within the same play.
.. contents::
:local:
@@ -99,52 +99,10 @@ Delegating Ansible tasks is like delegating tasks in the real world - your groce
This task gathers facts for the machines in the dbservers group and assigns the facts to those machines, even though the play targets the app_servers group. This way you can lookup `hostvars['dbhost1']['ansible_default_ipv4']['address']` even though dbservers were not part of the play, or left out by using `--limit`.
-.. _run_once:
-
-Run once
---------
-
-If you want a task to run only on the first host in your batch of hosts, set ``run_once`` to true on that task::
-
- ---
- # ...
-
- tasks:
-
- # ...
-
- - command: /opt/application/upgrade_db.py
- run_once: true
-
- # ...
-
-Ansible executes this task on the first host in the current batch and applies all results and facts to all the hosts in the same batch. This approach is similar to applying a conditional to a task such as::
-
- - command: /opt/application/upgrade_db.py
- when: inventory_hostname == webservers[0]
-
-However, with ``run_once``, the results are applied to all the hosts. To specify an individual host to execute on, delegate the task::
-
- - command: /opt/application/upgrade_db.py
- run_once: true
- delegate_to: web01.example.org
-
-As always with delegation, the action will be executed on the delegated host, but the information is still that of the original host in the task.
-
-.. note::
- When used together with "serial", tasks marked as "run_once" will be run on one host in *each* serial batch. If the task must run only once regardless of "serial" mode, use
- :code:`when: inventory_hostname == ansible_play_hosts_all[0]` construct.
-
-.. note::
- Any conditional (i.e `when:`) will use the variables of the 'first host' to decide if the task runs or not, no other hosts will be tested.
-
-.. note::
- If you want to avoid the default behavior of setting the fact for all hosts, set `delegate_facts: True` for the specific task or block.
-
.. _local_playbooks:
Local playbooks
-```````````````
+---------------
It may be useful to use a playbook locally on a remote host, rather than by connecting over SSH. This can be useful for assuring the configuration of a system by putting a playbook in a crontab. This may also be used
to run a playbook inside an OS installer, such as an Anaconda kickstart.
@@ -165,11 +123,12 @@ use the default remote connection type::
under {{ ansible_playbook_python }}. Be sure to set ansible_python_interpreter: "{{ ansible_playbook_python }}" in
host_vars/localhost.yml, for example. You can avoid this issue by using ``local_action`` or ``delegate_to: localhost`` instead.
-
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
+ :ref:`playbooks_strategies`
+ More ways to control how and where Ansible executes
`Ansible Examples on GitHub <https://github.com/ansible/ansible-examples>`_
Many examples of full-stack deployments
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_error_handling.rst b/docs/docsite/rst/user_guide/playbooks_error_handling.rst
index c771ba7024..ecb68571c4 100644
--- a/docs/docsite/rst/user_guide/playbooks_error_handling.rst
+++ b/docs/docsite/rst/user_guide/playbooks_error_handling.rst
@@ -231,7 +231,7 @@ You can also use blocks to define responses to task errors. This approach is sim
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
diff --git a/docs/docsite/rst/user_guide/playbooks_filters.rst b/docs/docsite/rst/user_guide/playbooks_filters.rst
index bef389d883..1eceeda94e 100644
--- a/docs/docsite/rst/user_guide/playbooks_filters.rst
+++ b/docs/docsite/rst/user_guide/playbooks_filters.rst
@@ -1644,7 +1644,7 @@ This can then be used to reference hashes in Pod specifications::
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst b/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst
index 8c473a42a6..6fba27ebfc 100644
--- a/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst
+++ b/docs/docsite/rst/user_guide/playbooks_filters_ipaddr.rst
@@ -737,7 +737,7 @@ the filter ``slaac()`` generates an IPv6 address for a given network and a MAC A
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_loops.rst b/docs/docsite/rst/user_guide/playbooks_loops.rst
index 98cfd2575c..e90c92bdd3 100644
--- a/docs/docsite/rst/user_guide/playbooks_loops.rst
+++ b/docs/docsite/rst/user_guide/playbooks_loops.rst
@@ -430,7 +430,7 @@ Migrating from with_X to loop
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
diff --git a/docs/docsite/rst/user_guide/playbooks_reuse.rst b/docs/docsite/rst/user_guide/playbooks_reuse.rst
index 98af3189e6..5331b4866c 100644
--- a/docs/docsite/rst/user_guide/playbooks_reuse.rst
+++ b/docs/docsite/rst/user_guide/playbooks_reuse.rst
@@ -188,7 +188,7 @@ Imports are processed before the play begins, so the name of the import no longe
:ref:`playbooks_loops`
Loops in playbooks
:ref:`playbooks_best_practices`
- Various tips about managing playbooks in the real world
+ Tips and tricks for playbooks
:ref:`ansible_galaxy`
How to share roles on galaxy, role management
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_reuse_includes.rst b/docs/docsite/rst/user_guide/playbooks_reuse_includes.rst
index b2f302494f..229e0ef820 100644
--- a/docs/docsite/rst/user_guide/playbooks_reuse_includes.rst
+++ b/docs/docsite/rst/user_guide/playbooks_reuse_includes.rst
@@ -15,7 +15,7 @@ The content on this page has been moved to :ref:`playbooks_reuse`.
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`playbooks_best_practices`
- Various tips about managing playbooks in the real world
+ Tips and tricks for playbooks
:ref:`playbooks_variables`
All about variables in playbooks
:ref:`playbooks_conditionals`
diff --git a/docs/docsite/rst/user_guide/playbooks_reuse_roles.rst b/docs/docsite/rst/user_guide/playbooks_reuse_roles.rst
index d3ee5c2af7..8c1e4d8ba5 100644
--- a/docs/docsite/rst/user_guide/playbooks_reuse_roles.rst
+++ b/docs/docsite/rst/user_guide/playbooks_reuse_roles.rst
@@ -441,7 +441,7 @@ Read the `Ansible Galaxy documentation <https://galaxy.ansible.com/docs/>`_ page
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`playbooks_best_practices`
- Tips for managing playbooks in the real world
+ Tips and tricks for playbooks
:ref:`playbooks_variables`
Variables in playbooks
:ref:`playbooks_conditionals`
diff --git a/docs/docsite/rst/user_guide/playbooks_special_topics.rst b/docs/docsite/rst/user_guide/playbooks_special_topics.rst
index 7abcb3077a..a1646413d7 100644
--- a/docs/docsite/rst/user_guide/playbooks_special_topics.rst
+++ b/docs/docsite/rst/user_guide/playbooks_special_topics.rst
@@ -19,7 +19,7 @@ As you write more playbooks and roles, you might have some special use cases. Fo
../plugins/plugins
playbooks_prompts
playbooks_tags
- playbooks_vault
+ vault
playbooks_startnstep
../reference_appendices/playbooks_keywords
playbooks_lookups
diff --git a/docs/docsite/rst/user_guide/playbooks_strategies.rst b/docs/docsite/rst/user_guide/playbooks_strategies.rst
index 7bd5dfe92d..26b48af5d1 100644
--- a/docs/docsite/rst/user_guide/playbooks_strategies.rst
+++ b/docs/docsite/rst/user_guide/playbooks_strategies.rst
@@ -36,7 +36,7 @@ or pass it on the command line: `ansible-playbook -f 30 my_playbook.yml`.
Using keywords to control execution
-----------------------------------
-In addition to strategies, several :ref:`keywords<playbook_keywords>` also affect play execution. You can set a number, a percentage, or a list of numbers of hosts you want to manage at a time with ``serial``. Ansible completes the play on the specified number or percentage of hosts before starting the next batch of hosts. You can restrict the number of workers allotted to a block or task with ``throttle``. You can control how Ansible selects the next host in a group to execute against with ``order``. These keywords are not strategies. They are directives or options applied to a play, block, or task.
+In addition to strategies, several :ref:`keywords<playbook_keywords>` also affect play execution. You can set a number, a percentage, or a list of numbers of hosts you want to manage at a time with ``serial``. Ansible completes the play on the specified number or percentage of hosts before starting the next batch of hosts. You can restrict the number of workers allotted to a block or task with ``throttle``. You can control how Ansible selects the next host in a group to execute against with ``order``. You can run a task on a single host with ``run_once``. These keywords are not strategies. They are directives or options applied to a play, block, or task.
.. _rolling_update_batch_size:
@@ -160,10 +160,54 @@ shuffle:
Other keywords that affect play execution include ``ignore_errors``, ``ignore_unreachable``, and ``any_errors_fatal``. These options are documented in :ref:`playbooks_error_handling`.
+.. _run_once:
+
+Running on a single machine with ``run_once``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you want a task to run only on the first host in your batch of hosts, set ``run_once`` to true on that task::
+
+ ---
+ # ...
+
+ tasks:
+
+ # ...
+
+ - command: /opt/application/upgrade_db.py
+ run_once: true
+
+ # ...
+
+Ansible executes this task on the first host in the current batch and applies all results and facts to all the hosts in the same batch. This approach is similar to applying a conditional to a task such as::
+
+ - command: /opt/application/upgrade_db.py
+ when: inventory_hostname == webservers[0]
+
+However, with ``run_once``, the results are applied to all the hosts. To run the task on a specific host, instead of the first host in the batch, delegate the task::
+
+ - command: /opt/application/upgrade_db.py
+ run_once: true
+ delegate_to: web01.example.org
+
+As always with :ref:`delegation <playbooks_delegation>`, the action will be executed on the delegated host, but the information is still that of the original host in the task.
+
+.. note::
+ When used together with ``serial``, tasks marked as ``run_once`` will be run on one host in *each* serial batch. If the task must run only once regardless of ``serial`` mode, use
+ :code:`when: inventory_hostname == ansible_play_hosts_all[0]` construct.
+
+.. note::
+ Any conditional (i.e `when:`) will use the variables of the 'first host' to decide if the task runs or not, no other hosts will be tested.
+
+.. note::
+ If you want to avoid the default behavior of setting the fact for all hosts, set ``delegate_facts: True`` for the specific task or block.
+
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
+ :ref:`playbooks_delegation`
+ Running tasks on or assigning facts to specific machines
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_templating.rst b/docs/docsite/rst/user_guide/playbooks_templating.rst
index 350f659ce2..162ab81320 100644
--- a/docs/docsite/rst/user_guide/playbooks_templating.rst
+++ b/docs/docsite/rst/user_guide/playbooks_templating.rst
@@ -48,7 +48,7 @@ fmt
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_tests.rst b/docs/docsite/rst/user_guide/playbooks_tests.rst
index 5ba5b42060..cb74de53ef 100644
--- a/docs/docsite/rst/user_guide/playbooks_tests.rst
+++ b/docs/docsite/rst/user_guide/playbooks_tests.rst
@@ -386,7 +386,7 @@ The following tasks are illustrative of the tests meant to check the status of t
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_variables.rst b/docs/docsite/rst/user_guide/playbooks_variables.rst
index 5e296ec08e..c6ead40afe 100644
--- a/docs/docsite/rst/user_guide/playbooks_variables.rst
+++ b/docs/docsite/rst/user_guide/playbooks_variables.rst
@@ -437,7 +437,7 @@ For information about advanced YAML syntax used to declare variables and have mo
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`playbooks_best_practices`
- Best practices in playbooks
+ Tips and tricks for playbooks
:ref:`special_variables`
List of special variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
diff --git a/docs/docsite/rst/user_guide/playbooks_vault.rst b/docs/docsite/rst/user_guide/playbooks_vault.rst
index 522d58817e..03bd2c040f 100644
--- a/docs/docsite/rst/user_guide/playbooks_vault.rst
+++ b/docs/docsite/rst/user_guide/playbooks_vault.rst
@@ -1,157 +1,6 @@
-.. _playbooks_vault:
+:orphan:
-Using Vault in playbooks
+Using vault in playbooks
========================
-.. contents:: Topics
-
-The "Vault" is a feature of Ansible that allows you to keep sensitive data such as passwords or keys protected at rest, rather than as plaintext in playbooks or roles. These vaults can then be distributed or placed in source control.
-
-There are 2 types of vaulted content and each has their own uses and limitations:
-
-:Vaulted files:
- * The full file is encrypted in the vault, this can contain Ansible variables or any other type of content.
- * It will always be decrypted when loaded or referenced, Ansible cannot know if it needs the content unless it decrypts it.
- * It can be used for inventory, anything that loads variables (i.e vars_files, group_vars, host_vars, include_vars, etc)
- and some actions that deal with files (i.e M(copy), M(assemble), M(script), etc).
-
-:Single encrypted variable:
- * Only specific variables are encrypted inside a normal 'variable file'.
- * Does not work for other content, only variables.
- * Decrypted on demand, so you can have vaulted variables with different vault secrets and only provide those needed.
- * You can mix vaulted and non vaulted variables in the same file, even inline in a play or role.
-
-.. warning::
- * Vault ONLY protects data 'at rest'. Once decrypted, play and plugin authors are responsible for avoiding any secret disclosure,
- see :ref:`no_log <keep_secret_data>` for details on hiding output.
-
-To enable this feature, a command line tool, :ref:`ansible-vault` is used to edit files, and a command line flag :option:`--ask-vault-pass <ansible-vault-create --ask-vault-pass>`, :option:`--vault-password-file <ansible-vault-create --vault-password-file>` or :option:`--vault-id <ansible-playbook --vault-id>` is used. You can also modify your ``ansible.cfg`` file to specify the location of a password file or configure Ansible to always prompt for the password. These options require no command line flag usage.
-
-For best practices advice, refer to :ref:`best_practices_for_variables_and_vaults`.
-
-Running a Playbook With Vault
-`````````````````````````````
-
-To run a playbook that contains vault-encrypted data files, you must provide the vault password.
-
-To specify the vault-password interactively::
-
- ansible-playbook site.yml --ask-vault-pass
-
-This prompt will then be used to decrypt (in memory only) any vault encrypted files that are accessed.
-
-Alternatively, passwords can be specified with a file or a script (the script version will require Ansible 1.7 or later). When using this flag, ensure permissions on the file are such that no one else can access your key and do not add your key to source control::
-
- ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt
-
- ansible-playbook site.yml --vault-password-file ~/.vault_pass.py
-
-The password should be a string stored as a single line in the file.
-
-If you are using a script instead of a flat file, ensure that it is marked as executable, and that the password is printed to standard output. If your script needs to prompt for data, prompts can be sent to standard error.
-
-.. note::
- You can also set :envvar:`ANSIBLE_VAULT_PASSWORD_FILE` environment variable, e.g. ``ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass.txt`` and Ansible will automatically search for the password in that file.
-
- This is something you may wish to do if using Ansible from a continuous integration system like Jenkins.
-
-The :option:`--vault-password-file <ansible-pull --vault-password-file>` option can also be used with the :ref:`ansible-pull` command if you wish, though this would require distributing the keys to your nodes, so understand the implications -- vault is more intended for push mode.
-
-
-Multiple Vault Passwords
-````````````````````````
-
-Ansible 2.4 and later support the concept of multiple vaults that are encrypted with different passwords
-Different vaults can be given a label to distinguish them (generally values like dev, prod etc.).
-
-The :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` and
-:option:`--vault-password-file <ansible-playbook --vault-password-file>` options can be used as long as
-only a single password is needed for any given run.
-
-Alternatively the :option:`--vault-id <ansible-playbook --vault-id>` option can be used to provide the
-password and indicate which vault label it's for. This can be clearer when multiple vaults are used within
-a single inventory. For example:
-
-To be prompted for the 'dev' password:
-
-.. code-block:: bash
-
- ansible-playbook site.yml --vault-id dev@prompt
-
-To get the 'dev' password from a file or script:
-
-.. code-block:: bash
-
- ansible-playbook site.yml --vault-id dev@~/.vault_pass.txt
-
- ansible-playbook site.yml --vault-id dev@~/.vault_pass.py
-
-If multiple vault passwords are required for a single run, :option:`--vault-id <ansible-playbook --vault-id>` must
-be used as it can be specified multiple times to provide the multiple passwords. For example:
-
-To read the 'dev' password from a file and prompt for the 'prod' password:
-
-.. code-block:: bash
-
- ansible-playbook site.yml --vault-id dev@~/.vault_pass.txt --vault-id prod@prompt
-
-The :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or
-:option:`--vault-password-file <ansible-playbook --vault-password-file>` options can be used to specify one of
-the passwords, but it's generally cleaner to avoid mixing these with :option:`--vault-id <ansible-playbook --vault-id>`.
-
-.. note::
- By default the vault label (dev, prod etc.) is just a hint. Ansible will try to decrypt each
- vault with every provided password.
-
- Setting the config option :ref:`DEFAULT_VAULT_ID_MATCH` will change this behavior so that each password
- is only used to decrypt data that was encrypted with the same label. See :ref:`specifying_vault_ids`
- for more details.
-
-Vault Password Client Scripts
-`````````````````````````````
-
-Ansible 2.5 and later support using a single executable script to get different passwords depending on the
-vault label. These client scripts must have a file name that ends with :file:`-client`. For example:
-
-To get the dev password from the system keyring using the :file:`contrib/vault/vault-keyring-client.py` script:
-
-.. code-block:: bash
-
- ansible-playbook --vault-id dev@contrib/vault/vault-keyring-client.py
-
-See :ref:`vault_password_client_scripts` for a complete explanation of this topic.
-
-
-.. _single_encrypted_variable:
-
-Single Encrypted Variable
-`````````````````````````
-
-As of version 2.3, Ansible can now use a vaulted variable that lives in an otherwise 'clear text' YAML file::
-
- notsecret: myvalue
- mysecret: !vault |
- $ANSIBLE_VAULT;1.1;AES256
- 66386439653236336462626566653063336164663966303231363934653561363964363833313662
- 6431626536303530376336343832656537303632313433360a626438346336353331386135323734
- 62656361653630373231613662633962316233633936396165386439616533353965373339616234
- 3430613539666330390a313736323265656432366236633330313963326365653937323833366536
- 34623731376664623134383463316265643436343438623266623965636363326136
- other_plain_text: othervalue
-
-To create a vaulted variable, use the :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command. See :ref:`encrypt_string` for details.
-
-This vaulted variable will be decrypted with the supplied vault secret and used as a normal variable. The ``ansible-vault`` command line supports stdin and stdout for encrypting data on the fly, which can be used from your favorite editor to create these vaulted variables; you just have to be sure to add the ``!vault`` tag so both Ansible and YAML are aware of the need to decrypt. The ``|`` is also required, as vault encryption results in a multi-line string.
-
-.. note::
- Inline vaults ONLY work on variables, you cannot use directly on a task's options.
-
-.. _encrypt_string:
-
-Using encrypt_string
-````````````````````
-
-This command will output a string in the above format ready to be included in a YAML file.
-The string to encrypt can be provided via stdin, command line arguments, or via an interactive prompt.
-
-See :ref:`encrypt_string_for_use_in_yaml`.
+The documentation regarding Ansible Vault has moved. The new location is here: :ref:`vault`. Please update any links you may have made directly to this page.
diff --git a/docs/docsite/rst/user_guide/plugin_filtering_config.rst b/docs/docsite/rst/user_guide/plugin_filtering_config.rst
index fd5a3e9349..2e9900c9b8 100644
--- a/docs/docsite/rst/user_guide/plugin_filtering_config.rst
+++ b/docs/docsite/rst/user_guide/plugin_filtering_config.rst
@@ -1,12 +1,9 @@
.. _plugin_filtering_config:
-Plugin Filter Configuration
-===========================
+Blacklisting modules
+====================
-Ansible 2.5 adds the ability for a site administrator to blacklist modules that they do not want to
-be available to Ansible. This is configured via a yaml configuration file (by default,
-:file:`/etc/ansible/plugin_filters.yml`). Use ``plugin_filters_cfg`` configuration
-in ``defaults`` section to change this configuration file path. The format of the file is:
+If you want to avoid using certain modules, you can blacklist them to prevent Ansible from loading them. To blacklist plugins, create a yaml configuration file. The default location for this file is :file:`/etc/ansible/plugin_filters.yml`, or you can select a different path for the blacklist file using the :ref:`PLUGIN_FILTERS_CFG` setting in the ``defaults`` section of your ansible.cfg. Here is an example blacklist file:
.. code-block:: YAML
@@ -20,12 +17,10 @@ in ``defaults`` section to change this configuration file path. The format of th
The file contains two fields:
-* a version so that it will be possible to update the format while keeping backwards
- compatibility in the future. The present version should be the string, ``"1.0"``
+ * A file version so that you can update the format while keeping backwards compatibility in the future. The present version should be the string, ``"1.0"``
-* a list of modules to blacklist. Any module listed here will not be found by Ansible when it
- searches for a module to invoke for a task.
+ * A list of modules to blacklist. Any module in this list will not be loaded by Ansible when it searches for a module to invoke for a task.
.. note::
- The ``stat`` module is required for Ansible to run. So, please make sure you do not add this module in a blacklist modules list.
+ You cannot blacklist the ``stat`` module, as it is required for Ansible to run.
diff --git a/docs/docsite/rst/user_guide/vault.rst b/docs/docsite/rst/user_guide/vault.rst
index b7739639cc..67b86f14b2 100644
--- a/docs/docsite/rst/user_guide/vault.rst
+++ b/docs/docsite/rst/user_guide/vault.rst
@@ -1,209 +1,186 @@
.. _vault:
-Ansible Vault
-=============
+*************************************
+Encrypting content with Ansible Vault
+*************************************
-.. contents:: Topics
+Ansible Vault encrypts variables and files so you can protect sensitive content such as passwords or keys rather than leaving it visible as plaintext in playbooks or roles. To use Ansible Vault you need one or more passwords to encrypt and decrypt content. If you store your vault passwords in a third-party tool such as a secret manager, you need a script to access them. Use the passwords with the :ref:`ansible-vault` command-line tool to create and view encrypted variables, create encrypted files, encrypt existing files, or edit, re-key, or decrypt files. You can then place encrypted content under source control and share it more safely.
-Ansible Vault is a feature of ansible that allows you to keep sensitive data such as passwords or keys in encrypted files, rather than as plaintext in playbooks or roles. These vault files can then be distributed or placed in source control.
-
-To enable this feature, a command line tool - :ref:`ansible-vault` - is used to edit files, and a command line flag (:option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>`, :option:`--vault-password-file <ansible-playbook --vault-password-file>` or :option:`--vault-id <ansible-playbook --vault-id>`) is used. Alternately, you may specify the location of a password file or command Ansible to always prompt for the password in your ansible.cfg file. These options require no command line flag usage.
-
-For best practices advice, refer to :ref:`best_practices_for_variables_and_vaults`.
-
-.. _what_can_be_encrypted_with_vault:
-
-What Can Be Encrypted With Vault
-````````````````````````````````
-
-File-level encryption
-^^^^^^^^^^^^^^^^^^^^^
-
-Ansible Vault can encrypt any structured data file used by Ansible.
+.. warning::
+ * Encryption with Ansible Vault ONLY protects 'data at rest'. Once the content is decrypted ('data in use'), play and plugin authors are responsible for avoiding any secret disclosure, see :ref:`no_log <keep_secret_data>` for details on hiding output.
-This can include "group_vars/" or "host_vars/" inventory variables, variables loaded by "include_vars" or "vars_files", or variable files passed on the ansible-playbook command line with ``-e @file.yml`` or ``-e @file.json``. Role variables and defaults are also included.
+You can use encrypted variables and files in ad-hoc commands and playbooks by supplying the passwords you used to encrypt them. You can modify your ``ansible.cfg`` file to specify the location of a password file or to always prompt for the password.
-Ansible tasks, handlers, and so on are also data so these can be encrypted with vault as well. To hide the names of variables that you're using, you can encrypt the task files in their entirety.
+.. contents::
+ :local:
-Ansible Vault can also encrypt arbitrary files, even binary files. If a vault-encrypted file is
-given as the ``src`` argument to the :ref:`copy <copy_module>`, :ref:`template <template_module>`,
-:ref:`unarchive <unarchive_module>`, :ref:`script <script_module>` or :ref:`assemble
-<assemble_module>` modules, the file will be placed at the destination on the target host decrypted
-(assuming a valid vault password is supplied when running the play).
+Managing vault passwords
+========================
-.. note::
- The advantages of file-level encryption are that it is easy to use and that password rotation is straightforward with :ref:`rekeying <rekeying_files>`.
- The drawback is that the contents of files are no longer easy to access and read. This may be problematic if it is a list of tasks (when encrypting a variables file, :ref:`best practice <best_practices_for_variables_and_vaults>` is to keep references to these variables in a non-encrypted file).
+Managing your encrypted content is easier if you develop a strategy for managing your vault passwords. A vault password can be any string you choose. There is no special command to create a vault password. However, you need to keep track of your vault passwords. Each time you encrypt a variable or file with Ansible Vault, you must provide a password. When you use an encrypted variable or file in a command or playbook, you must provide the same password that was used to encrypt it. To develop a strategy for managing vault passwords, start with two questions:
+ * Do you want to encrypt all your content with the same password, or use different passwords for different needs?
+ * Where do you want to store your password or passwords?
-Variable-level encryption
-^^^^^^^^^^^^^^^^^^^^^^^^^
+Choosing between a single password and multiple passwords
+---------------------------------------------------------
-Ansible also supports encrypting single values inside a YAML file, using the `!vault` tag to let YAML and Ansible know it uses special processing. This feature is covered in more detail :ref:`below <encrypt_string_for_use_in_yaml>`.
-
-.. note::
- The advantage of variable-level encryption is that files are still easily legible even if they mix plaintext and encrypted variables.
- The drawback is that password rotation is not as simple as with file-level encryption: the :ref:`rekey <ansible_vault_rekey>` command does not work with this method.
+If you have a small team or few sensitive values, you can use a single password for everything you encrypt with Ansible Vault. Store your vault password securely in a file or a secret manager as described below.
+If you have a larger team or many sensitive values, you can use multiple passwords. For example, you can use different passwords for different users or different levels of access. Depending on your needs, you might want a different password for each encrypted file, for each directory, or for each environment. For example, you might have a playbook that includes two vars files, one for the dev environment and one for the production environment, encrypted with two different passwords. When you run the playbook, select the correct vault password for the environment you are targeting, using a vault ID.
.. _vault_ids:
-Vault IDs and Multiple Vault Passwords
-``````````````````````````````````````
+Managing multiple passwords with vault IDs
+------------------------------------------
+If you use multiple vault passwords, you can differentiate one password from another with vault IDs. You use the vault ID in three ways:
-A vault ID is an identifier for one or more vault secrets;
-Ansible supports multiple vault passwords.
+ * Pass it with :option:`--vault-id <ansible-playbook --vault-id>` to the :ref:`ansible-vault` command when you create encrypted content
+ * Include it wherever you store the password for that vault ID (see :ref:`storing_vault_passwords`)
+ * Pass it with :option:`--vault-id <ansible-playbook --vault-id>` to the :ref:`ansible-playbook` command when you run a playbook that uses content you encrypted with that vault ID
-Vault IDs provide labels to distinguish between individual vault passwords.
+When you pass a vault ID as an option to the :ref:`ansible-vault` command, you add a label (a hint or nickname) to the encrypted content. This label documents which password you used to encrypt it. The encrypted variable or file includes the vault ID label in plain text in the header. The vault ID is the last element before the encrypted content. For example::
-To use vault IDs, you must provide an ID *label* of your choosing and a *source* to obtain its password (either ``prompt`` or a file path):
+ my_encrytped_var: !vault |
+ $ANSIBLE_VAULT;1.2;AES256;dev
+ 30613233633461343837653833666333643061636561303338373661313838333565653635353162
+ 3263363434623733343538653462613064333634333464660a663633623939393439316636633863
+ 61636237636537333938306331383339353265363239643939666639386530626330633337633833
+ 6664656334373166630a363736393262666465663432613932613036303963343263623137386239
+ 6330
+
+In addition to the label, you must provide a source for the related password. The source can be a prompt, a file, or a script, depending on how you are storing your vault passwords. The pattern looks like this:
.. code-block:: bash
--vault-id label@source
-This switch is available for all Ansible commands that can interact with vaults: :ref:`ansible-vault`, :ref:`ansible-playbook`, etc.
-
-Vault-encrypted content can specify which vault ID it was encrypted with.
-
-For example, a playbook can now include a vars file encrypted with a 'dev' vault
-ID and a 'prod' vault ID.
-
-.. note:
- Older versions of Ansible, before 2.4, only supported using one single vault password at a time.
+If your playbook uses multiple encrypted variables or files that you encrypted with different passwords, you must pass the vault IDs when you run that playbook. You can use :option:`--vault-id <ansible-playbook --vault-id>` by itself, with :option:`--vault-password-file <ansible-playbook --vault-password-file>`, or with :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>`. The pattern is the same as when you create encrypted content: include the label and the source for the matching password.
+See below for examples of encrypting content with vault IDs and using content encrypted with vault IDs. The :option:`--vault-id <ansible-playbook --vault-id>` option works with any Ansible command that interacts with vaults, including :ref:`ansible-vault`, :ref:`ansible-playbook`, and so on.
-.. _creating_files:
-
-Creating Encrypted Files
-````````````````````````
-
-To create a new encrypted data file, run the following command:
-
-.. code-block:: bash
-
- ansible-vault create foo.yml
+Limitations of vault IDs
+^^^^^^^^^^^^^^^^^^^^^^^^
-First you will be prompted for a password. After providing a password, the tool will launch whatever editor you have defined with $EDITOR, and defaults to vi. Once you are done with the editor session, the file will be saved as encrypted data.
+Ansible does not enforce using the same password every time you use a particular vault ID label. You can encrypt different variables or files with the same vault ID label but different passwords. This usually happens when you type the password at a prompt and make a mistake. It is possible to use different passwords with the same vault ID label on purpose. For example, you could use each label as a reference to a class of passwords, rather than a single password. In this scenario, you must always know which specific password or file to use in context. However, you are more likely to encrypt two files with the same vault ID label and different passwords by mistake. If you encrypt two files with the same label but different passwords by accident, you can :ref:`rekey <rekeying_files>` one file to fix the issue.
-The default cipher is AES (which is shared-secret based).
+Enforcing vault ID matching
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
-To create a new encrypted data file with the Vault ID 'password1' assigned to it and be prompted for the password, run:
+By default the vault ID label is only a hint to remind you which password you used to encrypt a variable or file. Ansible does not check that the vault ID in the header of the encrypted content matches the vault ID you provide when you use the content. Ansible decrypts all files and variables called by your command or playbook that are encrypted with the password you provide. To check the encrypted content and decrypt it only when the vault ID it contains matches the one you provide with ``--vault-id``, set the config option :ref:`DEFAULT_VAULT_ID_MATCH`. When you set :ref:`DEFAULT_VAULT_ID_MATCH`, each password is only used to decrypt data that was encrypted with the same label. This is efficient, predictable, and can reduce errors when different values are encrypted with different passwords.
-.. code-block:: bash
+.. note::
+ Even with the :ref:`DEFAULT_VAULT_ID_MATCH` setting enabled, Ansible does not enforce using the same password every time you use a particular vault ID label.
- ansible-vault create --vault-id password1@prompt foo.yml
+.. _storing_vault_passwords:
+Storing and accessing vault passwords
+-------------------------------------
-.. _editing_encrypted_files:
+You can memorize your vault password, or manually copy vault passwords from any source and paste them at a command-line prompt, but most users store them securely and access them as needed from within Ansible. You have two options for storing vault passwords that work from within Ansible: in files, or in a third-party tool such as the system keyring or a secret manager. If you store your passwords in a third-party tool, you need a vault password client script to retrieve them from within Ansible.
-Editing Encrypted Files
-```````````````````````
+Storing passwords in files
+^^^^^^^^^^^^^^^^^^^^^^^^^^
-To edit an encrypted file in place, use the :ref:`ansible-vault edit <ansible_vault_edit>` command.
-This command will decrypt the file to a temporary file and allow you to edit
-the file, saving it back when done and removing the temporary file:
+To store a vault password in a file, enter the password as a string on a single line in the file. Make sure the permissions on the file are appropriate. Do not add password files to source control. If you have multiple passwords, you can store them all in a single file, as long as they all have vault IDs. For each password, create a separate line and enter the vault ID, a space, then the password as a string. For example:
-.. code-block:: bash
+.. code-block:: text
- ansible-vault edit foo.yml
+ dev my_dev_pass
+ test my_test_pass
+ prod my_prod_pass
-To edit a file encrypted with the 'vault2' password file and assigned the 'pass2' vault ID:
-.. code-block:: bash
+.. _vault_password_client_scripts:
- ansible-vault edit --vault-id pass2@vault2 foo.yml
+Storing passwords in third-party tools with vault password client scripts
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+You can store your vault passwords on the system keyring, in a database, or in a secret manager and retrieve them from within Ansible using a vault password client script. Enter the password as a string on a single line. If your password has a vault ID, store it in a way that works with your password storage tool.
-.. _rekeying_files:
+To create a vault password client script:
-Rekeying Encrypted Files
-````````````````````````
+ * Create a file with a name ending in ``-client.py``
+ * Make the file executable
+ * Within the script itself:
+ * Print the passwords to standard output
+ * Accept a ``--vault-id`` option
+ * If the script prompts for data (for example, a database password), send the prompts to standard error
-Should you wish to change your password on a vault-encrypted file or files, you can do so with the rekey command:
+When you run a playbook that uses vault passwords stored in a third-party tool, specify the script as the source within the ``--vault-id`` flag. For example:
.. code-block:: bash
- ansible-vault rekey foo.yml bar.yml baz.yml
-
-This command can rekey multiple data files at once and will ask for the original
-password and also the new password.
+ ansible-playbook --vault-id dev@contrib/vault/vault-keyring-client.py
-To rekey files encrypted with the 'preprod2' vault ID and the 'ppold' file and be prompted for the new password:
+Ansible executes the client script with a ``--vault-id`` option so the script knows which vault ID label you specified. For example a script loading passwords from a secret manager can use the vault ID label to pick either the 'dev' or 'prod' password. The example command above results in the following execution of the client script:
.. code-block:: bash
- ansible-vault rekey --vault-id preprod2@ppold --new-vault-id preprod2@prompt foo.yml bar.yml baz.yml
-
-A different ID could have been set for the rekeyed files by passing it to ``--new-vault-id``.
+ contrib/vault/vault-keyring-client.py --vault-id dev
-.. _encrypting_files:
+For an example of a client script that loads passwords from the system keyring, see :file:`contrib/vault/vault-keyring-client.py`.
-Encrypting Unencrypted Files
-````````````````````````````
-If you have existing files that you wish to encrypt, use
-the :ref:`ansible-vault encrypt <ansible_vault_encrypt>` command. This command can operate on multiple files at once:
+Encrypting content with Ansible Vault
+=====================================
-.. code-block:: bash
+Once you have a strategy for managing and storing vault passwords, you can start encrypting content. You can encrypt two types of content with Ansible Vault: variables and files. Encrypted content always includes the ``!vault`` tag, which tells Ansible and YAML that the content needs to be decrypted, and a ``|`` character, which allows multi-line strings. Encrypted content created with ``--vault-id`` also contains the vault ID label. For more details about the encryption process and the format of content encrypted with Ansible Vault, see :ref:`vault_format`. This table shows the main differences between encrypted variables and encrypted files:
- ansible-vault encrypt foo.yml bar.yml baz.yml
+.. table::
+ :class: documentation-table
-To encrypt existing files with the 'project' ID and be prompted for the password:
+ ====================== ================================= ====================================
+ .. Encrypted variables Encrypted files
+ ====================== ================================= ====================================
+ How much is encrypted? Variables within a plaintext file The entire file
-.. code-block:: bash
+ When is it decrypted? On demand, only when needed Whenever loaded or referenced [#f1]_
- ansible-vault encrypt --vault-id project@prompt foo.yml bar.yml baz.yml
+ What can be encrypted? Only variables Any structured data file
-.. note::
+ ====================== ================================= ====================================
- It is technically possible to separately encrypt files or strings with the *same* vault ID but *different* passwords, if different password files or prompted passwords are provided each time.
- This could be desirable if you use vault IDs as references to classes of passwords (rather than a single password) and you always know which specific password or file to use in context. However this may be an unnecessarily complex use-case.
- If two files are encrypted with the same vault ID but different passwords by accident, you can use the :ref:`rekey <rekeying_files>` command to fix the issue.
+.. [#f1] Ansible cannot know if it needs content from an encrypted file unless it decrypts the file, so it decrypts all encrypted files referenced in your playbooks and roles.
+.. _encrypting_variables:
+.. _single_encrypted_variable:
-.. _decrypting_files:
+Encrypting individual variables with Ansible Vault
+--------------------------------------------------
-Decrypting Encrypted Files
-``````````````````````````
+You can encrypt single values inside a YAML file using the :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command. For one way to keep your vaulted variables safely visible, see :ref:`tip_for_variables_and_vaults`.
-If you have existing files that you no longer want to keep encrypted, you can permanently decrypt
-them by running the :ref:`ansible-vault decrypt <ansible_vault_decrypt>` command. This command will save them unencrypted
-to the disk, so be sure you do not want :ref:`ansible-vault edit <ansible_vault_edit>` instead:
+Advantages and disadvantages of encrypting variables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. code-block:: bash
+With variable-level encryption, your files are still easily legible. You can mix plaintext and encrypted variables, even inline in a play or role. However, password rotation is not as simple as with file-level encryption. You cannot :ref:`rekey <rekeying_files>` encrypted variables. Also, variable-level encryption only works on variables. If you want to encrypt tasks or other content, you must encrypt the entire file.
- ansible-vault decrypt foo.yml bar.yml baz.yml
+.. _encrypt_string_for_use_in_yaml:
+Creating encrypted variables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-.. _viewing_files:
+The :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command encrypts and formats any string you type (or copy or generate) into a format that can be included in a playbook, role, or variables file. To create a basic encrypted variable, pass three options to the :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command:
-Viewing Encrypted Files
-```````````````````````
+ * a source for the vault password (prompt, file, or script, with or without a vault ID)
+ * the string to encrypt
+ * the string name (the name of the variable)
-If you want to view the contents of an encrypted file without editing it, you can use the :ref:`ansible-vault view <ansible_vault_view>` command:
+The pattern looks like this:
.. code-block:: bash
- ansible-vault view foo.yml bar.yml baz.yml
-
-
-.. _encrypt_string_for_use_in_yaml:
-
-Use encrypt_string to create encrypted variables to embed in yaml
-`````````````````````````````````````````````````````````````````
-
-The :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>` command will encrypt and format a provided string into a format
-that can be included in :ref:`ansible-playbook` YAML files.
+ ansible-vault encrypt_string <password_source> '<string_to_encrypt>' --name '<string_name_of_variable>'
-To encrypt a string provided as a cli arg:
+For example, to encrypt the string 'foobar' using the only password stored in 'a_password_file' and name the variable 'the_secret':
.. code-block:: bash
ansible-vault encrypt_string --vault-password-file a_password_file 'foobar' --name 'the_secret'
-Result::
+The command above creates this content::
the_secret: !vault |
$ANSIBLE_VAULT;1.1;AES256
@@ -213,13 +190,13 @@ Result::
3438626666666137650a353638643435666633633964366338633066623234616432373231333331
6564
-To use a vault-id label for 'dev' vault-id:
+To encrypt the string 'foooodev', add the vault ID label 'dev' with the 'dev' vault password stored in 'a_password_file', and call the encrypted variable 'the_dev_secret':
.. code-block:: bash
ansible-vault encrypt_string --vault-id dev@a_password_file 'foooodev' --name 'the_dev_secret'
-Result::
+The command above creates this content::
the_dev_secret: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
@@ -229,17 +206,17 @@ Result::
6664656334373166630a363736393262666465663432613932613036303963343263623137386239
6330
-To encrypt a string read from stdin and name it 'db_password':
+To encrypt the string 'letmein' read from stdin, add the vault ID 'test' using the 'test' vault password stored in `a_password_file`, and name the variable 'test_db_password':
.. code-block:: bash
- echo -n 'letmein' | ansible-vault encrypt_string --vault-id dev@a_password_file --stdin-name 'db_password'
+ echo -n 'letmein' | ansible-vault encrypt_string --vault-id test@a_password_file --stdin-name 'test_db_password'
.. warning::
- This method leaves the string in your shell history. Do not use it outside of testing.
+ Typing secret content directly at the command line (without a prompt) leaves the secret string in your shell history. Do not do this outside of testing.
-Result::
+The command above creates this output::
Reading plaintext input from stdin. (ctrl-d to end input)
db_password: !vault |
@@ -250,24 +227,25 @@ Result::
6565633133366366360a326566323363363936613664616364623437336130623133343530333739
3039
-To be prompted for a string to encrypt, encrypt it, and give it the name 'new_user_password':
-
+To be prompted for a string to encrypt, encrypt it with the 'dev' vault password from 'a_password_file', name the variable 'new_user_password' and give it the vault ID label 'dev':
.. code-block:: bash
ansible-vault encrypt_string --vault-id dev@a_password_file --stdin-name 'new_user_password'
-Output::
+The command above triggers this prompt:
+
+.. code-block:: bash
Reading plaintext input from stdin. (ctrl-d to end input)
-User enters 'hunter2' and hits ctrl-d.
+Type the string to encrypt (for example, 'hunter2'), hit ctrl-d, and wait.
.. warning::
- Do not press Enter after supplying the string. That will add a newline to the encrypted value.
+ Do not press ``Enter`` after supplying the string to encrypt. That will add a newline to the encrypted value.
-Result::
+The sequence above creates this output::
new_user_password: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
@@ -277,186 +255,263 @@ Result::
3866363862363335620a376466656164383032633338306162326639643635663936623939666238
3161
-See also :ref:`single_encrypted_variable`
+You can add the output from any of the examples above to any playbook, variables file, or role for future use. Encrypted variables are larger than plain-text variables, but they protect your sensitive content while leaving the rest of the playbook, variables file, or role in plain text so you can easily read it.
+
+Viewing encrypted variables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
-After you added the encrypted value to a var file (vars.yml), you can see the original value using the debug module.
+You can view the original value of an encrypted variable using the debug module. You must pass the password that was used to encrypt the variable. For example, if you stored the variable created by the last example above in a file called 'vars.yml', you could view the unencrypted value of that variable like this:
.. code-block:: console
- ansible localhost -m debug -a var="new_user_password" -e "@vars.yml" --ask-vault-pass
- Vault password:
+ ansible localhost -m debug -a var="new_user_password" -e "@vars.yml" --vault-id dev@a_password_file
localhost | SUCCESS => {
"new_user_password": "hunter2"
}
-.. _providing_vault_passwords:
+Encrypting files with Ansible Vault
+-----------------------------------
+
+Ansible Vault can encrypt any structured data file used by Ansible, including:
+
+ * group variables files from inventory
+ * host variables files from inventory
+ * variables files passed to ansible-playbook with ``-e @file.yml`` or ``-e @file.json``
+ * variables files loaded by ``include_vars`` or ``vars_files``
+ * variables files in roles
+ * defaults files in roles
+ * tasks files
+ * handlers files
+ * binary files or other arbitrary files
-Providing Vault Passwords
-`````````````````````````
+The full file is encrypted in the vault.
-When all data is encrypted using a single password the :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>`
-or :option:`--vault-password-file <ansible-playbook --vault-password-file>` cli options should be used.
+Advantages and disadvantages of encrypting files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+File-level encryption is easy to use. Password rotation for encrypted files is straightforward with the :ref:`rekey <rekeying_files>` command. Encrypting files can hide not only sensitive values, but the names of the variables you use. However, with file-level encryption the contents of files are no longer easy to access and read. This may be a problem with encrypted tasks files. When encrypting a variables file, see :ref:`tip_for_variables_and_vaults` for one way to keep references to these variables in a non-encrypted file. Ansible always decrypts the entire encrypted file when it is when loaded or referenced, because Ansible cannot know if it needs the content unless it decrypts it.
+
+.. _creating_files:
+
+Creating encrypted files
+^^^^^^^^^^^^^^^^^^^^^^^^
-For example, to use a password store in the text file :file:`/path/to/my/vault-password-file`:
+To create a new encrypted data file called 'foo.yml' with the 'test' vault password from 'multi_password_file':
.. code-block:: bash
- ansible-playbook --vault-password-file /path/to/my/vault-password-file site.yml
+ ansible-vault create --vault-id test@multi_password_file foo.yml
+
+The tool launches an editor (whatever editor you have defined with $EDITOR, default editor is vi). Add the content. When you close the the editor session, the file is saved as encrypted data. The file header reflects the vault ID used to create it:
+
+.. code-block:: text
-To prompt for a password:
+ ``$ANSIBLE_VAULT;1.2;AES256;test``
+
+To create a new encrypted data file with the vault ID 'my_new_password' assigned to it and be prompted for the password:
.. code-block:: bash
- ansible-playbook --ask-vault-pass site.yml
+ ansible-vault create --vault-id my_new_password@prompt foo.yml
+
+Again, add content to the file in the editor and save. Be sure to store the new password you created at the prompt, so you can find it when you want to decrypt that file.
+
+.. _encrypting_files:
+
+Encrypting existing files
+^^^^^^^^^^^^^^^^^^^^^^^^^
-To get the password from a vault password executable script :file:`my-vault-password.py`:
+To encrypt an existing file, use the :ref:`ansible-vault encrypt <ansible_vault_encrypt>` command. This command can operate on multiple files at once. For example:
.. code-block:: bash
- ansible-playbook --vault-password-file my-vault-password.py
+ ansible-vault encrypt foo.yml bar.yml baz.yml
-The config option :ref:`DEFAULT_VAULT_PASSWORD_FILE` can be used to specify a vault password file so that the
-:option:`--vault-password-file <ansible-playbook --vault-password-file>` cli option does not have to be
-specified every time.
+To encrypt existing files with the 'project' ID and be prompted for the password:
+.. code-block:: bash
-.. _specifying_vault_ids:
+ ansible-vault encrypt --vault-id project@prompt foo.yml bar.yml baz.yml
-Labelling Vaults
-^^^^^^^^^^^^^^^^
-Since Ansible 2.4 the :option:`--vault-id <ansible-playbook --vault-id>` can be used to indicate which vault ID
-('dev', 'prod', 'cloud', etc) a password is for as well as how to source the password (prompt, a file path, etc).
+.. _viewing_files:
-By default the vault-id label is only a hint, any values encrypted with the password will be decrypted.
-The config option :ref:`DEFAULT_VAULT_ID_MATCH` can be set to require the vault id to match the vault ID
-used when the value was encrypted.
-This can reduce errors when different values are encrypted with different passwords.
+Viewing encrypted files
+^^^^^^^^^^^^^^^^^^^^^^^
-For example, to use a password file :file:`dev-password` for the vault-id 'dev':
+To view the contents of an encrypted file without editing it, you can use the :ref:`ansible-vault view <ansible_vault_view>` command:
.. code-block:: bash
- ansible-playbook --vault-id dev@dev-password site.yml
+ ansible-vault view foo.yml bar.yml baz.yml
-To prompt for the password for the 'dev' vault ID:
+
+.. _editing_encrypted_files:
+
+Editing encrypted files
+^^^^^^^^^^^^^^^^^^^^^^^
+
+To edit an encrypted file in place, use the :ref:`ansible-vault edit <ansible_vault_edit>` command. This command decrypts the file to a temporary file, allows you to edit the content, then saves and re-encrypts the content and removes the temporary file when you close the editor. For example:
.. code-block:: bash
- ansible-playbook --vault-id dev@prompt site.yml
+ ansible-vault edit foo.yml
-To get the 'dev' vault ID password from an executable script :file:`my-vault-password.py`:
+To edit a file encrypted with the ``vault2`` password file and assigned the vault ID ``pass2``:
.. code-block:: bash
- ansible-playbook --vault-id dev@my-vault-password.py
+ ansible-vault edit --vault-id pass2@vault2 foo.yml
+
+.. _rekeying_files:
-The config option :ref:`DEFAULT_VAULT_IDENTITY_LIST` can be used to specify a default vault ID and password source
-so that the :option:`--vault-id <ansible-playbook --vault-id>` cli option does not have to be specified every time.
+Changing the password and/or vault ID on encrypted files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+To change the password on an encrypted file or files, use the :ref:`rekey <ansible_vault_rekey>` command:
-The :option:`--vault-id <ansible-playbook --vault-id>` option can also be used without specifying a vault-id.
-This behaviour is equivalent to :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or
-:option:`--vault-password-file <ansible-playbook --vault-password-file>` so is rarely used.
+.. code-block:: bash
-For example, to use a password file :file:`dev-password`:
+ ansible-vault rekey foo.yml bar.yml baz.yml
+
+This command can rekey multiple data files at once and will ask for the original password and also the new password. To set a different ID for the rekeyed files, pass the new ID to ``--new-vault-id``. For example, to rekey a list of files encrypted with the 'preprod1' vault ID from the 'ppold' file to the 'preprod2' vault ID and be prompted for the new password:
.. code-block:: bash
- ansible-playbook --vault-id dev-password site.yml
+ ansible-vault rekey --vault-id preprod1@ppold --new-vault-id preprod2@prompt foo.yml bar.yml baz.yml
+
+
+.. _decrypting_files:
+
+Decrypting encrypted files
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you have an encrypted file that you no longer want to keep encrypted, you can permanently decrypt it by running the :ref:`ansible-vault decrypt <ansible_vault_decrypt>` command. This command will save the file unencrypted to the disk, so be sure you do not want to :ref:`edit <ansible_vault_edit>` it instead.
+
+.. code-block:: bash
+
+ ansible-vault decrypt foo.yml bar.yml baz.yml
+
+
+.. _playbooks_vault:
+.. _providing_vault_passwords:
+
+Using encrypted variables and files
+===================================
+
+When you run a task or playbook that uses encrypted variables or files, you must provide the passwords to decrypt the variables or files. You can do this at the command line or in the playbook itself.
+
+Passing a single password
+-------------------------
+
+If all the encrypted variables and files your task or playbook needs use a single password, you can use the :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or :option:`--vault-password-file <ansible-playbook --vault-password-file>` cli options.
To prompt for the password:
.. code-block:: bash
- ansible-playbook --vault-id @prompt site.yml
+ ansible-playbook --ask-vault-pass site.yml
-To get the password from an executable script :file:`my-vault-password.py`:
+To retrieve the password from the :file:`/path/to/my/vault-password-file` file:
.. code-block:: bash
- ansible-playbook --vault-id my-vault-password.py
+ ansible-playbook --vault-password-file /path/to/my/vault-password-file site.yml
-.. note::
- Prior to Ansible 2.4, the :option:`--vault-id <ansible-playbook --vault-id>` option is not supported
- so :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or
- :option:`--vault-password-file <ansible-playbook --vault-password-file>` must be used.
+To get the password from the vault password client script :file:`my-vault-password-client.py`:
+.. code-block:: bash
+
+ ansible-playbook --vault-password-file my-vault-password-client.py
-Multiple Vault Passwords
-^^^^^^^^^^^^^^^^^^^^^^^^
-Ansible 2.4 and later support using multiple vault passwords, :option:`--vault-id <ansible-playbook --vault-id>` can
-be provided multiple times.
+.. _specifying_vault_ids:
+
+Passing vault IDs
+-----------------
-For example, to use a 'dev' password read from a file and to be prompted for the 'prod' password:
+You can also use the :option:`--vault-id <ansible-playbook --vault-id>` option to pass a single password with its vault label. This approach is clearer when multiple vaults are used within a single inventory.
+
+To prompt for the password for the 'dev' vault ID:
.. code-block:: bash
- ansible-playbook --vault-id dev@dev-password --vault-id prod@prompt site.yml
+ ansible-playbook --vault-id dev@prompt site.yml
+
+To retrieve the password for the 'dev' vault ID from the :file:`dev-password` file:
+
+.. code-block:: bash
+
+ ansible-playbook --vault-id dev@dev-password site.yml
-By default the vault ID labels (dev, prod etc.) are only hints, Ansible will attempt to decrypt vault content
-with each password. The password with the same label as the encrypted data will be tried first, after that
-each vault secret will be tried in the order they were provided on the command line.
+To get the password for the 'dev' vault ID from the vault password client script :file:`my-vault-password-client.py`:
-Where the encrypted data doesn't have a label, or the label doesn't match any of the provided labels, the
-passwords will be tried in the order they are specified.
+.. code-block:: bash
-In the above case, the 'dev' password will be tried first, then the 'prod' password for cases
-where Ansible doesn't know which vault ID is used to encrypt something.
+ ansible-playbook --vault-id dev@my-vault-password-client.py
-To add a vault ID label to the encrypted data use the :option:`--vault-id <ansible-vault-create --vault-id>` option
-with a label when encrypting the data.
+Passing multiple vault passwords
+--------------------------------
-The :ref:`DEFAULT_VAULT_ID_MATCH` config option can be set so that Ansible will only use the password with
-the same label as the encrypted data. This is more efficient and may be more predictable when multiple
-passwords are used.
+If your task or playbook requires multiple encrypted variables or files that you encrypted with different vault IDs, you must use the :option:`--vault-id <ansible-playbook --vault-id>` option, passing multiple ``--vault-id`` options to specify the vault IDs ('dev', 'prod', 'cloud', 'db') and sources for the passwords (prompt, file, script). . For example, to use a 'dev' password read from a file and to be prompted for the 'prod' password:
-The config option :ref:`DEFAULT_VAULT_IDENTITY_LIST` can have multiple values which is equivalent to multiple :option:`--vault-id <ansible-playbook --vault-id>` cli options.
+.. code-block:: bash
-The :option:`--vault-id <ansible-playbook --vault-id>` can be used in lieu of the :option:`--vault-password-file <ansible-playbook --vault-password-file>` or :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` options,
-or it can be used in combination with them.
+ ansible-playbook --vault-id dev@dev-password --vault-id prod@prompt site.yml
-When using :ref:`ansible-vault` commands that encrypt content (:ref:`ansible-vault encrypt <ansible_vault_encrypt>`, :ref:`ansible-vault encrypt_string <ansible_vault_encrypt_string>`, etc)
-only one vault-id can be used.
+By default the vault ID labels (dev, prod etc.) are only hints. Ansible attempts to decrypt vault content with each password. The password with the same label as the encrypted data will be tried first, after that each vault secret will be tried in the order they were provided on the command line.
+Where the encrypted data has no label, or the label does not match any of the provided labels, the passwords will be tried in the order they are specified. In the example above, the 'dev' password will be tried first, then the 'prod' password for cases where Ansible doesn't know which vault ID is used to encrypt something.
-.. _vault_password_client_scripts:
+Using ``--vault-id`` without a vault ID
+---------------------------------------
-Vault Password Client Scripts
-`````````````````````````````
+The :option:`--vault-id <ansible-playbook --vault-id>` option can also be used without specifying a vault-id. This behavior is equivalent to :option:`--ask-vault-pass <ansible-playbook --ask-vault-pass>` or :option:`--vault-password-file <ansible-playbook --vault-password-file>` so is rarely used.
-When implementing a script to obtain a vault password it may be convenient to know which vault ID label was
-requested. For example a script loading passwords from a secret manager may want to use the vault ID label to pick
-either the 'dev' or 'prod' password.
+For example, to use a password file :file:`dev-password`:
+
+.. code-block:: bash
+
+ ansible-playbook --vault-id dev-password site.yml
-Since Ansible 2.5 this is supported through the use of Client Scripts. A Client Script is an executable script
-with a name ending in ``-client``. Client Scripts are used to obtain vault passwords in the same way as any other
-executable script. For example:
+To prompt for the password:
.. code-block:: bash
- ansible-playbook --vault-id dev@contrib/vault/vault-keyring-client.py
+ ansible-playbook --vault-id @prompt site.yml
-The difference is in the implementation of the script. Client Scripts are executed with a ``--vault-id`` option
-so they know which vault ID label was requested. So the above Ansible execution results in the below execution
-of the Client Script:
+To get the password from an executable script :file:`my-vault-password-client.py`:
.. code-block:: bash
- contrib/vault/vault-keyring-client.py --vault-id dev
+ ansible-playbook --vault-id my-vault-password-client.py
+
-:file:`contrib/vault/vault-keyring-client.py` is an example of Client Script that loads passwords from the
-system keyring.
+Configuring defaults for using encrypted content
+================================================
+Setting a default vault ID
+--------------------------
+
+If you use one vault ID more frequently than any other, you can set the config option :ref:`DEFAULT_VAULT_IDENTITY_LIST` to specify a default vault ID and password source. Ansible will use the default vault ID and source any time you do not specify :option:`--vault-id <ansible-playbook --vault-id>`. You can set multiple values for this option. Setting multiple values is equivalent to passing multiple :option:`--vault-id <ansible-playbook --vault-id>` cli options.
+
+Setting a default password source
+---------------------------------
+
+If you use one vault password file more frequently than any other, you can set the :ref:`DEFAULT_VAULT_PASSWORD_FILE` config option or the :envvar:`ANSIBLE_VAULT_PASSWORD_FILE` environment variable to specify that file. For example, if you set ``ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass.txt``, Ansible will automatically search for the password in that file. This is useful if, for example, you use Ansible from a continuous integration system such as Jenkins.
+
+When are encrypted files made visible?
+======================================
+
+In general, content you encrypt with Ansible Vault remains encrypted after execution. However, there is one exception. If you pass an encrypted file as the ``src`` argument to the :ref:`copy <copy_module>`, :ref:`template <template_module>`, :ref:`unarchive <unarchive_module>`, :ref:`script <script_module>` or :ref:`assemble <assemble_module>` module, the file will not be encrypted on the target host (assuming you supply the correct vault password when you run the play). This behavior is intended and useful. You can encrypt a configuration file or template to avoid sharing the details of your configuration, but when you copy that configuration to servers in your environment, you want it to be decrypted so local users and processes can access it.
.. _speeding_up_vault:
-Speeding Up Vault Operations
-````````````````````````````
+Speeding up Ansible Vault
+=========================
If you have many encrypted files, decrypting them at startup may cause a perceptible delay. To speed this up, install the cryptography package:
@@ -467,14 +522,10 @@ If you have many encrypted files, decrypting them at startup may cause a percept
.. _vault_format:
-Vault Format
-````````````
+Format of files encrypted with Ansible Vault
+============================================
-A vault encrypted file is a UTF-8 encoded txt file.
-
-The file format includes a newline terminated header.
-
-For example::
+Ansible Vault creates UTF-8 encoded txt files. The file format includes a newline terminated header. For example::
$ANSIBLE_VAULT;1.1;AES256
@@ -482,25 +533,23 @@ or::
$ANSIBLE_VAULT;1.2;AES256;vault-id-label
-The header contains the vault format id, the vault format version, the vault cipher, and a vault-id label (with format version 1.2), separated by semi-colons ';'
-
-The first field ``$ANSIBLE_VAULT`` is the format id. Currently ``$ANSIBLE_VAULT`` is the only valid file format id. This is used to identify files that are vault encrypted (via vault.is_encrypted_file()).
+The header contains up to four elements, separated by semi-colons (``;``).
-The second field (``1.X``) is the vault format version. All supported versions of ansible will currently default to '1.1' or '1.2' if a labeled vault-id is supplied.
+ 1. The format ID (``$ANSIBLE_VAULT``). Currently ``$ANSIBLE_VAULT`` is the only valid format ID. The format ID identifies content that is encrypted with Ansible Vault (via vault.is_encrypted_file()).
-The '1.0' format is supported for reading only (and will be converted automatically to the '1.1' format on write). The format version is currently used as an exact string compare only (version numbers are not currently 'compared').
+ 2. The vault format version (``1.X``). All supported versions of Ansible will currently default to '1.1' or '1.2' if a labeled vault ID is supplied. The '1.0' format is supported for reading only (and will be converted automatically to the '1.1' format on write). The format version is currently used as an exact string compare only (version numbers are not currently 'compared').
-The third field (``AES256``) identifies the cipher algorithm used to encrypt the data. Currently, the only supported cipher is 'AES256'. [vault format 1.0 used 'AES', but current code always uses 'AES256']
+ 3. The cipher algorithm used to encrypt the data (``AES256``). Currently ``AES256`` is the only supported cipher algorithm. Vault format 1.0 used 'AES', but current code always uses 'AES256'.
-The fourth field (``vault-id-label``) identifies the vault-id label used to encrypt the data. For example using a vault-id of ``dev@prompt`` results in a vault-id-label of 'dev' being used.
+ 4. The vault ID label used to encrypt the data (optional, ``vault-id-label``) For example, if you encrypt a file with ``--vault-id dev@prompt``, the vault-id-label is ``dev``.
-Note: In the future, the header could change. Anything after the vault id and version can be considered to depend on the vault format version. This includes the cipher id, and any additional fields that could be after that.
+Note: In the future, the header could change. Fields after the format ID and format version depend on the format version, and future vault format versions may add more cipher algorithm options and/or additional fields.
The rest of the content of the file is the 'vaulttext'. The vaulttext is a text armored version of the
-encrypted ciphertext. Each line will be 80 characters wide, except for the last line which may be shorter.
+encrypted ciphertext. Each line is 80 characters wide, except for the last line which may be shorter.
-Vault Payload Format 1.1 - 1.2
-``````````````````````````````
+Ansible Vault payload format 1.1 - 1.2
+--------------------------------------
The vaulttext is a concatenation of the ciphertext and a SHA256 digest with the result 'hexlifyied'.
@@ -537,5 +586,3 @@ hexlify()'ed result of:
- the original plaintext
- padding up to the AES256 blocksize. (The data used for padding is based on `RFC5652 <https://tools.ietf.org/html/rfc5652#section-6.3>`_)
-
-
diff --git a/docs/docsite/rst/user_guide/windows_dsc.rst b/docs/docsite/rst/user_guide/windows_dsc.rst
index 10131f2581..4041630595 100644
--- a/docs/docsite/rst/user_guide/windows_dsc.rst
+++ b/docs/docsite/rst/user_guide/windows_dsc.rst
@@ -496,7 +496,7 @@ Setup IIS Website
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
:ref:`List of Windows Modules <windows_modules>`
Windows specific module list, all implemented in PowerShell
`User Mailing List <https://groups.google.com/group/ansible-project>`_
diff --git a/docs/docsite/rst/user_guide/windows_faq.rst b/docs/docsite/rst/user_guide/windows_faq.rst
index 92dd6f2a92..75e99d2ea8 100644
--- a/docs/docsite/rst/user_guide/windows_faq.rst
+++ b/docs/docsite/rst/user_guide/windows_faq.rst
@@ -229,7 +229,7 @@ host.
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-project>`_
Have a question? Stop by the google group!
`irc.freenode.net <http://irc.freenode.net>`_
diff --git a/docs/docsite/rst/user_guide/windows_setup.rst b/docs/docsite/rst/user_guide/windows_setup.rst
index c01db4b0ca..910fa06fc9 100644
--- a/docs/docsite/rst/user_guide/windows_setup.rst
+++ b/docs/docsite/rst/user_guide/windows_setup.rst
@@ -564,7 +564,7 @@ Here are the known ones:
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
:ref:`List of Windows Modules <windows_modules>`
Windows specific module list, all implemented in PowerShell
`User Mailing List <https://groups.google.com/group/ansible-project>`_
diff --git a/docs/docsite/rst/user_guide/windows_usage.rst b/docs/docsite/rst/user_guide/windows_usage.rst
index 245db79664..b39413cd03 100644
--- a/docs/docsite/rst/user_guide/windows_usage.rst
+++ b/docs/docsite/rst/user_guide/windows_usage.rst
@@ -504,7 +504,7 @@ guides for Windows modules differ substantially from those for standard standard
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
:ref:`List of Windows Modules <windows_modules>`
Windows specific module list, all implemented in PowerShell
`User Mailing List <https://groups.google.com/group/ansible-project>`_
diff --git a/docs/docsite/rst/user_guide/windows_winrm.rst b/docs/docsite/rst/user_guide/windows_winrm.rst
index 7c8410b638..03421cfbf1 100644
--- a/docs/docsite/rst/user_guide/windows_winrm.rst
+++ b/docs/docsite/rst/user_guide/windows_winrm.rst
@@ -904,7 +904,7 @@ Some of these limitations can be mitigated by doing one of the following:
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_best_practices`
- Best practices advice
+ Tips and tricks for playbooks
:ref:`List of Windows Modules <windows_modules>`
Windows specific module list, all implemented in PowerShell
`User Mailing List <https://groups.google.com/group/ansible-project>`_
diff --git a/docs/templates/list_of_CATEGORY_modules.rst.j2 b/docs/templates/list_of_CATEGORY_modules.rst.j2
deleted file mode 100644
index 92422406de..0000000000
--- a/docs/templates/list_of_CATEGORY_modules.rst.j2
+++ /dev/null
@@ -1,48 +0,0 @@
-{# avoids rST "isn't included in any toctree" errors for module docs #}
-:orphan:
-
-{% if title %}
-.. _@{ title.lower() + '_' + plugin_type + 's' }@:
-{% else %}
-.. _@{ plugin_type + 's' }@:
-{% endif %}
-
-{% if title %}
-@{ title }@ @{ plugin_type + 's' }@
-@{ '`' * title | length }@````````
-{% else %}
-@{ plugin_type + 's' }@
-```````
-{% endif %}
-
-{% if blurb %}
-@{ blurb }@
-
-{% endif %}
-
-{% if category['_modules'] %}
-
-{% for module in category['_modules'] | sort %}
- * :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%}
-{% endfor %}
-{% endif %}
-
-{% for name, info in subcategories.items() | sort %}
-
-.. _@{ name.lower() + '_' + title.lower() + '_' + plugin_type + 's' }@:
-
-@{ name.title() }@
-@{ '-' * name | length }@
-
-
-
-{% for module in info['_modules'] | sort %}
- * :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%}
-{% endfor %}
-
-{% endfor %}
-
-.. note::
- - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
- The module documentation details page may explain more about this rationale.
-
diff --git a/docs/templates/list_of_CATEGORY_plugins.rst.j2 b/docs/templates/list_of_CATEGORY_plugins.rst.j2
deleted file mode 100644
index 0f9b611fb1..0000000000
--- a/docs/templates/list_of_CATEGORY_plugins.rst.j2
+++ /dev/null
@@ -1,36 +0,0 @@
-.. _@{ title.lower() + '_' + plugin_type + 's' }@:
-
-@{ title }@ @{ plugin_type }@
-@{ '`' * title | length }@````````
-
-{% if blurb %}
-@{ blurb }@
-
-{% endif %}
-.. toctree:: :maxdepth: 1
-{% if category['_modules'] %}
-
-{% for module in category['_modules'] | sort %}
- @{ module }@{% if module_info[module]['deprecated'] %} **(D)**{% endif%}{% if module_info[module]['doc']['short_description'] %} -- @{ module_info[module]['doc']['short_description'] }@{% endif %} <plugins/@{ module_info[module]['primary_category'] }@/@{ module }@>
-{% endfor %}
-{% endif %}
-
-{% for name, info in subcategories.items() | sort %}
-
-.. _@{ name.lower() + '_' + title.lower() + '_' + plugin_type + 's' }@:
-
-@{ name.title() }@
-@{ '-' * name | length }@
-
-.. toctree:: :maxdepth: 1
-
-{% for module in info['_modules'] | sort %}
- :ref:`@{ module }@_@{ plugin_type }@`{% if module_info[module]['deprecated'] %} **(D)**{% endif%} -- @{ module_info[module]['doc']['short_description'] }@
-{% endfor %}
-
-{% endfor %}
-
-.. note::
- - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
- The module documentation details page may explain more about this rationale.
-
diff --git a/docs/templates/modules_by_support.rst.j2 b/docs/templates/modules_by_support.rst.j2
deleted file mode 100644
index eced773f3d..0000000000
--- a/docs/templates/modules_by_support.rst.j2
+++ /dev/null
@@ -1,45 +0,0 @@
-.. _@{ slug }@:
-
-{# avoids rST "isn't included in any toctree" errors for module index docs #}
-:orphan:
-
-**************************@{ '*' * maintainers | length }@
-Modules Maintained by the @{ maintainers }@
-**************************@{ '*' * maintainers | length }@
-
-.. contents::
- :local:
-
-{% for category, data in subcategories.items() | sort %}
-
-{% if category.lower() %}
-.. _@{ category.lower() + '_' + slug.lower() + '_categories' }@:
-{% else %}
-.. _@{ slug.lower() + '_categories' }@:
-{% endif %}
-
-@{ category.title() }@
-@{ '=' * category | length }@
-
-{% for name, info in data.items() | sort %}
-
-{% if name.lower() %}
-.. _@{ name.lower() + '_' + category + '_' + slug.lower() + '_' + plugin_type + 's' }@:
-{% else %}
-.. _@{ slug.lower() + '_' + category }@:
-{% endif %}
-
-@{ name.title() }@
-@{ '-' * name | length }@
-
-{% for module in info['_modules'] | sort %}
- * :ref:`@{ module }@_@{plugin_type}@`{% if module_info[module]['deprecated'] %} **(D)** {% endif%}
-{% endfor %}
-
-{% endfor %}
-
-{% endfor %}
-
-.. note::
- - **(D)**: This marks a module as deprecated, which means a module is kept for backwards compatibility but usage is discouraged.
- The module documentation details page may explain more about this rationale.
diff --git a/docs/templates/plugin.rst.j2 b/docs/templates/plugin.rst.j2
deleted file mode 100644
index f61d841b9a..0000000000
--- a/docs/templates/plugin.rst.j2
+++ /dev/null
@@ -1,442 +0,0 @@
-:source: @{ source }@
-
-{# avoids rST "isn't included in any toctree" errors for module docs #}
-{% if plugin_type == 'module' %}
-:orphan:
-{% endif %}
-
-.. _@{ module }@_@{ plugin_type }@:
-{% for alias in aliases %}
-.. _@{ alias }@_@{ plugin_type }@:
-{% endfor %}
-
-{% if short_description %}
-{% set title = module + ' -- ' + short_description | rst_ify %}
-{% else %}
-{% set title = module %}
-{% endif %}
-
-@{ title }@
-@{ '+' * title|length }@
-
-{% if version_added is defined and version_added != '' -%}
-.. versionadded:: @{ version_added | default('') }@
-{% endif %}
-
-.. contents::
- :local:
- :depth: 1
-
-{# ------------------------------------------
- #
- # Please note: this looks like a core dump
- # but it isn't one.
- #
- --------------------------------------------#}
-{% if deprecated is defined -%}
-
-
-DEPRECATED
-----------
-{# use unknown here? skip the fields? #}
-:Removed in Ansible: version: @{ deprecated['removed_in'] | default('') | string | rst_ify }@
-:Why: @{ deprecated['why'] | default('') | rst_ify }@
-:Alternative: @{ deprecated['alternative'] | default('') | rst_ify }@
-
-
-{% endif %}
-
-Synopsis
---------
-{% if description -%}
-
-{% for desc in description %}
-- @{ desc | rst_ify }@
-{% endfor %}
-
-{% endif %}
-
-{% if aliases is defined -%}
-Aliases: @{ ','.join(aliases) }@
-{% endif %}
-
-{% if requirements -%}
-
-Requirements
-------------
-{% if plugin_type == 'module' %}
-The below requirements are needed on the host that executes this @{ plugin_type }@.
-{% else %}
-The below requirements are needed on the local master node that executes this @{ plugin_type }@.
-{% endif %}
-
-{% for req in requirements %}
-- @{ req | rst_ify }@
-{% endfor %}
-
-{% endif %}
-
-{% if options -%}
-
-Parameters
-----------
-
-.. raw:: html
-
- <table border=0 cellpadding=0 class="documentation-table">
- {# Pre-compute the nesting depth to allocate columns -#}
- @{ to_kludge_ns('maxdepth', 1) -}@
- {% for key, value in options|dictsort recursive -%}
- @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
- {% if value.suboptions -%}
- {% if value.suboptions.items -%}
- @{ loop(value.suboptions.items()) -}@
- {% elif value.suboptions[0].items -%}
- @{ loop(value.suboptions[0].items()) -}@
- {% endif -%}
- {% endif -%}
- {% endfor -%}
- {# Header of the documentation -#}
- <tr>
- <th colspan="@{ from_kludge_ns('maxdepth') }@">Parameter</th>
- <th>Choices/<font color="blue">Defaults</font></th>
- {% if plugin_type != 'module' %}
- <th>Configuration</th>
- {% endif %}
- <th width="100%">Comments</th>
- </tr>
- {% for key, value in options|dictsort recursive %}
- <tr>
- {# indentation based on nesting level #}
- {% for i in range(1, loop.depth) %}
- <td class="elbow-placeholder"></td>
- {% endfor %}
- {# parameter name with required and/or introduced label #}
- <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
- <div class="ansibleOptionAnchor" id="parameter-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
- <b>@{ key }@</b>
- <a class="ansibleOptionLink" href="#parameter-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this option"></a>
- <div style="font-size: small">
- <span style="color: purple">@{ value.type | documented_type }@</span>
- {% if value.get('elements') %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
- {% if value.get('required', False) %} / <span style="color: red">required</span>{% endif %}
- </div>
- {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
- </td>
- {# default / choices #}
- <td>
- {# Turn boolean values in 'yes' and 'no' values #}
- {% if value.default is sameas true %}
- {% set _x = value.update({'default': 'yes'}) %}
- {% elif value.default is sameas false %}
- {% set _x = value.update({'default': 'no'}) %}
- {% endif %}
- {% if value.type == 'bool' %}
- {% set _x = value.update({'choices': ['no', 'yes']}) %}
- {% endif %}
- {# Show possible choices and highlight details #}
- {% if value.choices %}
- <ul style="margin: 0; padding: 0"><b>Choices:</b>
- {% for choice in value.choices %}
- {# Turn boolean values in 'yes' and 'no' values #}
- {% if choice is sameas true %}
- {% set choice = 'yes' %}
- {% elif choice is sameas false %}
- {% set choice = 'no' %}
- {% endif %}
- {% if (value.default is not list and value.default == choice) or (value.default is list and choice in value.default) %}
- <li><div style="color: blue"><b>@{ choice | escape }@</b>&nbsp;&larr;</div></li>
- {% else %}
- <li>@{ choice | escape }@</li>
- {% endif %}
- {% endfor %}
- </ul>
- {% endif %}
- {# Show default value, when multiple choice or no choices #}
- {% if value.default is defined and value.default not in value.choices %}
- <b>Default:</b><br/><div style="color: blue">@{ value.default | tojson | escape }@</div>
- {% endif %}
- </td>
- {# configuration #}
- {% if plugin_type != 'module' %}
- <td>
- {% if 'ini' in value %}
- <div> ini entries:
- {% for ini in value.ini %}
- <p>[@{ ini.section }@]<br>@{ ini.key }@ = @{ value.default | default('VALUE') }@</p>
- {% endfor %}
- </div>
- {% endif %}
- {% if 'env' in value %}
- {% for env in value.env %}
- <div>env:@{ env.name }@</div>
- {% endfor %}
- {% endif %}
- {% if 'vars' in value %}
- {% for myvar in value.vars %}
- <div>var: @{ myvar.name }@</div>
- {% endfor %}
- {% endif %}
- </td>
- {% endif %}
- {# description #}
- <td>
- {% for desc in value.description %}
- <div>@{ desc | replace('\n', '\n ') | html_ify }@</div>
- {% endfor %}
- {% if 'aliases' in value and value.aliases %}
- <div style="font-size: small; color: darkgreen"><br/>aliases: @{ value.aliases|join(', ') }@</div>
- {% endif %}
- </td>
- </tr>
- {% if value.suboptions %}
- {% if value.suboptions.items %}
- @{ loop(value.suboptions|dictsort) }@
- {% elif value.suboptions[0].items %}
- @{ loop(value.suboptions[0]|dictsort) }@
- {% endif %}
- {% endif %}
- {% endfor %}
- </table>
- <br/>
-
-{% endif %}
-
-{% if notes -%}
-Notes
------
-
-.. note::
-{% for note in notes %}
- - @{ note | rst_ify }@
-{% endfor %}
-
-{% endif %}
-
-{% if seealso -%}
-See Also
---------
-
-.. seealso::
-
-{% for item in seealso %}
-{% if item.module is defined and item.description is defined %}
- :ref:`@{ item.module }@_module`
- @{ item.description | rst_ify }@
-{% elif item.module is defined %}
- :ref:`@{ item.module }@_module`
- The official documentation on the **@{ item.module }@** module.
-{% elif item.name is defined and item.link is defined and item.description is defined %}
- `@{ item.name }@ <@{ item.link }@>`_
- @{ item.description | rst_ify }@
-{% elif item.ref is defined and item.description is defined %}
- :ref:`@{ item.ref }@`
- @{ item.description | rst_ify }@
-{% endif %}
-{% endfor %}
-
-{% endif %}
-
-{% if examples or plainexamples -%}
-
-Examples
---------
-
-.. code-block:: yaml+jinja
-
-{% for example in examples %}
-{% if example['description'] %}@{ example['description'] | indent(4, True) }@{% endif %}
-@{ example['code'] | escape | indent(4, True) }@
-{% endfor %}
-{% if plainexamples %}@{ plainexamples | indent(4, True) }@{% endif %}
-
-{% endif %}
-
-{% if not returnfacts and returndocs and returndocs.ansible_facts is defined %}
-{% set returnfacts = returndocs.ansible_facts.contains %}
-{% set _x = returndocs.pop('ansible_facts', None) %}
-{% endif %}
-
-{% if returnfacts -%}
-
-Returned Facts
---------------
-Facts returned by this module are added/updated in the ``hostvars`` host facts and can be referenced by name just like any other host fact. They do not need to be registered in order to use them.
-
-.. raw:: html
-
- <table border=0 cellpadding=0 class="documentation-table">
- {# Pre-compute the nesting depth to allocate columns #}
- @{ to_kludge_ns('maxdepth', 1) -}@
- {% for key, value in returnfacts|dictsort recursive %}
- @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
- {% if value.contains -%}
- {% if value.contains.items -%}
- @{ loop(value.contains.items()) -}@
- {% elif value.contains[0].items -%}
- @{ loop(value.contains[0].items()) -}@
- {% endif -%}
- {% endif -%}
- {% endfor -%}
- <tr>
- <th colspan="@{ from_kludge_ns('maxdepth') }@">Fact</th>
- <th>Returned</th>
- <th width="100%">Description</th>
- </tr>
- {% for key, value in returnfacts|dictsort recursive %}
- <tr>
- {% for i in range(1, loop.depth) %}
- <td class="elbow-placeholder"></td>
- {% endfor %}
- <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@" colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
- <div class="ansibleOptionAnchor" id="return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
- <b>@{ key }@</b>
- <a class="ansibleOptionLink" href="#return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this fact"></a>
- <div style="font-size: small">
- <span style="color: purple">@{ value.type | documented_type }@</span>
- {% if value.elements %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
- </div>
- {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
- </td>
- <td>@{ value.returned | html_ify }@</td>
- <td>
- {% if value.description is string %}
- <div>@{ value.description | html_ify }@
- </div>
- {% else %}
- {% for desc in value.description %}
- <div>@{ desc | html_ify }@
- </div>
- {% endfor %}
- {% endif %}
- <br/>
- {% if value.sample is defined and value.sample %}
- <div style="font-size: smaller"><b>Sample:</b></div>
- {# TODO: The sample should be escaped, using | escape or | htmlify, but both mess things up beyond repair with dicts #}
- <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
- {% endif %}
- </td>
- </tr>
- {# ---------------------------------------------------------
- # sadly we cannot blindly iterate through the child dicts,
- # since in some documentations,
- # lists are used instead of dicts. This handles both types
- # ---------------------------------------------------------#}
- {% if value.contains %}
- {% if value.contains.items %}
- @{ loop(value.contains|dictsort) }@
- {% elif value.contains[0].items %}
- @{ loop(value.contains[0]|dictsort) }@
- {% endif %}
- {% endif %}
- {% endfor %}
- </table>
- <br/><br/>
-
-{% endif %}
-
-{% if returndocs -%}
-
-Return Values
--------------
-Common return values are documented :ref:`here <common_return_values>`, the following are the fields unique to this @{ plugin_type }@:
-
-.. raw:: html
-
- <table border=0 cellpadding=0 class="documentation-table">
- @{ to_kludge_ns('maxdepth', 1) -}@
- {% for key, value in returndocs|dictsort recursive -%}
- @{ to_kludge_ns('maxdepth', [loop.depth, from_kludge_ns('maxdepth')] | max) -}@
- {% if value.contains -%}
- {% if value.contains.items -%}
- @{ loop(value.contains.items()) -}@
- {% elif value.contains[0].items -%}
- @{ loop(value.contains[0].items()) -}@
- {% endif -%}
- {% endif -%}
- {% endfor -%}
- <tr>
- <th colspan="@{ from_kludge_ns('maxdepth') }@">Key</th>
- <th>Returned</th>
- <th width="100%">Description</th>
- </tr>
- {% for key, value in returndocs|dictsort recursive %}
- <tr>
- {% for i in range(1, loop.depth) %}
- <td class="elbow-placeholder">&nbsp;</td>
- {% endfor %}
- <td colspan="@{ from_kludge_ns('maxdepth') - loop.depth0 }@">
- <div class="ansibleOptionAnchor" id="return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}"></div>
- <b>@{ key }@</b>
- <a class="ansibleOptionLink" href="#return-{% for part in value.full_key %}@{ part }@{% if not loop.last %}/{% endif %}{% endfor %}" title="Permalink to this return value"></a>
- <div style="font-size: small">
- <span style="color: purple">@{ value.type | documented_type }@</span>
- {% if value.elements %} / <span style="color: purple">elements=@{ value.elements | documented_type }@</span>{% endif %}
- </div>
- {% if value.version_added %}<div style="font-style: italic; font-size: small; color: darkgreen">added in @{value.version_added}@</div>{% endif %}
- </td>
- <td>@{ value.returned | html_ify }@</td>
- <td>
- {% if value.description is string %}
- <div>@{ value.description | html_ify |indent(4) | trim}@</div>
- {% else %}
- {% for desc in value.description %}
- <div>@{ desc | html_ify |indent(4) | trim}@</div>
- {% endfor %}
- {% endif %}
- <br/>
- {% if value.sample is defined and value.sample %}
- <div style="font-size: smaller"><b>Sample:</b></div>
- {# TODO: The sample should be escaped, using |escape or |htmlify, but both mess things up beyond repair with dicts #}
- <div style="font-size: smaller; color: blue; word-wrap: break-word; word-break: break-all;">@{ value.sample | replace('\n', '\n ') | html_ify }@</div>
- {% endif %}
- </td>
- </tr>
- {# ---------------------------------------------------------
- # sadly we cannot blindly iterate through the child dicts,
- # since in some documentations,
- # lists are used instead of dicts. This handles both types
- # ---------------------------------------------------------#}
- {% if value.contains %}
- {% if value.contains.items %}
- @{ loop(value.contains|dictsort) }@
- {% elif value.contains[0].items %}
- @{ loop(value.contains[0]|dictsort) }@
- {% endif %}
- {% endif %}
- {% endfor %}
- </table>
- <br/><br/>
-
-{% endif %}
-
-Status
-------
-
-{% if deprecated %}
-
-- This @{ plugin_type }@ will be removed in version @{ deprecated['removed_in'] | default('') | string | rst_ify }@. *[deprecated]*
-- For more information see `DEPRECATED`_.
-
-{% endif %}
-
-{% if author is defined -%}
-Authors
-~~~~~~~
-
-{% for author_name in author %}
-- @{ author_name }@
-{% endfor %}
-
-{% endif %}
-
-.. hint::
-{% if plugin_type == 'module' %}
- If you notice any issues in this documentation, you can `edit this document <https://github.com/ansible/ansible/edit/devel/lib/ansible/modules/@{ source }@?description=%23%23%23%23%23%20SUMMARY%0A%3C!---%20Your%20description%20here%20--%3E%0A%0A%0A%23%23%23%23%23%20ISSUE%20TYPE%0A-%20Docs%20Pull%20Request%0A%0A%2Blabel:%20docsite_pr>`_ to improve it.
-{% else %}
- If you notice any issues in this documentation, you can `edit this document <https://github.com/ansible/ansible/edit/devel/lib/ansible/plugins/@{ plugin_type }@/@{ source }@?description=%23%23%23%23%23%20SUMMARY%0A%3C!---%20Your%20description%20here%20--%3E%0A%0A%0A%23%23%23%23%23%20ISSUE%20TYPE%0A-%20Docs%20Pull%20Request%0A%0A%2Blabel:%20docsite_pr>`_ to improve it.
-
-
-.. hint::
- Configuration entries for each entry type have a low to high priority order. For example, a variable that is lower in the list will override a variable that is higher up.
-{% endif %}
diff --git a/docs/templates/plugin_deprecation_stub.rst.j2 b/docs/templates/plugin_deprecation_stub.rst.j2
deleted file mode 100644
index 5156f0bd96..0000000000
--- a/docs/templates/plugin_deprecation_stub.rst.j2
+++ /dev/null
@@ -1,18 +0,0 @@
-:source: @{ source }@
-
-{# avoids rST "isn't included in any toctree" errors for module docs #}
-:orphan:
-
-.. _@{ module }@_@{ plugin_type }@_alias_@{ alias }@:
-
-{% if short_description %}
-{% set title = alias + ' -- ' + short_description | rst_ify %}
-{% else %}
-{% set title = alias %}
-{% endif %}
-
-@{ title }@
-@{ '+' * title|length }@
-
-This is an alias for :ref:`@{ module }@ <@{ module }@_@{ plugin_type }@>`.
-This name has been **deprecated**. Please update your tasks to use the new name ``@{ module }@`` instead.
diff --git a/docs/templates/plugins_by_category.rst.j2 b/docs/templates/plugins_by_category.rst.j2
deleted file mode 100644
index 9febc09fad..0000000000
--- a/docs/templates/plugins_by_category.rst.j2
+++ /dev/null
@@ -1,9 +0,0 @@
-Plugin Index
-============
-
-
-.. toctree:: :maxdepth: 1
-
-{% for name in categories %}
- list_of_@{ name }@_plugins
-{% endfor %}
diff --git a/docs/templates/plugins_by_support.rst.j2 b/docs/templates/plugins_by_support.rst.j2
deleted file mode 100644
index fefe84a3b8..0000000000
--- a/docs/templates/plugins_by_support.rst.j2
+++ /dev/null
@@ -1,15 +0,0 @@
-.. _@{ slug }@:
-
-Plugins Maintained by the @{ maintainers }@
-``````````````````````````@{ '`' * maintainers | length }@
-
-.. toctree:: :maxdepth: 1
-
-{% for module in modules | sort %}
- @{ module }@{% if module_info[module]['deprecated'] %} **(D)**{% endif %} - @{ module_info[module]['doc']['short_description'] }@ <plugins/@{ module_info[module]['primary_category'] }@/@{ module }@>
-{% endfor %}
-
-.. note::
- - **(D)**: This marks a plugin as deprecated, which means a plugin is kept for backwards compatibility but usage is discouraged.
- The plugin documentation details page may explain more about this rationale.
-
diff --git a/hacking/build-ansible.py b/hacking/build-ansible.py
index 8b151d9d23..8ebb88d33b 100755
--- a/hacking/build-ansible.py
+++ b/hacking/build-ansible.py
@@ -22,15 +22,25 @@ except ImportError:
def build_lib_path(this_script=__file__):
- """Return path to the common build library directory"""
+ """Return path to the common build library directory."""
hacking_dir = os.path.dirname(this_script)
libdir = os.path.abspath(os.path.join(hacking_dir, 'build_library'))
return libdir
+def ansible_lib_path(this_script=__file__):
+ """Return path to the common build library directory."""
+ hacking_dir = os.path.dirname(this_script)
+ libdir = os.path.abspath(os.path.join(hacking_dir, '..', 'lib'))
+
+ return libdir
+
+
+sys.path.insert(0, ansible_lib_path())
sys.path.insert(0, build_lib_path())
+
from build_ansible import commands, errors
@@ -47,14 +57,15 @@ def create_arg_parser(program_name):
def main():
"""
- Main entrypoint of the script
+ Start our run.
"It all starts here"
"""
subcommands = load('build_ansible.command_plugins', subclasses=commands.Command)
arg_parser = create_arg_parser(os.path.basename(sys.argv[0]))
- arg_parser.add_argument('--debug', dest='debug', required=False, default=False, action='store_true',
+ arg_parser.add_argument('--debug', dest='debug', required=False, default=False,
+ action='store_true',
help='Show tracebacks and other debugging information')
subparsers = arg_parser.add_subparsers(title='Subcommands', dest='command',
help='for help use build-ansible.py SUBCOMMANDS -h')
diff --git a/hacking/build_library/build_ansible/command_plugins/collection_meta.py b/hacking/build_library/build_ansible/command_plugins/collection_meta.py
index be58bd4742..08c20c940d 100644
--- a/hacking/build_library/build_ansible/command_plugins/collection_meta.py
+++ b/hacking/build_library/build_ansible/command_plugins/collection_meta.py
@@ -11,14 +11,13 @@ import os.path
import pathlib
import yaml
-from jinja2 import Environment, FileSystemLoader
from ansible.module_utils.six import string_types
from ansible.module_utils._text import to_bytes
+from antsibull.jinja2.environment import doc_environment
# Pylint doesn't understand Python3 namespace modules.
from ..change_detection import update_file_if_different # pylint: disable=relative-beyond-top-level
from ..commands import Command # pylint: disable=relative-beyond-top-level
-from ..jinja2.filters import documented_type, rst_ify # pylint: disable=relative-beyond-top-level
DEFAULT_TEMPLATE_FILE = 'collections_galaxy_meta.rst.j2'
@@ -61,12 +60,7 @@ class DocumentCollectionMeta(Command):
normalize_options(options)
- env = Environment(loader=FileSystemLoader(template_dir),
- variable_start_string="@{",
- variable_end_string="}@",
- trim_blocks=True)
- env.filters['documented_type'] = documented_type
- env.filters['rst_ify'] = rst_ify
+ env = doc_environment(template_dir)
template = env.get_template(template_file)
output_name = os.path.join(output_dir, template_file.replace('.j2', ''))
diff --git a/hacking/build_library/build_ansible/command_plugins/docs_build.py b/hacking/build_library/build_ansible/command_plugins/docs_build.py
new file mode 100644
index 0000000000..fc6bcba44d
--- /dev/null
+++ b/hacking/build_library/build_ansible/command_plugins/docs_build.py
@@ -0,0 +1,164 @@
+# coding: utf-8
+# Copyright: (c) 2020, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Make coding more python3-ish
+from __future__ import absolute_import, division, print_function
+
+import glob
+import os
+import os.path
+import pathlib
+import shutil
+from tempfile import TemporaryDirectory
+
+import yaml
+
+from ansible.release import __version__ as ansible_base__version__
+
+# Pylint doesn't understand Python3 namespace modules.
+# pylint: disable=relative-beyond-top-level
+from ..commands import Command
+# pylint: enable=relative-beyond-top-level
+
+
+__metaclass__ = type
+
+
+DEFAULT_TOP_DIR = pathlib.Path(__file__).parents[4]
+DEFAULT_OUTPUT_DIR = pathlib.Path(__file__).parents[4] / 'docs/docsite'
+
+
+#
+# Subcommand base
+#
+
+def generate_base_docs(args):
+ """Regenerate the documentation for all plugins listed in the plugin_to_collection_file."""
+ # imports here so that they don't cause unnecessary deps for all of the plugins
+ from antsibull.cli import antsibull_docs
+
+ with TemporaryDirectory() as tmp_dir:
+ #
+ # Construct a deps file with our version of ansible_base in it
+ #
+ modified_deps_file = os.path.join(tmp_dir, 'ansible.deps')
+
+ # The _acd_version doesn't matter
+ deps_file_contents = {'_acd_version': ansible_base__version__,
+ '_ansible_base_version': ansible_base__version__}
+
+ with open(modified_deps_file, 'w') as f:
+ f.write(yaml.dump(deps_file_contents))
+
+ # Generate the plugin rst
+ return antsibull_docs.run(['antsibull-docs', 'stable', '--deps-file', modified_deps_file,
+ '--ansible-base-cache', str(args.top_dir),
+ '--dest-dir', args.output_dir])
+
+ # If we make this more than just a driver for antsibull:
+ # Run other rst generation
+ # Run sphinx build
+
+
+#
+# Subcommand full
+#
+
+def generate_full_docs(args):
+ """Regenerate the documentation for all plugins listed in the plugin_to_collection_file."""
+ # imports here so that they don't cause unnecessary deps for all of the plugins
+ import sh
+ from antsibull.cli import antsibull_docs
+ from packaging.version import Version
+
+ ansible_base_ver = Version(ansible_base__version__)
+ ansible_base_major_ver = '{0}.{1}'.format(ansible_base_ver.major, ansible_base_ver.minor)
+
+ with TemporaryDirectory() as tmp_dir:
+ sh.git(['clone', 'https://github.com/ansible-community/ansible-build-data'], _cwd=tmp_dir)
+ deps_files = glob.glob(os.path.join(tmp_dir, 'ansible-build-data',
+ ansible_base_major_ver, '*.deps'))
+ if not deps_files:
+ raise Exception('No deps files exist for version {0}'.format(ansible_base_major_ver))
+
+ # Find the latest version of the deps file for this version
+ latest = None
+ latest_ver = Version('0')
+ for filename in deps_files:
+ with open(filename, 'r') as f:
+ deps_data = yaml.safe_load(f.read())
+ new_version = Version(deps_data['_ansible_base_version'])
+ if new_version > latest_ver:
+ latest_ver = new_version
+ latest = filename
+
+ # Make a copy of the deps file so that we can set the ansible-base version to use
+ modified_deps_file = os.path.join(tmp_dir, 'ansible.deps')
+ shutil.copyfile(latest, modified_deps_file)
+
+ # Put our version of ansible-base into the deps file
+ with open(modified_deps_file, 'r') as f:
+ deps_data = yaml.safe_load(f.read())
+
+ deps_data['_ansible_base_version'] = ansible_base__version__
+
+ with open(modified_deps_file, 'w') as f:
+ f.write(yaml.dump(deps_data))
+
+ # Generate the plugin rst
+ return antsibull_docs.run(['antsibull-docs', 'stable', '--deps-file', modified_deps_file,
+ '--ansible-base-cache', str(args.top_dir),
+ '--dest-dir', args.output_dir])
+
+ # If we make this more than just a driver for antsibull:
+ # Run other rst generation
+ # Run sphinx build
+
+
+class CollectionPluginDocs(Command):
+ name = 'docs-build'
+ _ACTION_HELP = """Action to perform.
+ full: Regenerate the rst for the full ansible website.
+ base: Regenerate the rst for plugins in ansible-base and then build the website.
+ named: Regenerate the rst for the named plugins and then build the website.
+ """
+
+ @classmethod
+ def init_parser(cls, add_parser):
+ parser = add_parser(cls.name,
+ description='Generate documentation for plugins in collections.'
+ ' Plugins in collections will have a stub file in the normal plugin'
+ ' documentation location that says the module is in a collection and'
+ ' point to generated plugin documentation under the collections/'
+ ' hierarchy.')
+ parser.add_argument('action', action='store', choices=('full', 'base', 'named'),
+ default='full', help=cls._ACTION_HELP)
+ parser.add_argument("-o", "--output-dir", action="store", dest="output_dir",
+ default=DEFAULT_OUTPUT_DIR,
+ help="Output directory for generated doc files")
+ parser.add_argument("-t", "--top-dir", action="store", dest="top_dir",
+ default=DEFAULT_TOP_DIR,
+ help="Toplevel directory of this ansible-base checkout or expanded"
+ " tarball.")
+ parser.add_argument("-l", "--limit-to-modules", '--limit-to', action="store",
+ dest="limit_to", default=None,
+ help="Limit building module documentation to comma-separated list of"
+ " plugins. Specify non-existing plugin name for no plugins.")
+
+ @staticmethod
+ def main(args):
+ # normalize CLI args
+
+ if not args.output_dir:
+ args.output_dir = os.path.abspath(str(DEFAULT_OUTPUT_DIR))
+
+ if args.action == 'full':
+ return generate_full_docs(args)
+
+ if args.action == 'base':
+ return generate_base_docs(args)
+ # args.action == 'named' (Invalid actions are caught by argparse)
+ raise NotImplementedError('Building docs for specific files is not yet implemented')
+
+ # return 0
diff --git a/hacking/build_library/build_ansible/command_plugins/plugin_formatter.py b/hacking/build_library/build_ansible/command_plugins/plugin_formatter.py
deleted file mode 100644
index 475d86d97c..0000000000
--- a/hacking/build_library/build_ansible/command_plugins/plugin_formatter.py
+++ /dev/null
@@ -1,807 +0,0 @@
-# Copyright: (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
-# Copyright: (c) 2012-2014, Michael DeHaan <michael@ansible.com> and others
-# Copyright: (c) 2017, Ansible Project
-
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-
-import datetime
-import glob
-import json
-import os
-import re
-import sys
-import warnings
-from collections import defaultdict
-from copy import deepcopy
-from distutils.version import LooseVersion
-from functools import partial
-from pprint import PrettyPrinter
-
-try:
- from html import escape as html_escape
-except ImportError:
- # Python-3.2 or later
- import cgi
-
- def html_escape(text, quote=True):
- return cgi.escape(text, quote)
-
-import jinja2
-import yaml
-from jinja2 import Environment, FileSystemLoader
-
-from ansible.errors import AnsibleError
-from ansible.module_utils._text import to_bytes
-from ansible.module_utils.common.collections import is_sequence
-from ansible.module_utils.parsing.convert_bool import boolean
-from ansible.module_utils.six import iteritems, string_types
-from ansible.plugins.loader import fragment_loader
-from ansible.utils import plugin_docs
-from ansible.utils.display import Display
-
-# Pylint doesn't understand Python3 namespace modules.
-from ..change_detection import update_file_if_different # pylint: disable=relative-beyond-top-level
-from ..commands import Command # pylint: disable=relative-beyond-top-level
-from ..jinja2.filters import do_max, documented_type, html_ify, rst_fmt, rst_ify, rst_xline # pylint: disable=relative-beyond-top-level
-
-
-#####################################################################################
-# constants and paths
-
-# if a module is added in a version of Ansible older than this, don't print the version added information
-# in the module documentation because everyone is assumed to be running something newer than this already.
-TOO_OLD_TO_BE_NOTABLE = 2.4
-
-# Get parent directory of the directory this script lives in
-MODULEDIR = os.path.abspath(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), os.pardir, 'lib', 'ansible', 'modules'
-))
-
-# The name of the DOCUMENTATION template
-EXAMPLE_YAML = os.path.abspath(os.path.join(
- os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yml'
-))
-
-DEPRECATED = b" (D)"
-
-pp = PrettyPrinter()
-display = Display()
-
-
-# kludge_ns gives us a kludgey way to set variables inside of loops that need to be visible outside
-# the loop. We can get rid of this when we no longer need to build docs with less than Jinja-2.10
-# http://jinja.pocoo.org/docs/2.10/templates/#assignments
-# With Jinja-2.10 we can use jinja2's namespace feature, restoring the namespace template portion
-# of: fa5c0282a4816c4dd48e80b983ffc1e14506a1f5
-NS_MAP = {}
-
-
-def to_kludge_ns(key, value):
- NS_MAP[key] = value
- return ""
-
-
-def from_kludge_ns(key):
- return NS_MAP[key]
-
-
-test_list = partial(is_sequence, include_strings=False)
-
-
-def normalize_options(value):
- """Normalize boolean option value."""
-
- if value.get('type') == 'bool' and 'default' in value:
- try:
- value['default'] = boolean(value['default'], strict=True)
- except TypeError:
- pass
- return value
-
-
-def write_data(text, output_dir, outputname, module=None):
- ''' dumps module output to a file or the screen, as requested '''
-
- if output_dir is not None:
- if module:
- outputname = outputname % module
-
- if not os.path.exists(output_dir):
- os.makedirs(output_dir)
- fname = os.path.join(output_dir, outputname)
- fname = fname.replace(".py", "")
-
- try:
- updated = update_file_if_different(fname, to_bytes(text))
- except Exception as e:
- display.display("while rendering %s, an error occured: %s" % (module, e))
- raise
- if updated:
- display.display("rendering: %s" % module)
- else:
- print(text)
-
-
-IS_STDOUT_TTY = sys.stdout.isatty()
-
-
-def show_progress(progress):
- '''Show a little process indicator.'''
- if IS_STDOUT_TTY:
- sys.stdout.write('\r%s\r' % ("-/|\\"[progress % 4]))
- sys.stdout.flush()
-
-
-def get_plugin_info(module_dir, limit_to=None, verbose=False):
- '''
- Returns information about plugins and the categories that they belong to
-
- :arg module_dir: file system path to the top of the plugin directory
- :kwarg limit_to: If given, this is a list of plugin names to
- generate information for. All other plugins will be ignored.
- :returns: Tuple of two dicts containing module_info, categories, and
- aliases and a set listing deprecated modules:
-
- :module_info: mapping of module names to information about them. The fields of the dict are:
-
- :path: filesystem path to the module
- :deprecated: boolean. True means the module is deprecated otherwise not.
- :aliases: set of aliases to this module name
- :metadata: The modules metadata (as recorded in the module)
- :doc: The documentation structure for the module
- :seealso: The list of dictionaries with references to related subjects
- :examples: The module's examples
- :returndocs: The module's returndocs
-
- :categories: maps category names to a dict. The dict contains at
- least one key, '_modules' which contains a list of module names in
- that category. Any other keys in the dict are subcategories with
- the same structure.
-
- '''
-
- categories = dict()
- module_info = defaultdict(dict)
-
- # * windows powershell modules have documentation stubs in python docstring
- # format (they are not executed) so skip the ps1 format files
- # * One glob level for every module level that we're going to traverse
- files = (
- glob.glob("%s/*.py" % module_dir) +
- glob.glob("%s/*/*.py" % module_dir) +
- glob.glob("%s/*/*/*.py" % module_dir) +
- glob.glob("%s/*/*/*/*.py" % module_dir)
- )
-
- module_index = 0
- for module_path in files:
- # Do not list __init__.py files
- if module_path.endswith('__init__.py'):
- continue
-
- # Do not list blacklisted modules
- module = os.path.splitext(os.path.basename(module_path))[0]
- if module in plugin_docs.BLACKLIST['MODULE'] or module == 'base':
- continue
-
- # If requested, limit module documentation building only to passed-in
- # modules.
- if limit_to is not None and module.lower() not in limit_to:
- continue
-
- deprecated = False
- if module.startswith("_"):
- if os.path.islink(module_path):
- # Handle aliases
- source = os.path.splitext(os.path.basename(os.path.realpath(module_path)))[0]
- module = module.replace("_", "", 1)
- if source.startswith("_"):
- source = source.replace("_", "", 1)
- aliases = module_info[source].get('aliases', set())
- aliases.add(module)
- aliases_deprecated = module_info[source].get('aliases_deprecated', set())
- aliases_deprecated.add(module)
- # In case we just created this via get()'s fallback
- module_info[source]['aliases'] = aliases
- module_info[source]['aliases_deprecated'] = aliases_deprecated
- continue
- else:
- # Handle deprecations
- module = module.replace("_", "", 1)
- deprecated = True
-
- #
- # Regular module to process
- #
-
- module_index += 1
- show_progress(module_index)
-
- # use ansible core library to parse out doc metadata YAML and plaintext examples
- doc, examples, returndocs, metadata = plugin_docs.get_docstring(
- module_path, fragment_loader, verbose=verbose, collection_name='ansible.builtin')
-
- if metadata and 'removed' in metadata.get('status', []):
- continue
-
- category = categories
-
- # Start at the second directory because we don't want the "vendor"
- mod_path_only = os.path.dirname(module_path[len(module_dir):])
-
- # Find the subcategory for each module
- relative_dir = mod_path_only.split('/')[1]
- sub_category = mod_path_only[len(relative_dir) + 2:]
-
- primary_category = ''
- module_categories = []
- # build up the categories that this module belongs to
- for new_cat in mod_path_only.split('/')[1:]:
- if new_cat not in category:
- category[new_cat] = dict()
- category[new_cat]['_modules'] = []
- module_categories.append(new_cat)
- category = category[new_cat]
-
- category['_modules'].append(module)
-
- # the category we will use in links (so list_of_all_plugins can point to plugins/action_plugins/*'
- if module_categories:
- primary_category = module_categories[0]
-
- if not doc:
- display.error("*** ERROR: DOCUMENTATION section missing for %s. ***" % module_path)
- continue
-
- if 'options' in doc and doc['options'] is None:
- display.error("*** ERROR: DOCUMENTATION.options must be a dictionary/hash when used. ***")
- pos = getattr(doc, "ansible_pos", None)
- if pos is not None:
- display.error("Module position: %s, %d, %d" % doc.ansible_pos)
- doc['options'] = dict()
-
- for key, opt in doc.get('options', {}).items():
- doc['options'][key] = normalize_options(opt)
-
- # save all the information
- module_info[module] = {'path': module_path,
- 'source': os.path.relpath(module_path, module_dir),
- 'deprecated': deprecated,
- 'aliases': module_info[module].get('aliases', set()),
- 'aliases_deprecated': module_info[module].get('aliases_deprecated', set()),
- 'metadata': metadata,
- 'doc': doc,
- 'examples': examples,
- 'returndocs': returndocs,
- 'categories': module_categories,
- 'primary_category': primary_category,
- 'sub_category': sub_category,
- }
-
- # keep module tests out of becoming module docs
- if 'test' in categories:
- del categories['test']
-
- return module_info, categories
-
-
-def jinja2_environment(template_dir, typ, plugin_type):
-
- env = Environment(loader=FileSystemLoader(template_dir),
- variable_start_string="@{",
- variable_end_string="}@",
- trim_blocks=True)
- env.globals['xline'] = rst_xline
-
- # Can be removed (and template switched to use namespace) when we no longer need to build
- # with <Jinja-2.10
- env.globals['to_kludge_ns'] = to_kludge_ns
- env.globals['from_kludge_ns'] = from_kludge_ns
- if 'max' not in env.filters:
- # Jinja < 2.10
- env.filters['max'] = do_max
-
- if 'tojson' not in env.filters:
- # Jinja < 2.9
- env.filters['tojson'] = json.dumps
-
- templates = {}
- if typ == 'rst':
- env.filters['rst_ify'] = rst_ify
- env.filters['html_ify'] = html_ify
- env.filters['fmt'] = rst_fmt
- env.filters['xline'] = rst_xline
- env.filters['documented_type'] = documented_type
- env.tests['list'] = test_list
- templates['plugin'] = env.get_template('plugin.rst.j2')
- templates['plugin_deprecation_stub'] = env.get_template('plugin_deprecation_stub.rst.j2')
-
- if plugin_type == 'module':
- name = 'modules'
- else:
- name = 'plugins'
-
- templates['category_list'] = env.get_template('%s_by_category.rst.j2' % name)
- templates['support_list'] = env.get_template('%s_by_support.rst.j2' % name)
- templates['list_of_CATEGORY_modules'] = env.get_template('list_of_CATEGORY_%s.rst.j2' % name)
- else:
- raise Exception("Unsupported format type: %s" % typ)
-
- return templates
-
-
-def process_version_added(version_added):
- if not isinstance(version_added, string_types):
- return version_added
- if ':' not in version_added:
- return version_added
- # Strip tag from version_added. It suffices to do this here since
- # this is only used for ansible-base, and there the only valid tag
- # is `ansible.builtin:`.
- return version_added[version_added.index(':') + 1:]
-
-
-def too_old(added):
- if not added:
- return False
- try:
- added_tokens = str(added).split(".")
- readded = added_tokens[0] + "." + added_tokens[1]
- added_float = float(readded)
- except ValueError as e:
- warnings.warn("Could not parse %s: %s" % (added, str(e)))
- return False
- return added_float < TOO_OLD_TO_BE_NOTABLE
-
-
-def process_options(module, options, full_key=None):
- option_names = []
- if full_key is None:
- full_key = []
-
- if options:
- for (k, v) in iteritems(options):
- # Make sure that "full key" is contained
- full_key_k = full_key + [k]
- v['full_key'] = full_key_k
-
- # Error out if there's no description
- if 'description' not in v:
- raise AnsibleError("Missing required description for parameter '%s' in '%s' " % (k, module))
-
- # Make sure description is a list of lines for later formatting
- if isinstance(v['description'], string_types):
- v['description'] = [v['description']]
- elif not isinstance(v['description'], (list, tuple)):
- raise AnsibleError("Invalid type for options['%s']['description']."
- " Must be string or list of strings. Got %s" %
- (k, type(v['description'])))
-
- # Error out if required isn't a boolean (people have been putting
- # information on when something is required in here. Those need
- # to go in the description instead).
- required_value = v.get('required', False)
- if not isinstance(required_value, bool):
- raise AnsibleError("Invalid required value '%s' for parameter '%s' in '%s' (must be truthy)" % (required_value, k, module))
-
- # Strip old version_added information for options
- if 'version_added' in v:
- v['version_added'] = process_version_added(v['version_added'])
- if too_old(v['version_added']):
- del v['version_added']
-
- if 'suboptions' in v and v['suboptions']:
- if isinstance(v['suboptions'], dict):
- process_options(module, v['suboptions'], full_key=full_key_k)
- elif isinstance(v['suboptions'][0], dict):
- process_options(module, v['suboptions'][0], full_key=full_key_k)
-
- option_names.append(k)
-
- option_names.sort()
-
- return option_names
-
-
-def process_returndocs(returndocs, full_key=None):
- if full_key is None:
- full_key = []
-
- if returndocs:
- for (k, v) in iteritems(returndocs):
- # Make sure that "full key" is contained
- full_key_k = full_key + [k]
- v['full_key'] = full_key_k
-
- # Strip old version_added information for options
- if 'version_added' in v:
- v['version_added'] = process_version_added(v['version_added'])
- if too_old(v['version_added']):
- del v['version_added']
-
- # Process suboptions
- suboptions = v.get('contains')
- if suboptions:
- if isinstance(suboptions, dict):
- process_returndocs(suboptions, full_key=full_key_k)
- elif is_sequence(suboptions):
- process_returndocs(suboptions[0], full_key=full_key_k)
-
-
-def process_plugins(module_map, templates, outputname, output_dir, ansible_version, plugin_type):
- for module_index, module in enumerate(module_map):
-
- show_progress(module_index)
-
- fname = module_map[module]['path']
- display.vvvvv(pp.pformat(('process_plugins info: ', module_map[module])))
-
- # crash if module is missing documentation and not explicitly hidden from docs index
- if module_map[module]['doc'] is None:
- display.error("%s MISSING DOCUMENTATION" % (fname,))
- _doc = {plugin_type: module,
- 'version_added': '2.4',
- 'filename': fname}
- module_map[module]['doc'] = _doc
- # continue
-
- # Going to reference this heavily so make a short name to reference it by
- doc = module_map[module]['doc']
- display.vvvvv(pp.pformat(('process_plugins doc: ', doc)))
-
- # add some defaults for plugins that dont have most of the info
- doc['module'] = doc.get('module', module)
- doc['version_added'] = process_version_added(doc.get('version_added', 'historical'))
-
- doc['plugin_type'] = plugin_type
-
- if module_map[module]['deprecated'] and 'deprecated' not in doc:
- display.warning("%s PLUGIN MISSING DEPRECATION DOCUMENTATION: %s" % (fname, 'deprecated'))
-
- required_fields = ('short_description',)
- for field in required_fields:
- if field not in doc:
- display.warning("%s PLUGIN MISSING field '%s'" % (fname, field))
-
- not_nullable_fields = ('short_description',)
- for field in not_nullable_fields:
- if field in doc and doc[field] in (None, ''):
- print("%s: WARNING: MODULE field '%s' DOCUMENTATION is null/empty value=%s" % (fname, field, doc[field]))
-
- if 'description' in doc:
- if isinstance(doc['description'], string_types):
- doc['description'] = [doc['description']]
- elif not isinstance(doc['description'], (list, tuple)):
- raise AnsibleError("Description must be a string or list of strings. Got %s"
- % type(doc['description']))
- else:
- doc['description'] = []
-
- if 'version_added' not in doc:
- # Will never happen, since it has been explicitly inserted above.
- raise AnsibleError("*** ERROR: missing version_added in: %s ***\n" % module)
-
- #
- # The present template gets everything from doc so we spend most of this
- # function moving data into doc for the template to reference
- #
-
- if module_map[module]['aliases']:
- doc['aliases'] = module_map[module]['aliases']
-
- # don't show version added information if it's too old to be called out
- added = 0
- if doc['version_added'] == 'historical':
- del doc['version_added']
- else:
- added = doc['version_added']
-
- # Strip old version_added for the module
- if too_old(added):
- del doc['version_added']
-
- doc['option_keys'] = process_options(module, doc.get('options'))
- doc['filename'] = fname
- doc['source'] = module_map[module]['source']
- doc['docuri'] = doc['module'].replace('_', '-')
- doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d')
- doc['ansible_version'] = ansible_version
-
- # check the 'deprecated' field in doc. We expect a dict potentially with 'why', 'version', and 'alternative' fields
- # examples = module_map[module]['examples']
- # print('\n\n%s: type of examples: %s\n' % (module, type(examples)))
- # if examples and not isinstance(examples, (str, unicode, list)):
- # raise TypeError('module %s examples is wrong type (%s): %s' % (module, type(examples), examples))
-
- # use 'examples' for 'plainexamples' if 'examples' is a string
- if isinstance(module_map[module]['examples'], string_types):
- doc['plainexamples'] = module_map[module]['examples'] # plain text
- else:
- doc['plainexamples'] = ''
-
- doc['metadata'] = module_map[module]['metadata']
-
- display.vvvvv(pp.pformat(module_map[module]))
- if module_map[module]['returndocs']:
- doc['returndocs'] = module_map[module]['returndocs']
- process_returndocs(doc['returndocs'])
- else:
- doc['returndocs'] = None
-
- doc['author'] = doc.get('author', ['UNKNOWN'])
- if isinstance(doc['author'], string_types):
- doc['author'] = [doc['author']]
-
- display.v('about to template %s' % module)
- display.vvvvv(pp.pformat(doc))
- try:
- text = templates['plugin'].render(doc)
- except Exception as e:
- display.warning(msg="Could not parse %s due to %s" % (module, e))
- continue
-
- if LooseVersion(jinja2.__version__) < LooseVersion('2.10'):
- # jinja2 < 2.10's indent filter indents blank lines. Cleanup
- text = re.sub(' +\n', '\n', text)
-
- write_data(text, output_dir, outputname, module)
-
- # Create deprecation stub pages for deprecated aliases
- if module_map[module]['aliases']:
- for alias in module_map[module]['aliases']:
- if alias in module_map[module]['aliases_deprecated']:
- doc['alias'] = alias
-
- display.v('about to template %s (deprecation alias %s)' % (module, alias))
- display.vvvvv(pp.pformat(doc))
- try:
- text = templates['plugin_deprecation_stub'].render(doc)
- except Exception as e:
- display.warning(msg="Could not parse %s (deprecation alias %s) due to %s" % (module, alias, e))
- continue
-
- if LooseVersion(jinja2.__version__) < LooseVersion('2.10'):
- # jinja2 < 2.10's indent filter indents blank lines. Cleanup
- text = re.sub(' +\n', '\n', text)
-
- write_data(text, output_dir, outputname, alias)
-
-
-def process_categories(plugin_info, categories, templates, output_dir, output_name, plugin_type):
- # For some reason, this line is changing plugin_info:
- # text = templates['list_of_CATEGORY_modules'].render(template_data)
- # To avoid that, make a deepcopy of the data.
- # We should track that down and fix it at some point in the future.
- plugin_info = deepcopy(plugin_info)
- for category in sorted(categories.keys()):
- module_map = categories[category]
- category_filename = output_name % category
-
- display.display("*** recording category %s in %s ***" % (category, category_filename))
-
- # start a new category file
-
- category_name = category.replace("_", " ")
- category_title = category_name.title()
-
- subcategories = dict((k, v) for k, v in module_map.items() if k != '_modules')
- template_data = {'title': category_title,
- 'category_name': category_name,
- 'category': module_map,
- 'subcategories': subcategories,
- 'module_info': plugin_info,
- 'plugin_type': plugin_type
- }
-
- text = templates['list_of_CATEGORY_modules'].render(template_data)
- write_data(text, output_dir, category_filename)
-
-
-def process_support_levels(plugin_info, categories, templates, output_dir, plugin_type):
- supported_by = {'Ansible Core Team': {'slug': 'core_supported',
- 'modules': [],
- 'output': 'core_maintained.rst',
- 'blurb': "These are :doc:`modules maintained by the"
- " Ansible Core Team<core_maintained>` and will always ship"
- " with Ansible itself."},
- 'Ansible Network Team': {'slug': 'network_supported',
- 'modules': [],
- 'output': 'network_maintained.rst',
- 'blurb': "These are :doc:`modules maintained by the"
- " Ansible Network Team<network_maintained>` in"
- " a relationship similar to how the Ansible Core Team"
- " maintains the Core modules."},
- 'Ansible Partners': {'slug': 'certified_supported',
- 'modules': [],
- 'output': 'partner_maintained.rst',
- 'blurb': """
-Some examples of :doc:`Certified Modules<partner_maintained>` are those submitted by other
-companies. Maintainers of these types of modules must watch for any issues reported or pull requests
-raised against the module.
-
-The Ansible Core Team will review all modules becoming certified. Core committers will review
-proposed changes to existing Certified Modules once the community maintainers of the module have
-approved the changes. Core committers will also ensure that any issues that arise due to Ansible
-engine changes will be remediated. Also, it is strongly recommended (but not presently required)
-for these types of modules to have unit tests.
-
-These modules are currently shipped with Ansible, but might be shipped separately in the future.
-"""},
- 'Ansible Community': {'slug': 'community_supported',
- 'modules': [],
- 'output': 'community_maintained.rst',
- 'blurb': """
-These are :doc:`modules maintained by the Ansible Community<community_maintained>`. They **are
-not** supported by the Ansible Core Team or by companies/partners associated to the module.
-
-They are still fully usable, but the response rate to issues is purely up to the community. Best
-effort support will be provided but is not covered under any support contracts.
-
-These modules are currently shipped with Ansible, but will most likely be shipped separately in the future.
- """},
- }
-
- # only gen support pages for modules for now, need to split and namespace templates and generated docs
- if plugin_type == 'plugins':
- return
- # Separate the modules by support_level
- for module, info in plugin_info.items():
- if not info.get('metadata', None):
- display.warning('no metadata for %s' % module)
- continue
- if info['metadata']['supported_by'] == 'core':
- supported_by['Ansible Core Team']['modules'].append(module)
- elif info['metadata']['supported_by'] == 'network':
- supported_by['Ansible Network Team']['modules'].append(module)
- elif info['metadata']['supported_by'] == 'certified':
- supported_by['Ansible Partners']['modules'].append(module)
- elif info['metadata']['supported_by'] == 'community':
- supported_by['Ansible Community']['modules'].append(module)
- else:
- raise AnsibleError('Unknown supported_by value: %s' % info['metadata']['supported_by'])
-
- # Render the module lists based on category and subcategory
- for maintainers, data in supported_by.items():
- subcategories = {}
- subcategories[''] = {}
- for module in data['modules']:
- new_cat = plugin_info[module]['sub_category']
- category = plugin_info[module]['primary_category']
- if category not in subcategories:
- subcategories[category] = {}
- subcategories[category][''] = {}
- subcategories[category]['']['_modules'] = []
- if new_cat not in subcategories[category]:
- subcategories[category][new_cat] = {}
- subcategories[category][new_cat]['_modules'] = []
- subcategories[category][new_cat]['_modules'].append(module)
-
- template_data = {'maintainers': maintainers,
- 'subcategories': subcategories,
- 'modules': data['modules'],
- 'slug': data['slug'],
- 'module_info': plugin_info,
- 'plugin_type': plugin_type
- }
- text = templates['support_list'].render(template_data)
- write_data(text, output_dir, data['output'])
-
-
-def validate_options(options):
- ''' validate option parser options '''
-
- if not options.module_dir:
- sys.exit("--module-dir is required")
- if not os.path.exists(options.module_dir):
- sys.exit("--module-dir does not exist: %s" % options.module_dir)
- if not options.template_dir:
- sys.exit("--template-dir must be specified")
-
-
-class DocumentPlugins(Command):
- name = 'document-plugins'
-
- @classmethod
- def init_parser(cls, add_parser):
- parser = add_parser(cls.name, description='Generate module documentation from metadata')
-
- parser.add_argument("-A", "--ansible-version", action="store", dest="ansible_version",
- default="unknown", help="Ansible version number")
- parser.add_argument("-M", "--module-dir", action="store", dest="module_dir",
- default=MODULEDIR, help="Ansible library path")
- parser.add_argument("-P", "--plugin-type", action="store", dest="plugin_type",
- default='module', help="The type of plugin (module, lookup, etc)")
- parser.add_argument("-T", "--template-dir", action="append", dest="template_dir",
- help="directory containing Jinja2 templates")
- parser.add_argument("-t", "--type", action='store', dest='type', choices=['rst'],
- default='rst', help="Document type")
- parser.add_argument("-o", "--output-dir", action="store", dest="output_dir", default=None,
- help="Output directory for module files")
- parser.add_argument("-I", "--includes-file", action="store", dest="includes_file",
- default=None, help="Create a file containing list of processed modules")
- parser.add_argument("-l", "--limit-to-modules", '--limit-to', action="store",
- dest="limit_to", default=None, help="Limit building module documentation"
- " to comma-separated list of plugins. Specify non-existing plugin name"
- " for no plugins.")
- parser.add_argument('-V', action='version', help='Show version number and exit')
- parser.add_argument('-v', '--verbose', dest='verbosity', default=0, action="count",
- help="verbose mode (increase number of 'v's for more)")
-
- @staticmethod
- def main(args):
- if not args.template_dir:
- args.template_dir = ["hacking/templates"]
- validate_options(args)
- display.verbosity = args.verbosity
- plugin_type = args.plugin_type
-
- display.display("Evaluating %s files..." % plugin_type)
-
- # prep templating
- templates = jinja2_environment(args.template_dir, args.type, plugin_type)
-
- # set file/directory structure
- if plugin_type == 'module':
- # trim trailing s off of plugin_type for plugin_type=='modules'. ie 'copy_module.rst'
- outputname = '%s_' + '%s.rst' % plugin_type
- output_dir = args.output_dir
- else:
- # for plugins, just use 'ssh.rst' vs 'ssh_module.rst'
- outputname = '%s.rst'
- output_dir = '%s/plugins/%s' % (args.output_dir, plugin_type)
-
- display.vv('output name: %s' % outputname)
- display.vv('output dir: %s' % output_dir)
-
- # Convert passed-in limit_to to None or list of modules.
- if args.limit_to is not None:
- args.limit_to = [s.lower() for s in args.limit_to.split(",")]
-
- plugin_info, categories = get_plugin_info(args.module_dir, limit_to=args.limit_to, verbose=(args.verbosity > 0))
-
- categories['all'] = {'_modules': plugin_info.keys()}
-
- if display.verbosity >= 3:
- display.vvv(pp.pformat(categories))
- if display.verbosity >= 5:
- display.vvvvv(pp.pformat(plugin_info))
-
- # Transform the data
- if args.type == 'rst':
- display.v('Generating rst')
- for key, record in plugin_info.items():
- display.vv(key)
- if display.verbosity >= 5:
- display.vvvvv(pp.pformat(('record', record)))
- if record.get('doc', None):
- short_desc = record['doc']['short_description'].rstrip('.')
- if short_desc is None:
- display.warning('short_description for %s is None' % key)
- short_desc = ''
- record['doc']['short_description'] = rst_ify(short_desc)
-
- if plugin_type == 'module':
- display.v('Generating Categories')
- # Write module master category list
- category_list_text = templates['category_list'].render(categories=sorted(categories.keys()))
- category_index_name = '%ss_by_category.rst' % plugin_type
- write_data(category_list_text, output_dir, category_index_name)
-
- # Render all the individual plugin pages
- display.v('Generating plugin pages')
- process_plugins(plugin_info, templates, outputname, output_dir, args.ansible_version, plugin_type)
-
- # Render all the categories for modules
- if plugin_type == 'module':
- display.v('Generating Category lists')
- category_list_name_template = 'list_of_%s_' + '%ss.rst' % plugin_type
- process_categories(plugin_info, categories, templates, output_dir, category_list_name_template, plugin_type)
-
- # Render all the categories for modules
- process_support_levels(plugin_info, categories, templates, output_dir, plugin_type)
-
- return 0
diff --git a/hacking/build_library/build_ansible/jinja2/__init__.py b/hacking/build_library/build_ansible/jinja2/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
--- a/hacking/build_library/build_ansible/jinja2/__init__.py
+++ /dev/null
diff --git a/hacking/build_library/build_ansible/jinja2/filters.py b/hacking/build_library/build_ansible/jinja2/filters.py
deleted file mode 100644
index 21436eda90..0000000000
--- a/hacking/build_library/build_ansible/jinja2/filters.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright: (c) 2019, Ansible Project
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-
-# Make coding more python3-ish
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import re
-
-try:
- from html import escape as html_escape
-except ImportError:
- # Python-3.2 or later
- import cgi
-
- def html_escape(text, quote=True):
- return cgi.escape(text, quote)
-
-from jinja2.runtime import Undefined
-
-from ansible.errors import AnsibleError
-from ansible.module_utils._text import to_text
-from ansible.module_utils.six import string_types
-
-
-_ITALIC = re.compile(r"I\(([^)]+)\)")
-_BOLD = re.compile(r"B\(([^)]+)\)")
-_MODULE = re.compile(r"M\(([^)]+)\)")
-_URL = re.compile(r"U\(([^)]+)\)")
-_LINK = re.compile(r"L\(([^)]+), *([^)]+)\)")
-_CONST = re.compile(r"C\(([^)]+)\)")
-_RULER = re.compile(r"HORIZONTALLINE")
-
-
-def html_ify(text):
- ''' convert symbols like I(this is in italics) to valid HTML '''
-
- if not isinstance(text, string_types):
- text = to_text(text)
-
- t = html_escape(text)
- t = _ITALIC.sub(r"<em>\1</em>", t)
- t = _BOLD.sub(r"<b>\1</b>", t)
- t = _MODULE.sub(r"<span class='module'>\1</span>", t)
- t = _URL.sub(r"<a href='\1'>\1</a>", t)
- t = _LINK.sub(r"<a href='\2'>\1</a>", t)
- t = _CONST.sub(r"<code>\1</code>", t)
- t = _RULER.sub(r"<hr/>", t)
-
- return t.strip()
-
-
-def documented_type(text):
- ''' Convert any python type to a type for documentation '''
-
- if isinstance(text, Undefined):
- return '-'
- if text == 'str':
- return 'string'
- if text == 'bool':
- return 'boolean'
- if text == 'int':
- return 'integer'
- if text == 'dict':
- return 'dictionary'
- return text
-
-
-# The max filter was added in Jinja2-2.10. Until we can require that version, use this
-def do_max(seq):
- return max(seq)
-
-
-def rst_ify(text):
- ''' convert symbols like I(this is in italics) to valid restructured text '''
-
- try:
- t = _ITALIC.sub(r"*\1*", text)
- t = _BOLD.sub(r"**\1**", t)
- t = _MODULE.sub(r":ref:`\1 <\1_module>`", t)
- t = _LINK.sub(r"`\1 <\2>`_", t)
- t = _URL.sub(r"\1", t)
- t = _CONST.sub(r"``\1``", t)
- t = _RULER.sub(r"------------", t)
- except Exception as e:
- raise AnsibleError("Could not process (%s) : %s" % (text, e))
-
- return t
-
-
-def rst_fmt(text, fmt):
- ''' helper for Jinja2 to do format strings '''
-
- return fmt % (text)
-
-
-def rst_xline(width, char="="):
- ''' return a restructured text line of a given length '''
-
- return char * width
diff --git a/test/sanity/code-smell/docs-build.py b/test/sanity/code-smell/docs-build.py
index 2e9a0fb4f9..9b6cbd3f86 100755
--- a/test/sanity/code-smell/docs-build.py
+++ b/test/sanity/code-smell/docs-build.py
@@ -11,7 +11,7 @@ import sys
def main():
base_dir = os.getcwd() + os.path.sep
docs_dir = os.path.abspath('docs/docsite')
- cmd = ['make', 'singlehtmldocs']
+ cmd = ['make', 'base_singlehtmldocs']
sphinx = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=docs_dir)
stdout, stderr = sphinx.communicate()
diff --git a/test/sanity/code-smell/docs-build.requirements.txt b/test/sanity/code-smell/docs-build.requirements.txt
index 3a57c92f51..5e458795dd 100644
--- a/test/sanity/code-smell/docs-build.requirements.txt
+++ b/test/sanity/code-smell/docs-build.requirements.txt
@@ -3,3 +3,4 @@ pyyaml
sphinx
sphinx-notfound-page
straight.plugin
+antsibull
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index 1ca04c5253..287f9e2ee0 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -29,9 +29,6 @@ hacking/build_library/build_ansible/command_plugins/dump_keywords.py compile-3.5
hacking/build_library/build_ansible/command_plugins/generate_man.py compile-2.6!skip # docs build only, 3.6+ required
hacking/build_library/build_ansible/command_plugins/generate_man.py compile-2.7!skip # docs build only, 3.6+ required
hacking/build_library/build_ansible/command_plugins/generate_man.py compile-3.5!skip # docs build only, 3.6+ required
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-2.6!skip # docs build only, 3.6+ required
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-2.7!skip # docs build only, 3.6+ required
-hacking/build_library/build_ansible/command_plugins/plugin_formatter.py compile-3.5!skip # docs build only, 3.6+ required
hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-2.6!skip # release process only, 3.6+ required
hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-2.7!skip # release process only, 3.6+ required
hacking/build_library/build_ansible/command_plugins/porting_guide.py compile-3.5!skip # release process only, 3.6+ required