summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLee Garrett <lgarrett@rocketjump.eu>2021-03-24 02:59:56 +0100
committerLee Garrett <lgarrett@rocketjump.eu>2021-03-24 02:59:56 +0100
commitddff31825e92941ec9bfa2aa3ef36e98a4b3b4a7 (patch)
tree31ecc68a40758cea2cd14db073f471ddfc6dea53
parent0addf1f445fd4e07fc0be2f62931208314ac5603 (diff)
downloaddebian-ansible-core-ddff31825e92941ec9bfa2aa3ef36e98a4b3b4a7.zip
New upstream version 2.10.7+dfsg
-rw-r--r--MANIFEST.in2
-rw-r--r--Makefile5
-rw-r--r--PKG-INFO2
-rw-r--r--changelogs/CHANGELOG-v2.10.rst80
-rw-r--r--changelogs/changelog.yaml161
-rw-r--r--docs/docsite/Makefile60
-rw-r--r--docs/docsite/Makefile.sphinx3
-rw-r--r--docs/docsite/rst/2.10_index.rst (renamed from docs/docsite/rst/index.rst)2
-rw-r--r--docs/docsite/rst/ansible_index.rst104
-rw-r--r--docs/docsite/rst/core_index.rst84
-rw-r--r--docs/docsite/rst/dev_guide/developing_modules_documenting.rst12
-rw-r--r--docs/docsite/rst/dev_guide/developing_plugins.rst6
-rw-r--r--docs/docsite/rst/galaxy/user_guide.rst2
-rw-r--r--docs/docsite/rst/installation_guide/intro_installation.rst363
-rw-r--r--docs/docsite/rst/network/dev_guide/developing_resource_modules_network.rst2
-rw-r--r--docs/docsite/rst/network/user_guide/cli_parsing.rst6
-rw-r--r--docs/docsite/rst/porting_guides/core_porting_guides.rst15
-rw-r--r--docs/docsite/rst/reference_appendices/glossary.rst27
-rw-r--r--docs/docsite/rst/roadmap/COLLECTIONS_2_10.rst9
-rw-r--r--docs/docsite/rst/roadmap/COLLECTIONS_3_0.rst58
-rw-r--r--docs/docsite/rst/roadmap/ansible_base_roadmap_index.rst16
-rw-r--r--docs/docsite/rst/scenario_guides/guide_infoblox.rst4
-rw-r--r--docs/docsite/sphinx_conf/2.10_conf.py (renamed from docs/docsite/rst/conf.py)17
-rw-r--r--docs/docsite/sphinx_conf/ansible_conf.py306
-rw-r--r--docs/docsite/sphinx_conf/core_conf.py314
-rw-r--r--docs/man/man1/ansible-config.12
-rw-r--r--docs/man/man1/ansible-console.12
-rw-r--r--docs/man/man1/ansible-doc.12
-rw-r--r--docs/man/man1/ansible-galaxy.12
-rw-r--r--docs/man/man1/ansible-inventory.12
-rw-r--r--docs/man/man1/ansible-playbook.12
-rw-r--r--docs/man/man1/ansible-pull.12
-rw-r--r--docs/man/man1/ansible-vault.12
-rw-r--r--docs/man/man1/ansible.12
-rw-r--r--hacking/build_library/build_ansible/command_plugins/docs_build.py112
-rw-r--r--lib/ansible/cli/galaxy.py4
-rw-r--r--lib/ansible/cli/inventory.py6
-rw-r--r--lib/ansible/config/ansible_builtin_runtime.yml2
-rw-r--r--lib/ansible/config/manager.py5
-rw-r--r--lib/ansible/errors/__init__.py14
-rw-r--r--lib/ansible/inventory/manager.py25
-rw-r--r--lib/ansible/module_utils/basic.py59
-rw-r--r--lib/ansible/module_utils/facts/system/distribution.py2
-rw-r--r--lib/ansible/module_utils/facts/virtual/linux.py5
-rw-r--r--lib/ansible/modules/command.py41
-rw-r--r--lib/ansible/modules/debug.py2
-rw-r--r--lib/ansible/modules/expect.py10
-rw-r--r--lib/ansible/modules/find.py2
-rw-r--r--lib/ansible/modules/get_url.py2
-rw-r--r--lib/ansible/modules/git.py54
-rw-r--r--lib/ansible/modules/group.py2
-rw-r--r--lib/ansible/modules/group_by.py10
-rw-r--r--lib/ansible/modules/hostname.py8
-rw-r--r--lib/ansible/modules/import_playbook.py4
-rw-r--r--lib/ansible/modules/package.py9
-rw-r--r--lib/ansible/modules/package_facts.py14
-rw-r--r--lib/ansible/modules/user.py77
-rw-r--r--lib/ansible/playbook/helpers.py2
-rw-r--r--lib/ansible/playbook/playbook_include.py3
-rw-r--r--lib/ansible/playbook/role/__init__.py56
-rw-r--r--lib/ansible/plugins/action/pause.py3
-rw-r--r--lib/ansible/plugins/cache/__init__.py13
-rw-r--r--lib/ansible/plugins/callback/default.py4
-rw-r--r--lib/ansible/plugins/connection/local.py27
-rw-r--r--lib/ansible/plugins/connection/psrp.py2
-rw-r--r--lib/ansible/plugins/loader.py10
-rw-r--r--lib/ansible/plugins/lookup/unvault.py2
-rw-r--r--lib/ansible/release.py2
-rw-r--r--lib/ansible/template/vars.py36
-rwxr-xr-xtest/integration/targets/ansible-galaxy/runme.sh13
-rw-r--r--test/integration/targets/apt/tasks/apt-builddep.yml6
-rw-r--r--test/integration/targets/apt/tasks/apt-multiarch.yml27
-rw-r--r--test/integration/targets/apt/tasks/apt.yml3
-rw-r--r--test/integration/targets/apt/vars/Ubuntu-20.yml1
-rw-r--r--test/integration/targets/apt/vars/default.yml1
-rw-r--r--test/integration/targets/become_su/aliases3
-rwxr-xr-xtest/integration/targets/become_su/runme.sh6
-rw-r--r--test/integration/targets/callback_default/callback_default.out.free.stdout35
-rw-r--r--test/integration/targets/callback_default/callback_default.out.host_pinned.stdout35
-rw-r--r--test/integration/targets/callback_default/inventory5
-rwxr-xr-xtest/integration/targets/callback_default/runme.sh4
-rw-r--r--test/integration/targets/callback_default/test_non_lockstep.yml7
-rw-r--r--test/integration/targets/config/inline_comment_ansible.cfg2
-rwxr-xr-xtest/integration/targets/config/runme.sh3
-rw-r--r--test/integration/targets/find/tasks/main.yml44
-rw-r--r--test/integration/targets/git/tasks/gpg-verification.yml22
-rw-r--r--test/integration/targets/git/tasks/main.yml2
-rw-r--r--test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml2
-rw-r--r--test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xzbin15496 -> 20440 bytes
-rw-r--r--test/integration/targets/incidental_setup_mongodb/tasks/main.yml2
-rw-r--r--test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml8
-rw-r--r--test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml4
-rwxr-xr-xtest/integration/targets/include_import/runme.sh2
-rw-r--r--test/integration/targets/inventory/inv_with_int.yml6
-rwxr-xr-xtest/integration/targets/inventory/runme.sh64
-rw-r--r--test/integration/targets/inventory_cache/aliases1
-rw-r--r--test/integration/targets/inventory_cache/cache/.keep (renamed from test/integration/targets/yum_repository/vars/default.yml)0
-rw-r--r--test/integration/targets/inventory_cache/cache_host.yml4
-rw-r--r--test/integration/targets/inventory_cache/plugins/inventory/cache_host.py56
-rwxr-xr-xtest/integration/targets/inventory_cache/runme.sh23
-rw-r--r--test/integration/targets/module_utils/callback/pure_json.py31
-rw-r--r--test/integration/targets/module_utils/library/test_no_log.py35
-rw-r--r--test/integration/targets/module_utils/module_utils_test_no_log.yml9
-rw-r--r--test/integration/targets/module_utils/module_utils_vvvvv.yml27
-rwxr-xr-xtest/integration/targets/module_utils/runme.sh4
-rwxr-xr-xtest/integration/targets/pause/runme.sh25
-rw-r--r--test/integration/targets/pkg_resources/aliases1
-rw-r--r--test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py23
-rw-r--r--test/integration/targets/pkg_resources/tasks/main.yml3
-rw-r--r--test/integration/targets/roles/data_integrity.yml4
-rw-r--r--test/integration/targets/roles/roles/data/defaults/main/00.yml1
-rw-r--r--test/integration/targets/roles/roles/data/defaults/main/01.yml0
-rw-r--r--test/integration/targets/roles/roles/data/tasks/main.yml5
-rwxr-xr-xtest/integration/targets/roles/runme.sh4
-rw-r--r--test/integration/targets/setup_paramiko/aliases1
-rw-r--r--test/integration/targets/setup_paramiko/constraints.txt1
-rw-r--r--test/integration/targets/setup_paramiko/install-Darwin-python-3.yml3
-rw-r--r--test/integration/targets/setup_paramiko/install-FreeBSD-11-python-3.yml3
-rw-r--r--test/integration/targets/setup_paramiko/install-RedHat-8-python-3.yml3
-rw-r--r--test/integration/targets/setup_paramiko/setup-remote-constraints.yml12
-rw-r--r--test/integration/targets/setup_paramiko/setup.sh2
-rw-r--r--test/integration/targets/setup_rpm_repo/defaults/main.yml1
-rw-r--r--test/integration/targets/setup_rpm_repo/handlers/main.yml5
-rw-r--r--test/integration/targets/setup_rpm_repo/library/create_repo.py94
-rw-r--r--test/integration/targets/setup_rpm_repo/meta/main.yml2
-rw-r--r--test/integration/targets/setup_rpm_repo/tasks/main.yml39
-rw-r--r--test/integration/targets/setup_rpm_repo/vars/Fedora.yml1
-rw-r--r--test/integration/targets/setup_rpm_repo/vars/RedHat-6.yml1
-rw-r--r--test/integration/targets/setup_rpm_repo/vars/RedHat-7.yml1
-rw-r--r--test/integration/targets/subversion/vars/Ubuntu-20.yml6
-rw-r--r--test/integration/targets/template/6653.yml10
-rw-r--r--test/integration/targets/template/72262.yml6
-rw-r--r--test/integration/targets/template/72615.yml26
-rwxr-xr-xtest/integration/targets/template/runme.sh9
-rw-r--r--test/integration/targets/template/templates/6653-include.j21
-rw-r--r--test/integration/targets/template/templates/6653.j24
-rw-r--r--test/integration/targets/template/templates/72262-included.j21
-rw-r--r--test/integration/targets/template/templates/72262-vars.j21
-rw-r--r--test/integration/targets/template/templates/72262.j23
-rw-r--r--test/integration/targets/template/templates/72615-macro-nested.j24
-rw-r--r--test/integration/targets/template/templates/72615-macro.j28
-rw-r--r--test/integration/targets/template/templates/72615.j24
-rw-r--r--test/integration/targets/unsafe_writes/aliases6
-rw-r--r--test/integration/targets/unsafe_writes/basic.yml53
-rwxr-xr-xtest/integration/targets/unsafe_writes/runme.sh5
-rw-r--r--test/integration/targets/unvault/aliases1
-rw-r--r--test/integration/targets/unvault/main.yml9
-rw-r--r--test/integration/targets/unvault/password1
-rwxr-xr-xtest/integration/targets/unvault/runme.sh6
-rw-r--r--test/integration/targets/unvault/vault6
-rw-r--r--test/integration/targets/yum_repository/defaults/main.yml5
-rw-r--r--test/integration/targets/yum_repository/handlers/main.yml4
-rw-r--r--test/integration/targets/yum_repository/meta/main.yml4
-rw-r--r--test/integration/targets/yum_repository/tasks/main.yml33
-rw-r--r--test/integration/targets/yum_repository/vars/CentOS-8.yml10
-rw-r--r--test/integration/targets/yum_repository/vars/CentOS.yml10
-rw-r--r--test/integration/targets/yum_repository/vars/Fedora.yml5
-rw-r--r--test/lib/ansible_test/_data/completion/docker.txt1
-rw-r--r--test/lib/ansible_test/_data/requirements/constraints.txt2
-rw-r--r--test/lib/ansible_test/_data/sanity/pylint/plugins/deprecated.py2
-rw-r--r--test/lib/ansible_test/_data/sanity/validate-modules/validate_modules/main.py2
-rw-r--r--test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py2
-rw-r--r--test/lib/ansible_test/_data/setup/remote.sh7
-rw-r--r--test/lib/ansible_test/_internal/ansible_util.py54
-rw-r--r--test/lib/ansible_test/_internal/docker_util.py16
-rw-r--r--test/lib/ansible_test/_internal/executor.py46
-rwxr-xr-xtest/sanity/code-smell/docs-build.py26
-rwxr-xr-xtest/sanity/code-smell/package-data.py3
-rw-r--r--test/sanity/ignore.txt2
-rw-r--r--test/units/ansible_test/test_docker_util.py131
-rw-r--r--test/units/errors/test_errors.py46
-rw-r--r--test/units/mock/loader.py5
-rw-r--r--test/units/module_utils/basic/test_atomic_move.py1
-rw-r--r--test/units/module_utils/facts/system/distribution/fixtures/almalinux_8_3_beta.json53
-rw-r--r--test/units/module_utils/facts/virtual/__init__.py0
-rw-r--r--test/units/module_utils/facts/virtual/test_linux.py26
176 files changed, 3074 insertions, 631 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index 0385260d..cd5a2e8f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -5,6 +5,8 @@ include requirements.txt
recursive-include docs *
include docs/docsite/rst/collections/all_plugins.rst
exclude docs/docsite/rst_warnings
+exclude docs/docsite/rst/conf.py
+exclude docs/docsite/rst/index.rst
recursive-exclude docs/docsite/_build *
recursive-exclude docs/docsite/_extensions *.pyc *.pyo
include examples/hosts
diff --git a/Makefile b/Makefile
index f4e95348..d358c33b 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@
# useful targets:
# make clean ---------------- clean up
# make webdocs -------------- produce ansible doc at docs/docsite/_build/html
+# make coredocs ------------- produce core doc at docs/docsite/_build/html
# make sdist ---------------- produce a tarball
# make deb-src -------------- produce a DEB source
# make deb ------------------ produce a DEB
@@ -268,6 +269,10 @@ epub:
webdocs:
(cd docs/docsite/; CPUS=$(CPUS) $(MAKE) docs)
+.PHONY: coredocs
+coredocs:
+ (cd docs/docsite/; CPUS=$(CPUS) $(MAKE) coredocs)
+
.PHONY: linkcheckdocs
linkcheckdocs:
(cd docs/docsite/; CPUS=$(CPUS) $(MAKE) linkcheckdocs)
diff --git a/PKG-INFO b/PKG-INFO
index 2974df94..1cabee01 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: ansible-base
-Version: 2.10.5
+Version: 2.10.7
Summary: Radically simple IT automation
Home-page: https://ansible.com/
Author: Ansible, Inc.
diff --git a/changelogs/CHANGELOG-v2.10.rst b/changelogs/CHANGELOG-v2.10.rst
index 8ecf7f1e..76588732 100644
--- a/changelogs/CHANGELOG-v2.10.rst
+++ b/changelogs/CHANGELOG-v2.10.rst
@@ -5,6 +5,86 @@ Ansible Base 2.10 "When the Levee Breaks" Release Notes
.. contents:: Topics
+v2.10.7
+=======
+
+Release Summary
+---------------
+
+| Release Date: 2021-03-15
+| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+
+Minor Changes
+-------------
+
+- ansible-test - Generation of an ``egg-info`` directory, if needed, is now done after installing test dependencies and before running tests. When running from an installed version of ``ansible-test`` a temporary directory is used to avoid permissions issues. Previously it was done before installing test dependencies and adjacent to the installed directory.
+- ansible-test - now makes a better attempt to support podman when calling ``docker images`` and asking for JSON format.
+
+Bugfixes
+--------
+
+- ConfigManager - Normalize ConfigParser between Python2 and Python3 to for handling comments (https://github.com/ansible/ansible/issues/73709)
+- InventoryManager - Fix unhandled exception when given limit file was actually a directory.
+- InventoryManager - Fix unhandled exception when inventory directory was empty or contained empty subdirectories (https://github.com/ansible/ansible/issues/73658).
+- add AlmaLinux to fact gathering (https://github.com/ansible/ansible/pull/73458)
+- ansible-galaxy - fixed galaxy role init command (https://github.com/ansible/ansible/issues/71977).
+- ansible-inventory CLI - Deal with failures when sorting JSON and you have incompatible key types (https://github.com/ansible/ansible/issues/68950).
+- ansible-test - Running tests using an installed version of ``ansible-test`` against one Python version from another no longer fails due to a missing ``egg-info`` directory. This could occur when testing plugins which import ``pkg_resources``.
+- ansible-test - Running tests using an installed version of ``ansible-test`` no longer generates an error attempting to create an ``egg-info`` directory when an existing one is not found in the expected location. This could occur if the existing ``egg-info`` directory included a Python version specifier in the name.
+- default callback - Ensure that the ``host_pinned`` strategy is not treated as lockstep (https://github.com/ansible/ansible/issues/73364)
+- ensure find_mount_point consistently returns text.
+- ensure we don't clobber role vars data when getting an empty file
+- find module - Stop traversing directories past the requested depth. (https://github.com/ansible/ansible/issues/73627)
+- hostname - add Almalinux support (https://github.com/ansible/ansible/pull/73619)
+- runtime routing - redirect ``firewalld`` to ``ansible.posix.firewalld`` FQCN (https://github.com/ansible/ansible/issues/73689).
+- the unvault lookup plugin returned a byte string. Now returns a real string.
+- yamllint - do not raise an ``AttributeError`` if a value is assigned to a module attribute at the top of the module.
+
+v2.10.6
+=======
+
+Release Summary
+---------------
+
+| Release Date: 2021-02-17
+| `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+
+Minor Changes
+-------------
+
+- ansible-test - Added Ubuntu 20.04 LTS image to the default completion list
+- inventory cache - do not show a warning when the cache file does not (yet) exist.
+
+Security Fixes
+--------------
+
+- **security issue** - Mask default and fallback values for ``no_log`` module options (CVE-2021-20228)
+
+Bugfixes
+--------
+
+- Added unsafe_writes test.
+- Always mention the name of the deprecated or tombstoned plugin in routing deprecation/tombstone messages (https://github.com/ansible/ansible/pull/73059).
+- Correct the inventory source error parse handling, specifically make the config INVENTORY_ANY_UNPARSED_IS_FAILED work as expected.
+- Enabled unsafe_writes for get_url which was ignoring the paramter.
+- Fix incorrect variable scoping when using ``import with context`` in Jinja2 templates. (https://github.com/ansible/ansible/issues/72615)
+- Restored unsafe_writes functionality which was being skipped.
+- allow become method 'su' to work on 'local' connection by allocating a fake tty.
+- ansble-test - only require a collection name in deprecation warnings when necessary (https://github.com/ansible/ansible/pull/72987)
+- ansible-test - Temporarily limit ``cryptography`` to versions before 3.4 to enable tests to function.
+- ansible-test - The ``--remote`` option has been updated for Python 2.7 to work around breaking changes in the newly released ``get-pip.py`` bootstrapper.
+- ansible-test - The ``--remote`` option has been updated to use a versioned ``get-pip.py`` bootstrapper to avoid issues with future releases.
+- ansible-test sanity changelog test - bump dependency on antsibull-changelog to 0.9.0 so that `fragments that add new plugins or objects <https://github.com/ansible-community/antsibull-changelog/blob/main/docs/changelogs.rst#adding-new-roles-playbooks-test-and-filter-plugins>`_ will not fail validation (https://github.com/ansible/ansible/pull/73428).
+- display correct error information when an error exists in the last line of the file (https://github.com/ansible/ansible/issues/16456)
+- facts - properly report virtualization facts for Linux guests running on bhyve (https://github.com/ansible/ansible/issues/73167)
+- git - Only pass ``--raw`` flag to git verify commands (verify-tag, verify-commit) when ``gpg_whitelist`` is in use. Otherwise don't pass it so that non-whitelist GPG validation still works on older Git versions. (https://github.com/ansible/ansible/issues/64469)
+- import_playbook - change warning about extra parameters to deprecation (https://github.com/ansible/ansible/issues/72745)
+- pause - do not warn when running in the background if a timeout is provided (https://github.com/ansible/ansible/issues/73042)
+- psrp connection plugin - ``to_text(stdout)`` before ``json.loads`` in psrp.Connection.put_file in case ``stdout`` is bytes.
+- validate-modules - do not raise an ``AttributeError`` if a value is assigned to a module attribute in a try/except block.
+
v2.10.5
=======
diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml
index 55b3d52f..bafe846b 100644
--- a/changelogs/changelog.yaml
+++ b/changelogs/changelog.yaml
@@ -2355,3 +2355,164 @@ releases:
- v2.10.5rc1_summary.yaml
- wrap_native_text-non-collections-only.yml
release_date: '2021-01-11'
+ 2.10.6:
+ changes:
+ release_summary: '| Release Date: 2021-02-17
+
+ | `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+ '
+ codename: When the Levee Breaks
+ fragments:
+ - v2.10.6_summary.yaml
+ release_date: '2021-02-17'
+ 2.10.6rc1:
+ changes:
+ bugfixes:
+ - Added unsafe_writes test.
+ - Always mention the name of the deprecated or tombstoned plugin in routing
+ deprecation/tombstone messages (https://github.com/ansible/ansible/pull/73059).
+ - Correct the inventory source error parse handling, specifically make the config
+ INVENTORY_ANY_UNPARSED_IS_FAILED work as expected.
+ - Enabled unsafe_writes for get_url which was ignoring the paramter.
+ - Fix incorrect variable scoping when using ``import with context`` in Jinja2
+ templates. (https://github.com/ansible/ansible/issues/72615)
+ - Restored unsafe_writes functionality which was being skipped.
+ - allow become method 'su' to work on 'local' connection by allocating a fake
+ tty.
+ - ansble-test - only require a collection name in deprecation warnings when
+ necessary (https://github.com/ansible/ansible/pull/72987)
+ - ansible-test - Temporarily limit ``cryptography`` to versions before 3.4 to
+ enable tests to function.
+ - ansible-test - The ``--remote`` option has been updated for Python 2.7 to
+ work around breaking changes in the newly released ``get-pip.py`` bootstrapper.
+ - ansible-test - The ``--remote`` option has been updated to use a versioned
+ ``get-pip.py`` bootstrapper to avoid issues with future releases.
+ - ansible-test sanity changelog test - bump dependency on antsibull-changelog
+ to 0.9.0 so that `fragments that add new plugins or objects <https://github.com/ansible-community/antsibull-changelog/blob/main/docs/changelogs.rst#adding-new-roles-playbooks-test-and-filter-plugins>`_
+ will not fail validation (https://github.com/ansible/ansible/pull/73428).
+ - display correct error information when an error exists in the last line of
+ the file (https://github.com/ansible/ansible/issues/16456)
+ - facts - properly report virtualization facts for Linux guests running on bhyve
+ (https://github.com/ansible/ansible/issues/73167)
+ - git - Only pass ``--raw`` flag to git verify commands (verify-tag, verify-commit)
+ when ``gpg_whitelist`` is in use. Otherwise don't pass it so that non-whitelist
+ GPG validation still works on older Git versions. (https://github.com/ansible/ansible/issues/64469)
+ - import_playbook - change warning about extra parameters to deprecation (https://github.com/ansible/ansible/issues/72745)
+ - pause - do not warn when running in the background if a timeout is provided
+ (https://github.com/ansible/ansible/issues/73042)
+ - psrp connection plugin - ``to_text(stdout)`` before ``json.loads`` in psrp.Connection.put_file
+ in case ``stdout`` is bytes.
+ - validate-modules - do not raise an ``AttributeError`` if a value is assigned
+ to a module attribute in a try/except block.
+ minor_changes:
+ - ansible-test - Added Ubuntu 20.04 LTS image to the default completion list
+ - inventory cache - do not show a warning when the cache file does not (yet)
+ exist.
+ release_summary: '| Release Date: 2021-02-08
+
+ | `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+ '
+ security_fixes:
+ - '**security issue** - Mask default and fallback values for ``no_log`` module
+ options (CVE-2021-20228)'
+ codename: When the Levee Breaks
+ fragments:
+ - 16456-correct-YAML-error-message-when-file-load-failed.yml
+ - 64469_git_no_raw.yml
+ - 72615-jinja-import-context-fix.yml
+ - 72745-import_playbook-deprecation-extra-params.yml
+ - 73059-improve-deprecation-texts.yml
+ - 73167-bhyve-facts.yml
+ - 73428-changelog-linting-bump-version.yml
+ - ansible-test-pip-bootstrap-s3.yml
+ - ansible-test-pip-bootstrap.yml
+ - ansible-test-sanity-deprecation-collection-name.yml
+ - ansible-test-ubuntu2004.yml
+ - cryptography-fix.yml
+ - enable_su_on_local.yaml
+ - fix_inventory_source_parse_error_handling.yml
+ - inventory-cache-file-missing-warning.yaml
+ - no_log-fallback.yml
+ - pause-do-not-warn-background-with-seconds.yml
+ - psrp-json-loads-bytes.yml
+ - unsafe_writes_fix.yml
+ - v2.10.6rc1_summary.yaml
+ - validate-modules_found_try_except_import_fails_module_attribute.yaml
+ release_date: '2021-02-08'
+ 2.10.7:
+ changes:
+ release_summary: '| Release Date: 2021-03-15
+
+ | `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+ '
+ codename: When the Levee Breaks
+ fragments:
+ - v2.10.7_summary.yaml
+ release_date: '2021-03-15'
+ 2.10.7rc1:
+ changes:
+ bugfixes:
+ - ConfigManager - Normalize ConfigParser between Python2 and Python3 to for
+ handling comments (https://github.com/ansible/ansible/issues/73709)
+ - InventoryManager - Fix unhandled exception when given limit file was actually
+ a directory.
+ - InventoryManager - Fix unhandled exception when inventory directory was empty
+ or contained empty subdirectories (https://github.com/ansible/ansible/issues/73658).
+ - add AlmaLinux to fact gathering (https://github.com/ansible/ansible/pull/73458)
+ - ansible-galaxy - fixed galaxy role init command (https://github.com/ansible/ansible/issues/71977).
+ - ansible-inventory CLI - Deal with failures when sorting JSON and you have
+ incompatible key types (https://github.com/ansible/ansible/issues/68950).
+ - ansible-test - Running tests using an installed version of ``ansible-test``
+ against one Python version from another no longer fails due to a missing ``egg-info``
+ directory. This could occur when testing plugins which import ``pkg_resources``.
+ - ansible-test - Running tests using an installed version of ``ansible-test``
+ no longer generates an error attempting to create an ``egg-info`` directory
+ when an existing one is not found in the expected location. This could occur
+ if the existing ``egg-info`` directory included a Python version specifier
+ in the name.
+ - default callback - Ensure that the ``host_pinned`` strategy is not treated
+ as lockstep (https://github.com/ansible/ansible/issues/73364)
+ - ensure find_mount_point consistently returns text.
+ - ensure we don't clobber role vars data when getting an empty file
+ - find module - Stop traversing directories past the requested depth. (https://github.com/ansible/ansible/issues/73627)
+ - hostname - add Almalinux support (https://github.com/ansible/ansible/pull/73619)
+ - runtime routing - redirect ``firewalld`` to ``ansible.posix.firewalld`` FQCN
+ (https://github.com/ansible/ansible/issues/73689).
+ - the unvault lookup plugin returned a byte string. Now returns a real string.
+ - yamllint - do not raise an ``AttributeError`` if a value is assigned to a
+ module attribute at the top of the module.
+ minor_changes:
+ - ansible-test - Generation of an ``egg-info`` directory, if needed, is now
+ done after installing test dependencies and before running tests. When running
+ from an installed version of ``ansible-test`` a temporary directory is used
+ to avoid permissions issues. Previously it was done before installing test
+ dependencies and adjacent to the installed directory.
+ - ansible-test - now makes a better attempt to support podman when calling ``docker
+ images`` and asking for JSON format.
+ release_summary: '| Release Date: 2021-03-08
+
+ | `Porting Guide <https://docs.ansible.com/ansible/devel/porting_guides.html>`__
+
+ '
+ codename: When the Levee Breaks
+ fragments:
+ - 71977-ansible-galaxy-role-init.yml
+ - 73364-default-callback-host-pinned-not-lockstep.yml
+ - 73456-let-vault-lookup-output-string.yml
+ - 73619-hostname-almalinux-support.yml
+ - 73658-inventorymanager-throws-on-empty-inventory-dir.yml
+ - 73689-move-firewalld-to-ansible-posix.yaml
+ - 73709-normalize-configparser.yml
+ - 73718-find-dir-depth-traversal.yml
+ - ansible-test-egg-info-handling.yml
+ - ansible-test-podman-json-format.yml
+ - ansible_test_yamllint_avoid_attribute_exception.yaml
+ - fix_mount_point.yml
+ - fix_role_var_loading.yml
+ - inv_json_sort_types_fix.yml
+ - support_almalinux.yml
+ - v2.10.7rc1_summary.yaml
+ release_date: '2021-03-08'
diff --git a/docs/docsite/Makefile b/docs/docsite/Makefile
index bca734a5..02158a41 100644
--- a/docs/docsite/Makefile
+++ b/docs/docsite/Makefile
@@ -31,16 +31,30 @@ ifdef PLUGINS
endif
endif
+PYTHON=python
+MAJOR_VERSION := $(shell $(PYTHON) ../../packaging/release/versionhelper/version_helper.py --majorversion || echo error)
+
+ANSIBLE_VERSION_ARGS=
+ifndef ANSIBLE_VERSION
+ # Only needed to make stable-2.10 docs build correctly. Do not apply to devel and future branches
+ ANSIBLE_VERSION=$(MAJOR_VERSION)
+endif
+ifdef ANSIBLE_VERSION
+ ANSIBLE_VERSION_ARGS=--ansible-version=$(ANSIBLE_VERSION)
+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
+ifeq ($(findstring error,$(MAJOR_VERSION)), error)
+$(error "version_helper failed to determine major version")
+endif
+
assertrst:
ifndef rst
$(error specify document or pattern with rst=somefile.rst)
@@ -50,23 +64,47 @@ all: docs
docs: htmldocs
+coredocs: core_htmldocs
+
generate_rst: collections_meta config cli keywords plugins testing
-base_generate_rst: collections_meta config cli keywords base_plugins testing
+core_generate_rst: collections_meta config cli keywords base_plugins testing
+
+# The following two symlinks are necessary to produce two different docsets
+# from the same set of rst files (Ansible the package docs, and core docs).
+# Symlink the relevant index into place for building Ansible docs
+ansible_structure: generate_rst
+ # We must have python and python-packaging for the version_helper
+ # script so use it for version comparison
+ if python -c "import sys, packaging.version as p; sys.exit(not p.Version('$(ANSIBLE_VERSION)') > p.Version('2.10'))" ; then \
+ echo "Creating symlinks in generate_rst"; \
+ ln -sf ../rst/ansible_index.rst rst/index.rst; \
+ ln -sf ../sphinx_conf/ansible_conf.py rst/conf.py; \
+ else \
+ echo 'Creating symlinks for older ansible in generate_rst'; \
+ ln -sf ../rst/2.10_index.rst rst/index.rst; \
+ ln -sf ../sphinx_conf/2.10_conf.py rst/conf.py; \
+ fi
-htmldocs: generate_rst
+# Symlink the relevant index into place for building core docs
+core_structure: core_generate_rst
+ @echo "Creating symlinks in core_generate_rst"
+ -ln -sf ../rst/core_index.rst rst/index.rst
+ -ln -sf ../sphinx_conf/core_conf.py rst/conf.py
+
+htmldocs: ansible_structure
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
-base_htmldocs: base_generate_rst
+core_htmldocs: core_structure
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx html
-singlehtmldocs: generate_rst
+singlehtmldocs: ansible_structure
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
-base_singlehtmldocs: base_generate_rst
+core_singlehtmldocs: core_structure
CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx singlehtml
linkcheckdocs: generate_rst
- CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx linkcheck
+ CPUS=$(CPUS) $(MAKE) -f Makefile.sphinx linkcheck
webdocs: docs
@@ -99,6 +137,8 @@ clean:
rm -rf "rst/collections/$$filename"; \
fi \
done
+ @echo "Cleanning up generated ansible_structure"
+ find -type l -delete
@echo "Cleaning up legacy generated rst locations"
rm -rf rst/modules
rm -f rst/plugins/*/*.rst
@@ -122,11 +162,7 @@ config: ../templates/config.rst.j2
# 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 "$(VERSION)" : '.*[.]dev[0-9]\{1,\}$$' &> /dev/null; then \
- $(PLUGIN_FORMATTER) base -o rst $(PLUGIN_ARGS);\
- else \
- $(PLUGIN_FORMATTER) full -o rst $(PLUGIN_ARGS);\
- fi
+ $(PLUGIN_FORMATTER) full -o rst $(ANSIBLE_VERSION_ARGS) $(PLUGIN_ARGS);\
# This only builds the plugin docs included with ansible-base
base_plugins:
diff --git a/docs/docsite/Makefile.sphinx b/docs/docsite/Makefile.sphinx
index d8435064..c05e3c3e 100644
--- a/docs/docsite/Makefile.sphinx
+++ b/docs/docsite/Makefile.sphinx
@@ -2,7 +2,8 @@
#
# You can set these variables from the command line.
-SPHINXOPTS = -j $(CPUS) -n -w rst_warnings
+SPHINXCONFDIR = rst
+SPHINXOPTS = -j $(CPUS) -n -w rst_warnings -c "$(SPHINXCONFDIR)"
SPHINXBUILD = sphinx-build
SPHINXPROJ = sdfsdf
SOURCEDIR = rst
diff --git a/docs/docsite/rst/index.rst b/docs/docsite/rst/2.10_index.rst
index c001115d..1ff97d0b 100644
--- a/docs/docsite/rst/index.rst
+++ b/docs/docsite/rst/2.10_index.rst
@@ -18,7 +18,7 @@ Ansible manages machines in an agent-less manner. There is never a question of h
This documentation covers the version of Ansible noted in the upper left corner of this page. We maintain multiple versions of Ansible and of the documentation, so please be sure you are using the version of the documentation that covers the version of Ansible you're using. For recent features, we note the version of Ansible where the feature was added.
-Ansible releases a new major release of Ansible approximately three to four times per year. The core application evolves somewhat conservatively, valuing simplicity in language design and setup. Contributors develop and change modules and plugins, hosted in collections since version 2.10, much more quickly.
+Ansible releases a new major release approximately twice a year. The core application evolves somewhat conservatively, valuing simplicity in language design and setup. Contributors develop and change modules and plugins, hosted in collections since version 2.10, much more quickly.
.. toctree::
:maxdepth: 2
diff --git a/docs/docsite/rst/ansible_index.rst b/docs/docsite/rst/ansible_index.rst
new file mode 100644
index 00000000..4d3a6011
--- /dev/null
+++ b/docs/docsite/rst/ansible_index.rst
@@ -0,0 +1,104 @@
+.. _ansible_documentation:
+..
+ This is the index file for Ansible the package. It gets symlinked to index.rst by the Makefile
+
+Ansible Documentation
+=====================
+
+About Ansible
+`````````````
+
+Ansible is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates.
+
+Ansible's main goals are simplicity and ease-of-use. It also has a strong focus on security and reliability, featuring a minimum of moving parts, usage of OpenSSH for transport (with other transports and pull modes as alternatives), and a language that is designed around auditability by humans--even those not familiar with the program.
+
+We believe simplicity is relevant to all sizes of environments, so we design for busy users of all types: developers, sysadmins, release engineers, IT managers, and everyone in between. Ansible is appropriate for managing all environments, from small setups with a handful of instances to enterprise environments with many thousands of instances.
+
+You can learn more at `AnsibleFest <https://www.ansible.com/ansiblefest>`_, the annual event for all Ansible contributors, users, and customers hosted by Red Hat. AnsibleFest is the place to connect with others, learn new skills, and find a new friend to automate with.
+
+Ansible manages machines in an agent-less manner. There is never a question of how to upgrade remote daemons or the problem of not being able to manage systems because daemons are uninstalled. Because OpenSSH is one of the most peer-reviewed open source components, security exposure is greatly reduced. Ansible is decentralized--it relies on your existing OS credentials to control access to remote machines. If needed, Ansible can easily connect with Kerberos, LDAP, and other centralized authentication management systems.
+
+This documentation covers the version of Ansible noted in the upper left corner of this page. We maintain multiple versions of Ansible and of the documentation, so please be sure you are using the version of the documentation that covers the version of Ansible you're using. For recent features, we note the version of Ansible where the feature was added.
+
+Ansible releases a new major release approximately twice a year. The core application evolves somewhat conservatively, valuing simplicity in language design and setup. Contributors develop and change modules and plugins, hosted in collections since version 2.10, much more quickly.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Installation, Upgrade & Configuration
+
+ installation_guide/index
+ porting_guides/porting_guides
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Using Ansible
+
+ user_guide/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contributing to Ansible
+
+ community/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Extending Ansible
+
+ dev_guide/index
+
+.. toctree::
+ :glob:
+ :maxdepth: 1
+ :caption: Common Ansible Scenarios
+
+ scenario_guides/cloud_guides
+ scenario_guides/network_guides
+ scenario_guides/virt_guides
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Network Automation
+
+ network/getting_started/index
+ network/user_guide/index
+ network/dev_guide/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Ansible Galaxy
+
+ galaxy/user_guide.rst
+ galaxy/dev_guide.rst
+
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Reference & Appendices
+
+ collections/index
+ collections/all_plugins
+ reference_appendices/playbooks_keywords
+ reference_appendices/common_return_values
+ reference_appendices/config
+ reference_appendices/general_precedence
+ reference_appendices/YAMLSyntax
+ reference_appendices/python_3_support
+ reference_appendices/interpreter_discovery
+ reference_appendices/release_and_maintenance
+ reference_appendices/test_strategies
+ dev_guide/testing/sanity/index
+ reference_appendices/faq
+ reference_appendices/glossary
+ reference_appendices/module_utils
+ reference_appendices/special_variables
+ reference_appendices/tower
+ reference_appendices/automationhub
+ reference_appendices/logging
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Roadmaps
+
+ roadmap/ansible_roadmap_index.rst
diff --git a/docs/docsite/rst/core_index.rst b/docs/docsite/rst/core_index.rst
new file mode 100644
index 00000000..0ee63ca9
--- /dev/null
+++ b/docs/docsite/rst/core_index.rst
@@ -0,0 +1,84 @@
+.. _ansible_core_documentation:
+
+..
+ This is the index file for ansible-core. It gets symlinked to index.rst by the Makefile
+
+**************************
+Ansible Core Documentation
+**************************
+
+About ansible-core
+===================
+
+Ansible is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates.
+
+Ansible's main goals are simplicity and ease-of-use. It also has a strong focus on security and reliability, featuring a minimum of moving parts, usage of OpenSSH for transport (with other transports and pull modes as alternatives), and a language that is designed around auditability by humans--even those not familiar with the program.
+
+We believe simplicity is relevant to all sizes of environments, so we design for busy users of all types: developers, sysadmins, release engineers, IT managers, and everyone in between. Ansible is appropriate for managing all environments, from small setups with a handful of instances to enterprise environments with many thousands of instances.
+
+You can learn more at `AnsibleFest <https://www.ansible.com/ansiblefest>`_, the annual event for all Ansible contributors, users, and customers hosted by Red Hat. AnsibleFest is the place to connect with others, learn new skills, and find a new friend to automate with.
+
+Ansible manages machines in an agent-less manner. There is never a question of how to upgrade remote daemons or the problem of not being able to manage systems because daemons are uninstalled. Because OpenSSH is one of the most peer-reviewed open source components, security exposure is greatly reduced. Ansible is decentralized--it relies on your existing OS credentials to control access to remote machines. If needed, Ansible can easily connect with Kerberos, LDAP, and other centralized authentication management systems.
+
+This documentation covers the version of Ansible noted in the upper left corner of this page. We maintain multiple versions of Ansible and of the documentation, so please be sure you are using the version of the documentation that covers the version of Ansible you're using. For recent features, we note the version of Ansible where the feature was added.
+
+
+``ansible-core`` releases a new major release approximately twice a year. The core application evolves somewhat conservatively, valuing simplicity in language design and setup. Contributors develop and change modules and plugins, hosted in collections since version 2.10, much more quickly.
+
+
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Installation, Upgrade & Configuration
+
+ installation_guide/index
+ porting_guides/core_porting_guides
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Using Ansible Core
+
+ user_guide/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contributing to Ansible Core
+
+ community/index
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Extending Ansible
+
+ dev_guide/index
+
+
+.. toctree::
+ :maxdepth: 1
+ :caption: Reference & Appendices
+
+ collections/index
+ collections/all_plugins
+ reference_appendices/playbooks_keywords
+ reference_appendices/common_return_values
+ reference_appendices/config
+ reference_appendices/general_precedence
+ reference_appendices/YAMLSyntax
+ reference_appendices/python_3_support
+ reference_appendices/interpreter_discovery
+ reference_appendices/release_and_maintenance
+ reference_appendices/test_strategies
+ dev_guide/testing/sanity/index
+ reference_appendices/faq
+ reference_appendices/glossary
+ reference_appendices/module_utils
+ reference_appendices/special_variables
+ reference_appendices/tower
+ reference_appendices/automationhub
+ reference_appendices/logging
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Roadmaps
+
+ roadmap/ansible_base_roadmap_index.rst
diff --git a/docs/docsite/rst/dev_guide/developing_modules_documenting.rst b/docs/docsite/rst/dev_guide/developing_modules_documenting.rst
index 6be4fcd0..096e9f17 100644
--- a/docs/docsite/rst/dev_guide/developing_modules_documenting.rst
+++ b/docs/docsite/rst/dev_guide/developing_modules_documenting.rst
@@ -114,7 +114,8 @@ All fields in the ``DOCUMENTATION`` block are lower-case. All fields are require
:version_added:
* The version of Ansible when the module was added.
- * This is a string, and not a float, for example, ``version_added: '2.1'``
+ * This is a string, and not a float, for example, ``version_added: '2.1'``.
+ * In collections, this must be the collection version the module was added to, not the Ansible version. For example, ``version_added: 1.0.0``.
:author:
@@ -182,6 +183,7 @@ All fields in the ``DOCUMENTATION`` block are lower-case. All fields are require
* Only needed if this option was extended after initial Ansible release, in other words, this is greater than the top level `version_added` field.
* This is a string, and not a float, for example, ``version_added: '2.3'``.
+ * In collections, this must be the collection version the option was added to, not the Ansible version. For example, ``version_added: 1.0.0``.
:suboptions:
@@ -241,7 +243,7 @@ content in a uniform way:
* ``I()`` for option names. For example: ``Required if I(state=present).`` This is italicized in
the documentation.
-* ``C()`` for files and option values. For example: ``If not set the environment variable C(ACME_PASSWORD) will be used.`` This displays with a mono-space font in the documentation.
+* ``C()`` for files, option values, and inline code. For example: ``If not set the environment variable C(ACME_PASSWORD) will be used.`` or ``Use C(var | foo.bar.my_filter) to transform C(var) into the required format.`` This displays with a mono-space font in the documentation.
* ``B()`` currently has no standardized usage. It is displayed in boldface in the documentation.
* ``HORIZONTALLINE`` is used sparingly as a separator in long descriptions. It becomes a horizontal rule (the ``<hr>`` html tag) in the documentation.
@@ -372,7 +374,7 @@ Otherwise, for each value returned, provide the following fields. All fields are
Only needed if this return was extended after initial Ansible release, in other words, this is greater than the top level `version_added` field.
This is a string, and not a float, for example, ``version_added: '2.3'``.
:contains:
- Optional. To describe nested return values, set ``type: complex``, ``type: dict``, or ``type: list``/``elements: dict`` and repeat the elements above for each sub-field.
+ Optional. To describe nested return values, set ``type: dict``, or ``type: list``/``elements: dict``, or if you really have to, ``type: complex``, and repeat the elements above for each sub-field.
Here are two example ``RETURN`` sections, one with three simple fields and one with a complex nested field::
@@ -398,12 +400,13 @@ Here are two example ``RETURN`` sections, one with three simple fields and one w
packages:
description: Information about package requirements.
returned: success
- type: complex
+ type: dict
contains:
missing:
description: Packages that are missing from the system.
returned: success
type: list
+ elements: str
sample:
- libmysqlclient-dev
- libxml2-dev
@@ -411,6 +414,7 @@ Here are two example ``RETURN`` sections, one with three simple fields and one w
description: Packages that are installed but at bad versions.
returned: success
type: list
+ elements: dict
sample:
- package: libxml2-dev
version: 2.9.4+dfsg1-2
diff --git a/docs/docsite/rst/dev_guide/developing_plugins.rst b/docs/docsite/rst/dev_guide/developing_plugins.rst
index 4ec08505..e40a3281 100644
--- a/docs/docsite/rst/dev_guide/developing_plugins.rst
+++ b/docs/docsite/rst/dev_guide/developing_plugins.rst
@@ -221,7 +221,7 @@ but with an extra option so you can see how configuration works in Ansible versi
requirements:
- whitelist in configuration
short_description: Adds time to play stats
- version_added: "2.0"
+ version_added: "2.0" # for collections, use the collection version, not the Ansible version
description:
- This callback just adds total play duration to the play stats.
options:
@@ -334,7 +334,7 @@ Here's a simple lookup plugin implementation --- this lookup returns the content
DOCUMENTATION = """
lookup: file
author: Daniel Hokka Zakrisson <daniel@hozac.com>
- version_added: "0.9"
+ version_added: "0.9" # for collections, use the collection version, not the Ansible version
short_description: read file contents
description:
- This lookup returns the contents from a file on the Ansible controller's file system.
@@ -460,7 +460,7 @@ Include the ``vars_plugin_staging`` documentation fragment to allow users to det
DOCUMENTATION = '''
vars: custom_hostvars
- version_added: "2.10"
+ version_added: "2.10" # for collections, use the collection version, not the Ansible version
short_description: Load custom host vars
description: Load custom host vars
options:
diff --git a/docs/docsite/rst/galaxy/user_guide.rst b/docs/docsite/rst/galaxy/user_guide.rst
index bbadfe8c..161839b1 100644
--- a/docs/docsite/rst/galaxy/user_guide.rst
+++ b/docs/docsite/rst/galaxy/user_guide.rst
@@ -385,7 +385,7 @@ There are two ways to define the dependencies of a role:
Using ``meta/requirements.yml``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-`.. versionadded:: 2.10`
+.. versionadded:: 2.10
You can create the file ``meta/requirements.yml`` and define dependencies in the same format used for :file:`requirements.yml` described in the `Installing multiple roles from a file`_ section.
diff --git a/docs/docsite/rst/installation_guide/intro_installation.rst b/docs/docsite/rst/installation_guide/intro_installation.rst
index eca7a92a..b5084431 100644
--- a/docs/docsite/rst/installation_guide/intro_installation.rst
+++ b/docs/docsite/rst/installation_guide/intro_installation.rst
@@ -1,66 +1,60 @@
.. _installation_guide:
.. _intro_installation_guide:
+******************
Installing Ansible
-===================
+******************
-This page describes how to install Ansible on different platforms.
-Ansible is an agentless automation tool that by default manages machines over the SSH protocol. Once installed, Ansible does
-not add a database, and there will be no daemons to start or keep running. You only need to install it on one machine (which could easily be a laptop) and it can manage an entire fleet of remote machines from that central point. When Ansible manages remote machines, it does not leave software installed or running on them, so there's no real question about how to upgrade Ansible when moving to a new version.
+Ansible is an agentless automation tool that you install on a control node. From the control node, Ansible manages machines and other devices remotely (by default, over the SSH protocol).
+To install Ansible for use at the command line, simply install the Ansible package on one machine (which could easily be a laptop). You do not need to install a database or run any daemons. Ansible can manage an entire fleet of remote machines from that one control node.
.. contents::
:local:
Prerequisites
---------------
+=============
-You install Ansible on a control node, which then uses SSH (by default) to communicate with your managed nodes (those end devices you want to automate).
+Before you install Ansible, review the requirements for a control node. Before you use Ansible, review the requirements for managed nodes (those end devices you want to automate). Control nodes and managed nodes have different minimum requirements.
.. _control_node_requirements:
Control node requirements
-^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------------------
-Currently Ansible can be run from any machine with Python 2 (version 2.7) or Python 3 (versions 3.5 and higher) installed.
+For your control node (the machine that runs Ansible), you can use any machine with Python 2 (version 2.7) or Python 3 (versions 3.5 and higher) installed. ansible-core 2.11 and Ansible 4.0.0 will make Python 3.8 a soft dependency for the control node, but will function with the aforementioned requirements. ansible-core 2.12 and Ansible 5.0.0 will require Python 3.8 or newer to function on the control node. Starting with ansible-core 2.11, the project will only be packaged for Python 3.8 and newer.
This includes Red Hat, Debian, CentOS, macOS, any of the BSDs, and so on.
Windows is not supported for the control node, read more about this in `Matt Davis's blog post <http://blog.rolpdog.com/2020/03/why-no-ansible-controller-for-windows.html>`_.
-When choosing a control node, bear in mind that any management system benefits from being run near the machines being managed. If you are running Ansible in a cloud, consider running it from a machine inside that cloud. In most cases this will work better than on the open Internet.
-
-.. note::
+.. warning::
- macOS by default is configured for a small number of file handles, so if you want to use 15 or more forks you'll need to raise the ulimit with ``sudo launchctl limit maxfiles unlimited``. This command can also fix any "Too many open files" error.
+ Please note that some plugins that run on the control node have additional requirements. These requirements should be listed in the plugin documentation.
+When choosing a control node, remember that any management system benefits from being run near the machines being managed. If you are using Ansible to manage machines in a cloud, consider using a machine inside that cloud as your control node. In most cases Ansible will perform better from a machine on the cloud than from a machine on the open Internet.
.. warning::
- Please note that some modules and plugins have additional requirements. For modules these need to be satisfied on the 'target' machine (the managed node) and should be listed in the module specific docs.
+ Ansible 2.11 will make Python 3.8 a soft dependency for the control node, but will function with the aforementioned requirements. Ansible 2.12 will require Python 3.8 or newer to function on the control node. Starting with Ansible 2.11, the project will only be packaged for Python 3.8 and newer.
+
.. _managed_node_requirements:
Managed node requirements
-^^^^^^^^^^^^^^^^^^^^^^^^^
+-------------------------
-On the managed nodes, you need a way to communicate, which is normally SSH. By
-default this uses SFTP. If that's not available, you can switch to SCP in
-:ref:`ansible.cfg <ansible_configuration_settings>`. You also need Python 2 (version 2.6 or later) or Python 3 (version 3.5 or
-later).
+Although you do not need a daemon on your managed nodes, you do need a way for Ansible to communicate with them. For most managed nodes, Ansible makes a connection over SSH and transfers modules using SFTP. If SSH works but SFTP is not available on some of your managed nodes, you can switch to SCP in :ref:`ansible.cfg <ansible_configuration_settings>`. For any machine or device that can run Python, you also need Python 2 (version 2.6 or later) or Python 3 (version 3.5 or later).
+
+.. warning::
+
+ Please note that some modules have additional requirements that need to be satisfied on the 'target' machine (the managed node). These requirements should be listed in the module documentation.
.. note::
- * If you have SELinux enabled on remote nodes, you will also want to install
- libselinux-python on them before using any copy/file/template related functions in Ansible. You
- can use the :ref:`yum module<yum_module>` or :ref:`dnf module<dnf_module>` in Ansible to install this package on remote systems
- that do not have it.
+ * If you have SELinux enabled on remote nodes, you will also want to install libselinux-python on them before using any copy/file/template related functions in Ansible. You can use the :ref:`yum module<yum_module>` or :ref:`dnf module<dnf_module>` in Ansible to install this package on remote systems that do not have it.
* By default, before the first Python module in a playbook runs on a host, Ansible attempts to discover a suitable Python interpreter on that host. You can override the discovery behavior by setting the :ref:`ansible_python_interpreter<ansible_python_interpreter>` inventory variable to a specific interpreter, and in other ways. See :ref:`interpreter_discovery` for details.
- * Ansible's :ref:`raw module<raw_module>`, and the :ref:`script module<script_module>`, do not depend
- on a client side install of Python to run. Technically, you can use Ansible to install a compatible
- version of Python using the :ref:`raw module<raw_module>`, which then allows you to use everything else.
- For example, if you need to bootstrap Python 2 onto a RHEL-based system, you can install it
- as follows:
+ * Ansible's :ref:`raw module<raw_module>`, and the :ref:`script module<script_module>`, do not depend on a client side install of Python to run. Technically, you can use Ansible to install a compatible version of Python using the :ref:`raw module<raw_module>`, which then allows you to use everything else. For example, if you need to bootstrap Python 2 onto a RHEL-based system, you can install it as follows:
.. code-block:: shell
@@ -68,28 +62,135 @@ later).
.. _what_version:
-Selecting an Ansible version to install
----------------------------------------
+Selecting an Ansible artifact and version to install
+====================================================
+
+Starting with version 2.10, Ansible distributes two artifacts: a community package called ``ansible`` and a minimalist language and runtime called ``ansible-core`` (called `ansible-base` in version 2.10). Choose the Ansible artifact and version that matches your particular needs.
+
+Installing the Ansible community package
+----------------------------------------
-Which Ansible version to install is based on your particular needs. You can choose any of the following ways to install Ansible:
+The ``ansible`` package includes the Ansible language and runtime plus a range of community curated Collections. It recreates and expands on the functionality that was included in Ansible 2.9.
+
+You can choose any of the following ways to install the Ansible community package:
* Install the latest release with your OS package manager (for Red Hat Enterprise Linux (TM), CentOS, Fedora, Debian, or Ubuntu).
* Install with ``pip`` (the Python package manager).
-* Install ``ansible-base`` from source to access the development (``devel``) version to develop or test the latest features.
+
+Installing `ansible-core`
+-------------------------
+
+Ansible also distributes a minimalist object called ``ansible-core`` (or ``ansible-base`` in version 2.10). It contains the Ansible language, runtime, and a short list of core modules and other plugins. You can build functionality on top of ``ansible-core`` by installing collections from Galaxy, Automation Hub, or any other source.
+
+You can choose any of the following ways to install ``ansible-core``:
+
+* Install ``ansible-core`` (version 2.11 and greater) or ``ansible-base`` (version 2.10) with ``pip``.
+* Install ``ansible-core`` from source from the ansible/ansible GitHub repository to access the development (``devel``) version to develop or test the latest features.
+
+.. note::
+
+ You should only run ``ansible-core`` from ``devel`` if you are modifying ``ansible-core``, or trying out features under development. This is a rapidly changing source of code and can become unstable at any point.
+
+Ansible generally creates new releases twice a year. See :ref:`release_and_maintenance` for information on release timing and maintenance of older releases.
+
+.. _from_pip:
+
+Installing and upgrading Ansible with ``pip``
+=============================================
+
+Ansible can be installed on many systems with ``pip``, the Python package manager.
+
+Prerequisites: Installing ``pip``
+----------------------------------
+
+If ``pip`` is not already available on your system, run the following commands to install it::
+
+ $ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
+ $ python get-pip.py --user
+
+You may need to perform some additional configuration before you are able to run Ansible. See the Python documentation on `installing to the user site`_ for more information.
+
+.. _installing to the user site: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site
+
+Installing Ansible with ``pip``
+-------------------------------
+
+Once ``pip`` is installed, you can install Ansible [1]_::
+
+ $ python -m pip install --user ansible
+
+In order to use the ``paramiko`` connection plugin or modules that require ``paramiko``, install the required module [2]_::
+
+ $ python -m pip install --user paramiko
+
+If you wish to install Ansible globally, run the following commands::
+
+ $ sudo python get-pip.py
+ $ sudo python -m pip install ansible
+
+.. note::
+
+ Running ``pip`` with ``sudo`` will make global changes to the system. Since ``pip`` does not coordinate with system package managers, it could make changes to your system that leaves it in an inconsistent or non-functioning state. This is particularly true for macOS. Installing with ``--user`` is recommended unless you understand fully the implications of modifying global files on the system.
+
+.. note::
+
+ Older versions of ``pip`` default to http://pypi.python.org/simple, which no longer works.
+ Please make sure you have the latest version of ``pip`` before installing Ansible.
+ If you have an older version of ``pip`` installed, you can upgrade by following `pip's upgrade instructions <https://pip.pypa.io/en/stable/installing/#upgrading-pip>`_ .
+
+.. _from_pip_venv:
+
+Installing Ansible in a virtual environment with ``pip``
+--------------------------------------------------------
.. note::
- You should only run ``ansible-base`` from ``devel`` if you are modifying ``ansible-base``, or trying out features under development. This is a rapidly changing source of code and can become unstable at any point.
+ If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
+
+Ansible can also be installed inside a new or existing ``virtualenv``::
+
+ $ python -m virtualenv ansible # Create a virtualenv if one does not already exist
+ $ source ansible/bin/activate # Activate the virtual environment
+ $ python -m pip install ansible
+
+.. _pip_upgrade:
+
+Upgrading Ansible with ``pip``
+------------------------------
+Upgrading from 2.9 or earlier to 2.10
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Ansible creates new releases two to three times a year. Due to this short release cycle,
-minor bugs will generally be fixed in the next release rather than maintaining backports on the stable branch.
-Major bugs will still have maintenance releases when needed, though these are infrequent.
+Starting in version 2.10, Ansible is made of two packages. When you upgrade from version 2.9 and older to version 2.10 or later, you need to uninstall the old Ansible version (2.9 or earlier) before upgrading. If you do not uninstall the older version of Ansible, you will see the following message, and no change will be performed:
+.. code-block:: console
+
+ Cannot install ansible-base with a pre-existing ansible==2.x installation.
+
+ Installing ansible-base with ansible-2.9 or older currently installed with
+ pip is known to cause problems. Please uninstall ansible and install the new
+ version:
+
+ pip uninstall ansible
+ pip install ansible-base
+
+ ...
+
+As explained by the message, to upgrade you must first remove the version of Ansible installed and then install it to the latest version.
+
+.. code-block:: console
+
+ $ pip uninstall ansible
+ $ pip install ansible
.. _installing_the_control_node:
.. _from_yum:
+Installing Ansible on specific operating systems
+================================================
+
+Follow these instructions to install the Ansible community package on a variety of operating systems.
+
Installing Ansible on RHEL, CentOS, or Fedora
----------------------------------------------
@@ -151,8 +252,6 @@ Debian/Ubuntu packages can also be built from the source checkout, run:
$ make deb
-You may also wish to run from source to get the development branch, which is covered below.
-
Installing Ansible on Debian
----------------------------
@@ -229,7 +328,11 @@ The instructions can be found in :ref:`from_pip`. If you are running macOS versi
.. note::
- If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
+ To upgrade from Ansible 2.9 or older to Ansible 3 or later, you must ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
+
+.. note::
+
+ macOS by default is configured for a small number of file handles, so if you want to use 15 or more forks you'll need to raise the ulimit with ``sudo launchctl limit maxfiles unlimited``. This command can also fix any "Too many open files" errors.
If you are installing on macOS Mavericks (10.9), you may encounter some noise from your compiler. A workaround is to do the following::
@@ -290,143 +393,70 @@ Update of the software will be managed by the swupd tool::
$ sudo swupd update
-.. _from_pip:
-
-Installing Ansible with ``pip``
---------------------------------
-
-Ansible can be installed with ``pip``, the Python package manager. If ``pip`` isn't already available on your system of Python, run the following commands to install it::
-
- $ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
- $ python get-pip.py --user
-
-.. note::
-
- If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
-
-Then install Ansible [1]_::
-
- $ python -m pip install --user ansible
-
-In order to use the ``paramiko`` connection plugin or modules that require ``paramiko``, install the required module [2]_::
+.. _from_pip_devel:
+.. _getting_ansible:
- $ python -m pip install --user paramiko
+Installing and running the ``devel`` branch from source
+=======================================================
-If you wish to install Ansible globally, run the following commands::
+In Ansible 2.10 and later, the `ansible/ansible repository <https://github.com/ansible/ansible>`_ contains the code for basic features and functions, such as copying module code to managed nodes. This code is also known as ``ansible-core``.
- $ sudo python get-pip.py
- $ sudo python -m pip install ansible
+New features are added to ``ansible-core`` on a branch called ``devel``. If you are testing new features, fixing bugs, or otherwise working with the development team on changes to the core code, you can install and run ``devel``.
.. note::
- Running ``pip`` with ``sudo`` will make global changes to the system. Since ``pip`` does not coordinate with system package managers, it could make changes to your system that leaves it in an inconsistent or non-functioning state. This is particularly true for macOS. Installing with ``--user`` is recommended unless you understand fully the implications of modifying global files on the system.
+ You should only install and run the ``devel`` branch if you are modifying ``ansible-core`` or trying out features under development. This is a rapidly changing source of code and can become unstable at any point.
.. note::
- Older versions of ``pip`` default to http://pypi.python.org/simple, which no longer works.
- Please make sure you have the latest version of ``pip`` before installing Ansible.
- If you have an older version of ``pip`` installed, you can upgrade by following `pip's upgrade instructions <https://pip.pypa.io/en/stable/installing/#upgrading-pip>`_ .
+ If you want to use Ansible Tower as the control node, do not install or run the ``devel`` branch of Ansible. Use an OS package manager (like ``apt`` or ``yum``) or ``pip`` to install a stable version.
-Upgrading Ansible from version 2.9 and older to version 2.10 or later
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+If you are running Ansible from source, you may also wish to follow the `Ansible GitHub project <https://github.com/ansible/ansible>`_. We track issues, document bugs, and share feature ideas in this and other related repositories.
-Starting in version 2.10, Ansible is made of two packages. You need to first uninstall the old Ansible version (2.9 or earlier) before upgrading.
-If you do not uninstall the older version of Ansible, you will see the following message, and no change will be performed:
+For more information on getting involved in the Ansible project, see the :ref:`ansible_community_guide`. For more information on creating Ansible modules and Collections, see the :ref:`developer_guide`.
-.. code-block:: console
-
- Cannot install ansible-base with a pre-existing ansible==2.x installation.
-
- Installing ansible-base with ansible-2.9 or older currently installed with
- pip is known to cause problems. Please uninstall ansible and install the new
- version:
-
- pip uninstall ansible
- pip install ansible-base
-
- ...
-
-As explained by the message, to upgrade you must first remove the version of Ansible installed and then install it
-to the latest version.
-
-.. code-block:: console
-
- $ pip uninstall ansible
- $ pip install ansible
-
-.. _from_pip_devel:
-
-Installing the development version of ``ansible-base``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Installing ``devel`` from GitHub with ``pip``
+---------------------------------------------
-In Ansible 2.10 and later, The `ansible/ansible repository <https://github.com/ansible/ansible>`_ contains the code for basic features and functions, such as copying module code to managed nodes. This code is also known as ``ansible-base``.
+You can install the ``devel`` branch of ``ansible-core`` directly from GitHub with ``pip``:
-.. note::
+.. code-block:: bash
- You should only run ``ansible-base`` from ``devel`` if you are modifying ``ansible-base`` or trying out features under development. This is a rapidly changing source of code and can become unstable at any point.
+ $ python -m pip install --user https://github.com/ansible/ansible/archive/devel.tar.gz
.. note::
- If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
-
-
-You can install the development version of ``ansible-base`` directly from GitHub with pip.
-
-.. code-block:: bash
-
- $ python -m pip install --user https://github.com/ansible/ansible/archive/devel.tar.gz
+ If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it. See :ref:`pip_upgrade` for more details.
-Replace ``devel`` in the URL mentioned above, with any other branch or tag on GitHub to install older versions of Ansible (prior to ``ansible-base`` 2.10.) This installs all of Ansible.
+You can replace ``devel`` in the URL mentioned above, with any other branch or tag on GitHub to install older versions of Ansible (prior to ``ansible-base`` 2.10.), tagged alpha or beta versions, and release candidates. This installs all of Ansible.
.. code-block:: bash
$ python -m pip install --user https://github.com/ansible/ansible/archive/stable-2.9.tar.gz
-See :ref:`from_source` for instructions on how to run ``ansible-base`` directly from source, without the requirement of installation.
+See :ref:`from_source` for instructions on how to run ``ansible-core`` directly from source.
-.. _from_pip_venv:
-Virtual Environments
-^^^^^^^^^^^^^^^^^^^^
+Installing ``devel`` from GitHub by cloning
+-------------------------------------------
-.. note::
+You can install the ``devel`` branch of ``ansible-core`` by cloning the GitHub repository:
- If you have Ansible 2.9 or older installed, you need to use ``pip uninstall ansible`` first to remove older versions of Ansible before re-installing it.
+.. code-block:: bash
-Ansible can also be installed inside a new or existing ``virtualenv``::
+ $ git clone https://github.com/ansible/ansible.git
+ $ cd ./ansible
- $ python -m virtualenv ansible # Create a virtualenv if one does not already exist
- $ source ansible/bin/activate # Activate the virtual environment
- $ python -m pip install ansible
+The default branch is ``devel``.
.. _from_source:
-Running ``ansible-base`` from source (devel)
----------------------------------------------
-
-In Ansible 2.10 and later, The `ansible/ansible repository <https://github.com/ansible/ansible>`_ contains the code for basic features and functions, such as copying module code to managed nodes. This code is also known as ``ansible-base``.
-
-.. note::
-
- You should only run ``ansible-base`` from ``devel`` if you are modifying ``ansible-base`` or trying out features under development. This is a rapidly changing source of code and can become unstable at any point.
-
-``ansible-base`` is easy to run from source. You do not need ``root`` permissions
-to use it and there is no software to actually install. No daemons
-or database setup are required.
-
-.. note::
-
- If you want to use Ansible Tower as the control node, do not use a source installation of Ansible. Please use an OS package manager (like ``apt`` or ``yum``) or ``pip`` to install a stable version.
-
-
-To install from source, clone the ``ansible-base`` git repository:
-
-.. code-block:: bash
+Running the ``devel`` branch from a clone
+-----------------------------------------
- $ git clone https://github.com/ansible/ansible.git
- $ cd ./ansible
+``ansible-core`` is easy to run from source. You do not need ``root`` permissions to use it and there is no software to actually install. No daemons or database setup are required.
-Once ``git`` has cloned the ``ansible-base`` repository, setup the Ansible environment:
+Once you have installed the ``ansible-core`` repository by cloning, setup the Ansible environment:
Using Bash:
@@ -442,7 +472,7 @@ If you want to suppress spurious warnings/errors, use::
$ source ./hacking/env-setup -q
-If you don't have ``pip`` installed in your version of Python, install it::
+If you do not have ``pip`` installed in your version of Python, install it::
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ python get-pip.py --user
@@ -453,7 +483,7 @@ Ansible also uses the following Python modules that need to be installed [1]_:
$ python -m pip install --user -r ./requirements.txt
-To update ``ansible-base`` checkouts, use pull-with-rebase so any local changes are replayed.
+To update the ``devel`` branch of ``ansible-core`` on your local machine, use pull-with-rebase so any local changes are replayed.
.. code-block:: bash
@@ -464,9 +494,7 @@ To update ``ansible-base`` checkouts, use pull-with-rebase so any local changes
$ git pull --rebase #same as above
$ git submodule update --init --recursive
-Once running the env-setup script you'll be running from checkout and the default inventory file
-will be ``/etc/ansible/hosts``. You can optionally specify an inventory file (see :ref:`inventory`)
-other than ``/etc/ansible/hosts``:
+After you run the the env-setup script, you will be running from the source code. The default inventory file will be ``/etc/ansible/hosts``. You can optionally specify an inventory file (see :ref:`inventory`) other than ``/etc/ansible/hosts``:
.. code-block:: bash
@@ -475,7 +503,10 @@ other than ``/etc/ansible/hosts``:
You can read more about the inventory file at :ref:`inventory`.
-Now let's test things with a ping command:
+Confirming your installation
+============================
+
+Whatever method of installing Ansible you chose, you can test that it is installed correctly with a ping command:
.. code-block:: bash
@@ -486,7 +517,7 @@ You can also use "sudo make install".
.. _tagged_releases:
Finding tarballs of tagged releases
------------------------------------
+===================================
Packaging Ansible or wanting to build a local package yourself, but don't want to do a git checkout? Tarballs of releases are available from ``pypi`` as https://pypi.python.org/packages/source/a/ansible/ansible-{{VERSION}}.tar.gz. You can make VERSION a variable in your package managing system that you update in one place whenever you package a new version. Alternately, you can download https://pypi.python.org/project/ansible to get the latest stable release.
@@ -499,18 +530,17 @@ These releases are also tagged in the `git repository <https://github.com/ansibl
.. _shell_completion:
-Ansible command shell completion
---------------------------------
+Adding Ansible command shell completion
+=======================================
-As of Ansible 2.9, shell completion of the Ansible command line utilities is available and provided through an optional dependency
-called ``argcomplete``. ``argcomplete`` supports bash, and has limited support for zsh and tcsh.
+As of Ansible 2.9, you can add shell completion of the Ansible command line utilities by installing an optional dependency called ``argcomplete``. ``argcomplete`` supports bash, and has limited support for zsh and tcsh.
You can install ``python-argcomplete`` from EPEL on Red Hat Enterprise based distributions, and or from the standard OS repositories for many other distributions.
-For more information about installing and configuration see the `argcomplete documentation <https://argcomplete.readthedocs.io/en/latest/>`_.
+For more information about installation and configuration, see the `argcomplete documentation <https://argcomplete.readthedocs.io/en/latest/>`_.
Installing ``argcomplete`` on RHEL, CentOS, or Fedora
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+-----------------------------------------------------
On Fedora:
@@ -527,7 +557,7 @@ On RHEL and CentOS:
Installing ``argcomplete`` with ``apt``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+---------------------------------------
.. code-block:: bash
@@ -535,19 +565,19 @@ Installing ``argcomplete`` with ``apt``
Installing ``argcomplete`` with ``pip``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+---------------------------------------
.. code-block:: bash
$ python -m pip install argcomplete
Configuring ``argcomplete``
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+---------------------------
There are 2 ways to configure ``argcomplete`` to allow shell completion of the Ansible command line utilities: globally or per command.
-Globally
-"""""""""
+Global configuration
+^^^^^^^^^^^^^^^^^^^^
Global completion requires bash 4.2.
@@ -557,8 +587,8 @@ Global completion requires bash 4.2.
This will write a bash completion file to a global location. Use ``--dest`` to change the location.
-Per command
-"""""""""""
+Per command configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^
If you do not have bash 4.2, you must register each script independently.
@@ -576,20 +606,11 @@ If you do not have bash 4.2, you must register each script independently.
You should place the above commands into your shells profile file such as ``~/.profile`` or ``~/.bash_profile``.
-``argcomplete`` with zsh or tcsh
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Using ``argcomplete`` with zsh or tcsh
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
See the `argcomplete documentation <https://argcomplete.readthedocs.io/en/latest/>`_.
-.. _getting_ansible:
-
-``ansible-base`` on GitHub
----------------------------
-
-You may also wish to follow the `GitHub project <https://github.com/ansible/ansible>`_ if
-you have a GitHub account. This is also where we keep the issue tracker for sharing
-bugs and feature ideas.
-
.. seealso::
diff --git a/docs/docsite/rst/network/dev_guide/developing_resource_modules_network.rst b/docs/docsite/rst/network/dev_guide/developing_resource_modules_network.rst
index e19067a3..f5715b5f 100644
--- a/docs/docsite/rst/network/dev_guide/developing_resource_modules_network.rst
+++ b/docs/docsite/rst/network/dev_guide/developing_resource_modules_network.rst
@@ -167,7 +167,7 @@ For example, the resource model builder includes the ``myos_interfaces.yml`` sam
description:
- The some_int.
type: int
- version_added: '1.1'
+ version_added: '1.1.0'
some_dict:
type: dict
description:
diff --git a/docs/docsite/rst/network/user_guide/cli_parsing.rst b/docs/docsite/rst/network/user_guide/cli_parsing.rst
index 0dd81e2e..dd9443da 100644
--- a/docs/docsite/rst/network/user_guide/cli_parsing.rst
+++ b/docs/docsite/rst/network/user_guide/cli_parsing.rst
@@ -59,7 +59,7 @@ The ``cli_parse`` module includes the following cli_parsing plugins:
``json``
Converts JSON output at the CLI to an Ansible native data structure
-Although Ansible contains a number of plugins that can convert XML to Ansible native data structures, the``cli_parse`` module runs the command on devices that return XML and returns the converted data in a single task.
+Although Ansible contains a number of plugins that can convert XML to Ansible native data structures, the ``cli_parse`` module runs the command on devices that return XML and returns the converted data in a single task.
Because ``cli_parse`` uses a plugin based architecture, it can use additional parsing engines from any Ansible collection.
@@ -538,7 +538,7 @@ Parsing with TTP
TTP is a Python library for semi-structured text parsing using templates. TTP uses a jinja-like syntax to limit the need for regular expressions. Users familiar with jinja templating may find the TTP template syntax familiar.
-The following is an example TTP template stored as ``templates/nxos_show_interfaces.ttp``:
+The following is an example TTP template stored as ``templates/nxos_show_interface.ttp``:
.. code-block:: jinja
@@ -586,7 +586,7 @@ The task sets the follow fact as the ``interfaces`` fact for the host:
Converting XML
-----------------
-Although Ansible contains a number of plugins that can convert XML to Ansible native data structures, the``cli_parse`` module runs the command on devices that return XML and returns the converted data in a single task.
+Although Ansible contains a number of plugins that can convert XML to Ansible native data structures, the ``cli_parse`` module runs the command on devices that return XML and returns the converted data in a single task.
This example task runs the ``show interface`` command and parses the output as XML:
diff --git a/docs/docsite/rst/porting_guides/core_porting_guides.rst b/docs/docsite/rst/porting_guides/core_porting_guides.rst
new file mode 100644
index 00000000..0c8f3d56
--- /dev/null
+++ b/docs/docsite/rst/porting_guides/core_porting_guides.rst
@@ -0,0 +1,15 @@
+.. _core_porting_guides:
+
+***************************
+Ansible Core Porting Guides
+***************************
+
+This section lists porting guides that can help you in updating playbooks, plugins and other parts of your Ansible infrastructure from one version of ``ansible-core`` to the next.
+
+Please note that this is not a complete list. If you believe any extra information would be useful in these pages, you can edit by clicking `Edit on GitHub` on the top right, or raising an issue.
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ porting_guide_base_2.10
diff --git a/docs/docsite/rst/reference_appendices/glossary.rst b/docs/docsite/rst/reference_appendices/glossary.rst
index 6b36a7ea..c1170c6d 100644
--- a/docs/docsite/rst/reference_appendices/glossary.rst
+++ b/docs/docsite/rst/reference_appendices/glossary.rst
@@ -23,6 +23,15 @@ when a term comes up on the mailing list.
writing a :term:`playbook <playbooks>` and playbooks can also glue
lots of other operations together.
+ Ansible (the package)
+ A software package (Python, deb, rpm, and so on) that contains ansible-base and a select group of collections. Playbooks that worked with Ansible 2.9 should still work with the Ansible 2.10 package. See the :file:`ansible-<version>.build` file in the release-specific directory at `ansible-build-data <https://github.com/ansible-community/ansible-build-data>`_ for a list of collections included in Ansible, as well as the included ``ansible-base`` version.
+
+ ansible-base
+ New for 2.10. The installable package (RPM/Python/Deb package) generated from the `ansible/ansible repository <https://github.com/ansible/ansible>`_. Contains the command-line tools and the code for basic features and functions, such as copying module code to managed nodes. The ``ansible-base`` package includes a few modules and plugins and allows you to add others by installing collections.
+
+ Ansible Galaxy
+ An `online resource <galaxy.ansible.com>`_ for finding and sharing Ansible community content. Also, the command-line utility that lets users install individual Ansible Collections, for example`` ansible-galaxy install community.crypto``.
+
Async
Refers to a task that is configured to run in the background rather
than waiting for completion. If you have a long process that would
@@ -49,6 +58,18 @@ when a term comes up on the mailing list.
other systems). Use this to get an idea of what might happen, but do
not substitute it for a good staging environment.
+ Collection
+ A packaging format for bundling and distributing Ansible content, including plugins, roles, modules, and more. Collections release independent of other collections or ``ansible-base`` so features can be available sooner to users. Some collections are packaged with Ansible (version 2.10 or later). You can install other collections (or other versions of collections) with ``ansible-galaxy collection install <namespace.collection>``.
+
+ Collection name
+ The second part of a Fully Qualified Collection Name. The collection name divides the collection namespace and usually reflects the function of the collection content. For example, the ``cisco`` namespace might contain ``cisco.ios``, ``cisco.aci``, and ``cisco.nxos``, with content for managing the different network devices maintained by Cisco.
+
+ community.general (collection)
+ A special collection managed by the Ansible Community Team containing all the modules and plugins which shipped in Ansible 2.9 that do ont have their own dedicated Collection. See `community.general <https://galaxy.ansible.com/community/general>`_` on Galaxy.
+
+ community.network (collection)
+ Similar to ``community.general``, focusing on network content. `community.network <https://galaxy.ansible.com/community/network>`_` on Galaxy.
+
Connection Plugin
By default, Ansible talks to remote machines through pluggable
libraries. Ansible uses native OpenSSH (:term:`SSH (Native)`) or
@@ -121,6 +142,9 @@ when a term comes up on the mailing list.
forks, though if you have a lot of RAM, you can easily set this to
a value like 50 for increased parallelism.
+ Fully Qualified Collection Name (FQCN)
+ The full definition of a module, plugin, or role hosted within a collection, in the form <namespace.collection.content_name>. Allows a Playbook to refer to a specific module or plugin from a specific source in an unambiguous manner, for example, ``community.grafana.grafana_dashboard``. The FQCN is required when you want to specify the exact source of a plugin. For example, if multiple collections contain a module plugin called ``user``, the FQCN specifies which one to use for a given task. When you have multiple collections installed, the FQCN is always the explicit and authoritative indicator of which collection to search for the correct plugin for each task.
+
Gather Facts (Boolean)
:term:`Facts` are mentioned above. Sometimes when running a multi-play
:term:`playbook <playbooks>`, it is desirable to have some plays that
@@ -294,6 +318,9 @@ when a term comes up on the mailing list.
models entire IT topologies and workflows rather than looking at
configuration from a "one system at a time" perspective.
+ Namespace
+ The first part of a fully qualified collection name, the namespace usually reflects a functional content category. Example: in ``cisco.ios.ios_config``, ``cisco`` is the namespace. Namespaces are reserved and distributed by Red Hat at Red Hat's discretion. Many, but not all, namespaces will correspond with vendor names. See `Galaxy namespaces <https://galaxy.ansible.com/docs/contributing/namespaces.html#galaxy-namespaces>`_ on the Galaxy docsite for namespace requirements.
+
Notify
The act of a :term:`task <tasks>` registering a change event and
informing a :term:`handler <handlers>` task that another
diff --git a/docs/docsite/rst/roadmap/COLLECTIONS_2_10.rst b/docs/docsite/rst/roadmap/COLLECTIONS_2_10.rst
index dd845bad..88d6fdcd 100644
--- a/docs/docsite/rst/roadmap/COLLECTIONS_2_10.rst
+++ b/docs/docsite/rst/roadmap/COLLECTIONS_2_10.rst
@@ -39,8 +39,13 @@ Release Schedule
Ansible-2.10.x patch releases will occur roughly every three weeks if changes to collections have been made or if it is deemed necessary to force an upgrade to a later ansible-base-2.10.x. Ansible-2.10.x patch releases may contain new features but not backwards incompatibilities. In practice, this means we will include new collection versions where either the patch or the minor version number has changed but not when the major number has changed (example: Ansible-2.10 ships with community-crypto-1.1.0; ansible-2.10.1 may ship with community-crypto-1.2.0 but would not ship with community-crypto-2.0.0).
-Breaking changes may be introduced in ansible-2.11.0 although we encourage collection owners to use deprecation periods that will show up in at least one Ansible release before being changed incompatibly.
-The rough schedule for Ansible-2.11 and beyond (such as how many months we'll aim for between versions) is still to be discussed and likely will be made after 2.10.0 has been released.
+.. note::
+
+ Minor releases will stop when :ref:`Ansible-3 <ansible_3_roadmap>` is released. See the :ref:`Release and Maintenance Page <release_and_maintenance>` for more information.
+
+
+Breaking changes may be introduced in ansible-3.0 although we encourage collection owners to use deprecation periods that will show up in at least one Ansible release before being changed incompatibly.
+
For more information, reach out on a mailing list or an IRC channel - see :ref:`communication` for more details.
diff --git a/docs/docsite/rst/roadmap/COLLECTIONS_3_0.rst b/docs/docsite/rst/roadmap/COLLECTIONS_3_0.rst
new file mode 100644
index 00000000..2af350ab
--- /dev/null
+++ b/docs/docsite/rst/roadmap/COLLECTIONS_3_0.rst
@@ -0,0 +1,58 @@
+.. _ansible_3_roadmap:
+
+===================
+Ansible project 3.0
+===================
+
+This release schedule includes dates for the `ansible <https://pypi.org/project/ansible/>`_ package, with a few dates for the `ansible-base <https://pypi.org/project/ansible-base/>`_ package as well. All dates are subject to change. Ansible 3.x.x includes ``ansible-base`` 2.10. See :ref:`base_roadmap_2_10` for the most recent updates on ``ansible-base``.
+
+.. contents::
+ :local:
+
+Release schedule
+=================
+
+.. note::
+
+ Ansible is switching from its traditional versioning scheme to `semantic versioning <https://semver.org/>`_ starting with this release. So this version is 3.0.0 instead of 2.11.0.
+
+
+
+:2020-12-16: Finalize rules for net-new collections submitted for the ansible release.
+:2021-01-27: Final day for new collections to be **reviewed and approved**. They MUST be
+ submitted prior to this to give reviewers a chance to look them over and for collection owners
+ to fix any problems.
+:2021-02-02: Ansible-3.0.0-beta1 -- feature freeze [1]_
+:2021-02-09: Ansible-3.0.0-rc1 -- final freeze [2]_ [3]_
+:2021-02-16: Release of Ansible-3.0.0
+:2021-03-09: Release of Ansible-3.1.0 (bugfix + compatible features: every three weeks)
+
+.. [1] No new modules or major features accepted after this date. In practice this means we will freeze the semver collection versions to compatible release versions. For example, if the version of community.crypto on this date was community-crypto-2.1.0; ansible-3.0.0 could ship with community-crypto-2.1.1. It would not ship with community-crypto-2.2.0.
+
+.. [2] After this date only changes blocking a release are accepted. Accepted changes require creating a new rc and may slip the final release date.
+.. [3] Collections will only be updated to a new version if a blocker is approved. Collection owners should discuss any blockers at a community IRC meeting (before this freeze) to decide whether to bump the version of the collection for a fix. See the `Community IRC meeting agenda <https://github.com/ansible/community/issues/539>`_.
+
+
+.. note::
+
+ Breaking changes may be introduced in Ansible 3.0.0, although we encourage collection owners to use deprecation periods that will show up in at least one Ansible release before the breaking change happens.
+
+
+Ansible minor releases
+=======================
+
+Ansible 3.x.x minor releases will occur approximately every three weeks if changes to collections have been made or if it is deemed necessary to force an upgrade to a later ansible-base-2.10.x. Ansible 3.x.x minor releases may contain new features but not backwards incompatibilities. In practice, this means we will include new collection versions where either the patch or the minor version number has changed but not when the major number has changed. For example, if Ansible-3.0.0 ships with community-crypto-2.1.0; Ansible-3.1.0 may ship with community-crypto-2.2.0 but would not ship with community-crypto-3.0.0).
+
+
+.. note::
+
+ Minor releases will stop when :ref:`Ansible-4 <ansible_4_roadmap>` is released. See the :ref:`Release and Maintenance Page <release_and_maintenance>` for more information.
+
+
+For more information, reach out on a mailing list or an IRC channel - see :ref:`communication` for more details.
+
+
+ansible-base release
+====================
+
+Ansible 3.x.x works with ``ansible-base`` 2.10. See :ref:`base_roadmap_2_10` for details.
diff --git a/docs/docsite/rst/roadmap/ansible_base_roadmap_index.rst b/docs/docsite/rst/roadmap/ansible_base_roadmap_index.rst
index 4cf7740c..8bc68547 100644
--- a/docs/docsite/rst/roadmap/ansible_base_roadmap_index.rst
+++ b/docs/docsite/rst/roadmap/ansible_base_roadmap_index.rst
@@ -1,15 +1,19 @@
-.. _ansible_base_roadmaps:
+.. _ansible_core_roadmaps:
-ansible-base Roadmap
+ansible-core Roadmaps
=====================
-The ``ansible-base`` team develops a roadmap for each major and minor ``ansible-base`` release. The latest roadmap shows current work; older roadmaps provide a history of the project. We don't publish roadmaps for subminor versions. So 2.10 and 2.11 have roadmaps, but 2.10.1 does not.
+The ``ansible-core`` team develops a roadmap for each major and minor ``ansible-core`` release. The latest roadmap shows current work; older roadmaps provide a history of the project. We don't publish roadmaps for subminor versions. So 2.10 and 2.11 have roadmaps, but 2.10.1 does not.
+
+.. note::
+
+ Ansible renamed ``ansible-base`` to ``ansible-core``.
We incorporate team and community feedback in each roadmap, and aim for further transparency and better inclusion of both community desires and submissions.
-Each roadmap offers a *best guess*, based on the ``ansible-base`` team's experience and on requests and feedback from the community, of what will be included in a given release. However, some items on the roadmap may be dropped due to time constraints, lack of community maintainers, and so on.
+Each roadmap offers a *best guess*, based on the ``ansible-core`` team's experience and on requests and feedback from the community, of what will be included in a given release. However, some items on the roadmap may be dropped due to time constraints, lack of community maintainers, and so on.
-Each roadmap is published both as an idea of what is upcoming in ``ansible-base``, and as a medium for seeking further feedback from the community.
+Each roadmap is published both as an idea of what is upcoming in ``ansible-core``, and as a medium for seeking further feedback from the community.
You can submit feedback on the current roadmap in multiple ways:
@@ -22,6 +26,6 @@ See :ref:`Ansible communication channels <communication>` for details on how to
.. toctree::
:maxdepth: 1
:glob:
- :caption: ansible-base Roadmaps
+ :caption: ansible-core Roadmaps
ROADMAP_2_10
diff --git a/docs/docsite/rst/scenario_guides/guide_infoblox.rst b/docs/docsite/rst/scenario_guides/guide_infoblox.rst
index 2fa90703..d4597d90 100644
--- a/docs/docsite/rst/scenario_guides/guide_infoblox.rst
+++ b/docs/docsite/rst/scenario_guides/guide_infoblox.rst
@@ -252,6 +252,10 @@ You can use the Infoblox dynamic inventory script to import your network node in
- `infoblox.py <https://raw.githubusercontent.com/ansible-collections/community.general/main/scripts/inventory/infoblox.py>`_ - The python script that retrieves the NIOS inventory.
+.. note::
+
+ Please note that the inventory script only works when Ansible 2.9, 2.10 or 3 have been installed. The inventory script will eventually be removed from `community.general <https://galaxy.ansible.com/community/general>`_, and will not work if `community.general` is only installed with `ansible-galaxy collection install`. Please use the inventory plugin from `infoblox.nios_modules <https://galaxy.ansible.com/infoblox/nios_modules>`_ instead.
+
To use the Infoblox dynamic inventory script:
#. Download the ``infoblox.yaml`` file and save it in the ``/etc/ansible`` directory.
diff --git a/docs/docsite/rst/conf.py b/docs/docsite/sphinx_conf/2.10_conf.py
index 0a72676e..e68b90c8 100644
--- a/docs/docsite/rst/conf.py
+++ b/docs/docsite/sphinx_conf/2.10_conf.py
@@ -13,6 +13,9 @@
# All configuration values have a default value; values that are commented out
# serve to show the default value.
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
import sys
import os
@@ -62,7 +65,7 @@ master_doc = 'index'
# General substitutions.
project = 'Ansible'
-copyright = "2019 Red Hat, Inc."
+copyright = "2021 Red Hat, Inc."
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
@@ -87,8 +90,12 @@ today_fmt = '%B %d, %Y'
# A list of glob-style patterns that should be excluded when looking
# for source files.
-# OBSOLETE - removing this - dharmabumstead 2018-02-06
-# exclude_patterns = ['modules']
+exclude_patterns = [
+ '2.10_index.rst',
+ 'ansible_index.rst',
+ 'core_index.rst',
+ 'porting_guides/core_porting_guides',
+]
# The reST default role (used for this markup: `text`) to use for all
# documents.
@@ -145,9 +152,9 @@ html_context = {
'github_root_dir': 'devel/lib/ansible',
'github_cli_version': 'devel/lib/ansible/cli/',
'current_version': version,
- 'latest_version': '2.10',
+ 'latest_version': '3',
# list specifically out of order to make latest work
- 'available_versions': ('latest', '2.9', '2.9_ja', '2.8', 'devel'),
+ 'available_versions': ('latest', '2.10', '2.9', '2.9_ja', '2.8', 'devel'),
'css_files': ('_static/ansible.css', # overrides to the standard theme
),
}
diff --git a/docs/docsite/sphinx_conf/ansible_conf.py b/docs/docsite/sphinx_conf/ansible_conf.py
new file mode 100644
index 00000000..44af3496
--- /dev/null
+++ b/docs/docsite/sphinx_conf/ansible_conf.py
@@ -0,0 +1,306 @@
+# -*- coding: utf-8 -*-
+#
+# documentation build configuration file, created by
+# sphinx-quickstart on Sat Sep 27 13:23:22 2008-2009.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed
+# automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+import os
+
+# pip install sphinx_rtd_theme
+# import sphinx_rtd_theme
+# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+# sys.path.append(os.path.abspath('some/directory'))
+#
+sys.path.insert(0, os.path.join('ansible', 'lib'))
+sys.path.append(os.path.abspath(os.path.join('..', '_extensions')))
+
+# We want sphinx to document the ansible modules contained in this repository,
+# not those that may happen to be installed in the version
+# of Python used to run sphinx. When sphinx loads in order to document,
+# the repository version needs to be the one that is loaded:
+sys.path.insert(0, os.path.abspath(os.path.join('..', '..', '..', 'lib')))
+
+VERSION = '3'
+AUTHOR = 'Ansible, Inc'
+
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings.
+# They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+# TEST: 'sphinxcontrib.fulltoc'
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'pygments_lexer', 'notfound.extension']
+
+# Later on, add 'sphinx.ext.viewcode' to the list if you want to have
+# colorized code generated too for references.
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'Ansible'
+copyright = "2021 Red Hat, Inc."
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = VERSION
+# The full version, including alpha/beta/rc tags.
+release = VERSION
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+# unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be
+# searched for source files.
+# exclude_dirs = []
+
+# A list of glob-style patterns that should be excluded when looking
+# for source files.
+exclude_patterns = [
+ '2.10_index.rst',
+ 'ansible_index.rst',
+ 'core_index.rst',
+ 'porting_guides/core_porting_guides.rst',
+ 'porting_guides/porting_guide_base_2.10.rst',
+ 'porting_guides/porting_guide_core_2.11.rst',
+ 'roadmap/index.rst',
+ 'roadmap/ansible_base_roadmap_index.rst',
+ 'roadmap/ROADMAP_2_10.rst',
+ 'roadmap/ROADMAP_2_11.rst'
+]
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+highlight_language = 'YAML+Jinja'
+
+# Substitutions, variables, entities, & shortcuts for text which do not need to link to anything.
+# For titles which should be a link, use the intersphinx anchors set at the index, chapter, and section levels, such as qi_start_:
+# |br| is useful for formatting fields inside of tables
+# |_| is a nonbreaking space; similarly useful inside of tables
+rst_epilog = """
+.. |br| raw:: html
+
+ <br>
+.. |_| unicode:: 0xA0
+ :trim:
+"""
+
+
+# Options for HTML output
+# -----------------------
+
+html_theme_path = ['../_themes']
+html_theme = 'sphinx_rtd_theme'
+html_short_title = 'Ansible Documentation'
+html_show_sphinx = False
+
+html_theme_options = {
+ 'canonical_url': "https://docs.ansible.com/ansible/latest/",
+ 'vcs_pageview_mode': 'edit'
+}
+
+html_context = {
+ 'display_github': 'True',
+ 'github_user': 'ansible',
+ 'github_repo': 'ansible',
+ 'github_version': 'devel/docs/docsite/rst/',
+ 'github_module_version': 'devel/lib/ansible/modules/',
+ 'github_root_dir': 'devel/lib/ansible',
+ 'github_cli_version': 'devel/lib/ansible/cli/',
+ 'current_version': version,
+ 'latest_version': '3',
+ # list specifically out of order to make latest work
+ 'available_versions': ('latest', '2.10', '2.9', '2.9_ja', '2.8', 'devel'),
+ 'css_files': ('_static/ansible.css', # overrides to the standard theme
+ ),
+}
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+# html_style = 'solar.css'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Ansible Documentation'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+# html_logo =
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = 'favicon.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['../_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_use_modindex = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+html_copy_source = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = 'https://docs.ansible.com/ansible/latest'
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Poseidodoc'
+
+# Configuration for sphinx-notfound-pages
+# with no 'notfound_template' and no 'notfound_context' set,
+# the extension builds 404.rst into a location-agnostic 404 page
+#
+# default is `en` - using this for the sub-site:
+notfound_default_language = "ansible"
+# default is `latest`:
+# setting explicitly - docsite serves up /ansible/latest/404.html
+# so keep this set to `latest` even on the `devel` branch
+# then no maintenance is needed when we branch a new stable_x.x
+notfound_default_version = "latest"
+# makes default setting explicit:
+notfound_no_urls_prefix = False
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+# latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+# latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class
+# [howto/manual]).
+latex_documents = [
+ ('index', 'ansible.tex', 'Ansible 2.2 Documentation', AUTHOR, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+# latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_use_modindex = True
+
+autoclass_content = 'both'
+
+# Note: Our strategy for intersphinx mappings is to have the upstream build location as the
+# canonical source and then cached copies of the mapping stored locally in case someone is building
+# when disconnected from the internet. We then have a script to update the cached copies.
+#
+# Because of that, each entry in this mapping should have this format:
+# name: ('http://UPSTREAM_URL', (None, 'path/to/local/cache.inv'))
+#
+# The update script depends on this format so deviating from this (for instance, adding a third
+# location for the mappning to live) will confuse it.
+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')),
+ 'ansible_2_10': ('https://docs.ansible.com/ansible/2.10/', (None, '../ansible_2_10.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')),
+ 'ansible_2_6': ('https://docs.ansible.com/ansible/2.6/', (None, '../ansible_2_6.inv')),
+ 'ansible_2_5': ('https://docs.ansible.com/ansible/2.5/', (None, '../ansible_2_5.inv')),
+ }
+
+# linckchecker settings
+linkcheck_ignore = [
+ r'http://irc\.freenode\.net',
+]
+linkcheck_workers = 25
+# linkcheck_anchors = False
diff --git a/docs/docsite/sphinx_conf/core_conf.py b/docs/docsite/sphinx_conf/core_conf.py
new file mode 100644
index 00000000..709a3d5e
--- /dev/null
+++ b/docs/docsite/sphinx_conf/core_conf.py
@@ -0,0 +1,314 @@
+# -*- coding: utf-8 -*-
+#
+# documentation build configuration file, created by
+# sphinx-quickstart on Sat Sep 27 13:23:22 2008-2009.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed
+# automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+import os
+
+# pip install sphinx_rtd_theme
+# import sphinx_rtd_theme
+# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+# sys.path.append(os.path.abspath('some/directory'))
+#
+sys.path.insert(0, os.path.join('ansible', 'lib'))
+sys.path.append(os.path.abspath(os.path.join('..', '_extensions')))
+
+# We want sphinx to document the ansible modules contained in this repository,
+# not those that may happen to be installed in the version
+# of Python used to run sphinx. When sphinx loads in order to document,
+# the repository version needs to be the one that is loaded:
+sys.path.insert(0, os.path.abspath(os.path.join('..', '..', '..', 'lib')))
+
+VERSION = 'devel'
+AUTHOR = 'Ansible, Inc'
+
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings.
+# They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+# TEST: 'sphinxcontrib.fulltoc'
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'pygments_lexer', 'notfound.extension']
+
+# Later on, add 'sphinx.ext.viewcode' to the list if you want to have
+# colorized code generated too for references.
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = 'Ansible'
+copyright = "2021 Red Hat, Inc."
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = VERSION
+# The full version, including alpha/beta/rc tags.
+release = VERSION
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+# today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+# unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be
+# searched for source files.
+# exclude_dirs = []
+
+# A list of glob-style patterns that should be excluded when looking
+# for source files.
+exclude_patterns = [
+ '2.10_index.rst',
+ 'ansible_index.rst',
+ 'core_index.rst',
+ 'galaxy',
+ 'network',
+ 'scenario_guides',
+ 'porting_guides/porting_guides.rst',
+ 'porting_guides/porting_guide_2*',
+ 'porting_guides/porting_guide_3*',
+ 'roadmap/index.rst',
+ 'roadmap/ansible_roadmap_index.rst',
+ 'roadmap/old_roadmap_index.rst',
+ 'roadmap/ROADMAP_2_5.rst',
+ 'roadmap/ROADMAP_2_6.rst',
+ 'roadmap/ROADMAP_2_7.rst',
+ 'roadmap/ROADMAP_2_8.rst',
+ 'roadmap/ROADMAP_2_9.rst',
+ 'roadmap/COLLECTIONS*'
+]
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+highlight_language = 'YAML+Jinja'
+
+# Substitutions, variables, entities, & shortcuts for text which do not need to link to anything.
+# For titles which should be a link, use the intersphinx anchors set at the index, chapter, and section levels, such as qi_start_:
+# |br| is useful for formatting fields inside of tables
+# |_| is a nonbreaking space; similarly useful inside of tables
+rst_epilog = """
+.. |br| raw:: html
+
+ <br>
+.. |_| unicode:: 0xA0
+ :trim:
+"""
+
+
+# Options for HTML output
+# -----------------------
+
+html_theme_path = ['../_themes']
+html_theme = 'sphinx_rtd_theme'
+html_short_title = 'Ansible Core Documentation'
+html_show_sphinx = False
+
+html_theme_options = {
+ 'canonical_url': "https://docs.ansible.com/ansible/latest/",
+ 'vcs_pageview_mode': 'edit'
+}
+
+html_context = {
+ 'display_github': 'True',
+ 'github_user': 'ansible',
+ 'github_repo': 'ansible',
+ 'github_version': 'devel/docs/docsite/rst/',
+ 'github_module_version': 'devel/lib/ansible/modules/',
+ 'github_root_dir': 'devel/lib/ansible',
+ 'github_cli_version': 'devel/lib/ansible/cli/',
+ 'current_version': version,
+ 'latest_version': '2.10',
+ # list specifically out of order to make latest work
+ 'available_versions': ('2.10', 'devel',),
+ 'css_files': ('_static/ansible.css', # overrides to the standard theme
+ ),
+}
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+# html_style = 'solar.css'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+html_title = 'Ansible Core Documentation'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+# html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+# html_logo =
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+# html_favicon = 'favicon.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['../_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+# html_use_modindex = True
+
+# If false, no index is generated.
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+# html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+html_copy_source = False
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+# html_use_opensearch = 'https://docs.ansible.com/ansible/latest'
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Poseidodoc'
+
+# Configuration for sphinx-notfound-pages
+# with no 'notfound_template' and no 'notfound_context' set,
+# the extension builds 404.rst into a location-agnostic 404 page
+#
+# default is `en` - using this for the sub-site:
+notfound_default_language = "ansible"
+# default is `latest`:
+# setting explicitly - docsite serves up /ansible/latest/404.html
+# so keep this set to `latest` even on the `devel` branch
+# then no maintenance is needed when we branch a new stable_x.x
+notfound_default_version = "latest"
+# makes default setting explicit:
+notfound_no_urls_prefix = False
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+# latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+# latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class
+# [howto/manual]).
+latex_documents = [
+ ('index', 'ansible.tex', 'Ansible 2.2 Documentation', AUTHOR, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+# latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+# latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+# latex_appendices = []
+
+# If false, no module index is generated.
+# latex_use_modindex = True
+
+autoclass_content = 'both'
+
+# Note: Our strategy for intersphinx mappings is to have the upstream build location as the
+# canonical source and then cached copies of the mapping stored locally in case someone is building
+# when disconnected from the internet. We then have a script to update the cached copies.
+#
+# Because of that, each entry in this mapping should have this format:
+# name: ('http://UPSTREAM_URL', (None, 'path/to/local/cache.inv'))
+#
+# The update script depends on this format so deviating from this (for instance, adding a third
+# location for the mappning to live) will confuse it.
+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')),
+ 'ansible_2_10': ('https://docs.ansible.com/ansible/2.10/', (None, '../ansible_2_10.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')),
+ 'ansible_2_6': ('https://docs.ansible.com/ansible/2.6/', (None, '../ansible_2_6.inv')),
+ 'ansible_2_5': ('https://docs.ansible.com/ansible/2.5/', (None, '../ansible_2_5.inv')),
+ }
+
+# linckchecker settings
+linkcheck_ignore = [
+ r'http://irc\.freenode\.net',
+]
+linkcheck_workers = 25
+# linkcheck_anchors = False
diff --git a/docs/man/man1/ansible-config.1 b/docs/man/man1/ansible-config.1
index 01a7e73f..ab3ed68e 100644
--- a/docs/man/man1/ansible-config.1
+++ b/docs/man/man1/ansible-config.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-CONFIG 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-CONFIG 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-config \- View ansible configuration.
.
diff --git a/docs/man/man1/ansible-console.1 b/docs/man/man1/ansible-console.1
index f4ddb069..490f556e 100644
--- a/docs/man/man1/ansible-console.1
+++ b/docs/man/man1/ansible-console.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-CONSOLE 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-CONSOLE 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-console \- REPL console for executing Ansible tasks.
.
diff --git a/docs/man/man1/ansible-doc.1 b/docs/man/man1/ansible-doc.1
index 240beefd..5de4c468 100644
--- a/docs/man/man1/ansible-doc.1
+++ b/docs/man/man1/ansible-doc.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-DOC 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-DOC 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-doc \- plugin documentation tool
.
diff --git a/docs/man/man1/ansible-galaxy.1 b/docs/man/man1/ansible-galaxy.1
index f678a4aa..df7586f0 100644
--- a/docs/man/man1/ansible-galaxy.1
+++ b/docs/man/man1/ansible-galaxy.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-GALAXY 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-GALAXY 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-galaxy \- Perform various Role and Collection related operations.
.
diff --git a/docs/man/man1/ansible-inventory.1 b/docs/man/man1/ansible-inventory.1
index e9b4206b..c6a9f77f 100644
--- a/docs/man/man1/ansible-inventory.1
+++ b/docs/man/man1/ansible-inventory.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-INVENTORY 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-INVENTORY 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-inventory \- None
.
diff --git a/docs/man/man1/ansible-playbook.1 b/docs/man/man1/ansible-playbook.1
index d694c8f4..0c6651c5 100644
--- a/docs/man/man1/ansible-playbook.1
+++ b/docs/man/man1/ansible-playbook.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-PLAYBOOK 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-PLAYBOOK 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-playbook \- Runs Ansible playbooks, executing the defined tasks on the targeted hosts.
.
diff --git a/docs/man/man1/ansible-pull.1 b/docs/man/man1/ansible-pull.1
index 18ad3be6..4c03f88e 100644
--- a/docs/man/man1/ansible-pull.1
+++ b/docs/man/man1/ansible-pull.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-PULL 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-PULL 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-pull \- pulls playbooks from a VCS repo and executes them for the local host
.
diff --git a/docs/man/man1/ansible-vault.1 b/docs/man/man1/ansible-vault.1
index 042fbc9a..2ecb1078 100644
--- a/docs/man/man1/ansible-vault.1
+++ b/docs/man/man1/ansible-vault.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE-VAULT 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE-VAULT 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible-vault \- encryption/decryption utility for Ansible data files
.
diff --git a/docs/man/man1/ansible.1 b/docs/man/man1/ansible.1
index 61c87271..61dfcc25 100644
--- a/docs/man/man1/ansible.1
+++ b/docs/man/man1/ansible.1
@@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
-.TH ANSIBLE 1 "" "Ansible 2.10.5" "System administration commands"
+.TH ANSIBLE 1 "" "Ansible 2.10.7" "System administration commands"
.SH NAME
ansible \- Define and run a single task 'playbook' against a set of hosts
.
diff --git a/hacking/build_library/build_ansible/command_plugins/docs_build.py b/hacking/build_library/build_ansible/command_plugins/docs_build.py
index 21b09b03..e38ef201 100644
--- a/hacking/build_library/build_ansible/command_plugins/docs_build.py
+++ b/hacking/build_library/build_ansible/command_plugins/docs_build.py
@@ -19,6 +19,7 @@ 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
+from ..errors import InvalidUserInput, MissingUserInput
# pylint: enable=relative-beyond-top-level
@@ -29,6 +30,67 @@ DEFAULT_TOP_DIR = pathlib.Path(__file__).parents[4]
DEFAULT_OUTPUT_DIR = pathlib.Path(__file__).parents[4] / 'docs/docsite'
+class NoSuchFile(Exception):
+ """An expected file was not found."""
+
+
+#
+# Helpers
+#
+
+def find_latest_ansible_dir(build_data_working):
+ """Find the most recent ansible major version."""
+ # imports here so that they don't cause unnecessary deps for all of the plugins
+ from packaging.version import InvalidVersion, Version
+
+ ansible_directories = glob.glob(os.path.join(build_data_working, '[0-9.]*'))
+
+ # Find the latest ansible version directory
+ latest = None
+ latest_ver = Version('0')
+ for directory_name in (d for d in ansible_directories if os.path.isdir(d)):
+ try:
+ new_version = Version(os.path.basename(directory_name))
+ except InvalidVersion:
+ continue
+
+ if new_version > latest_ver:
+ latest_ver = new_version
+ latest = directory_name
+
+ if latest is None:
+ raise NoSuchFile('Could not find an ansible data directory in {0}'.format(build_data_working))
+
+ return latest
+
+
+def find_latest_deps_file(build_data_working, ansible_version):
+ """Find the most recent ansible deps file for the given ansible major version."""
+ # imports here so that they don't cause unnecessary deps for all of the plugins
+ from packaging.version import Version
+
+ data_dir = os.path.join(build_data_working, ansible_version)
+ deps_files = glob.glob(os.path.join(data_dir, '*.deps'))
+ if not deps_files:
+ raise Exception('No deps files exist for version {0}'.format(ansible_version))
+
+ # Find the latest version of the deps file for this major 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_version'])
+ if new_version > latest_ver:
+ latest_ver = new_version
+ latest = filename
+
+ if latest is None:
+ raise NoSuchFile('Could not find an ansible deps file in {0}'.format(data_dir))
+
+ return latest
+
+
#
# Subcommand base
#
@@ -70,36 +132,23 @@ def generate_full_docs(args):
# 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)
- # This is wrong. Once ansible and ansible-base major.minor versions get out of sync this
- # will stop working. We probably need to walk all subdirectories in reverse version order
- # looking for the latest ansible version which uses something compatible with
- # ansible_base_major_ver.
- 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_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
+ # If we want to validate that the ansible version and ansible-base branch version match,
+ # this would be the place to do it.
+
+ build_data_working = os.path.join(tmp_dir, 'ansible-build-data')
+
+ ansible_version = args.ansible_version
+ if ansible_version is None:
+ ansible_version = find_latest_ansible_dir(build_data_working)
+
+ latest_filename = find_latest_deps_file(build_data_working, ansible_version)
+
+ # Make a copy of the deps file so that we can set the ansible-base version we'll use
modified_deps_file = os.path.join(tmp_dir, 'ansible.deps')
- shutil.copyfile(latest, modified_deps_file)
+ shutil.copyfile(latest_filename, modified_deps_file)
# Put our version of ansible-base into the deps file
with open(modified_deps_file, 'r') as f:
@@ -136,6 +185,8 @@ class CollectionPluginDocs(Command):
' documentation location that says the module is in a collection and'
' point to generated plugin documentation under the collections/'
' hierarchy.')
+ # I think we should make the actions a subparser but need to look in git history and see if
+ # we tried that and changed it for some reason.
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",
@@ -149,10 +200,17 @@ class CollectionPluginDocs(Command):
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('--ansible-version', action='store',
+ dest='ansible_version', default=None,
+ help='The version of the ansible package to make documentation for.'
+ ' This only makes sense when used with full.')
@staticmethod
def main(args):
- # normalize CLI args
+ # normalize and validate CLI args
+
+ if args.ansible_version and args.action != 'full':
+ raise InvalidUserInput('--ansible-version is only for use with "full".')
if not args.output_dir:
args.output_dir = os.path.abspath(str(DEFAULT_OUTPUT_DIR))
diff --git a/lib/ansible/cli/galaxy.py b/lib/ansible/cli/galaxy.py
index 71cdee49..35d8fa80 100644
--- a/lib/ansible/cli/galaxy.py
+++ b/lib/ansible/cli/galaxy.py
@@ -877,7 +877,9 @@ class GalaxyCLI(CLI):
else:
in_templates_dir = rel_root_dir == 'templates'
- dirs = [d for d in dirs if not any(r.match(d) for r in skeleton_ignore_re)]
+ # Filter out ignored directory names
+ # Use [:] to mutate the list os.walk uses
+ dirs[:] = [d for d in dirs if not any(r.match(d) for r in skeleton_ignore_re)]
for f in files:
filename, ext = os.path.splitext(f)
diff --git a/lib/ansible/cli/inventory.py b/lib/ansible/cli/inventory.py
index 9f423747..58df4a5a 100644
--- a/lib/ansible/cli/inventory.py
+++ b/lib/ansible/cli/inventory.py
@@ -182,7 +182,11 @@ class InventoryCLI(CLI):
else:
import json
from ansible.parsing.ajson import AnsibleJSONEncoder
- results = json.dumps(stuff, cls=AnsibleJSONEncoder, sort_keys=True, indent=4, preprocess_unsafe=True)
+ try:
+ results = json.dumps(stuff, cls=AnsibleJSONEncoder, sort_keys=True, indent=4, preprocess_unsafe=True)
+ except TypeError as e:
+ results = json.dumps(stuff, cls=AnsibleJSONEncoder, sort_keys=False, indent=4, preprocess_unsafe=True)
+ display.warning("Could not sort JSON output due to issues while sorting keys: %s" % to_native(e))
return results
diff --git a/lib/ansible/config/ansible_builtin_runtime.yml b/lib/ansible/config/ansible_builtin_runtime.yml
index 3ad73d98..d02a3047 100644
--- a/lib/ansible/config/ansible_builtin_runtime.yml
+++ b/lib/ansible/config/ansible_builtin_runtime.yml
@@ -3101,7 +3101,7 @@ plugin_routing:
filesystem:
redirect: community.general.filesystem
firewalld:
- redirect: community.general.firewalld
+ redirect: ansible.posix.firewalld
gconftool2:
redirect: community.general.gconftool2
interfaces_file:
diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py
index 858dc4c6..99fc49fd 100644
--- a/lib/ansible/config/manager.py
+++ b/lib/ansible/config/manager.py
@@ -325,7 +325,10 @@ class ConfigManager(object):
ftype = get_config_type(cfile)
if cfile is not None:
if ftype == 'ini':
- self._parsers[cfile] = configparser.ConfigParser()
+ kwargs = {}
+ if PY3:
+ kwargs['inline_comment_prefixes'] = (';',)
+ self._parsers[cfile] = configparser.ConfigParser(**kwargs)
with open(to_bytes(cfile), 'rb') as f:
try:
cfg_text = to_text(f.read(), errors='surrogate_or_strict')
diff --git a/lib/ansible/errors/__init__.py b/lib/ansible/errors/__init__.py
index 563c5d25..b942197e 100644
--- a/lib/ansible/errors/__init__.py
+++ b/lib/ansible/errors/__init__.py
@@ -89,7 +89,21 @@ class AnsibleError(Exception):
with open(file_name, 'r') as f:
lines = f.readlines()
+ # In case of a YAML loading error, PyYAML will report the very last line
+ # as the location of the error. Avoid an index error here in order to
+ # return a helpful message.
+ file_length = len(lines)
+ if line_number >= file_length:
+ line_number = file_length - 1
+
+ # If target_line contains only whitespace, move backwards until
+ # actual code is found. If there are several empty lines after target_line,
+ # the error lines would just be blank, which is not very helpful.
target_line = lines[line_number]
+ while not target_line.strip():
+ line_number -= 1
+ target_line = lines[line_number]
+
if line_number > 0:
prev_line = lines[line_number - 1]
diff --git a/lib/ansible/inventory/manager.py b/lib/ansible/inventory/manager.py
index 5606b265..27b30da4 100644
--- a/lib/ansible/inventory/manager.py
+++ b/lib/ansible/inventory/manager.py
@@ -243,6 +243,7 @@ class InventoryManager(object):
''' Generate or update inventory for the source provided '''
parsed = False
+ failures = []
display.debug(u'Examining possible inventory source: %s' % source)
# use binary for path functions
@@ -271,7 +272,6 @@ class InventoryManager(object):
self._inventory.current_source = source
# try source with each plugin
- failures = []
for plugin in self._fetch_inventory_plugins():
plugin_name = to_text(getattr(plugin, '_load_name', getattr(plugin, '_original_path', '')))
@@ -305,19 +305,24 @@ class InventoryManager(object):
failures.append({'src': source, 'plugin': plugin_name, 'exc': AnsibleError(e), 'tb': tb})
else:
display.vvv("%s declined parsing %s as it did not pass its verify_file() method" % (plugin_name, source))
- else:
- if not parsed and failures:
+
+ if not parsed:
+ # only warn/error if NOT using the default or using it and the file is present
+ # TODO: handle 'non file' inventorya and detect vs hardcode default
+ if source != '/etc/ansible/hosts' or os.path.exists(source):
+
+ if failures:
# only if no plugin processed files should we show errors.
for fail in failures:
display.warning(u'\n* Failed to parse %s with %s plugin: %s' % (to_text(fail['src']), fail['plugin'], to_text(fail['exc'])))
if 'tb' in fail:
display.vvv(to_text(fail['tb']))
- if C.INVENTORY_ANY_UNPARSED_IS_FAILED:
- raise AnsibleError(u'Completely failed to parse inventory source %s' % (source))
- if not parsed:
- if source != '/etc/ansible/hosts' or os.path.exists(source):
- # only warn if NOT using the default and if using it, only if the file is present
- display.warning("Unable to parse %s as an inventory source" % source)
+
+ # final erorr/warning on inventory source failure
+ if C.INVENTORY_ANY_UNPARSED_IS_FAILED:
+ raise AnsibleError(u'Completely failed to parse inventory source %s' % (source))
+ else:
+ display.warning("Unable to parse %s as an inventory source" % source)
# clear up, jic
self._inventory.current_source = None
@@ -627,6 +632,8 @@ class InventoryManager(object):
b_limit_file = to_bytes(x[1:])
if not os.path.exists(b_limit_file):
raise AnsibleError(u'Unable to find limit file %s' % b_limit_file)
+ if not os.path.isfile(b_limit_file):
+ raise AnsibleError(u'Limit starting with "@" must be a file, not a directory: %s' % b_limit_file)
with open(b_limit_file) as fd:
results.extend([to_text(l.strip()) for l in fd.read().split("\n")])
else:
diff --git a/lib/ansible/module_utils/basic.py b/lib/ansible/module_utils/basic.py
index cd190f50..333c5878 100644
--- a/lib/ansible/module_utils/basic.py
+++ b/lib/ansible/module_utils/basic.py
@@ -712,6 +712,9 @@ class AnsibleModule(object):
if k not in self.argument_spec:
self.argument_spec[k] = v
+ # Save parameter values that should never be logged
+ self.no_log_values = set()
+
self._load_params()
self._set_fallbacks()
@@ -723,8 +726,6 @@ class AnsibleModule(object):
print('\n{"failed": true, "msg": "Module alias error: %s"}' % to_native(e))
sys.exit(1)
- # Save parameter values that should never be logged
- self.no_log_values = set()
self._handle_no_log_values()
# check the locale as set by the current environment, and reset to
@@ -954,17 +955,17 @@ class AnsibleModule(object):
return (uid, gid)
def find_mount_point(self, path):
- path_is_bytes = False
- if isinstance(path, binary_type):
- path_is_bytes = True
+ '''
+ Takes a path and returns it's mount point
+
+ :param path: a string type with a filesystem path
+ :returns: the path to the mount point as a text type
+ '''
b_path = os.path.realpath(to_bytes(os.path.expanduser(os.path.expandvars(path)), errors='surrogate_or_strict'))
while not os.path.ismount(b_path):
b_path = os.path.dirname(b_path)
- if path_is_bytes:
- return b_path
-
return to_text(b_path, errors='surrogate_or_strict')
def is_special_selinux_path(self, path):
@@ -1944,14 +1945,15 @@ class AnsibleModule(object):
param = self.params
for (k, v) in spec.items():
default = v.get('default', None)
- if pre is True:
- # this prevents setting defaults on required items
- if default is not None and k not in param:
- param[k] = default
- else:
- # make sure things without a default still get set None
- if k not in param:
- param[k] = default
+
+ # This prevents setting defaults on required items on the 1st run,
+ # otherwise will set things without a default to None on the 2nd.
+ if k not in param and (default is not None or not pre):
+ # Make sure any default value for no_log fields are masked.
+ if v.get('no_log', False) and default:
+ self.no_log_values.add(default)
+
+ param[k] = default
def _set_fallbacks(self, spec=None, param=None):
if spec is None:
@@ -1971,9 +1973,13 @@ class AnsibleModule(object):
else:
fallback_args = item
try:
- param[k] = fallback_strategy(*fallback_args, **fallback_kwargs)
+ fallback_value = fallback_strategy(*fallback_args, **fallback_kwargs)
except AnsibleFallbackNotFound:
continue
+ else:
+ if v.get('no_log', False) and fallback_value:
+ self.no_log_values.add(fallback_value)
+ param[k] = fallback_value
def _load_params(self):
''' read the input and set the params attribute.
@@ -2375,8 +2381,7 @@ class AnsibleModule(object):
if e.errno not in [errno.EPERM, errno.EXDEV, errno.EACCES, errno.ETXTBSY, errno.EBUSY]:
# only try workarounds for errno 18 (cross device), 1 (not permitted), 13 (permission denied)
# and 26 (text file busy) which happens on vagrant synced folders and other 'exotic' non posix file systems
- self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)),
- exception=traceback.format_exc())
+ self.fail_json(msg='Could not replace file: %s to %s: %s' % (src, dest, to_native(e)), exception=traceback.format_exc())
else:
# Use bytes here. In the shippable CI, this fails with
# a UnicodeError with surrogateescape'd strings for an unknown
@@ -2386,14 +2391,13 @@ class AnsibleModule(object):
error_msg = None
tmp_dest_name = None
try:
- tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp',
- dir=b_dest_dir, suffix=b_suffix)
+ tmp_dest_fd, tmp_dest_name = tempfile.mkstemp(prefix=b'.ansible_tmp', dir=b_dest_dir, suffix=b_suffix)
except (OSError, IOError) as e:
error_msg = 'The destination directory (%s) is not writable by the current user. Error was: %s' % (os.path.dirname(dest), to_native(e))
except TypeError:
# We expect that this is happening because python3.4.x and
- # below can't handle byte strings in mkstemp(). Traceback
- # would end in something like:
+ # below can't handle byte strings in mkstemp().
+ # Traceback would end in something like:
# file = _os.path.join(dir, pre + name + suf)
# TypeError: can't concat bytes to str
error_msg = ('Failed creating tmp file for atomic move. This usually happens when using Python3 less than Python3.5. '
@@ -2437,11 +2441,12 @@ class AnsibleModule(object):
self._unsafe_writes(b_tmp_dest_name, b_dest)
else:
self.fail_json(msg='Unable to make %s into to %s, failed final rename from %s: %s' %
- (src, dest, b_tmp_dest_name, to_native(e)),
- exception=traceback.format_exc())
+ (src, dest, b_tmp_dest_name, to_native(e)), exception=traceback.format_exc())
except (shutil.Error, OSError, IOError) as e:
- self.fail_json(msg='Failed to replace file: %s to %s: %s' % (src, dest, to_native(e)),
- exception=traceback.format_exc())
+ if unsafe_writes:
+ self._unsafe_writes(b_src, b_dest)
+ else:
+ self.fail_json(msg='Failed to replace file: %s to %s: %s' % (src, dest, to_native(e)), exception=traceback.format_exc())
finally:
self.cleanup(b_tmp_dest_name)
diff --git a/lib/ansible/module_utils/facts/system/distribution.py b/lib/ansible/module_utils/facts/system/distribution.py
index 8c2c7b42..09236a9a 100644
--- a/lib/ansible/module_utils/facts/system/distribution.py
+++ b/lib/ansible/module_utils/facts/system/distribution.py
@@ -485,7 +485,7 @@ class Distribution(object):
OS_FAMILY_MAP = {'RedHat': ['RedHat', 'Fedora', 'CentOS', 'Scientific', 'SLC',
'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS',
'OEL', 'Amazon', 'Virtuozzo', 'XenServer', 'Alibaba',
- 'EulerOS', 'openEuler'],
+ 'EulerOS', 'openEuler', 'AlmaLinux'],
'Debian': ['Debian', 'Ubuntu', 'Raspbian', 'Neon', 'KDE neon',
'Linux Mint', 'SteamOS', 'Devuan', 'Kali', 'Cumulus Linux',
'Pop!_OS', ],
diff --git a/lib/ansible/module_utils/facts/virtual/linux.py b/lib/ansible/module_utils/facts/virtual/linux.py
index e133df42..5722e462 100644
--- a/lib/ansible/module_utils/facts/virtual/linux.py
+++ b/lib/ansible/module_utils/facts/virtual/linux.py
@@ -238,6 +238,11 @@ class LinuxVirtual(Virtual):
virtual_facts['virtualization_role'] = 'guest'
return virtual_facts
+ if 'BHYVE' in out:
+ virtual_facts['virtualization_type'] = 'bhyve'
+ virtual_facts['virtualization_role'] = 'guest'
+ return virtual_facts
+
# If none of the above matches, return 'NA' for virtualization_type
# and virtualization_role. This allows for proper grouping.
virtual_facts['virtualization_type'] = 'NA'
diff --git a/lib/ansible/modules/command.py b/lib/ansible/modules/command.py
index dcecbe64..16805432 100644
--- a/lib/ansible/modules/command.py
+++ b/lib/ansible/modules/command.py
@@ -45,7 +45,7 @@ options:
creates:
type: path
description:
- - A filename or (since 2.0) glob pattern. If a matching file already exists, this step B(won't) be run.
+ - A filename or (since 2.0) glob pattern. If a matching file already exists, this step B(will not) be run.
removes:
type: path
description:
@@ -79,11 +79,12 @@ options:
type: bool
default: yes
notes:
- - If you want to run a command through the shell (say you are using C(<), C(>), C(|), etc), you actually want the M(ansible.builtin.shell) module instead.
+ - If you want to run a command through the shell (say you are using C(<), C(>), C(|), and so on),
+ you actually want the M(ansible.builtin.shell) module instead.
Parsing shell metacharacters can lead to unexpected commands being executed if quoting is not done correctly so it is more secure to
use the C(command) module when possible.
- - " C(creates), C(removes), and C(chdir) can be specified after the command.
- For instance, if you only want to run a command if a certain file does not exist, use this."
+ - C(creates), C(removes), and C(chdir) can be specified after the command.
+ For instance, if you only want to run a command if a certain file does not exist, use this.
- Check mode is supported when passing C(creates) or C(removes). If running in check mode and either of these are specified, the module will
check for the existence of the file and report the correct changed status. If these are not supplied, the task will be skipped.
- The C(executable) parameter is removed since version 2.4. If you have a need for this parameter, use the M(ansible.builtin.shell) module instead.
@@ -101,28 +102,28 @@ author:
EXAMPLES = r'''
- name: Return motd to registered var
- command: cat /etc/motd
+ ansible.builtin.command: cat /etc/motd
register: mymotd
# free-form (string) arguments, all arguments on one line
- name: Run command if /path/to/database does not exist (without 'args')
- command: /usr/bin/make_database.sh db_user db_name creates=/path/to/database
+ ansible.builtin.command: /usr/bin/make_database.sh db_user db_name creates=/path/to/database
# free-form (string) arguments, some arguments on separate lines with the 'args' keyword
# 'args' is a task keyword, passed at the same level as the module
- name: Run command if /path/to/database does not exist (with 'args' keyword)
- command: /usr/bin/make_database.sh db_user db_name
+ ansible.builtin.command: /usr/bin/make_database.sh db_user db_name
args:
creates: /path/to/database
# 'cmd' is module parameter
- name: Run command if /path/to/database does not exist (with 'cmd' parameter)
- command:
+ ansible.builtin.command:
cmd: /usr/bin/make_database.sh db_user db_name
creates: /path/to/database
- name: Change the working directory to somedir/ and run the command as db_owner if /path/to/database does not exist
- command: /usr/bin/make_database.sh db_user db_name
+ ansible.builtin.command: /usr/bin/make_database.sh db_user db_name
become: yes
become_user: db_owner
args:
@@ -132,7 +133,7 @@ EXAMPLES = r'''
# argv (list) arguments, each argument on a separate line, 'args' keyword not necessary
# 'argv' is a parameter, indented one level from the module
- name: Use 'argv' to send a command as a list - leave 'command' empty
- command:
+ ansible.builtin.command:
argv:
- /usr/bin/make_database.sh
- Username with whitespace
@@ -140,7 +141,7 @@ EXAMPLES = r'''
creates: /path/to/database
- name: Safely use templated variable to run command. Always use the quote filter to avoid injection issues
- command: cat {{ myfile|quote }}
+ ansible.builtin.command: cat {{ myfile|quote }}
register: myoutput
'''
@@ -151,49 +152,49 @@ msg:
type: bool
sample: True
start:
- description: The command execution start time
+ description: The command execution start time.
returned: always
type: str
sample: '2017-09-29 22:03:48.083128'
end:
- description: The command execution end time
+ description: The command execution end time.
returned: always
type: str
sample: '2017-09-29 22:03:48.084657'
delta:
- description: The command execution delta time
+ description: The command execution delta time.
returned: always
type: str
sample: '0:00:00.001529'
stdout:
- description: The command standard output
+ description: The command standard output.
returned: always
type: str
sample: 'Clustering node rabbit@slave1 with rabbit@master …'
stderr:
- description: The command standard error
+ description: The command standard error.
returned: always
type: str
sample: 'ls cannot access foo: No such file or directory'
cmd:
- description: The command executed by the task
+ description: The command executed by the task.
returned: always
type: list
sample:
- echo
- hello
rc:
- description: The command return code (0 means success)
+ description: The command return code (0 means success).
returned: always
type: int
sample: 0
stdout_lines:
- description: The command standard output split in lines
+ description: The command standard output split in lines.
returned: always
type: list
sample: [u'Clustering node rabbit@slave1 with rabbit@master …']
stderr_lines:
- description: The command standard error split in lines
+ description: The command standard error split in lines.
returned: always
type: list
sample: [u'ls cannot access foo: No such file or directory', u'ls …']
diff --git a/lib/ansible/modules/debug.py b/lib/ansible/modules/debug.py
index 86c4b951..c98bcc57 100644
--- a/lib/ansible/modules/debug.py
+++ b/lib/ansible/modules/debug.py
@@ -34,7 +34,7 @@ options:
type: str
verbosity:
description:
- - A number that controls when the debug is run, if you set to 3 it will only run debug when -vvv or above
+ - A number that controls when the debug is run, if you set to 3 it will only run debug when -vvv or above.
type: int
default: 0
version_added: '2.1'
diff --git a/lib/ansible/modules/expect.py b/lib/ansible/modules/expect.py
index 0f262156..3bb9d960 100644
--- a/lib/ansible/modules/expect.py
+++ b/lib/ansible/modules/expect.py
@@ -12,7 +12,7 @@ DOCUMENTATION = r'''
---
module: expect
version_added: '2.0'
-short_description: Executes a command and responds to prompts.
+short_description: Executes a command and responds to prompts
description:
- The C(expect) module executes a command and responds to prompts.
- The given command will be executed on all selected nodes. It will not be
@@ -58,7 +58,7 @@ requirements:
- pexpect >= 3.3
notes:
- If you want to run a command through the shell (say you are using C(<),
- C(>), C(|), etc), you must specify a shell in the command such as
+ C(>), C(|), and so on), you must specify a shell in the command such as
C(/bin/bash -c "/path/to/something | grep else").
- The question, or key, under I(responses) is a python regex match. Case
insensitive searches are indicated with a prefix of C(?i).
@@ -68,7 +68,7 @@ notes:
the response. The list functionality is new in 2.1.
- The M(ansible.builtin.expect) module is designed for simple scenarios.
For more complex needs, consider the use of expect code with the M(ansible.builtin.shell)
- or M(ansible.builtin.script) modules. (An example is part of the M(ansible.builtin.shell) module documentation)
+ or M(ansible.builtin.script) modules. (An example is part of the M(ansible.builtin.shell) module documentation).
seealso:
- module: ansible.builtin.script
- module: ansible.builtin.shell
@@ -77,7 +77,7 @@ author: "Matt Martz (@sivel)"
EXAMPLES = r'''
- name: Case insensitive password string match
- expect:
+ ansible.builtin.expect:
command: passwd username
responses:
(?i)password: "MySekretPa$$word"
@@ -85,7 +85,7 @@ EXAMPLES = r'''
no_log: true
- name: Generic question with multiple different responses
- expect:
+ ansible.builtin.expect:
command: /path/to/custom/command
responses:
Question:
diff --git a/lib/ansible/modules/find.py b/lib/ansible/modules/find.py
index b6aad844..3b52ac0a 100644
--- a/lib/ansible/modules/find.py
+++ b/lib/ansible/modules/find.py
@@ -413,6 +413,8 @@ def main():
wpath = npath.rstrip(os.path.sep) + os.path.sep
depth = int(fsname.count(os.path.sep)) - int(wpath.count(os.path.sep)) + 1
if depth > params['depth']:
+ # Empty the list used by os.walk to avoid traversing deeper unnecessarily
+ del(dirs[:])
continue
if os.path.basename(fsname).startswith('.') and not params['hidden']:
continue
diff --git a/lib/ansible/modules/get_url.py b/lib/ansible/modules/get_url.py
index c89a4401..9036b354 100644
--- a/lib/ansible/modules/get_url.py
+++ b/lib/ansible/modules/get_url.py
@@ -610,7 +610,7 @@ def main():
if backup:
if os.path.exists(dest):
backup_file = module.backup_local(dest)
- module.atomic_move(tmpsrc, dest)
+ module.atomic_move(tmpsrc, dest, unsafe_writes=module.params['unsafe_writes'])
except Exception as e:
if os.path.exists(tmpsrc):
os.remove(tmpsrc)
diff --git a/lib/ansible/modules/git.py b/lib/ansible/modules/git.py
index b47e9451..2c2b36db 100644
--- a/lib/ansible/modules/git.py
+++ b/lib/ansible/modules/git.py
@@ -33,12 +33,12 @@ options:
description:
- What version of the repository to check out. This can be
the literal string C(HEAD), a branch name, a tag name.
- It can also be a I(SHA-1) hash, in which case C(refspec) needs
+ It can also be a I(SHA-1) hash, in which case I(refspec) needs
to be specified if the given revision is not already available.
default: "HEAD"
accept_hostkey:
description:
- - if C(yes), ensure that "-o StrictHostKeyChecking=no" is
+ - If C(yes), ensure that "-o StrictHostKeyChecking=no" is
present as an ssh option.
type: bool
default: 'no'
@@ -57,7 +57,7 @@ options:
version_added: "1.5"
reference:
description:
- - Reference repository (see "git clone --reference ...")
+ - Reference repository (see "git clone --reference ...").
version_added: "1.4"
remote:
description:
@@ -77,7 +77,7 @@ options:
- If C(yes), any modified files in the working
repository will be discarded. Prior to 0.7, this was always
'yes' and could not be disabled. Prior to 1.9, the default was
- `yes`
+ `yes`.
type: bool
default: 'no'
version_added: "0.7"
@@ -89,13 +89,13 @@ options:
version_added: "1.2"
clone:
description:
- - If C(no), do not clone the repository even if it does not exist locally
+ - If C(no), do not clone the repository even if it does not exist locally.
type: bool
default: 'yes'
version_added: "1.9"
update:
description:
- - If C(no), do not retrieve new revisions from the origin repository
+ - If C(no), do not retrieve new revisions from the origin repository.
- Operations like archive will work on the existing (old) repository and might
not respond to changes to the options version or remote.
type: bool
@@ -108,7 +108,7 @@ options:
version_added: "1.4"
bare:
description:
- - if C(yes), repository will be created as a bare repo, otherwise
+ - If C(yes), repository will be created as a bare repo, otherwise
it will be a standard repo with a workspace.
type: bool
default: 'no'
@@ -121,7 +121,7 @@ options:
recursive:
description:
- - if C(no), repository will be cloned without the --recursive
+ - If C(no), repository will be cloned without the --recursive
option, skipping sub-modules.
type: bool
default: 'yes'
@@ -129,7 +129,7 @@ options:
track_submodules:
description:
- - if C(yes), submodules will track the latest commit on their
+ - If C(yes), submodules will track the latest commit on their
master branch (or other branch specified in .gitmodules). If
C(no), submodules will be kept at the revision specified by the
main project. This is equivalent to specifying the --remote flag
@@ -140,8 +140,8 @@ options:
verify_commit:
description:
- - if C(yes), when cloning or checking out a C(version) verify the
- signature of a GPG signed commit. This requires C(git) version>=2.1.0
+ - If C(yes), when cloning or checking out a I(version) verify the
+ signature of a GPG signed commit. This requires git version>=2.1.0
to be installed. The commit MUST be signed and the public key MUST
be present in the GPG keyring.
type: bool
@@ -153,14 +153,14 @@ options:
- Specify archive file path with extension. If specified, creates an
archive file of the specified format containing the tree structure
for the source tree.
- Allowed archive formats ["zip", "tar.gz", "tar", "tgz"]
+ Allowed archive formats ["zip", "tar.gz", "tar", "tgz"].
- This will clone and perform git archive from local directory as not
all git servers support git archive.
version_added: "2.4"
archive_prefix:
description:
- - Specify a prefix to add to each file path in archive. Requires C(archive) to be specified.
+ - Specify a prefix to add to each file path in archive. Requires I(archive) to be specified.
version_added: "2.10"
type: str
@@ -175,6 +175,7 @@ options:
- A list of trusted GPG fingerprints to compare to the fingerprint of the
GPG-signed commit.
- Only used when I(verify_commit=yes).
+ - Use of this feature requires Git 2.6+ due to its reliance on git's C(--raw) flag to C(verify-commit) and C(verify-tag).
type: list
default: []
version_added: "2.9"
@@ -188,47 +189,48 @@ notes:
one solution is to use the option accept_hostkey. Another solution is to
add the remote host public key in C(/etc/ssh/ssh_known_hosts) before calling
the git module, with the following command: ssh-keyscan -H remote_host.com >> /etc/ssh/ssh_known_hosts."
+ - Supports C(check_mode).
'''
EXAMPLES = '''
- name: Git checkout
- git:
+ ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
version: release-0.22
- name: Read-write git checkout from github
- git:
+ ansible.builtin.git:
repo: git@github.com:mylogin/hello.git
dest: /home/mylogin/hello
- name: Just ensuring the repo checkout exists
- git:
+ ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
update: no
- name: Just get information about the repository whether or not it has already been cloned locally
- git:
+ ansible.builtin.git:
repo: 'https://foosball.example.org/path/to/repo.git'
dest: /srv/checkout
clone: no
update: no
- name: Checkout a github repo and use refspec to fetch all pull requests
- git:
+ ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
refspec: '+refs/pull/*:refs/heads/*'
- name: Create git archive from repo
- git:
+ ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
archive: /tmp/ansible-examples.zip
- name: Clone a repo with separate git directory
- git:
+ ansible.builtin.git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
separate_git_dir: /src/ansible-examples.git
@@ -236,12 +238,12 @@ EXAMPLES = '''
RETURN = '''
after:
- description: last commit revision of the repository retrieved during the update
+ description: Last commit revision of the repository retrieved during the update.
returned: success
type: str
sample: 4c020102a9cd6fe908c9a4a326a38f972f63a903
before:
- description: commit revision before the repository was updated, "null" for new repository
+ description: Commit revision before the repository was updated, "null" for new repository.
returned: success
type: str
sample: 67c04ebe40a003bda0efb34eacfb93b0cafdf628
@@ -256,12 +258,12 @@ warnings:
type: str
sample: Your git version is too old to fully support the depth argument. Falling back to full checkouts.
git_dir_now:
- description: Contains the new path of .git directory if it's changed
+ description: Contains the new path of .git directory if it is changed.
returned: success
type: str
sample: /path/to/new/git/dir
git_dir_before:
- description: Contains the original path of .git directory if it's changed
+ description: Contains the original path of .git directory if it is changed.
returned: success
type: str
sample: /path/to/old/git/dir
@@ -934,7 +936,9 @@ def verify_commit_sign(git_path, module, dest, version, gpg_whitelist):
git_sub = "verify-tag"
else:
git_sub = "verify-commit"
- cmd = "%s %s %s --raw" % (git_path, git_sub, version)
+ cmd = "%s %s %s" % (git_path, git_sub, version)
+ if gpg_whitelist:
+ cmd += " --raw"
(rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0:
module.fail_json(msg='Failed to verify GPG signature of commit/tag "%s"' % version, stdout=out, stderr=err, rc=rc)
diff --git a/lib/ansible/modules/group.py b/lib/ansible/modules/group.py
index 1deb967a..297e2579 100644
--- a/lib/ansible/modules/group.py
+++ b/lib/ansible/modules/group.py
@@ -45,7 +45,7 @@ options:
description:
- Forces the use of "local" command alternatives on platforms that implement it.
- This is useful in environments that use centralized authentication when you want to manipulate the local groups.
- (e.g. it uses C(lgroupadd) instead of C(groupadd)).
+ (for example, it uses C(lgroupadd) instead of C(groupadd)).
- This requires that these commands exist on the targeted host, otherwise it will be a fatal error.
type: bool
default: no
diff --git a/lib/ansible/modules/group_by.py b/lib/ansible/modules/group_by.py
index 4a709d28..34290d02 100644
--- a/lib/ansible/modules/group_by.py
+++ b/lib/ansible/modules/group_by.py
@@ -39,20 +39,20 @@ author:
EXAMPLES = r'''
- name: Create groups based on the machine architecture
- group_by:
+ ansible.builtin.group_by:
key: machine_{{ ansible_machine }}
- name: Create groups like 'virt_kvm_host'
- group_by:
+ ansible.builtin.group_by:
key: virt_{{ ansible_virtualization_type }}_{{ ansible_virtualization_role }}
- name: Create nested groups
- group_by:
+ ansible.builtin.group_by:
key: el{{ ansible_distribution_major_version }}-{{ ansible_architecture }}
parents:
- el{{ ansible_distribution_major_version }}
-# Add all active hosts to a static group
-- group_by:
+- name: Add all active hosts to a static group
+ ansible.builtin.group_by:
key: done
'''
diff --git a/lib/ansible/modules/hostname.py b/lib/ansible/modules/hostname.py
index c0ffe37a..f27e27ea 100644
--- a/lib/ansible/modules/hostname.py
+++ b/lib/ansible/modules/hostname.py
@@ -36,7 +36,7 @@ options:
EXAMPLES = '''
- name: Set a hostname
- hostname:
+ ansible.builtin.hostname:
name: web01
'''
@@ -632,6 +632,12 @@ class ArchARMHostname(Hostname):
strategy_class = SystemdStrategy
+class AlmaLinuxHostname(Hostname):
+ platform = 'Linux'
+ distribution = 'Almalinux'
+ strategy_class = SystemdStrategy
+
+
class ManjaroHostname(Hostname):
platform = 'Linux'
distribution = 'Manjaro'
diff --git a/lib/ansible/modules/import_playbook.py b/lib/ansible/modules/import_playbook.py
index 51ef23fa..767a8a45 100644
--- a/lib/ansible/modules/import_playbook.py
+++ b/lib/ansible/modules/import_playbook.py
@@ -42,6 +42,10 @@ EXAMPLES = r'''
- name: Include a play after another play
import_playbook: otherplays.yaml
+- name: Set variables on an imported playbook
+ import_playbook: otherplays.yml
+ vars:
+ service: httpd
- name: This DOES NOT WORK
hosts: all
diff --git a/lib/ansible/modules/package.py b/lib/ansible/modules/package.py
index a99dab1d..f15ad5af 100644
--- a/lib/ansible/modules/package.py
+++ b/lib/ansible/modules/package.py
@@ -33,9 +33,8 @@ options:
required: true
use:
description:
- - The required package manager module to use (yum, apt, etc). The default 'auto' will use existing facts or try to autodetect it.
+ - The required package manager module to use (`yum`, `apt`, and so on). The default 'auto' will use existing facts or try to autodetect it.
- You should only use this field if the automatic selection is not working for some reason.
- required: false
default: auto
requirements:
- Whatever is required for the package plugins specific for each system.
@@ -45,18 +44,18 @@ notes:
'''
EXAMPLES = '''
- name: Install ntpdate
- package:
+ ansible.builtin.package:
name: ntpdate
state: present
# This uses a variable as this changes per distribution.
- name: Remove the apache package
- package:
+ ansible.builtin.package:
name: "{{ apache }}"
state: absent
- name: Install the latest version of Apache and MariaDB
- package:
+ ansible.builtin.package:
name:
- httpd
- mariadb-server
diff --git a/lib/ansible/modules/package_facts.py b/lib/ansible/modules/package_facts.py
index ed314b4a..97bfd6f0 100644
--- a/lib/ansible/modules/package_facts.py
+++ b/lib/ansible/modules/package_facts.py
@@ -10,9 +10,9 @@ __metaclass__ = type
DOCUMENTATION = '''
module: package_facts
-short_description: package information as facts
+short_description: Package information as facts
description:
- - Return information about installed packages as facts
+ - Return information about installed packages as facts.
options:
manager:
description:
@@ -39,19 +39,21 @@ author:
- Matthew Jones (@matburt)
- Brian Coca (@bcoca)
- Adam Miller (@maxamillion)
+notes:
+ - Supports C(check_mode).
'''
EXAMPLES = '''
- name: Gather the package facts
- package_facts:
+ ansible.builtin.package_facts:
manager: auto
- name: Print the package facts
- debug:
+ ansible.builtin.debug:
var: ansible_facts.packages
- name: Check whether a package called foobar is installed
- debug:
+ ansible.builtin.debug:
msg: "{{ ansible_facts.packages['foobar'] | length }} versions of foobar are installed!"
when: "'foobar' in ansible_facts.packages"
@@ -59,7 +61,7 @@ EXAMPLES = '''
RETURN = '''
ansible_facts:
- description: facts to add to ansible_facts
+ description: Facts to add to ansible_facts.
returned: always
type: complex
contains:
diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py
index 0e37cbf5..583dfe22 100644
--- a/lib/ansible/modules/user.py
+++ b/lib/ansible/modules/user.py
@@ -203,7 +203,7 @@ options:
description:
- Forces the use of "local" command alternatives on platforms that implement it.
- This is useful in environments that use centralized authentication when you want to manipulate the local users
- (i.e. it uses C(luseradd) instead of C(useradd)).
+ (in other words, it uses C(luseradd) instead of C(useradd)).
- This will check C(/etc/passwd) for an existing account before invoking commands. If the local account database
exists somewhere other than C(/etc/passwd), this setting will not work properly.
- This requires that the above commands as well as C(/etc/passwd) must exist on the target host, otherwise it will be a fatal error.
@@ -250,6 +250,7 @@ notes:
C(pw userdel) remove, C(pw lock) to lock, and C(pw unlock) to unlock accounts.
- On all other platforms, this module uses C(useradd) to create, C(usermod) to modify, and
C(userdel) to remove accounts.
+ - Supports C(check_mode).
seealso:
- module: ansible.posix.authorized_key
- module: ansible.builtin.group
@@ -260,64 +261,64 @@ author:
EXAMPLES = r'''
- name: Add the user 'johnd' with a specific uid and a primary group of 'admin'
- user:
+ ansible.builtin.user:
name: johnd
comment: John Doe
uid: 1040
group: admin
- name: Add the user 'james' with a bash shell, appending the group 'admins' and 'developers' to the user's groups
- user:
+ ansible.builtin.user:
name: james
shell: /bin/bash
groups: admins,developers
append: yes
- name: Remove the user 'johnd'
- user:
+ ansible.builtin.user:
name: johnd
state: absent
remove: yes
- name: Create a 2048-bit SSH key for user jsmith in ~jsmith/.ssh/id_rsa
- user:
+ ansible.builtin.user:
name: jsmith
generate_ssh_key: yes
ssh_key_bits: 2048
ssh_key_file: .ssh/id_rsa
- name: Added a consultant whose account you want to expire
- user:
+ ansible.builtin.user:
name: james18
shell: /bin/zsh
groups: developers
expires: 1422403387
- name: Starting at Ansible 2.6, modify user, remove expiry time
- user:
+ ansible.builtin.user:
name: james18
expires: -1
'''
RETURN = r'''
append:
- description: Whether or not to append the user to groups
- returned: When state is 'present' and the user exists
+ description: Whether or not to append the user to groups.
+ returned: When state is C(present) and the user exists
type: bool
sample: True
comment:
- description: Comment section from passwd file, usually the user name
+ description: Comment section from passwd file, usually the user name.
returned: When user exists
type: str
sample: Agent Smith
create_home:
- description: Whether or not to create the home directory
+ description: Whether or not to create the home directory.
returned: When user does not exist and not check mode
type: bool
sample: True
force:
- description: Whether or not a user account was forcibly deleted
- returned: When state is 'absent' and user exists
+ description: Whether or not a user account was forcibly deleted.
+ returned: When I(state) is C(absent) and user exists
type: bool
sample: False
group:
@@ -326,76 +327,76 @@ group:
type: int
sample: 1001
groups:
- description: List of groups of which the user is a member
- returned: When C(groups) is not empty and C(state) is 'present'
+ description: List of groups of which the user is a member.
+ returned: When I(groups) is not empty and I(state) is C(present)
type: str
sample: 'chrony,apache'
home:
- description: "Path to user's home directory"
- returned: When C(state) is 'present'
+ description: "Path to user's home directory."
+ returned: When I(state) is C(present)
type: str
sample: '/home/asmith'
move_home:
- description: Whether or not to move an existing home directory
- returned: When C(state) is 'present' and user exists
+ description: Whether or not to move an existing home directory.
+ returned: When I(state) is C(present) and user exists
type: bool
sample: False
name:
- description: User account name
+ description: User account name.
returned: always
type: str
sample: asmith
password:
- description: Masked value of the password
- returned: When C(state) is 'present' and C(password) is not empty
+ description: Masked value of the password.
+ returned: When I(state) is C(present) and I(password) is not empty
type: str
sample: 'NOT_LOGGING_PASSWORD'
remove:
- description: Whether or not to remove the user account
- returned: When C(state) is 'absent' and user exists
+ description: Whether or not to remove the user account.
+ returned: When I(state) is C(absent) and user exists
type: bool
sample: True
shell:
- description: User login shell
- returned: When C(state) is 'present'
+ description: User login shell.
+ returned: When I(state) is C(present)
type: str
sample: '/bin/bash'
ssh_fingerprint:
- description: Fingerprint of generated SSH key
- returned: When C(generate_ssh_key) is C(True)
+ description: Fingerprint of generated SSH key.
+ returned: When I(generate_ssh_key) is C(True)
type: str
sample: '2048 SHA256:aYNHYcyVm87Igh0IMEDMbvW0QDlRQfE0aJugp684ko8 ansible-generated on host (RSA)'
ssh_key_file:
- description: Path to generated SSH private key file
- returned: When C(generate_ssh_key) is C(True)
+ description: Path to generated SSH private key file.
+ returned: When I(generate_ssh_key) is C(True)
type: str
sample: /home/asmith/.ssh/id_rsa
ssh_public_key:
- description: Generated SSH public key file
- returned: When C(generate_ssh_key) is C(True)
+ description: Generated SSH public key file.
+ returned: When I(generate_ssh_key) is C(True)
type: str
sample: >
'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC95opt4SPEC06tOYsJQJIuN23BbLMGmYo8ysVZQc4h2DZE9ugbjWWGS1/pweUGjVstgzMkBEeBCByaEf/RJKNecKRPeGd2Bw9DCj/bn5Z6rGfNENKBmo
618mUJBvdlEgea96QGjOwSB7/gmonduC7gsWDMNcOdSE3wJMTim4lddiBx4RgC9yXsJ6Tkz9BHD73MXPpT5ETnse+A3fw3IGVSjaueVnlUyUmOBf7fzmZbhlFVXf2Zi2rFTXqvbdGHKkzpw1U8eB8xFPP7y
d5u1u0e6Acju/8aZ/l17IDFiLke5IzlqIMRTEbDwLNeO84YQKWTm9fODHzhYe0yvxqLiK07 ansible-generated on host'
stderr:
- description: Standard error from running commands
+ description: Standard error from running commands.
returned: When stderr is returned by a command that is run
type: str
sample: Group wheels does not exist
stdout:
- description: Standard output from running commands
+ description: Standard output from running commands.
returned: When standard output is returned by the command that is run
type: str
sample:
system:
- description: Whether or not the account is a system account
- returned: When C(system) is passed to the module and the account does not exist
+ description: Whether or not the account is a system account.
+ returned: When I(system) is passed to the module and the account does not exist
type: bool
sample: True
uid:
- description: User ID of the user account
- returned: When C(UID) is passed to the module
+ description: User ID of the user account.
+ returned: When I(uid) is passed to the module
type: int
sample: 1044
'''
diff --git a/lib/ansible/playbook/helpers.py b/lib/ansible/playbook/helpers.py
index 892ce158..7f049018 100644
--- a/lib/ansible/playbook/helpers.py
+++ b/lib/ansible/playbook/helpers.py
@@ -236,7 +236,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
try:
data = loader.load_from_file(include_file)
- if data is None:
+ if not data:
display.warning('file %s is empty and had no tasks to include' % include_file)
continue
elif not isinstance(data, list):
diff --git a/lib/ansible/playbook/playbook_include.py b/lib/ansible/playbook/playbook_include.py
index 4aeecb1e..01c98031 100644
--- a/lib/ansible/playbook/playbook_include.py
+++ b/lib/ansible/playbook/playbook_include.py
@@ -150,7 +150,8 @@ class PlaybookInclude(Base, Conditional, Taggable):
else:
new_ds['import_playbook'] = items[0].strip()
if len(items) > 1:
- display.warning('Additional parameters in import_playbook statements are not supported. This will be an error in version 2.14')
+ display.deprecated("Additional parameters in import_playbook statements are deprecated. "
+ "Use 'vars' instead. See 'import_playbook' documentation for examples.", version='2.14')
# rejoin the parameter portion of the arguments and
# then use parse_kv() to get a dict of params back
params = parse_kv(" ".join(items[1:]))
diff --git a/lib/ansible/playbook/role/__init__.py b/lib/ansible/playbook/role/__init__.py
index b7456afc..30162c3a 100644
--- a/lib/ansible/playbook/role/__init__.py
+++ b/lib/ansible/playbook/role/__init__.py
@@ -22,6 +22,7 @@ __metaclass__ = type
import os
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleAssertionError
+from ansible.module_utils._text import to_text
from ansible.module_utils.six import iteritems, binary_type, text_type
from ansible.module_utils.common._collections_compat import Container, Mapping, Set, Sequence
from ansible.playbook.attribute import FieldAttribute
@@ -33,8 +34,10 @@ from ansible.playbook.role.metadata import RoleMetadata
from ansible.playbook.taggable import Taggable
from ansible.plugins.loader import add_all_plugin_dirs
from ansible.utils.collection_loader import AnsibleCollectionConfig
+from ansible.utils.display import Display
from ansible.utils.vars import combine_vars
+display = Display()
__all__ = ['Role', 'hash_params']
@@ -207,14 +210,14 @@ class Role(Base, Conditional, Taggable, CollectionSearch):
# vars and default vars are regular dictionaries
self._role_vars = self._load_role_yaml('vars', main=self._from_files.get('vars'), allow_dir=True)
if self._role_vars is None:
- self._role_vars = dict()
- elif not isinstance(self._role_vars, dict):
+ self._role_vars = {}
+ elif not isinstance(self._role_vars, Mapping):
raise AnsibleParserError("The vars/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
self._default_vars = self._load_role_yaml('defaults', main=self._from_files.get('defaults'), allow_dir=True)
if self._default_vars is None:
- self._default_vars = dict()
- elif not isinstance(self._default_vars, dict):
+ self._default_vars = {}
+ elif not isinstance(self._default_vars, Mapping):
raise AnsibleParserError("The defaults/main.yml file for role '%s' must contain a dictionary of variables" % self._role_name)
# load the role's other files, if they exist
@@ -269,32 +272,53 @@ class Role(Base, Conditional, Taggable, CollectionSearch):
obj=handler_data, orig_exc=e)
def _load_role_yaml(self, subdir, main=None, allow_dir=False):
+ '''
+ Find and load role YAML files and return data found.
+ :param subdir: subdir of role to search (vars, files, tasks, handlers, defaults)
+ :type subdir: string
+ :param main: filename to match, will default to 'main.<ext>' if not provided.
+ :type main: string
+ :param allow_dir: If true we combine results of multiple matching files found.
+ If false, highlander rules. Only for vars(dicts) and not tasks(lists).
+ :type allow_dir: bool
+
+ :returns: data from the matched file(s), type can be dict or list depending on vars or tasks.
+ '''
+ data = None
file_path = os.path.join(self._role_path, subdir)
if self._loader.path_exists(file_path) and self._loader.is_directory(file_path):
- # Valid extensions and ordering for roles is hard-coded to maintain
- # role portability
- extensions = ['.yml', '.yaml', '.json']
- # If no <main> is specified by the user, look for files with
- # extensions before bare name. Otherwise, look for bare name first.
+ # Valid extensions and ordering for roles is hard-coded to maintain portability
+ extensions = ['.yml', '.yaml', '.json'] # same as default for YAML_FILENAME_EXTENSIONS
+
+ # look for files w/o extensions before/after bare name depending on it being set or not
+ # keep 'main' as original to figure out errors if no files found
if main is None:
_main = 'main'
extensions.append('')
else:
_main = main
extensions.insert(0, '')
+
+ # not really 'find_vars_files' but find_files_with_extensions_default_to_yaml_filename_extensions
found_files = self._loader.find_vars_files(file_path, _main, extensions, allow_dir)
if found_files:
- data = {}
for found in found_files:
new_data = self._loader.load_from_file(found)
- if new_data and allow_dir:
- data = combine_vars(data, new_data)
- else:
- data = new_data
- return data
+ if new_data:
+ if data is not None and isinstance(new_data, Mapping):
+ data = combine_vars(data, new_data)
+ else:
+ data = new_data
+
+ # found data so no need to continue unless we want to merge
+ if not allow_dir:
+ break
+
elif main is not None:
+ # this won't trigger with default only when <subdir>_from is specified
raise AnsibleParserError("Could not find specified file in role: %s/%s" % (subdir, main))
- return None
+
+ return data
def _load_dependencies(self):
'''
diff --git a/lib/ansible/plugins/action/pause.py b/lib/ansible/plugins/action/pause.py
index 5dbaa020..88babe61 100644
--- a/lib/ansible/plugins/action/pause.py
+++ b/lib/ansible/plugins/action/pause.py
@@ -231,7 +231,8 @@ class ActionModule(ActionBase):
while True:
if not interactive:
- display.warning("Not waiting for response to prompt as stdin is not interactive")
+ if seconds is None:
+ display.warning("Not waiting for response to prompt as stdin is not interactive")
if seconds is not None:
# Give the signal handler enough time to timeout
time.sleep(seconds + 1)
diff --git a/lib/ansible/plugins/cache/__init__.py b/lib/ansible/plugins/cache/__init__.py
index 68b960e1..20df37d8 100644
--- a/lib/ansible/plugins/cache/__init__.py
+++ b/lib/ansible/plugins/cache/__init__.py
@@ -319,12 +319,13 @@ class CachePluginAdjudicator(MutableMapping):
def _do_load_key(self, key):
load = False
- if key not in self._cache and key not in self._retrieved and self._plugin_name != 'memory':
- if isinstance(self._plugin, BaseFileCacheModule):
- load = True
- elif not isinstance(self._plugin, BaseFileCacheModule) and self._plugin.contains(key):
- # Database-backed caches don't raise KeyError for expired keys, so only load if the key is valid by checking contains()
- load = True
+ if all([
+ key not in self._cache,
+ key not in self._retrieved,
+ self._plugin_name != 'memory',
+ self._plugin.contains(key),
+ ]):
+ load = True
return load
def __getitem__(self, key):
diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py
index e735e0ad..129e7eea 100644
--- a/lib/ansible/plugins/callback/default.py
+++ b/lib/ansible/plugins/callback/default.py
@@ -186,8 +186,8 @@ class CallbackModule(CallbackBase):
# Preserve task name, as all vars may not be available for templating
# when we need it later
- if self._play.strategy == 'free':
- # Explicitly set to None for strategy 'free' to account for any cached
+ if self._play.strategy in ('free', 'host_pinned'):
+ # Explicitly set to None for strategy free/host_pinned to account for any cached
# task title from a previous non-free play
self._last_task_name = None
else:
diff --git a/lib/ansible/plugins/connection/local.py b/lib/ansible/plugins/connection/local.py
index 29505cc2..85cb72b0 100644
--- a/lib/ansible/plugins/connection/local.py
+++ b/lib/ansible/plugins/connection/local.py
@@ -17,6 +17,7 @@ DOCUMENTATION = '''
'''
import os
+import pty
import shutil
import subprocess
import fcntl
@@ -79,15 +80,32 @@ class Connection(ConnectionBase):
else:
cmd = map(to_bytes, cmd)
+ master = None
+ stdin = subprocess.PIPE
+ if sudoable and self.become and self.become.expect_prompt():
+ # Create a pty if sudoable for privlege escalation that needs it.
+ # Falls back to using a standard pipe if this fails, which may
+ # cause the command to fail in certain situations where we are escalating
+ # privileges or the command otherwise needs a pty.
+ try:
+ master, stdin = pty.openpty()
+ except (IOError, OSError) as e:
+ display.debug("Unable to open pty: %s" % to_native(e))
+
p = subprocess.Popen(
cmd,
shell=isinstance(cmd, (text_type, binary_type)),
executable=executable,
cwd=self.cwd,
- stdin=subprocess.PIPE,
+ stdin=stdin,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
+
+ # if we created a master, we can close the other half of the pty now
+ if master is not None:
+ os.close(stdin)
+
display.debug("done running command with Popen()")
if self.become and self.become.expect_prompt() and sudoable:
@@ -120,7 +138,8 @@ class Connection(ConnectionBase):
if not self.become.check_success(become_output):
become_pass = self.become.get_option('become_pass', playcontext=self._play_context)
- p.stdin.write(to_bytes(become_pass, errors='surrogate_or_strict') + b'\n')
+ os.write(master, to_bytes(become_pass, errors='surrogate_or_strict') + b'\n')
+
fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK)
fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK)
@@ -128,6 +147,10 @@ class Connection(ConnectionBase):
stdout, stderr = p.communicate(in_data)
display.debug("done communicating")
+ # finally, close the other half of the pty, if it was created
+ if master:
+ os.close(master)
+
display.debug("done with local.exec_command()")
return (p.returncode, stdout, stderr)
diff --git a/lib/ansible/plugins/connection/psrp.py b/lib/ansible/plugins/connection/psrp.py
index 38a6259b..f03eb878 100644
--- a/lib/ansible/plugins/connection/psrp.py
+++ b/lib/ansible/plugins/connection/psrp.py
@@ -470,7 +470,7 @@ class Connection(ConnectionBase):
if rc != 0:
raise AnsibleError(to_native(stderr))
- put_output = json.loads(stdout)
+ put_output = json.loads(to_text(stdout))
remote_sha1 = put_output.get("sha1")
if not remote_sha1:
diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py
index 957fa725..3c241274 100644
--- a/lib/ansible/plugins/loader.py
+++ b/lib/ansible/plugins/loader.py
@@ -140,14 +140,15 @@ class PluginLoadContext(object):
if not deprecation:
return self
- warning_text = deprecation.get('warning_text', None)
+ # The `or ''` instead of using `.get(..., '')` makes sure that even if the user explicitly
+ # sets `warning_text` to `~` (None) or `false`, we still get an empty string.
+ warning_text = deprecation.get('warning_text', None) or ''
removal_date = deprecation.get('removal_date', None)
removal_version = deprecation.get('removal_version', None)
# If both removal_date and removal_version are specified, use removal_date
if removal_date is not None:
removal_version = None
- if not warning_text:
- warning_text = '{0} has been deprecated'.format(name)
+ warning_text = '{0} has been deprecated.{1}{2}'.format(name, ' ' if warning_text else '', warning_text)
display.deprecated(warning_text, date=removal_date, version=removal_version, collection_name=collection_name)
@@ -462,7 +463,8 @@ class PluginLoader:
if tombstone:
removal_date = tombstone.get('removal_date')
removal_version = tombstone.get('removal_version')
- warning_text = tombstone.get('warning_text') or '{0} has been removed.'.format(fq_name)
+ warning_text = tombstone.get('warning_text') or ''
+ warning_text = '{0} has been removed.{1}{2}'.format(fq_name, ' ' if warning_text else '', warning_text)
removed_msg = display.get_deprecation_message(msg=warning_text, version=removal_version,
date=removal_date, removed=True,
collection_name=acr.collection)
diff --git a/lib/ansible/plugins/lookup/unvault.py b/lib/ansible/plugins/lookup/unvault.py
index 234a52a7..16c1d71d 100644
--- a/lib/ansible/plugins/lookup/unvault.py
+++ b/lib/ansible/plugins/lookup/unvault.py
@@ -56,7 +56,7 @@ class LookupModule(LookupBase):
actual_file = self._loader.get_real_file(lookupfile, decrypt=True)
with open(actual_file, 'rb') as f:
b_contents = f.read()
- ret.append(b_contents)
+ ret.append(to_text(b_contents))
else:
raise AnsibleParserError('Unable to find file matching "%s" ' % term)
diff --git a/lib/ansible/release.py b/lib/ansible/release.py
index b0f9555a..b5ae9fcc 100644
--- a/lib/ansible/release.py
+++ b/lib/ansible/release.py
@@ -19,6 +19,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-__version__ = '2.10.5'
+__version__ = '2.10.7'
__author__ = 'Ansible, Inc.'
__codename__ = 'When the Levee Breaks'
diff --git a/lib/ansible/template/vars.py b/lib/ansible/template/vars.py
index 464deecf..513c7329 100644
--- a/lib/ansible/template/vars.py
+++ b/lib/ansible/template/vars.py
@@ -40,7 +40,7 @@ class AnsibleJ2Vars(Mapping):
To facilitate using builtin jinja2 things like range, globals are also handled here.
'''
- def __init__(self, templar, globals, locals=None, *extras):
+ def __init__(self, templar, globals, locals=None):
'''
Initializes this object with a valid Templar() object, as
well as several dictionaries of variables representing
@@ -49,7 +49,6 @@ class AnsibleJ2Vars(Mapping):
self._templar = templar
self._globals = globals
- self._extras = extras
self._locals = dict()
if isinstance(locals, dict):
for key, val in iteritems(locals):
@@ -60,40 +59,33 @@ class AnsibleJ2Vars(Mapping):
self._locals[key] = val
def __contains__(self, k):
- if k in self._templar.available_variables:
- return True
if k in self._locals:
return True
- for i in self._extras:
- if k in i:
- return True
+ if k in self._templar.available_variables:
+ return True
if k in self._globals:
return True
return False
def __iter__(self):
keys = set()
- keys.update(self._templar.available_variables, self._locals, self._globals, *self._extras)
+ keys.update(self._templar.available_variables, self._locals, self._globals)
return iter(keys)
def __len__(self):
keys = set()
- keys.update(self._templar.available_variables, self._locals, self._globals, *self._extras)
+ keys.update(self._templar.available_variables, self._locals, self._globals)
return len(keys)
def __getitem__(self, varname):
- if varname not in self._templar.available_variables:
- if varname in self._locals:
- return self._locals[varname]
- for i in self._extras:
- if varname in i:
- return i[varname]
- if varname in self._globals:
- return self._globals[varname]
- else:
- raise KeyError("undefined variable: %s" % varname)
-
- variable = self._templar.available_variables[varname]
+ if varname in self._locals:
+ return self._locals[varname]
+ if varname in self._templar.available_variables:
+ variable = self._templar.available_variables[varname]
+ elif varname in self._globals:
+ return self._globals[varname]
+ else:
+ raise KeyError("undefined variable: %s" % varname)
# HostVars is special, return it as-is, as is the special variable
# 'vars', which contains the vars structure
@@ -127,4 +119,4 @@ class AnsibleJ2Vars(Mapping):
new_locals = self._locals.copy()
new_locals.update(locals)
- return AnsibleJ2Vars(self._templar, self._globals, locals=new_locals, *self._extras)
+ return AnsibleJ2Vars(self._templar, self._globals, locals=new_locals)
diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh
index 3a7c4a39..22587001 100755
--- a/test/integration/targets/ansible-galaxy/runme.sh
+++ b/test/integration/targets/ansible-galaxy/runme.sh
@@ -317,6 +317,19 @@ pushd "${role_testdir}"
popd # ${role_testdir}
rm -rf "${role_testdir}"
+f_ansible_galaxy_status \
+ "Test if git hidden directories are skipped while using role skeleton (#71977)"
+
+role_testdir=$(mktemp -d)
+pushd "${role_testdir}"
+
+ ansible-galaxy role init sample-role-skeleton
+ git init ./sample-role-skeleton
+ ansible-galaxy role init --role-skeleton=sample-role-skeleton example
+
+popd # ${role_testdir}
+rm -rf "${role_testdir}"
+
#################################
# ansible-galaxy collection tests
#################################
diff --git a/test/integration/targets/apt/tasks/apt-builddep.yml b/test/integration/targets/apt/tasks/apt-builddep.yml
index d35c90b2..24ee1dc2 100644
--- a/test/integration/targets/apt/tasks/apt-builddep.yml
+++ b/test/integration/targets/apt/tasks/apt-builddep.yml
@@ -29,9 +29,9 @@
when: dpkg_result is successful
tags: ['test_apt_builddep']
-# install build-dep for netcat
-- name: install netcat build-dep with apt
- apt: pkg=netcat state=build-dep
+# install build-dep for rolldice
+- name: install rolldice build-dep with apt
+ apt: pkg=rolldice state=build-dep
register: apt_result
tags: ['test_apt_builddep']
diff --git a/test/integration/targets/apt/tasks/apt-multiarch.yml b/test/integration/targets/apt/tasks/apt-multiarch.yml
index 6241664d..df008d47 100644
--- a/test/integration/targets/apt/tasks/apt-multiarch.yml
+++ b/test/integration/targets/apt/tasks/apt-multiarch.yml
@@ -1,24 +1,37 @@
# verify that apt is handling multi-arch systems properly
+
+- name: load version specific vars
+ include_vars: '{{ item }}'
+ with_first_found:
+ - files:
+ - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml'
+ - 'default.yml'
+ paths: '../vars'
+
- name: add architecture {{ apt_foreign_arch }}
command: dpkg --add-architecture {{ apt_foreign_arch }}
-- name: install hello:{{ apt_foreign_arch }} with apt
- apt: pkg=hello:{{ apt_foreign_arch }} state=present update_cache=yes
+- name: install {{ multiarch_test_pkg }}:{{ apt_foreign_arch }} with apt
+ apt: pkg={{ multiarch_test_pkg }}:{{ apt_foreign_arch }} state=present update_cache=yes
register: apt_result
until: apt_result is success
-- name: uninstall hello:{{ apt_foreign_arch }} with apt
- apt: pkg=hello:{{ apt_foreign_arch }} state=absent purge=yes
+- name: check {{ multiarch_test_pkg }} version
+ shell: dpkg -s {{ multiarch_test_pkg }} | grep Version | awk '{print $2}'
+ register: pkg_version
+
+- name: uninstall {{ multiarch_test_pkg }}:{{ apt_foreign_arch }} with apt
+ apt: pkg={{ multiarch_test_pkg }}:{{ apt_foreign_arch }} state=absent purge=yes
- name: install deb file
- apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ apt_foreign_arch }}.deb"
+ apt: deb="/var/cache/apt/archives/{{ multiarch_test_pkg }}_{{ pkg_version.stdout }}_{{ apt_foreign_arch }}.deb"
register: apt_multi_initial
- name: install deb file again
- apt: deb="/var/cache/apt/archives/hello_{{ hello_version.stdout }}_{{ apt_foreign_arch }}.deb"
+ apt: deb="/var/cache/apt/archives/{{ multiarch_test_pkg }}_{{ pkg_version.stdout }}_{{ apt_foreign_arch }}.deb"
register: apt_multi_secondary
-- name: verify installation of hello:{{ apt_foreign_arch }}
+- name: verify installation of {{ multiarch_test_pkg }}:{{ apt_foreign_arch }}
assert:
that:
- "apt_multi_initial.changed"
diff --git a/test/integration/targets/apt/tasks/apt.yml b/test/integration/targets/apt/tasks/apt.yml
index 5bb6e7b9..68728376 100644
--- a/test/integration/targets/apt/tasks/apt.yml
+++ b/test/integration/targets/apt/tasks/apt.yml
@@ -271,6 +271,9 @@
- name: autoclean during install
apt: pkg=hello state=present autoclean=yes
+- name: undo previous install
+ apt: pkg=hello state=absent
+
# https://github.com/ansible/ansible/issues/23155
- name: create a repo file
copy:
diff --git a/test/integration/targets/apt/vars/Ubuntu-20.yml b/test/integration/targets/apt/vars/Ubuntu-20.yml
new file mode 100644
index 00000000..7b32755f
--- /dev/null
+++ b/test/integration/targets/apt/vars/Ubuntu-20.yml
@@ -0,0 +1 @@
+multiarch_test_pkg: libunistring2
diff --git a/test/integration/targets/apt/vars/default.yml b/test/integration/targets/apt/vars/default.yml
new file mode 100644
index 00000000..bed3a965
--- /dev/null
+++ b/test/integration/targets/apt/vars/default.yml
@@ -0,0 +1 @@
+multiarch_test_pkg: hello
diff --git a/test/integration/targets/become_su/aliases b/test/integration/targets/become_su/aliases
new file mode 100644
index 00000000..3a07aab3
--- /dev/null
+++ b/test/integration/targets/become_su/aliases
@@ -0,0 +1,3 @@
+destructive
+shippable/posix/group1
+skip/aix
diff --git a/test/integration/targets/become_su/runme.sh b/test/integration/targets/become_su/runme.sh
new file mode 100755
index 00000000..87a3511f
--- /dev/null
+++ b/test/integration/targets/become_su/runme.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -eux
+
+# ensure we execute su with a pseudo terminal
+[ "$(ansible -a whoami --become-method=su localhost --become)" != "su: requires a terminal to execute" ]
diff --git a/test/integration/targets/callback_default/callback_default.out.free.stdout b/test/integration/targets/callback_default/callback_default.out.free.stdout
new file mode 100644
index 00000000..0ec04479
--- /dev/null
+++ b/test/integration/targets/callback_default/callback_default.out.free.stdout
@@ -0,0 +1,35 @@
+
+PLAY [nonlockstep] *************************************************************
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+PLAY RECAP *********************************************************************
+testhost10 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+testhost11 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+testhost12 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+
diff --git a/test/integration/targets/callback_default/callback_default.out.host_pinned.stdout b/test/integration/targets/callback_default/callback_default.out.host_pinned.stdout
new file mode 100644
index 00000000..0ec04479
--- /dev/null
+++ b/test/integration/targets/callback_default/callback_default.out.host_pinned.stdout
@@ -0,0 +1,35 @@
+
+PLAY [nonlockstep] *************************************************************
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost10]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost11]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+TASK [command] *****************************************************************
+changed: [testhost12]
+
+PLAY RECAP *********************************************************************
+testhost10 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+testhost11 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+testhost12 : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+
diff --git a/test/integration/targets/callback_default/inventory b/test/integration/targets/callback_default/inventory
index e75c585d..6d9b3028 100644
--- a/test/integration/targets/callback_default/inventory
+++ b/test/integration/targets/callback_default/inventory
@@ -3,3 +3,8 @@ testhost ansible_connection=local ansible_python_interpreter="{{ ansible_playboo
[nonexistent]
testhost5 ansible_host=169.254.199.200 # no connection is ever established with this host
+
+[nonlockstep]
+testhost10 i=0.5 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"
+testhost11 i=3.0 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"
+testhost12 i=12.0 ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"
diff --git a/test/integration/targets/callback_default/runme.sh b/test/integration/targets/callback_default/runme.sh
index 6e6983b1..a8033d7d 100755
--- a/test/integration/targets/callback_default/runme.sh
+++ b/test/integration/targets/callback_default/runme.sh
@@ -184,3 +184,7 @@ run_test_dryrun check_nomarkers_wet
# Test the dry run without check markers
run_test_dryrun check_nomarkers_dry --check
+
+# Ensure free/host_pinned non-lockstep strategies display correctly
+diff -u callback_default.out.free.stdout <(ANSIBLE_STRATEGY=free ansible-playbook -i inventory test_non_lockstep.yml 2>/dev/null)
+diff -u callback_default.out.host_pinned.stdout <(ANSIBLE_STRATEGY=host_pinned ansible-playbook -i inventory test_non_lockstep.yml 2>/dev/null)
diff --git a/test/integration/targets/callback_default/test_non_lockstep.yml b/test/integration/targets/callback_default/test_non_lockstep.yml
new file mode 100644
index 00000000..b656ee91
--- /dev/null
+++ b/test/integration/targets/callback_default/test_non_lockstep.yml
@@ -0,0 +1,7 @@
+---
+- hosts: nonlockstep
+ gather_facts: false
+ tasks:
+ - command: sleep {{ i }}
+ - command: sleep {{ i }}
+ - command: sleep {{ i }}
diff --git a/test/integration/targets/config/inline_comment_ansible.cfg b/test/integration/targets/config/inline_comment_ansible.cfg
new file mode 100644
index 00000000..afe9197d
--- /dev/null
+++ b/test/integration/targets/config/inline_comment_ansible.cfg
@@ -0,0 +1,2 @@
+[defaults]
+cow_whitelist = ansibull ; BOOM
diff --git a/test/integration/targets/config/runme.sh b/test/integration/targets/config/runme.sh
index 73c3778b..ea3989b8 100755
--- a/test/integration/targets/config/runme.sh
+++ b/test/integration/targets/config/runme.sh
@@ -15,3 +15,6 @@ ANSIBLE_REMOTE_TMP="$HOME/.ansible/directory_with_no_space" ansible -m ping tes
ANSIBLE_REMOTE_TMP="$HOME/.ansible/directory with space" ansible -m ping testhost -i ../../inventory "$@"
ANSIBLE_CONFIG=nonexistent.cfg ansible-config dump --only-changed -v | grep 'No config file found; using defaults'
+
+# https://github.com/ansible/ansible/pull/73715
+ANSIBLE_CONFIG=inline_comment_ansible.cfg ansible-config dump --only-changed | grep "'ansibull'"
diff --git a/test/integration/targets/find/tasks/main.yml b/test/integration/targets/find/tasks/main.yml
index 7fd61dd2..6879404d 100644
--- a/test/integration/targets/find/tasks/main.yml
+++ b/test/integration/targets/find/tasks/main.yml
@@ -67,6 +67,7 @@
- 'find_test0.msg is defined'
- 'find_test0.matched == 8'
- 'find_test0.files | length == 8'
+ - 'find_test0.examined == 16'
- name: find the xml and img files
find:
@@ -95,3 +96,46 @@
- 'find_test2.matched == 1'
- 'find_test2.files[0].pw_name is defined'
- 'find_test2.files[0].gr_name is defined'
+
+- name: test number of examined directories/files
+ block:
+ - name: Get all files/directories in the path
+ find:
+ paths: "{{ output_dir_test }}"
+ recurse: yes
+ file_type: any
+ register: total_contents
+
+ - assert:
+ that:
+ - total_contents.matched == 16
+ - total_contents.examined == 16
+
+ - name: Get files and directories with depth
+ find:
+ paths: "{{ output_dir_test }}"
+ recurse: yes
+ file_type: any
+ depth: 2
+ register: contents_with_depth
+
+ - assert:
+ that:
+ - contents_with_depth.matched == 6
+ # dir contents are considered until the depth exceeds the requested depth
+ # there are 6 files/directories in the requested depth and 4 that exceed it by 1
+ - contents_with_depth.examined == 10
+
+ - name: Find files with depth
+ find:
+ paths: "{{ output_dir_test }}"
+ depth: 2
+ recurse: yes
+ register: files_with_depth
+
+ - assert:
+ that:
+ - files_with_depth.matched == 2
+ # dir contents are considered until the depth exceeds the requested depth
+ # there are 6 files/directories in the requested depth and 4 that exceed it by 1
+ - files_with_depth.examined == 10
diff --git a/test/integration/targets/git/tasks/gpg-verification.yml b/test/integration/targets/git/tasks/gpg-verification.yml
index 143b7e55..8c8834a9 100644
--- a/test/integration/targets/git/tasks/gpg-verification.yml
+++ b/test/integration/targets/git/tasks/gpg-verification.yml
@@ -75,6 +75,8 @@
repo: "{{ git_gpg_source }}"
dest: "{{ git_gpg_dest }}"
verify_commit: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify a signed lightweight tag
environment:
@@ -84,6 +86,8 @@
dest: "{{ git_gpg_dest }}"
version: lightweight_tag/signed_commit
verify_commit: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify an unsigned lightweight tag (should fail)
environment:
@@ -95,12 +99,16 @@
verify_commit: yes
register: git_verify
ignore_errors: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Check that unsigned lightweight tag verification failed
assert:
that:
- git_verify is failed
- git_verify.msg is match("Failed to verify GPG signature of commit/tag.+")
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify a signed commit
environment:
@@ -110,6 +118,8 @@
dest: "{{ git_gpg_dest }}"
version: "{{ git_gpg_signed_commit.stdout }}"
verify_commit: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify an unsigned commit
environment:
@@ -121,12 +131,16 @@
verify_commit: yes
register: git_verify
ignore_errors: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Check that unsigned commit verification failed
assert:
that:
- git_verify is failed
- git_verify.msg is match("Failed to verify GPG signature of commit/tag.+")
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify a signed annotated tag
environment:
@@ -162,6 +176,8 @@
dest: "{{ git_gpg_dest }}"
version: some_branch/signed_tip
verify_commit: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Clone repo and verify an unsigned branch (should fail)
environment:
@@ -173,18 +189,22 @@
verify_commit: yes
register: git_verify
ignore_errors: yes
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Check that unsigned branch verification failed
assert:
that:
- git_verify is failed
- git_verify.msg is match("Failed to verify GPG signature of commit/tag.+")
+ when:
+ - git_version.stdout is version("2.1.0", '>=')
- name: GPG-VERIFICATION | Stop gpg-agent so we can remove any locks on the GnuPG dir
command: gpgconf --kill gpg-agent
- when: ansible_os_family != 'Suse' or ansible_distribution_version != '42.3' # OpenSUSE 42.3 ships with an older version of gpg-agent that doesn't support this
environment:
GNUPGHOME: "{{ git_gpg_gpghome }}"
+ ignore_errors: yes
- name: GPG-VERIFICATION | Remove GnuPG verification workdir
file:
diff --git a/test/integration/targets/git/tasks/main.yml b/test/integration/targets/git/tasks/main.yml
index 9d750c5c..722713bf 100644
--- a/test/integration/targets/git/tasks/main.yml
+++ b/test/integration/targets/git/tasks/main.yml
@@ -31,7 +31,7 @@
when:
- not gpg_version.stderr
- gpg_version.stdout
- - git_version.stdout is version("2.1.0", '>=')
+ - not (ansible_os_family == 'RedHat' and ansible_distribution_major_version is version('7', '<'))
- include_tasks: localmods.yml
- include_tasks: reset-origin.yml
- include_tasks: ambiguous-ref.yml
diff --git a/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml b/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml
index 740f8998..7c9553c5 100644
--- a/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml
+++ b/test/integration/targets/incidental_lookup_rabbitmq/tasks/main.yml
@@ -2,4 +2,4 @@
- include: ubuntu.yml
when:
- ansible_distribution == 'Ubuntu'
- - ansible_distribution_release != 'trusty'
+ - ansible_distribution_release not in ('trusty', 'focal')
diff --git a/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz b/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz
index 41a89c46..544bf706 100644
--- a/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz
+++ b/test/integration/targets/incidental_setup_flatpak_remote/files/repo.tar.xz
Binary files differ
diff --git a/test/integration/targets/incidental_setup_mongodb/tasks/main.yml b/test/integration/targets/incidental_setup_mongodb/tasks/main.yml
index 16382ce5..3bd090ca 100644
--- a/test/integration/targets/incidental_setup_mongodb/tasks/main.yml
+++ b/test/integration/targets/incidental_setup_mongodb/tasks/main.yml
@@ -20,8 +20,10 @@
# https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/
# Support for Ubuntu 14.04 has been removed from MongoDB 4.0.10+, 3.6.13+, and 3.4.21+.
# CentOS6 has python version issues
+# Ubuntu 20.04 does not yet have the required packages
- meta: end_play
when: (ansible_distribution == 'Ubuntu' and ansible_distribution_version == '14.04')
+ or (ansible_distribution == 'Ubuntu' and ansible_distribution_version == '20.04')
or (ansible_os_family == "RedHat" and ansible_distribution_major_version == '6')
or ansible_os_family == "Suse"
or ansible_distribution == 'Fedora'
diff --git a/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml
new file mode 100644
index 00000000..7322bcb2
--- /dev/null
+++ b/test/integration/targets/incidental_setup_postgresql_db/vars/Ubuntu-20-py3.yml
@@ -0,0 +1,8 @@
+postgresql_packages:
+ - "postgresql"
+ - "postgresql-common"
+ - "python3-psycopg2"
+
+pg_hba_location: "/etc/postgresql/12/main/pg_hba.conf"
+pg_dir: "/var/lib/postgresql/12/main"
+pg_ver: 12
diff --git a/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml b/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml
index 4f35f16f..ad401fb3 100644
--- a/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml
+++ b/test/integration/targets/incidental_setup_rabbitmq/tasks/main.yml
@@ -1,3 +1,5 @@
---
- include: ubuntu.yml
- when: ansible_distribution == 'Ubuntu'
+ when:
+ - ansible_distribution == 'Ubuntu'
+ - ansible_distribution_release != 'focal'
diff --git a/test/integration/targets/include_import/runme.sh b/test/integration/targets/include_import/runme.sh
index 28115a5b..68b12a1f 100755
--- a/test/integration/targets/include_import/runme.sh
+++ b/test/integration/targets/include_import/runme.sh
@@ -17,7 +17,7 @@ ansible -m include_role -a name=role1 localhost
## Import (static)
# Playbook
-test "$(ansible-playbook -i ../../inventory playbook/test_import_playbook.yml "$@" 2>&1 | grep -c '\[WARNING\]: Additional parameters in import_playbook')" = 1
+test "$(ANSIBLE_DEPRECATION_WARNINGS=1 ansible-playbook -i ../../inventory playbook/test_import_playbook.yml "$@" 2>&1 | grep -c '\[DEPRECATION WARNING\]: Additional parameters in import_playbook')" = 1
ANSIBLE_STRATEGY='linear' ansible-playbook playbook/test_import_playbook_tags.yml -i inventory "$@" --tags canary1,canary22,validate --skip-tags skipme
diff --git a/test/integration/targets/inventory/inv_with_int.yml b/test/integration/targets/inventory/inv_with_int.yml
new file mode 100644
index 00000000..5b2f21da
--- /dev/null
+++ b/test/integration/targets/inventory/inv_with_int.yml
@@ -0,0 +1,6 @@
+all:
+ hosts:
+ testing123:
+ x:
+ a: 1
+ 0: 2
diff --git a/test/integration/targets/inventory/runme.sh b/test/integration/targets/inventory/runme.sh
index 3cd533cd..87bef447 100755
--- a/test/integration/targets/inventory/runme.sh
+++ b/test/integration/targets/inventory/runme.sh
@@ -1,36 +1,86 @@
#!/usr/bin/env bash
-set -x
+set -eux
-empty_limit_file="/tmp/limit_file"
+empty_limit_file="$(mktemp)"
touch "${empty_limit_file}"
+tmpdir="$(mktemp -d)"
+
cleanup() {
if [[ -f "${empty_limit_file}" ]]; then
rm -rf "${empty_limit_file}"
fi
+ rm -rf "$tmpdir"
}
trap 'cleanup' EXIT
# https://github.com/ansible/ansible/issues/52152
# Ensure that non-matching limit causes failure with rc 1
-ansible-playbook -i ../../inventory --limit foo playbook.yml
-if [ "$?" != "1" ]; then
+if ansible-playbook -i ../../inventory --limit foo playbook.yml; then
echo "Non-matching limit should cause failure"
exit 1
fi
# Ensure that non-existing limit file causes failure with rc 1
-ansible-playbook -i ../../inventory --limit @foo playbook.yml
-if [ "$?" != "1" ]; then
+if ansible-playbook -i ../../inventory --limit @foo playbook.yml; then
echo "Non-existing limit file should cause failure"
exit 1
fi
-# Ensure that non-matching limit causes failure with rc 1
+if ! ansible-playbook -i ../../inventory --limit @"$tmpdir" playbook.yml 2>&1 | grep 'must be a file'; then
+ echo "Using a directory as a limit file should throw proper AnsibleError"
+ exit 1
+fi
+
+# Ensure that empty limit file does not cause IndexError #59695
ansible-playbook -i ../../inventory --limit @"${empty_limit_file}" playbook.yml
ansible-playbook -i ../../inventory "$@" strategy.yml
ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS=always ansible-playbook -i ../../inventory "$@" strategy.yml
ANSIBLE_TRANSFORM_INVALID_GROUP_CHARS=never ansible-playbook -i ../../inventory "$@" strategy.yml
+
+# Do not fail when all inventories fail to parse.
+# Do not fail when any inventory fails to parse.
+ANSIBLE_INVENTORY_UNPARSED_FAILED=False ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False ansible -m ping localhost -i /idontexist "$@"
+
+# Fail when all inventories fail to parse.
+# Do not fail when just one inventory fails to parse.
+if ANSIBLE_INVENTORY_UNPARSED_FAILED=True ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False ansible -m ping localhost -i /idontexist; then
+ echo "All inventories failed/did not exist, should cause failure"
+ echo "ran with: ANSIBLE_INVENTORY_UNPARSED_FAILED=True ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False"
+ exit 1
+fi
+
+# Same as above but ensuring no failure we *only* fail when all inventories fail to parse.
+# Fail when all inventories fail to parse.
+# Do not fail when just one inventory fails to parse.
+ANSIBLE_INVENTORY_UNPARSED_FAILED=True ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False ansible -m ping localhost -i /idontexist -i ../../inventory "$@"
+# Fail when all inventories fail to parse.
+# Do not fail when just one inventory fails to parse.
+
+# Fail when any inventories fail to parse.
+if ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=True ansible -m ping localhost -i /idontexist -i ../../inventory; then
+ echo "One inventory failed/did not exist, should NOT cause failure"
+ echo "ran with: ANSIBLE_INVENTORY_UNPARSED_FAILED=True ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=False"
+ exit 1
+fi
+
+# Ensure we don't throw when an empty directory is used as inventory
+ansible-playbook -i "$tmpdir" playbook.yml
+
+# Ensure we can use a directory of inventories
+cp ../../inventory "$tmpdir"
+ansible-playbook -i "$tmpdir" playbook.yml
+
+# ... even if it contains another empty directory
+mkdir "$tmpdir/empty"
+ansible-playbook -i "$tmpdir" playbook.yml
+
+if ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=True ansible -m ping localhost -i "$tmpdir"; then
+ echo "Empty directory should cause failure when ANSIBLE_INVENTORY_ANY_UNPARSED_IS_FAILED=True"
+ exit 1
+fi
+
+ansible-inventory -i inv_with_int.yml --list "$@"
diff --git a/test/integration/targets/inventory_cache/aliases b/test/integration/targets/inventory_cache/aliases
new file mode 100644
index 00000000..70a7b7a9
--- /dev/null
+++ b/test/integration/targets/inventory_cache/aliases
@@ -0,0 +1 @@
+shippable/posix/group5
diff --git a/test/integration/targets/yum_repository/vars/default.yml b/test/integration/targets/inventory_cache/cache/.keep
index e69de29b..e69de29b 100644
--- a/test/integration/targets/yum_repository/vars/default.yml
+++ b/test/integration/targets/inventory_cache/cache/.keep
diff --git a/test/integration/targets/inventory_cache/cache_host.yml b/test/integration/targets/inventory_cache/cache_host.yml
new file mode 100644
index 00000000..3630641b
--- /dev/null
+++ b/test/integration/targets/inventory_cache/cache_host.yml
@@ -0,0 +1,4 @@
+plugin: cache_host
+cache: true
+cache_plugin: jsonfile
+cache_connection: ./cache
diff --git a/test/integration/targets/inventory_cache/plugins/inventory/cache_host.py b/test/integration/targets/inventory_cache/plugins/inventory/cache_host.py
new file mode 100644
index 00000000..628aba15
--- /dev/null
+++ b/test/integration/targets/inventory_cache/plugins/inventory/cache_host.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2021 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
+
+DOCUMENTATION = '''
+ inventory: cache_host
+ short_description: add a host to inventory and cache it
+ description: add a host to inventory and cache it
+ extends_documentation_fragment:
+ - inventory_cache
+ options:
+ plugin:
+ required: true
+ description: name of the plugin (cache_host)
+'''
+
+from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable
+import random
+
+
+class InventoryModule(BaseInventoryPlugin, Cacheable):
+
+ NAME = 'cache_host'
+
+ def verify_file(self, path):
+ if not path.endswith(('cache_host.yml', 'cache_host.yaml',)):
+ return False
+ return super(InventoryModule, self).verify_file(path)
+
+ def parse(self, inventory, loader, path, cache=None):
+ super(InventoryModule, self).parse(inventory, loader, path)
+ self._read_config_data(path)
+
+ cache_key = self.get_cache_key(path)
+ # user has enabled cache and the cache is not being flushed
+ read_cache = self.get_option('cache') and cache
+ # user has enabled cache and the cache is being flushed
+ update_cache = self.get_option('cache') and not cache
+
+ host = None
+ if read_cache:
+ try:
+ host = self._cache[cache_key]
+ except KeyError:
+ # cache expired
+ update_cache = True
+
+ if host is None:
+ host = 'testhost{0}'.format(random.randint(0, 50))
+
+ self.inventory.add_host(host, 'all')
+
+ if update_cache:
+ self._cache[cache_key] = host
diff --git a/test/integration/targets/inventory_cache/runme.sh b/test/integration/targets/inventory_cache/runme.sh
new file mode 100755
index 00000000..098439eb
--- /dev/null
+++ b/test/integration/targets/inventory_cache/runme.sh
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+
+set -eux
+
+export ANSIBLE_INVENTORY_PLUGINS=./plugins/inventory
+
+cleanup() {
+ for f in ./cache/ansible_inventory*; do
+ if [ -f "$f" ]; then rm -rf "$f"; fi
+ done
+}
+
+trap 'cleanup' EXIT
+
+# Test no warning when writing to the cache for the first time
+test "$(ansible-inventory -i cache_host.yml --graph 2>&1 | tee out.txt | grep -c '\[WARNING\]')" = 0
+writehost="$(grep "testhost[0-9]\{1,2\}" out.txt)"
+
+# Test reading from the cache
+test "$(ansible-inventory -i cache_host.yml --graph 2>&1 | tee out.txt | grep -c '\[WARNING\]')" = 0
+readhost="$(grep 'testhost[0-9]\{1,2\}' out.txt)"
+
+test "$readhost" = "$writehost"
diff --git a/test/integration/targets/module_utils/callback/pure_json.py b/test/integration/targets/module_utils/callback/pure_json.py
new file mode 100644
index 00000000..1723d7bb
--- /dev/null
+++ b/test/integration/targets/module_utils/callback/pure_json.py
@@ -0,0 +1,31 @@
+# (c) 2021 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
+
+DOCUMENTATION = '''
+ name: pure_json
+ type: stdout
+ short_description: only outputs the module results as json
+'''
+
+import json
+
+from ansible.plugins.callback import CallbackBase
+
+
+class CallbackModule(CallbackBase):
+
+ CALLBACK_VERSION = 2.0
+ CALLBACK_TYPE = 'stdout'
+ CALLBACK_NAME = 'pure_json'
+
+ def v2_runner_on_failed(self, result, ignore_errors=False):
+ self._display.display(json.dumps(result._result))
+
+ def v2_runner_on_ok(self, result):
+ self._display.display(json.dumps(result._result))
+
+ def v2_runner_on_skipped(self, result):
+ self._display.display(json.dumps(result._result))
diff --git a/test/integration/targets/module_utils/library/test_no_log.py b/test/integration/targets/module_utils/library/test_no_log.py
new file mode 100644
index 00000000..770e0b3a
--- /dev/null
+++ b/test/integration/targets/module_utils/library/test_no_log.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# (c) 2021 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
+
+
+from ansible.module_utils.basic import AnsibleModule, env_fallback
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec=dict(
+ explicit_pass=dict(type='str', no_log=True),
+ fallback_pass=dict(type='str', no_log=True, fallback=(env_fallback, ['SECRET_ENV'])),
+ default_pass=dict(type='str', no_log=True, default='zyx'),
+ normal=dict(type='str', default='plaintext'),
+ suboption=dict(
+ type='dict',
+ options=dict(
+ explicit_sub_pass=dict(type='str', no_log=True),
+ fallback_sub_pass=dict(type='str', no_log=True, fallback=(env_fallback, ['SECRET_SUB_ENV'])),
+ default_sub_pass=dict(type='str', no_log=True, default='xvu'),
+ normal=dict(type='str', default='plaintext'),
+ ),
+ ),
+ ),
+ )
+
+ module.exit_json(changed=False)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/module_utils/module_utils_test_no_log.yml b/test/integration/targets/module_utils/module_utils_test_no_log.yml
new file mode 100644
index 00000000..bad2efd4
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_test_no_log.yml
@@ -0,0 +1,9 @@
+# This is called by module_utils_vvvvv.yml with a custom callback
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ - name: Check no_log invocation results
+ test_no_log:
+ explicit_pass: abc
+ suboption:
+ explicit_sub_pass: def
diff --git a/test/integration/targets/module_utils/module_utils_vvvvv.yml b/test/integration/targets/module_utils/module_utils_vvvvv.yml
new file mode 100644
index 00000000..1fd91d25
--- /dev/null
+++ b/test/integration/targets/module_utils/module_utils_vvvvv.yml
@@ -0,0 +1,27 @@
+- hosts: testhost
+ gather_facts: no
+ tasks:
+ # Invocation usually is output with 3vs or more, our callback plugin displays it anyway
+ - name: Check no_log invocation results
+ command: ansible-playbook -i {{ inventory_file }} module_utils_test_no_log.yml
+ environment:
+ ANSIBLE_CALLBACK_PLUGINS: callback
+ ANSIBLE_STDOUT_CALLBACK: pure_json
+ SECRET_ENV: ghi
+ SECRET_SUB_ENV: jkl
+ register: no_log_invocation
+
+ - set_fact:
+ no_log_invocation: '{{ no_log_invocation.stdout | trim | from_json }}'
+
+ - name: check no log values from fallback or default are masked
+ assert:
+ that:
+ - no_log_invocation.invocation.module_args.default_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.explicit_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.fallback_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.normal == 'plaintext'
+ - no_log_invocation.invocation.module_args.suboption.default_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.explicit_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.fallback_sub_pass == 'VALUE_SPECIFIED_IN_NO_LOG_PARAMETER'
+ - no_log_invocation.invocation.module_args.suboption.normal == 'plaintext'
diff --git a/test/integration/targets/module_utils/runme.sh b/test/integration/targets/module_utils/runme.sh
index 7a9e458e..f25dba63 100755
--- a/test/integration/targets/module_utils/runme.sh
+++ b/test/integration/targets/module_utils/runme.sh
@@ -4,6 +4,10 @@ set -eux
ANSIBLE_ROLES_PATH=../ ansible-playbook module_utils_basic_setcwd.yml -i ../../inventory "$@"
+# Keep the -vvvvv here. This acts as a test for testing that higher verbosity
+# doesn't traceback with unicode in the custom module_utils directory path.
+ansible-playbook module_utils_vvvvv.yml -i ../../inventory -vvvvv "$@"
+
ansible-playbook module_utils_test.yml -i ../../inventory -v "$@"
ANSIBLE_MODULE_UTILS=other_mu_dir ansible-playbook module_utils_envvar.yml -i ../../inventory -v "$@"
diff --git a/test/integration/targets/pause/runme.sh b/test/integration/targets/pause/runme.sh
index 932f49ec..eb2c6f7c 100755
--- a/test/integration/targets/pause/runme.sh
+++ b/test/integration/targets/pause/runme.sh
@@ -4,8 +4,8 @@ set -eux
ANSIBLE_ROLES_PATH=../ ansible-playbook setup.yml
-# Test pause module when no tty and non-interactive. This is to prevent playbooks
-# from hanging in cron and Tower jobs.
+# Test pause module when no tty and non-interactive with no seconds parameter.
+# This is to prevent playbooks from hanging in cron and Tower jobs.
/usr/bin/env bash << EOF
ansible-playbook test-pause-no-tty.yml 2>&1 | \
grep '\[WARNING\]: Not waiting for response to prompt as stdin is not interactive' && {
@@ -17,11 +17,24 @@ ansible-playbook test-pause-no-tty.yml 2>&1 | \
}
EOF
+# Do not issue a warning when run in the background if a timeout is given
+# https://github.com/ansible/ansible/issues/73042
+if sleep 0 | ansible localhost -m pause -a 'seconds=1' 2>&1 | grep '\[WARNING\]: Not waiting for response'; then
+ echo "Incorrectly issued warning when run in the background"
+ exit 1
+else
+ echo "Succesfully ran in the background with no warning"
+fi
+
# Test redirecting stdout
-# Issue #41717
-ansible-playbook pause-3.yml > /dev/null \
- && echo "Successfully redirected stdout" \
- || echo "Failure when attempting to redirect stdout"
+# https://github.com/ansible/ansible/issues/41717
+if ansible-playbook pause-3.yml > /dev/null ; then
+ echo "Successfully redirected stdout"
+else
+ echo "Failure when attempting to redirect stdout"
+ exit 1
+fi
+
# Test pause with seconds and minutes specified
ansible-playbook test-pause.yml "$@"
diff --git a/test/integration/targets/pkg_resources/aliases b/test/integration/targets/pkg_resources/aliases
new file mode 100644
index 00000000..a6dafcf8
--- /dev/null
+++ b/test/integration/targets/pkg_resources/aliases
@@ -0,0 +1 @@
+shippable/posix/group1
diff --git a/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py b/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py
new file mode 100644
index 00000000..9f1c5c0b
--- /dev/null
+++ b/test/integration/targets/pkg_resources/lookup_plugins/check_pkg_resources.py
@@ -0,0 +1,23 @@
+"""
+This test case verifies that pkg_resources imports from ansible plugins are functional.
+
+If pkg_resources is not installed this test will succeed.
+If pkg_resources is installed but is unable to function, this test will fail.
+
+One known failure case this test can detect is when ansible declares a __requires__ and then tests are run without an egg-info directory.
+"""
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+# noinspection PyUnresolvedReferences
+try:
+ from pkg_resources import Requirement
+except ImportError:
+ Requirement = None
+
+from ansible.plugins.lookup import LookupBase
+
+
+class LookupModule(LookupBase):
+ def run(self, terms, variables=None, **kwargs):
+ return []
diff --git a/test/integration/targets/pkg_resources/tasks/main.yml b/test/integration/targets/pkg_resources/tasks/main.yml
new file mode 100644
index 00000000..b19d0ebd
--- /dev/null
+++ b/test/integration/targets/pkg_resources/tasks/main.yml
@@ -0,0 +1,3 @@
+- name: Verify that pkg_resources imports are functional
+ debug:
+ msg: "{{ lookup('check_pkg_resources') }}"
diff --git a/test/integration/targets/roles/data_integrity.yml b/test/integration/targets/roles/data_integrity.yml
new file mode 100644
index 00000000..5eb4fb32
--- /dev/null
+++ b/test/integration/targets/roles/data_integrity.yml
@@ -0,0 +1,4 @@
+- hosts: all
+ gather_facts: false
+ roles:
+ - data
diff --git a/test/integration/targets/roles/roles/data/defaults/main/00.yml b/test/integration/targets/roles/roles/data/defaults/main/00.yml
new file mode 100644
index 00000000..98c13a15
--- /dev/null
+++ b/test/integration/targets/roles/roles/data/defaults/main/00.yml
@@ -0,0 +1 @@
+defined_var: 1
diff --git a/test/integration/targets/roles/roles/data/defaults/main/01.yml b/test/integration/targets/roles/roles/data/defaults/main/01.yml
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/integration/targets/roles/roles/data/defaults/main/01.yml
diff --git a/test/integration/targets/roles/roles/data/tasks/main.yml b/test/integration/targets/roles/roles/data/tasks/main.yml
new file mode 100644
index 00000000..8d85580c
--- /dev/null
+++ b/test/integration/targets/roles/roles/data/tasks/main.yml
@@ -0,0 +1,5 @@
+- name: ensure data was correctly defind
+ assert:
+ that:
+ - defined_var is defined
+ - defined_var == 1
diff --git a/test/integration/targets/roles/runme.sh b/test/integration/targets/roles/runme.sh
index fe99ea10..f2058ff1 100755
--- a/test/integration/targets/roles/runme.sh
+++ b/test/integration/targets/roles/runme.sh
@@ -12,3 +12,7 @@ set -eux
# include/import can execute another instance of role
[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags importrole "$@" | grep -c '"msg": "A"')" = "2" ]
[ "$(ansible-playbook allowed_dupes.yml -i ../../inventory --tags includerole "$@" | grep -c '"msg": "A"')" = "2" ]
+
+
+# ensure role data is merged correctly
+ansible-playbook data_integrity.yml -i ../../inventory "$@"
diff --git a/test/integration/targets/setup_paramiko/aliases b/test/integration/targets/setup_paramiko/aliases
new file mode 100644
index 00000000..c49be254
--- /dev/null
+++ b/test/integration/targets/setup_paramiko/aliases
@@ -0,0 +1 @@
+needs/target/setup_remote_tmp_dir
diff --git a/test/integration/targets/setup_paramiko/constraints.txt b/test/integration/targets/setup_paramiko/constraints.txt
new file mode 100644
index 00000000..c502ba0d
--- /dev/null
+++ b/test/integration/targets/setup_paramiko/constraints.txt
@@ -0,0 +1 @@
+cryptography >= 2.5, < 3.4
diff --git a/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml b/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml
index a156f806..8926fe33 100644
--- a/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml
+++ b/test/integration/targets/setup_paramiko/install-Darwin-python-3.yml
@@ -1,6 +1,9 @@
+- name: Setup remote constraints
+ include_tasks: setup-remote-constraints.yml
- name: Install Paramiko for Python 3 on MacOS
pip: # no homebrew package manager in core, just use pip
name: paramiko
+ extra_args: "-c {{ remote_constraints }}"
environment:
# Not sure why this fixes the test, but it does.
SETUPTOOLS_USE_DISTUTILS: stdlib
diff --git a/test/integration/targets/setup_paramiko/install-FreeBSD-11-python-3.yml b/test/integration/targets/setup_paramiko/install-FreeBSD-11-python-3.yml
index b8ca6c9e..eb01d00f 100644
--- a/test/integration/targets/setup_paramiko/install-FreeBSD-11-python-3.yml
+++ b/test/integration/targets/setup_paramiko/install-FreeBSD-11-python-3.yml
@@ -4,6 +4,9 @@
# installation without a virtualenv succeeds
pip:
name: pip==18.1
+- name: Setup remote constraints
+ include_tasks: setup-remote-constraints.yml
- name: Install Paramiko for Python 3 on FreeBSD 11
pip: # no py36-paramiko package exists for FreeBSD 11
name: paramiko
+ extra_args: "-c {{ remote_constraints }}"
diff --git a/test/integration/targets/setup_paramiko/install-RedHat-8-python-3.yml b/test/integration/targets/setup_paramiko/install-RedHat-8-python-3.yml
index dbc0f65c..19fd3f63 100644
--- a/test/integration/targets/setup_paramiko/install-RedHat-8-python-3.yml
+++ b/test/integration/targets/setup_paramiko/install-RedHat-8-python-3.yml
@@ -1,3 +1,6 @@
+- name: Setup remote constraints
+ include_tasks: setup-remote-constraints.yml
- name: Install Paramiko for Python 3 on RHEL 8
pip: # no python3-paramiko package exists for RHEL 8
name: paramiko
+ extra_args: "-c {{ remote_constraints }}"
diff --git a/test/integration/targets/setup_paramiko/setup-remote-constraints.yml b/test/integration/targets/setup_paramiko/setup-remote-constraints.yml
new file mode 100644
index 00000000..a86d4777
--- /dev/null
+++ b/test/integration/targets/setup_paramiko/setup-remote-constraints.yml
@@ -0,0 +1,12 @@
+- name: Setup remote temporary directory
+ include_role:
+ name: setup_remote_tmp_dir
+
+- name: Record constraints.txt path on remote host
+ set_fact:
+ remote_constraints: "{{ remote_tmp_dir }}/constraints.txt"
+
+- name: Copy constraints.txt to remote host
+ copy:
+ src: "constraints.txt"
+ dest: "{{ remote_constraints }}"
diff --git a/test/integration/targets/setup_paramiko/setup.sh b/test/integration/targets/setup_paramiko/setup.sh
index 8c4f6f1c..316320c3 100644
--- a/test/integration/targets/setup_paramiko/setup.sh
+++ b/test/integration/targets/setup_paramiko/setup.sh
@@ -5,5 +5,5 @@ set -eux
export ANSIBLE_TEST_PREFER_VENV=1
source virtualenv.sh # for pip installs, if needed, otherwise unused
-ansible-playbook ../setup_paramiko/install.yml -i ../setup_paramiko/inventory "$@"
+ANSIBLE_ROLES_PATH=../ ansible-playbook ../setup_paramiko/install.yml -i ../setup_paramiko/inventory "$@"
trap 'ansible-playbook ../setup_paramiko/uninstall.yml -i ../setup_paramiko/inventory "$@"' EXIT
diff --git a/test/integration/targets/setup_rpm_repo/defaults/main.yml b/test/integration/targets/setup_rpm_repo/defaults/main.yml
index e69de29b..19c033b9 100644
--- a/test/integration/targets/setup_rpm_repo/defaults/main.yml
+++ b/test/integration/targets/setup_rpm_repo/defaults/main.yml
@@ -0,0 +1 @@
+install_repos: yes
diff --git a/test/integration/targets/setup_rpm_repo/handlers/main.yml b/test/integration/targets/setup_rpm_repo/handlers/main.yml
new file mode 100644
index 00000000..a0af3c92
--- /dev/null
+++ b/test/integration/targets/setup_rpm_repo/handlers/main.yml
@@ -0,0 +1,5 @@
+- name: remove repos
+ yum_repository:
+ state: absent
+ name: "{{ item }}"
+ loop: "{{ repos }}"
diff --git a/test/integration/targets/setup_rpm_repo/library/create_repo.py b/test/integration/targets/setup_rpm_repo/library/create_repo.py
new file mode 100644
index 00000000..6dc1e457
--- /dev/null
+++ b/test/integration/targets/setup_rpm_repo/library/create_repo.py
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+import sys
+import tempfile
+
+from collections import namedtuple
+
+from ansible.module_utils.basic import AnsibleModule
+
+try:
+ from rpmfluff import SimpleRpmBuild
+ from rpmfluff import YumRepoBuild
+except ImportError:
+ from rpmfluff.rpmbuild import SimpleRpmBuild
+ from rpmfluff.yumrepobuild import YumRepoBuild
+
+try:
+ from rpmfluff import can_use_rpm_weak_deps
+except ImportError:
+ try:
+ from rpmfluff.utils import can_use_rpm_weak_deps
+ except ImportError:
+ can_use_rpm_weak_deps = None
+
+RPM = namedtuple('RPM', ['name', 'version', 'release', 'epoch', 'recommends'])
+
+
+SPECS = [
+ RPM('dinginessentail', '1.0', '1', None, None),
+ RPM('dinginessentail', '1.0', '2', '1', None),
+ RPM('dinginessentail', '1.1', '1', '1', None),
+ RPM('dinginessentail-olive', '1.0', '1', None, None),
+ RPM('dinginessentail-olive', '1.1', '1', None, None),
+ RPM('landsidescalping', '1.0', '1', None, None),
+ RPM('landsidescalping', '1.1', '1', None, None),
+ RPM('dinginessentail-with-weak-dep', '1.0', '1', None, ['dinginessentail-weak-dep']),
+ RPM('dinginessentail-weak-dep', '1.0', '1', None, None),
+]
+
+
+def create_repo(arch='x86_64'):
+ pkgs = []
+ for spec in SPECS:
+ pkg = SimpleRpmBuild(spec.name, spec.version, spec.release, [arch])
+ pkg.epoch = spec.epoch
+
+ if spec.recommends:
+ # Skip packages that require weak deps but an older version of RPM is being used
+ if not can_use_rpm_weak_deps or not can_use_rpm_weak_deps():
+ continue
+
+ for recommend in spec.recommends:
+ pkg.add_recommends(recommend)
+
+ pkgs.append(pkg)
+
+ repo = YumRepoBuild(pkgs)
+ repo.make(arch)
+
+ for pkg in pkgs:
+ pkg.clean()
+
+ return repo.repoDir
+
+
+def main():
+ module = AnsibleModule(
+ argument_spec={
+ 'arch': {'required': True},
+ 'tempdir': {'type': 'path'},
+ }
+ )
+
+ arch = module.params['arch']
+ tempdir = module.params['tempdir']
+
+ # Save current temp dir so we can set it back later
+ original_tempdir = tempfile.tempdir
+ tempfile.tempdir = tempdir
+
+ try:
+ repo_dir = create_repo(arch)
+ finally:
+ tempfile.tempdir = original_tempdir
+
+ module.exit_json(repo_dir=repo_dir, tmpfile=tempfile.gettempdir())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/integration/targets/setup_rpm_repo/meta/main.yml b/test/integration/targets/setup_rpm_repo/meta/main.yml
new file mode 100644
index 00000000..1810d4be
--- /dev/null
+++ b/test/integration/targets/setup_rpm_repo/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_remote_tmp_dir
diff --git a/test/integration/targets/setup_rpm_repo/tasks/main.yml b/test/integration/targets/setup_rpm_repo/tasks/main.yml
index 14addf79..a6766f20 100644
--- a/test/integration/targets/setup_rpm_repo/tasks/main.yml
+++ b/test/integration/targets/setup_rpm_repo/tasks/main.yml
@@ -31,18 +31,22 @@
- ansible_facts.distribution in ['RedHat', 'CentOS']
- ansible_facts.distribution_major_version is version('8', '>=')
- - name: Copy script for creating a repo
- copy:
- src: create-repo.py
- dest: /tmp/create-repo.py
- mode: 0755
+ - set_fact:
+ repos:
+ - "fake-{{ ansible_architecture }}"
+ - "fake-i686"
+ - "fake-ppc64"
+ changed_when: yes
+ notify: remove repos
- name: Create RPMs and put them into a repo
- shell: "{{ansible_python_interpreter}} /tmp/create-repo.py {{ ansible_architecture }}"
+ create_repo:
+ arch: "{{ ansible_architecture }}"
+ tempdir: "{{ remote_tmp_dir }}"
register: repo
- set_fact:
- repodir: "{{ repo.stdout_lines[-1] }}"
+ repodir: "{{ repo.repo_dir }}"
- name: Install the repo
yum_repository:
@@ -50,6 +54,7 @@
description: "fake-{{ ansible_architecture }}"
baseurl: "file://{{ repodir }}"
gpgcheck: no
+ when: install_repos | bool
- name: Copy comps.xml file
copy:
@@ -61,11 +66,13 @@
command: createrepo -g {{ repodir_comps.dest | quote }} {{ repodir | quote }}
- name: Create RPMs and put them into a repo (i686)
- shell: "{{ansible_python_interpreter}} /tmp/create-repo.py i686"
+ create_repo:
+ arch: i686
+ tempdir: "{{ remote_tmp_dir }}"
register: repo_i686
- set_fact:
- repodir_i686: "{{ repo_i686.stdout_lines[-1] }}"
+ repodir_i686: "{{ repo_i686.repo_dir }}"
- name: Install the repo (i686)
yum_repository:
@@ -73,13 +80,16 @@
description: "fake-i686"
baseurl: "file://{{ repodir_i686 }}"
gpgcheck: no
+ when: install_repos | bool
- name: Create RPMs and put them into a repo (ppc64)
- shell: "{{ansible_python_interpreter}} /tmp/create-repo.py ppc64"
+ create_repo:
+ arch: ppc64
+ tempdir: "{{ remote_tmp_dir }}"
register: repo_ppc64
- set_fact:
- repodir_ppc64: "{{ repo_ppc64.stdout_lines[-1] }}"
+ repodir_ppc64: "{{ repo_ppc64.repo_dir }}"
- name: Install the repo (ppc64)
yum_repository:
@@ -87,11 +97,6 @@
description: "fake-ppc64"
baseurl: "file://{{ repodir_ppc64 }}"
gpgcheck: no
-
- - set_fact:
- repos:
- - "fake-{{ ansible_architecture }}"
- - "fake-i686"
- - "fake-ppc64"
+ when: install_repos | bool
when: ansible_distribution in ['RedHat', 'CentOS', 'ScientificLinux', 'Fedora']
diff --git a/test/integration/targets/setup_rpm_repo/vars/Fedora.yml b/test/integration/targets/setup_rpm_repo/vars/Fedora.yml
index 6e8fdaaa..004f42bc 100644
--- a/test/integration/targets/setup_rpm_repo/vars/Fedora.yml
+++ b/test/integration/targets/setup_rpm_repo/vars/Fedora.yml
@@ -1,3 +1,4 @@
rpm_repo_packages:
- "{{ 'python' ~ rpm_repo_python_major_version ~ '-rpmfluff' }}"
- createrepo
+ - rpm-build
diff --git a/test/integration/targets/setup_rpm_repo/vars/RedHat-6.yml b/test/integration/targets/setup_rpm_repo/vars/RedHat-6.yml
index 69615d2c..6edee17d 100644
--- a/test/integration/targets/setup_rpm_repo/vars/RedHat-6.yml
+++ b/test/integration/targets/setup_rpm_repo/vars/RedHat-6.yml
@@ -1,4 +1,5 @@
rpm_repo_packages:
+ - rpm-build
- python-rpmfluff
- createrepo_c
- createrepo
diff --git a/test/integration/targets/setup_rpm_repo/vars/RedHat-7.yml b/test/integration/targets/setup_rpm_repo/vars/RedHat-7.yml
index 69615d2c..6edee17d 100644
--- a/test/integration/targets/setup_rpm_repo/vars/RedHat-7.yml
+++ b/test/integration/targets/setup_rpm_repo/vars/RedHat-7.yml
@@ -1,4 +1,5 @@
rpm_repo_packages:
+ - rpm-build
- python-rpmfluff
- createrepo_c
- createrepo
diff --git a/test/integration/targets/subversion/vars/Ubuntu-20.yml b/test/integration/targets/subversion/vars/Ubuntu-20.yml
new file mode 100644
index 00000000..dfe131b0
--- /dev/null
+++ b/test/integration/targets/subversion/vars/Ubuntu-20.yml
@@ -0,0 +1,6 @@
+---
+subversion_packages:
+- subversion
+- libapache2-mod-svn
+apache_user: www-data
+apache_group: www-data
diff --git a/test/integration/targets/template/6653.yml b/test/integration/targets/template/6653.yml
new file mode 100644
index 00000000..970478f9
--- /dev/null
+++ b/test/integration/targets/template/6653.yml
@@ -0,0 +1,10 @@
+- hosts: localhost
+ gather_facts: no
+ vars:
+ mylist:
+ - alpha
+ - bravo
+ tasks:
+ - name: Should not fail on undefined variable
+ set_fact:
+ template_result: "{{ lookup('template', '6653.j2') }}"
diff --git a/test/integration/targets/template/72262.yml b/test/integration/targets/template/72262.yml
new file mode 100644
index 00000000..33c610d4
--- /dev/null
+++ b/test/integration/targets/template/72262.yml
@@ -0,0 +1,6 @@
+- hosts: localhost
+ gather_facts: no
+ tasks:
+ - name: Should not fail on undefined variable
+ set_fact:
+ template_result: "{{ lookup('template', '72262.j2') }}"
diff --git a/test/integration/targets/template/72615.yml b/test/integration/targets/template/72615.yml
new file mode 100644
index 00000000..9a6eb941
--- /dev/null
+++ b/test/integration/targets/template/72615.yml
@@ -0,0 +1,26 @@
+- hosts: localhost
+ gather_facts: no
+ vars:
+ foo: "top-level-foo"
+ tasks:
+ - set_fact:
+ template_result: "{{ lookup('template', '72615.j2') }}"
+
+ - assert:
+ that:
+ - "'template-level-bar' in template_result"
+ - "'template-nested-level-bar' in template_result"
+
+ - assert:
+ that:
+ - "'top-level-foo' not in template_result"
+ - "'template-level-foo' in template_result"
+ - "'template-nested-level-foo' in template_result"
+ when: lookup('pipe', ansible_python_interpreter ~ ' -c "import jinja2; print(jinja2.__version__)"') is version('2.9', '>=')
+
+ - assert:
+ that:
+ - "'top-level-foo' in template_result"
+ - "'template-level-foo' not in template_result"
+ - "'template-nested-level-foo' not in template_result"
+ when: lookup('pipe', ansible_python_interpreter ~ ' -c "import jinja2; print(jinja2.__version__)"') is version('2.9', '<')
diff --git a/test/integration/targets/template/runme.sh b/test/integration/targets/template/runme.sh
index 8e21352d..cb00df75 100755
--- a/test/integration/targets/template/runme.sh
+++ b/test/integration/targets/template/runme.sh
@@ -25,3 +25,12 @@ ansible-playbook unused_vars_include.yml -v "$@"
# https://github.com/ansible/ansible/issues/55152
ansible-playbook undefined_var_info.yml -v "$@"
+
+# https://github.com/ansible/ansible/issues/72615
+ansible-playbook 72615.yml -v "$@"
+
+# https://github.com/ansible/ansible/issues/6653
+ansible-playbook 6653.yml -v "$@"
+
+# https://github.com/ansible/ansible/issues/72262
+ansible-playbook 72262.yml -v "$@"
diff --git a/test/integration/targets/template/templates/6653-include.j2 b/test/integration/targets/template/templates/6653-include.j2
new file mode 100644
index 00000000..26443b15
--- /dev/null
+++ b/test/integration/targets/template/templates/6653-include.j2
@@ -0,0 +1 @@
+{{ x }}
diff --git a/test/integration/targets/template/templates/6653.j2 b/test/integration/targets/template/templates/6653.j2
new file mode 100644
index 00000000..8026a79b
--- /dev/null
+++ b/test/integration/targets/template/templates/6653.j2
@@ -0,0 +1,4 @@
+{% for x in mylist %}
+{{ x }}
+{% include '6653-include.j2' with context %}
+{% endfor %}
diff --git a/test/integration/targets/template/templates/72262-included.j2 b/test/integration/targets/template/templates/72262-included.j2
new file mode 100644
index 00000000..35700cb8
--- /dev/null
+++ b/test/integration/targets/template/templates/72262-included.j2
@@ -0,0 +1 @@
+{{ vars.test }}
diff --git a/test/integration/targets/template/templates/72262-vars.j2 b/test/integration/targets/template/templates/72262-vars.j2
new file mode 100644
index 00000000..6ef92208
--- /dev/null
+++ b/test/integration/targets/template/templates/72262-vars.j2
@@ -0,0 +1 @@
+{% set test = "I'm test variable" %}
diff --git a/test/integration/targets/template/templates/72262.j2 b/test/integration/targets/template/templates/72262.j2
new file mode 100644
index 00000000..b72be0d1
--- /dev/null
+++ b/test/integration/targets/template/templates/72262.j2
@@ -0,0 +1,3 @@
+{% import '72262-vars.j2' as vars with context %}
+{% macro included() %}{% include '72262-included.j2' %}{% endmacro %}
+{{ included()|indent }}
diff --git a/test/integration/targets/template/templates/72615-macro-nested.j2 b/test/integration/targets/template/templates/72615-macro-nested.j2
new file mode 100644
index 00000000..c47a4992
--- /dev/null
+++ b/test/integration/targets/template/templates/72615-macro-nested.j2
@@ -0,0 +1,4 @@
+{% macro print_context_vars_nested(value) %}
+foo: {{ foo }}
+bar: {{ value }}
+{% endmacro %}
diff --git a/test/integration/targets/template/templates/72615-macro.j2 b/test/integration/targets/template/templates/72615-macro.j2
new file mode 100644
index 00000000..328c271c
--- /dev/null
+++ b/test/integration/targets/template/templates/72615-macro.j2
@@ -0,0 +1,8 @@
+{% macro print_context_vars(value) %}
+{{ foo }}
+{{ value }}
+{% set foo = "template-nested-level-foo" %}
+{% set bar = "template-nested-level-bar" %}
+{% from '72615-macro-nested.j2' import print_context_vars_nested with context %}
+{{ print_context_vars_nested(bar) }}
+{% endmacro %}
diff --git a/test/integration/targets/template/templates/72615.j2 b/test/integration/targets/template/templates/72615.j2
new file mode 100644
index 00000000..b79f88e2
--- /dev/null
+++ b/test/integration/targets/template/templates/72615.j2
@@ -0,0 +1,4 @@
+{% set foo = "template-level-foo" %}
+{% set bar = "template-level-bar" %}
+{% from '72615-macro.j2' import print_context_vars with context %}
+{{ print_context_vars(bar) }}
diff --git a/test/integration/targets/unsafe_writes/aliases b/test/integration/targets/unsafe_writes/aliases
new file mode 100644
index 00000000..4fb7a116
--- /dev/null
+++ b/test/integration/targets/unsafe_writes/aliases
@@ -0,0 +1,6 @@
+needs/root
+skip/freebsd
+skip/osx
+skip/macos
+skip/aix
+shippable/posix/group3
diff --git a/test/integration/targets/unsafe_writes/basic.yml b/test/integration/targets/unsafe_writes/basic.yml
new file mode 100644
index 00000000..b173c7f8
--- /dev/null
+++ b/test/integration/targets/unsafe_writes/basic.yml
@@ -0,0 +1,53 @@
+- hosts: testhost
+ gather_facts: false
+ vars:
+ testudir: '{{output_dir}}/unsafe_writes_test'
+ testufile: '{{testudir}}/unreplacablefile.txt'
+ tasks:
+ - name: test unsafe_writes on immutable dir (file cannot be atomically replaced)
+ block:
+ - name: create target dir
+ file: path={{testudir}} state=directory
+ - name: setup test file
+ copy: content=ORIGINAL dest={{testufile}}
+ - name: make target dir immutable (cannot write to file w/o unsafe_writes)
+ file: path={{testudir}} state=directory attributes="+i"
+ become: yes
+ ignore_errors: true
+ register: madeimmutable
+
+ - name: only run if immutable dir command worked, some of our test systems don't allow for it
+ when: madeimmutable is success
+ block:
+ - name: test this is actually immmutable working as we expect
+ file: path={{testufile}} state=absent
+ register: breakimmutable
+ ignore_errors: True
+
+ - name: only run if reallyh immutable dir
+ when: breakimmutable is failed
+ block:
+ - name: test overwriting file w/o unsafe
+ copy: content=NEW dest={{testufile}} unsafe_writes=False
+ ignore_errors: true
+ register: copy_without
+
+ - name: ensure we properly failed
+ assert:
+ that:
+ - copy_without is failed
+
+ - name: test overwriting file with unsafe
+ copy: content=NEW dest={{testufile}} unsafe_writes=True
+ register: copy_with
+
+ - name: ensure we properly changed
+ assert:
+ that:
+ - copy_with is changed
+
+ always:
+ - name: remove immutable flag from dir to prevent issues with cleanup
+ file: path={{testudir}} state=directory attributes="-i"
+ ignore_errors: true
+ become: yes
diff --git a/test/integration/targets/unsafe_writes/runme.sh b/test/integration/targets/unsafe_writes/runme.sh
new file mode 100755
index 00000000..5c37f727
--- /dev/null
+++ b/test/integration/targets/unsafe_writes/runme.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -eux
+
+ansible-playbook basic.yml -i ../../inventory -e "output_dir=${OUTPUT_DIR}" "$@"
diff --git a/test/integration/targets/unvault/aliases b/test/integration/targets/unvault/aliases
new file mode 100644
index 00000000..765b70da
--- /dev/null
+++ b/test/integration/targets/unvault/aliases
@@ -0,0 +1 @@
+shippable/posix/group2
diff --git a/test/integration/targets/unvault/main.yml b/test/integration/targets/unvault/main.yml
new file mode 100644
index 00000000..a0f97b4b
--- /dev/null
+++ b/test/integration/targets/unvault/main.yml
@@ -0,0 +1,9 @@
+- hosts: localhost
+ tasks:
+ - set_fact:
+ unvaulted: "{{ lookup('unvault', 'vault') }}"
+ - debug:
+ msg: "{{ unvaulted }}"
+ - assert:
+ that:
+ - "unvaulted == 'foo: bar\n'"
diff --git a/test/integration/targets/unvault/password b/test/integration/targets/unvault/password
new file mode 100644
index 00000000..d97c5ead
--- /dev/null
+++ b/test/integration/targets/unvault/password
@@ -0,0 +1 @@
+secret
diff --git a/test/integration/targets/unvault/runme.sh b/test/integration/targets/unvault/runme.sh
new file mode 100755
index 00000000..df4585e3
--- /dev/null
+++ b/test/integration/targets/unvault/runme.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -eux
+
+
+ansible-playbook --vault-password-file password main.yml
diff --git a/test/integration/targets/unvault/vault b/test/integration/targets/unvault/vault
new file mode 100644
index 00000000..828d3696
--- /dev/null
+++ b/test/integration/targets/unvault/vault
@@ -0,0 +1,6 @@
+$ANSIBLE_VAULT;1.1;AES256
+33386337343963393533343039333563323733646137636162346266643134323539396237646333
+3663363965336335663161656236616532346363303832310a393264356663393330346137613239
+34633765333936633466353932663166343531616230326161383365323966386434366431353839
+3838623233373231340a303166666433613439303464393661363365643765666137393137653138
+3631
diff --git a/test/integration/targets/yum_repository/defaults/main.yml b/test/integration/targets/yum_repository/defaults/main.yml
new file mode 100644
index 00000000..4c1fbc65
--- /dev/null
+++ b/test/integration/targets/yum_repository/defaults/main.yml
@@ -0,0 +1,5 @@
+yum_repository_test_package: dinginessentail
+yum_repository_test_repo:
+ name: fakerepo
+ description: Fake Repo
+ baseurl: "file://{{ repodir }}"
diff --git a/test/integration/targets/yum_repository/handlers/main.yml b/test/integration/targets/yum_repository/handlers/main.yml
new file mode 100644
index 00000000..f96c2391
--- /dev/null
+++ b/test/integration/targets/yum_repository/handlers/main.yml
@@ -0,0 +1,4 @@
+- name: remove listtest repo
+ yum_repository:
+ name: listtest
+ state: absent
diff --git a/test/integration/targets/yum_repository/meta/main.yml b/test/integration/targets/yum_repository/meta/main.yml
new file mode 100644
index 00000000..56539a4f
--- /dev/null
+++ b/test/integration/targets/yum_repository/meta/main.yml
@@ -0,0 +1,4 @@
+dependencies:
+ - role: setup_rpm_repo
+ vars:
+ install_repos: no
diff --git a/test/integration/targets/yum_repository/tasks/main.yml b/test/integration/targets/yum_repository/tasks/main.yml
index 3884d46c..0ff0bcff 100644
--- a/test/integration/targets/yum_repository/tasks/main.yml
+++ b/test/integration/targets/yum_repository/tasks/main.yml
@@ -1,17 +1,6 @@
- name: Run tests
when: ansible_facts.distribution in ['CentOS', 'Fedora']
block:
- - name: Include distribution specific variables
- include_vars: "{{ lookup('first_found', params) }}"
- vars:
- params:
- files:
- - "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
- - "{{ ansible_facts.distribution }}.yml"
- - default.yml
- paths:
- - vars
-
- name: ensure {{ yum_repository_test_package }} is uninstalled to begin with
action: "{{ ansible_facts.pkg_mgr }}"
args:
@@ -181,6 +170,7 @@
includepkgs:
- ccc
- ddd
+ notify: remove listtest repo
- name: Assert that lists were properly inserted
assert:
@@ -195,24 +185,3 @@
repofile: "{{ lookup('file', '/etc/yum.repos.d/listtest.repo') }}"
url_hostname: "{{ yum_repository_test_repo.baseurl | urlsplit('hostname') }}"
url_hostname2: "{{ url_hostname | replace('download[0-9]?\\.', 'download2\\.', 1) }}"
-
- - name: CLEANUP | Remove list test repo
- yum_repository:
- name: listtest
- state: absent
-
- - name: CLEANUP | Remove {{ yum_repository_test_repo.name }}
- yum_repository:
- name: "{{ yum_repository_test_repo.name }}"
- state: absent
-
- - name: CLEANUP | Enable EPEL
- yum_repository:
- name: epel
- state: present
- description: "{{ yum_repository_epel.description | default(omit) }}"
- metalink: "{{ yum_repository_epel.metalink | default(omit) }}"
- mirrorlist: "{{ yum_repository_epel.mirrorlist | default(omit) }}"
- gpgkey: "{{ yum_repository_epel.gpgkey }}"
- gpgcheck: yes
- when: ansible_facts.distribution == 'CentOS'
diff --git a/test/integration/targets/yum_repository/vars/CentOS-8.yml b/test/integration/targets/yum_repository/vars/CentOS-8.yml
deleted file mode 100644
index 22d4d13e..00000000
--- a/test/integration/targets/yum_repository/vars/CentOS-8.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-yum_repository_test_package: sshpass
-yum_repository_test_repo:
- name: epel
- description: EPEL yum repo
- baseurl: https://download.fedoraproject.org/pub/epel/$releasever/Everything/$basearch
-
-yum_repository_epel:
- description: Extra Packages for Enterprise Linux $releasever - $basearch
- metalink: https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch&infra=$infra&content=$contentdir
- gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-{{ ansible_facts.distribution_major_version }}
diff --git a/test/integration/targets/yum_repository/vars/CentOS.yml b/test/integration/targets/yum_repository/vars/CentOS.yml
deleted file mode 100644
index db9947d5..00000000
--- a/test/integration/targets/yum_repository/vars/CentOS.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-yum_repository_test_package: sl
-yum_repository_test_repo:
- name: epel
- description: EPEL yum repo
- baseurl: https://archives.fedoraproject.org/pub/archive/epel/{{ ansible_facts.distribution_major_version }}/$basearch
-
-yum_repository_epel:
- description: Extra Packages for Enterprise Linux {{ ansible_facts.distribution_major_version }} - $basearch
- mirrorlist: https://mirrors.fedoraproject.org/metalink?repo=epel-{{ ansible_facts.distribution_major_version }}&arch=$basearch
- gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-{{ ansible_facts.distribution_major_version }}
diff --git a/test/integration/targets/yum_repository/vars/Fedora.yml b/test/integration/targets/yum_repository/vars/Fedora.yml
deleted file mode 100644
index 8c37eaa1..00000000
--- a/test/integration/targets/yum_repository/vars/Fedora.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-yum_repository_test_package: libbdplus
-yum_repository_test_repo:
- name: rpmfusion-free
- description: RPM Fusion for Fedora {{ ansible_distribution_major_version }} - Free
- baseurl: http://download1.rpmfusion.org/free/fedora/releases/{{ ansible_distribution_major_version }}/Everything/{{ ansible_architecture }}/os/
diff --git a/test/lib/ansible_test/_data/completion/docker.txt b/test/lib/ansible_test/_data/completion/docker.txt
index 978ba703..3e4566dc 100644
--- a/test/lib/ansible_test/_data/completion/docker.txt
+++ b/test/lib/ansible_test/_data/completion/docker.txt
@@ -10,3 +10,4 @@ opensuse15py2 name=quay.io/ansible/opensuse15py2-test-container:1.21.0 python=2.
opensuse15 name=quay.io/ansible/opensuse15-test-container:1.21.0 python=3.6
ubuntu1604 name=quay.io/ansible/ubuntu1604-test-container:1.21.0 python=2.7 seccomp=unconfined
ubuntu1804 name=quay.io/ansible/ubuntu1804-test-container:1.21.0 python=3.6 seccomp=unconfined
+ubuntu2004 name=quay.io/ansible/ubuntu2004-test-container:1.21.0 python=3.8 seccomp=unconfined
diff --git a/test/lib/ansible_test/_data/requirements/constraints.txt b/test/lib/ansible_test/_data/requirements/constraints.txt
index e043bc52..81ee480c 100644
--- a/test/lib/ansible_test/_data/requirements/constraints.txt
+++ b/test/lib/ansible_test/_data/requirements/constraints.txt
@@ -46,7 +46,7 @@ setuptools < 37 ; python_version == '2.6' # setuptools 37 and later require pyth
setuptools < 45 ; python_version == '2.7' # setuptools 45 and later require python 3.5 or later
# freeze antsibull-changelog for consistent test results
-antsibull-changelog == 0.7.0
+antsibull-changelog == 0.9.0
# Make sure we have a new enough antsibull for the CLI args we use
antsibull >= 0.21.0
diff --git a/test/lib/ansible_test/_data/sanity/pylint/plugins/deprecated.py b/test/lib/ansible_test/_data/sanity/pylint/plugins/deprecated.py
index c88e5e5a..c06059c4 100644
--- a/test/lib/ansible_test/_data/sanity/pylint/plugins/deprecated.py
+++ b/test/lib/ansible_test/_data/sanity/pylint/plugins/deprecated.py
@@ -233,7 +233,7 @@ class AnsibleDeprecatedChecker(BaseChecker):
this_collection = collection_name == (self.collection_name or 'ansible.builtin')
if not this_collection:
self.add_message('wrong-collection-deprecated', node=node, args=(collection_name,))
- else:
+ elif self.collection_name is not None:
self.add_message('ansible-deprecated-no-collection-name', node=node)
if date:
diff --git a/test/lib/ansible_test/_data/sanity/validate-modules/validate_modules/main.py b/test/lib/ansible_test/_data/sanity/validate-modules/validate_modules/main.py
index 79614211..e7379288 100644
--- a/test/lib/ansible_test/_data/sanity/validate-modules/validate_modules/main.py
+++ b/test/lib/ansible_test/_data/sanity/validate-modules/validate_modules/main.py
@@ -666,6 +666,8 @@ class ModuleValidator(Validator):
found_try_except_import = True
if isinstance(grandchild, ast.Assign):
for target in grandchild.targets:
+ if not isinstance(target, ast.Name):
+ continue
if target.id.lower().startswith('has_'):
found_has = True
if found_try_except_import and not found_has:
diff --git a/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py b/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py
index 933debe7..c9cdc19c 100644
--- a/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py
+++ b/test/lib/ansible_test/_data/sanity/yamllint/yamllinter.py
@@ -180,7 +180,7 @@ class YamlChecker:
def check_assignment(statement, doc_types=None):
"""Check the given statement for a documentation assignment."""
for target in statement.targets:
- if isinstance(target, ast.Tuple):
+ if not isinstance(target, ast.Name):
continue
if doc_types and target.id not in doc_types:
diff --git a/test/lib/ansible_test/_data/setup/remote.sh b/test/lib/ansible_test/_data/setup/remote.sh
index 93dead5d..35167e21 100644
--- a/test/lib/ansible_test/_data/setup/remote.sh
+++ b/test/lib/ansible_test/_data/setup/remote.sh
@@ -10,7 +10,12 @@ cd ~/
install_pip () {
if ! "${python_interpreter}" -m pip.__main__ --version --disable-pip-version-check 2>/dev/null; then
- curl --silent --show-error https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py
+ case "${python_version}" in
+ *)
+ pip_bootstrap_url="https://ansible-ci-files.s3.amazonaws.com/ansible-test/get-pip-20.3.4.py"
+ ;;
+ esac
+ curl --silent --show-error "${pip_bootstrap_url}" -o /tmp/get-pip.py
"${python_interpreter}" /tmp/get-pip.py --disable-pip-version-check --quiet
rm /tmp/get-pip.py
fi
diff --git a/test/lib/ansible_test/_internal/ansible_util.py b/test/lib/ansible_test/_internal/ansible_util.py
index 5e9b5d7d..c1cf8552 100644
--- a/test/lib/ansible_test/_internal/ansible_util.py
+++ b/test/lib/ansible_test/_internal/ansible_util.py
@@ -11,6 +11,10 @@ from .constants import (
SOFT_RLIMIT_NOFILE,
)
+from .io import (
+ write_text_file,
+)
+
from .util import (
common_environment,
display,
@@ -20,6 +24,7 @@ from .util import (
ANSIBLE_TEST_DATA_ROOT,
ANSIBLE_BIN_PATH,
ANSIBLE_SOURCE_ROOT,
+ get_ansible_version,
)
from .util_common import (
@@ -74,7 +79,7 @@ def ansible_environment(args, color=True, ansible_config=None):
ANSIBLE_CONFIG=ansible_config,
ANSIBLE_LIBRARY='/dev/null',
ANSIBLE_DEVEL_WARNING='false', # Don't show warnings that CI is running devel
- PYTHONPATH=get_ansible_python_path(),
+ PYTHONPATH=get_ansible_python_path(args),
PAGER='/bin/cat',
PATH=path,
# give TQM worker processes time to report code coverage results
@@ -170,28 +175,59 @@ def configure_plugin_paths(args): # type: (CommonConfig) -> t.Dict[str, str]
return env
-def get_ansible_python_path(): # type: () -> str
+def get_ansible_python_path(args): # type: (CommonConfig) -> str
"""
Return a directory usable for PYTHONPATH, containing only the ansible package.
If a temporary directory is required, it will be cached for the lifetime of the process and cleaned up at exit.
"""
- if ANSIBLE_SOURCE_ROOT:
- # when running from source there is no need for a temporary directory to isolate the ansible package
- return os.path.dirname(ANSIBLE_LIB_ROOT)
-
try:
return get_ansible_python_path.python_path
except AttributeError:
pass
- python_path = create_temp_dir(prefix='ansible-test-')
- get_ansible_python_path.python_path = python_path
+ if ANSIBLE_SOURCE_ROOT:
+ # when running from source there is no need for a temporary directory to isolate the ansible package
+ python_path = os.path.dirname(ANSIBLE_LIB_ROOT)
+ else:
+ # when not running from source the installed directory is unsafe to add to PYTHONPATH
+ # doing so would expose many unwanted packages on sys.path
+ # instead a temporary directory is created which contains only ansible using a symlink
+ python_path = create_temp_dir(prefix='ansible-test-')
+
+ os.symlink(ANSIBLE_LIB_ROOT, os.path.join(python_path, 'ansible'))
+
+ if not args.explain:
+ generate_egg_info(python_path)
- os.symlink(ANSIBLE_LIB_ROOT, os.path.join(python_path, 'ansible'))
+ get_ansible_python_path.python_path = python_path
return python_path
+def generate_egg_info(path): # type: (str) -> None
+ """Generate an egg-info in the specified base directory."""
+ # minimal PKG-INFO stub following the format defined in PEP 241
+ # required for older setuptools versions to avoid a traceback when importing pkg_resources from packages like cryptography
+ # newer setuptools versions are happy with an empty directory
+ # including a stub here means we don't need to locate the existing file or have setup.py generate it when running from source
+ pkg_info = '''
+Metadata-Version: 1.0
+Name: ansible
+Version: %s
+Platform: UNKNOWN
+Summary: Radically simple IT automation
+Author-email: info@ansible.com
+License: GPLv3+
+''' % get_ansible_version()
+
+ pkg_info_path = os.path.join(path, 'ansible_base.egg-info', 'PKG-INFO')
+
+ if os.path.exists(pkg_info_path):
+ return
+
+ write_text_file(pkg_info_path, pkg_info.lstrip(), create_directories=True)
+
+
def check_pyyaml(args, version, required=True, quiet=False):
"""
:type args: EnvironmentConfig
diff --git a/test/lib/ansible_test/_internal/docker_util.py b/test/lib/ansible_test/_internal/docker_util.py
index 54007d1c..1b47364d 100644
--- a/test/lib/ansible_test/_internal/docker_util.py
+++ b/test/lib/ansible_test/_internal/docker_util.py
@@ -271,11 +271,21 @@ def docker_images(args, image):
stdout, _dummy = docker_command(args, ['images', image, '--format', '{{json .}}'], capture=True, always=True)
except SubprocessError as ex:
if 'no such image' in ex.stderr:
- stdout = '' # podman does not handle this gracefully, exits 125
+ return [] # podman does not handle this gracefully, exits 125
+
+ if 'function "json" not defined' in ex.stderr:
+ # podman > 2 && < 2.2.0 breaks with --format {{json .}}, and requires --format json
+ # So we try this as a fallback. If it fails again, we just raise the exception and bail.
+ stdout, _dummy = docker_command(args, ['images', image, '--format', 'json'], capture=True, always=True)
else:
raise ex
- results = [json.loads(line) for line in stdout.splitlines()]
- return results
+
+ if stdout.startswith('['):
+ # modern podman outputs a pretty-printed json list. Just load the whole thing.
+ return json.loads(stdout)
+
+ # docker outputs one json object per line (jsonl)
+ return [json.loads(line) for line in stdout.splitlines()]
def docker_rm(args, container_id):
diff --git a/test/lib/ansible_test/_internal/executor.py b/test/lib/ansible_test/_internal/executor.py
index 881439ef..4f613049 100644
--- a/test/lib/ansible_test/_internal/executor.py
+++ b/test/lib/ansible_test/_internal/executor.py
@@ -252,7 +252,9 @@ def get_cryptography_requirement(args, python_version): # type: (EnvironmentCon
# see https://cryptography.io/en/latest/changelog.html#v3-2
cryptography = 'cryptography < 3.2'
else:
- cryptography = 'cryptography'
+ # cryptography 3.4+ fails to install on many systems
+ # this is a temporary work-around until a more permanent solution is available
+ cryptography = 'cryptography < 3.4'
else:
# cryptography 2.1+ requires setuptools 18.5+
# see https://github.com/pyca/cryptography/blob/62287ae18383447585606b9d0765c0f1b8a9777c/setup.py#L26
@@ -276,8 +278,6 @@ def install_command_requirements(args, python_version=None, context=None, enable
if args.raw:
return
- generate_egg_info(args)
-
if not args.requirements:
return
@@ -423,46 +423,6 @@ def pip_list(args, pip):
return stdout
-def generate_egg_info(args):
- """
- :type args: EnvironmentConfig
- """
- if args.explain:
- return
-
- ansible_version = get_ansible_version()
-
- # inclusion of the version number in the path is optional
- # see: https://setuptools.readthedocs.io/en/latest/formats.html#filename-embedded-metadata
- egg_info_path = ANSIBLE_LIB_ROOT + '_base-%s.egg-info' % ansible_version
-
- if os.path.exists(egg_info_path):
- return
-
- egg_info_path = ANSIBLE_LIB_ROOT + '_base.egg-info'
-
- if os.path.exists(egg_info_path):
- return
-
- # minimal PKG-INFO stub following the format defined in PEP 241
- # required for older setuptools versions to avoid a traceback when importing pkg_resources from packages like cryptography
- # newer setuptools versions are happy with an empty directory
- # including a stub here means we don't need to locate the existing file or have setup.py generate it when running from source
- pkg_info = '''
-Metadata-Version: 1.0
-Name: ansible
-Version: %s
-Platform: UNKNOWN
-Summary: Radically simple IT automation
-Author-email: info@ansible.com
-License: GPLv3+
-''' % get_ansible_version()
-
- pkg_info_path = os.path.join(egg_info_path, 'PKG-INFO')
-
- write_text_file(pkg_info_path, pkg_info.lstrip(), create_directories=True)
-
-
def generate_pip_install(pip, command, packages=None, constraints=None, use_constraints=True, context=None):
"""
:type pip: list[str]
diff --git a/test/sanity/code-smell/docs-build.py b/test/sanity/code-smell/docs-build.py
index 9b6cbd3f..80eca15f 100755
--- a/test/sanity/code-smell/docs-build.py
+++ b/test/sanity/code-smell/docs-build.py
@@ -4,17 +4,37 @@ __metaclass__ = type
import os
import re
+import shutil
import subprocess
import sys
+import tempfile
def main():
base_dir = os.getcwd() + os.path.sep
docs_dir = os.path.abspath('docs/docsite')
- cmd = ['make', 'base_singlehtmldocs']
- sphinx = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=docs_dir)
- stdout, stderr = sphinx.communicate()
+ # TODO: Remove this temporary hack to constrain 'cryptography' when we have
+ # a better story for dealing with it.
+ tmpfd, tmp = tempfile.mkstemp()
+ requirements_txt = os.path.join(base_dir, 'requirements.txt')
+ shutil.copy2(requirements_txt, tmp)
+ lines = []
+ with open(requirements_txt, 'r') as f:
+ for line in f.readlines():
+ if line.strip() == 'cryptography':
+ line = 'cryptography < 3.4\n'
+ lines.append(line)
+
+ with open(requirements_txt, 'w') as f:
+ f.writelines(lines)
+
+ try:
+ cmd = ['make', 'core_singlehtmldocs']
+ sphinx = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=docs_dir)
+ stdout, stderr = sphinx.communicate()
+ finally:
+ shutil.move(tmp, requirements_txt)
stdout = stdout.decode('utf-8')
stderr = stderr.decode('utf-8')
diff --git a/test/sanity/code-smell/package-data.py b/test/sanity/code-smell/package-data.py
index 822c11d5..ca5f5ef5 100755
--- a/test/sanity/code-smell/package-data.py
+++ b/test/sanity/code-smell/package-data.py
@@ -53,6 +53,9 @@ def assemble_files_to_ship(complete_file_list):
'test/support/README.md',
'.cherry_picker.toml',
'.mailmap',
+ # Generated as part of a build step
+ 'docs/docsite/rst/conf.py',
+ 'docs/docsite/rst/index.rst',
# Possibly should be included
'examples/scripts/uptime.py',
'examples/scripts/my_test.py',
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index f1b926e6..dbbce537 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -4,8 +4,6 @@ docs/docsite/_extensions/pygments_lexer.py future-import-boilerplate
docs/docsite/_extensions/pygments_lexer.py metaclass-boilerplate
docs/docsite/_themes/sphinx_rtd_theme/__init__.py future-import-boilerplate
docs/docsite/_themes/sphinx_rtd_theme/__init__.py metaclass-boilerplate
-docs/docsite/rst/conf.py future-import-boilerplate
-docs/docsite/rst/conf.py metaclass-boilerplate
docs/docsite/rst/dev_guide/testing/sanity/no-smart-quotes.rst no-smart-quotes
examples/play.yml shebang
examples/scripts/my_test.py shebang # example module but not in a normal module location
diff --git a/test/units/ansible_test/test_docker_util.py b/test/units/ansible_test/test_docker_util.py
new file mode 100644
index 00000000..8427f0f2
--- /dev/null
+++ b/test/units/ansible_test/test_docker_util.py
@@ -0,0 +1,131 @@
+# This file is part of Ansible
+# -*- coding: utf-8 -*-
+#
+#
+# Ansible is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Ansible is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Make coding more python3-ish
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import os
+
+import pytest
+from units.compat.mock import call, patch, MagicMock
+
+# docker images quay.io/ansible/centos7-test-container --format '{{json .}}'
+DOCKER_OUTPUT_MULTIPLE = """
+{"Containers":"N/A","CreatedAt":"2020-06-11 17:05:58 -0500 CDT","CreatedSince":"3 months ago","Digest":"\u003cnone\u003e","ID":"b0f914b26cc1","Repository":"quay.io/ansible/centos7-test-container","SharedSize":"N/A","Size":"556MB","Tag":"1.17.0","UniqueSize":"N/A","VirtualSize":"555.6MB"}
+{"Containers":"N/A","CreatedAt":"2020-06-11 17:05:58 -0500 CDT","CreatedSince":"3 months ago","Digest":"\u003cnone\u003e","ID":"b0f914b26cc1","Repository":"quay.io/ansible/centos7-test-container","SharedSize":"N/A","Size":"556MB","Tag":"latest","UniqueSize":"N/A","VirtualSize":"555.6MB"}
+{"Containers":"N/A","CreatedAt":"2019-04-01 19:59:39 -0500 CDT","CreatedSince":"18 months ago","Digest":"\u003cnone\u003e","ID":"dd3d10e03dd3","Repository":"quay.io/ansible/centos7-test-container","SharedSize":"N/A","Size":"678MB","Tag":"1.8.0","UniqueSize":"N/A","VirtualSize":"678MB"}
+""".lstrip() # noqa: E501
+
+PODMAN_OUTPUT = """
+[
+ {
+ "id": "dd3d10e03dd3580de865560c3440c812a33fd7a1fca8ed8e4a1219ff3d809e3a",
+ "names": [
+ "quay.io/ansible/centos7-test-container:1.8.0"
+ ],
+ "digest": "sha256:6e5d9c99aa558779715a80715e5cf0c227a4b59d95e6803c148290c5d0d9d352",
+ "created": "2019-04-02T00:59:39.234584184Z",
+ "size": 702761933
+ },
+ {
+ "id": "b0f914b26cc1088ab8705413c2f2cf247306ceeea51260d64c26894190d188bd",
+ "names": [
+ "quay.io/ansible/centos7-test-container:latest"
+ ],
+ "digest": "sha256:d8431aa74f60f4ff0f1bd36bc9a227bbb2066330acd8bf25e29d8614ee99e39c",
+ "created": "2020-06-11T22:05:58.382459136Z",
+ "size": 578513505
+ }
+]
+""".lstrip()
+
+
+@pytest.fixture
+def docker_images():
+ from ansible_test._internal.docker_util import docker_images
+ return docker_images
+
+
+@pytest.fixture
+def ansible_test(ansible_test):
+ import ansible_test
+ return ansible_test
+
+
+@pytest.fixture
+def subprocess_error():
+ from ansible_test._internal.util import SubprocessError
+ return SubprocessError
+
+
+@pytest.mark.parametrize(
+ ('returned_items_count', 'patched_dc_stdout'),
+ (
+ (3, (DOCKER_OUTPUT_MULTIPLE, '')),
+ (2, (PODMAN_OUTPUT, '')),
+ (0, ('', '')),
+ ),
+ ids=('docker JSONL', 'podman JSON sequence', 'empty output'))
+def test_docker_images(docker_images, mocker, returned_items_count, patched_dc_stdout):
+ mocker.patch(
+ 'ansible_test._internal.docker_util.docker_command',
+ return_value=patched_dc_stdout)
+ ret = docker_images('', 'quay.io/ansible/centos7-test-container')
+ assert len(ret) == returned_items_count
+
+
+def test_podman_fallback(ansible_test, docker_images, subprocess_error, mocker):
+ '''Test podman >2 && <2.2 fallback'''
+
+ cmd = ['docker', 'images', 'quay.io/ansible/centos7-test-container', '--format', '{{json .}}']
+ docker_command_results = [
+ subprocess_error(cmd, status=1, stderr='function "json" not defined'),
+ (PODMAN_OUTPUT, ''),
+ ]
+ mocker.patch(
+ 'ansible_test._internal.docker_util.docker_command',
+ side_effect=docker_command_results)
+
+ ret = docker_images('', 'quay.io/ansible/centos7-test-container')
+ calls = [
+ call(
+ '',
+ ['images', 'quay.io/ansible/centos7-test-container', '--format', '{{json .}}'],
+ capture=True,
+ always=True),
+ call(
+ '',
+ ['images', 'quay.io/ansible/centos7-test-container', '--format', 'json'],
+ capture=True,
+ always=True),
+ ]
+ ansible_test._internal.docker_util.docker_command.assert_has_calls(calls)
+ assert len(ret) == 2
+
+
+def test_podman_no_such_image(ansible_test, docker_images, subprocess_error, mocker):
+ '''Test podman "no such image" error'''
+
+ cmd = ['docker', 'images', 'quay.io/ansible/centos7-test-container', '--format', '{{json .}}']
+ exc = subprocess_error(cmd, status=1, stderr='no such image'),
+ mocker.patch(
+ 'ansible_test._internal.docker_util.docker_command',
+ side_effect=exc)
+ ret = docker_images('', 'quay.io/ansible/centos7-test-container')
+ assert ret == []
diff --git a/test/units/errors/test_errors.py b/test/units/errors/test_errors.py
index ab5c19cd..136a2695 100644
--- a/test/units/errors/test_errors.py
+++ b/test/units/errors/test_errors.py
@@ -97,14 +97,15 @@ class TestErrors(unittest.TestCase):
"the exact syntax problem.\n\nThe offending line appears to be:\n\n\nthis is line 1\n^ here\n")
)
- # this line will not be found, as it is out of the index range
- self.obj.ansible_pos = ('foo.yml', 2, 1)
- e = AnsibleError(self.message, self.obj)
- self.assertEqual(
- e.message,
- ("This is the error message\n\nThe error appears to be in 'foo.yml': line 2, column 1, but may\nbe elsewhere in the file depending on "
- "the exact syntax problem.\n\n(specified line no longer in file, maybe it changed?)")
- )
+ with patch('ansible.errors.to_text', side_effect=IndexError('Raised intentionally')):
+ # raise an IndexError
+ self.obj.ansible_pos = ('foo.yml', 2, 1)
+ e = AnsibleError(self.message, self.obj)
+ self.assertEqual(
+ e.message,
+ ("This is the error message\n\nThe error appears to be in 'foo.yml': line 2, column 1, but may\nbe elsewhere in the file depending on "
+ "the exact syntax problem.\n\n(specified line no longer in file, maybe it changed?)")
+ )
m = mock_open()
m.return_value.readlines.return_value = ['this line has unicode \xf0\x9f\x98\xa8 in it!\n']
@@ -119,3 +120,32 @@ class TestErrors(unittest.TestCase):
"file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\nthis line has unicode \xf0\x9f\x98\xa8 in it!\n^ "
"here\n")
)
+
+ def test_get_error_lines_error_in_last_line(self):
+ m = mock_open()
+ m.return_value.readlines.return_value = ['this is line 1\n', 'this is line 2\n', 'this is line 3\n']
+
+ with patch('{0}.open'.format(BUILTINS), m):
+ # If the error occurs in the last line of the file, use the correct index to get the line
+ # and avoid the IndexError
+ self.obj.ansible_pos = ('foo.yml', 4, 1)
+ e = AnsibleError(self.message, self.obj)
+ self.assertEqual(
+ e.message,
+ ("This is the error message\n\nThe error appears to be in 'foo.yml': line 4, column 1, but may\nbe elsewhere in the file depending on "
+ "the exact syntax problem.\n\nThe offending line appears to be:\n\nthis is line 2\nthis is line 3\n^ here\n")
+ )
+
+ def test_get_error_lines_error_empty_lines_around_error(self):
+ """Test that trailing whitespace after the error is removed"""
+ m = mock_open()
+ m.return_value.readlines.return_value = ['this is line 1\n', 'this is line 2\n', 'this is line 3\n', ' \n', ' \n', ' ']
+
+ with patch('{0}.open'.format(BUILTINS), m):
+ self.obj.ansible_pos = ('foo.yml', 5, 1)
+ e = AnsibleError(self.message, self.obj)
+ self.assertEqual(
+ e.message,
+ ("This is the error message\n\nThe error appears to be in 'foo.yml': line 5, column 1, but may\nbe elsewhere in the file depending on "
+ "the exact syntax problem.\n\nThe offending line appears to be:\n\nthis is line 2\nthis is line 3\n^ here\n")
+ )
diff --git a/test/units/mock/loader.py b/test/units/mock/loader.py
index 0ee47fbb..c47ec39e 100644
--- a/test/units/mock/loader.py
+++ b/test/units/mock/loader.py
@@ -39,10 +39,11 @@ class DictDataLoader(DataLoader):
self._vault_secrets = None
def load_from_file(self, path, cache=True, unsafe=False):
+ data = None
path = to_text(path)
if path in self._file_mapping:
- return self.load(self._file_mapping[path], path)
- return None
+ data = self.load(self._file_mapping[path], path)
+ return data
# TODO: the real _get_file_contents returns a bytestring, so we actually convert the
# unicode/text it's created with to utf-8
diff --git a/test/units/module_utils/basic/test_atomic_move.py b/test/units/module_utils/basic/test_atomic_move.py
index 7bd9496e..bbdb0519 100644
--- a/test/units/module_utils/basic/test_atomic_move.py
+++ b/test/units/module_utils/basic/test_atomic_move.py
@@ -23,6 +23,7 @@ def atomic_am(am, mocker):
am.selinux_context = mocker.MagicMock()
am.selinux_default_context = mocker.MagicMock()
am.set_context_if_different = mocker.MagicMock()
+ am._unsafe_writes = mocker.MagicMock()
yield am
diff --git a/test/units/module_utils/facts/system/distribution/fixtures/almalinux_8_3_beta.json b/test/units/module_utils/facts/system/distribution/fixtures/almalinux_8_3_beta.json
new file mode 100644
index 00000000..2d8df50b
--- /dev/null
+++ b/test/units/module_utils/facts/system/distribution/fixtures/almalinux_8_3_beta.json
@@ -0,0 +1,53 @@
+{
+ "name": "AlmaLinux 8.3",
+ "distro": {
+ "codename": "Purple Manul",
+ "id": "almalinux",
+ "name": "AlmaLinux",
+ "version": "8.3",
+ "version_best": "8.3",
+ "lsb_release_info": {
+ "lsb_version": ":core-4.1-amd64:core-4.1-noarch",
+ "distributor_id": "AlmaLinux",
+ "description": "AlmaLinux release 8.3 Beta (Purple Manul)",
+ "release": "8.3",
+ "codename": "PurpleManul"
+ },
+ "os_release_info": {
+ "name": "AlmaLinux",
+ "version": "8.3 (Purple Manul)",
+ "id": "almalinux",
+ "id_like": "rhel centos fedora",
+ "version_id": "8.3",
+ "platform_id": "platform:el8",
+ "pretty_name": "AlmaLinux 8.3 Beta (Purple Manul)",
+ "ansi_color": "0;34",
+ "cpe_name": "cpe:/o:almalinux:almalinux:8.3:beta",
+ "home_url": "https://almalinux.org/",
+ "bug_report_url": "https://bugs.almalinux.org/",
+ "almalinux_mantisbt_project": "AlmaLinux-8",
+ "almalinux_mantisbt_project_version": "8",
+ "codename": "Purple Manul"
+ }
+ },
+ "input": {
+ "/etc/centos-release": "AlmaLinux release 8.3 Beta (Purple Manul)\n",
+ "/etc/redhat-release": "AlmaLinux release 8.3 Beta (Purple Manul)\n",
+ "/etc/system-release": "AlmaLinux release 8.3 Beta (Purple Manul)\n",
+ "/etc/os-release": "NAME=\"AlmaLinux\"\nVERSION=\"8.3 (Purple Manul)\"\nID=\"almalinux\"\nID_LIKE=\"rhel centos fedora\"\nVERSION_ID=\"8.3\"\nPLATFORM_ID=\"platform:el8\"\nPRETTY_NAME=\"AlmaLinux 8.3 Beta (Purple Manul)\"\nANSI_COLOR=\"0;34\"\nCPE_NAME=\"cpe:/o:almalinux:almalinux:8.3:beta\"\nHOME_URL=\"https://almalinux.org/\"\nBUG_REPORT_URL=\"https://bugs.almalinux.org/\"\n\nALMALINUX_MANTISBT_PROJECT=\"AlmaLinux-8\" \nALMALINUX_MANTISBT_PROJECT_VERSION=\"8\" \n\n",
+ "/usr/lib/os-release": "NAME=\"AlmaLinux\"\nVERSION=\"8.3 (Purple Manul)\"\nID=\"almalinux\"\nID_LIKE=\"rhel centos fedora\"\nVERSION_ID=\"8.3\"\nPLATFORM_ID=\"platform:el8\"\nPRETTY_NAME=\"AlmaLinux 8.3 Beta (Purple Manul)\"\nANSI_COLOR=\"0;34\"\nCPE_NAME=\"cpe:/o:almalinux:almalinux:8.3:beta\"\nHOME_URL=\"https://almalinux.org/\"\nBUG_REPORT_URL=\"https://bugs.almalinux.org/\"\n\nALMALINUX_MANTISBT_PROJECT=\"AlmaLinux-8\" \nALMALINUX_MANTISBT_PROJECT_VERSION=\"8\" \n\n"
+ },
+ "platform.dist": [
+ "almalinux",
+ "8.3",
+ "Purple Manul"
+ ],
+ "result": {
+ "distribution": "AlmaLinux",
+ "distribution_version": "8.3",
+ "distribution_release": "Purple Manul",
+ "distribution_major_version": "8",
+ "os_family": "RedHat"
+ },
+ "platform.release": "4.18.0-240.el8.x86_64"
+}
diff --git a/test/units/module_utils/facts/virtual/__init__.py b/test/units/module_utils/facts/virtual/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/test/units/module_utils/facts/virtual/__init__.py
diff --git a/test/units/module_utils/facts/virtual/test_linux.py b/test/units/module_utils/facts/virtual/test_linux.py
new file mode 100644
index 00000000..d534478c
--- /dev/null
+++ b/test/units/module_utils/facts/virtual/test_linux.py
@@ -0,0 +1,26 @@
+# -*- 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)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible.module_utils.facts.virtual import linux
+
+
+def test_get_virtual_facts_bhyve(mocker):
+ mocker.patch('os.path.exists', return_value=False)
+ mocker.patch('ansible.module_utils.facts.virtual.linux.get_file_content', return_value='')
+ mocker.patch('ansible.module_utils.facts.virtual.linux.get_file_lines', return_value=[])
+
+ module = mocker.Mock()
+ module.run_command.return_value = (0, 'BHYVE\n', '')
+ inst = linux.LinuxVirtual(module)
+
+ facts = inst.get_virtual_facts()
+ expected = {
+ 'virtualization_role': 'guest',
+ 'virtualization_type': 'bhyve',
+ }
+
+ assert facts == expected