[2.10] Doc backports: 71753, 71754, 71769, 71770, 71771, 71772 (#71807)

* Docsite: misc fixes of playbooks_delegation (#71753)

(cherry picked from commit 05a45f63ff)

* Docsite: fix playbooks_conditionals (#71754)

(cherry picked from commit aa1f0bd062)

* Docsite: fix user_guide/playbooks_blocks (#71769)

(cherry picked from commit 79dc6fa948)

* Docsite: fix user_guide/playbooks_handlers (#71770)

(cherry picked from commit 1cf42897d2)

* Docsite: fix user_guide/playbooks_error_handling (#71771)

(cherry picked from commit 2c6661d4c1)

* Docsite: fix user_guide/playbooks_environment (#71772)

(cherry picked from commit a204f5f955)
This commit is contained in:
Andrew Klychkov 2020-09-21 19:20:57 +03:00 committed by GitHub
parent aacff5e35f
commit 47cabb1d32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 256 additions and 184 deletions

View file

@ -21,18 +21,20 @@ All tasks in a block inherit directives applied at the block level. Most of what
tasks:
- name: Install, configure, and start Apache
block:
- name: install httpd and memcached
yum:
- name: Install httpd and memcached
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
- name: apply the foo config template
template:
- name: Apply the foo config template
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
- name: start service bar and enable it
service:
- name: Start service bar and enable it
ansible.builtin.service:
name: bar
state: started
enabled: True
@ -62,14 +64,19 @@ Rescue blocks specify tasks to run when an earlier task in a block fails. This a
tasks:
- name: Handle the error
block:
- debug:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: i force a failure
command: /bin/false
- debug:
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- debug:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error, can do stuff here to fix it, :-)'
You can also add an ``always`` section to a block. Tasks in the ``always`` section run no matter what the task status of the previous block is.
@ -81,14 +88,19 @@ You can also add an ``always`` section to a block. Tasks in the ``always`` secti
- name: Always do X
block:
- debug:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: i force a failure
command: /bin/false
- debug:
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute :-('
always:
- debug:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes, :-)"
Together, these elements offer complex error handling.
@ -99,26 +111,35 @@ Together, these elements offer complex error handling.
- name: Attempt and graceful roll back demo
block:
- debug:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: i force a failure
command: /bin/false
- debug:
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- debug:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error'
- name: i force a failure in middle of recovery! >:-)
command: /bin/false
- debug:
- name: Force a failure in middle of recovery! >:-)
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I also never execute :-('
always:
- debug:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes"
The tasks in the ``block`` execute normally. If any tasks in the block return ``failed``, the ``rescue`` section executes tasks to recover from the error. The ``always`` section runs regardless of the results of the ``block`` and ``rescue`` sections.
If an error occurs in the block and the rescue task succeeds, Ansible reverts the failed status of the original task for the run and continues to run the play as if the original task had succeeded. The rescued task is considered successful, and does not not trigger ``max_fail_percentage`` or ``any_errors_fatal`` configurations. However, Ansible still reports a failure in the playbook statistics.
If an error occurs in the block and the rescue task succeeds, Ansible reverts the failed status of the original task for the run and continues to run the play as if the original task had succeeded. The rescued task is considered successful, and does not trigger ``max_fail_percentage`` or ``any_errors_fatal`` configurations. However, Ansible still reports a failure in the playbook statistics.
You can use blocks with ``flush_handlers`` in a rescue task to ensure that all handlers run even if an error occurs:
@ -129,17 +150,20 @@ You can use blocks with ``flush_handlers`` in a rescue task to ensure that all h
tasks:
- name: Attempt and graceful roll back demo
block:
- debug:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
changed_when: yes
notify: run me even after an error
- command: /bin/false
- name: Force a failure
ansible.builtin.command: /bin/false
rescue:
- name: make sure all handlers run
- name: Make sure all handlers run
meta: flush_handlers
handlers:
- name: run me even after an error
debug:
- name: Run me even after an error
ansible.builtin.debug:
msg: 'This handler runs even on error'

View file

@ -26,7 +26,10 @@ The simplest conditional statement applies to a single task. Create the task, th
tasks:
- name: Configure SELinux to start mysql on any port
seboolean: name=mysql_connect_any state=true persistent=yes
ansible.posix.seboolean:
name: mysql_connect_any
state: true
persistent: yes
when: ansible_selinux.status == "enabled"
# all variables can be used directly in conditionals without double curly braces
@ -41,15 +44,17 @@ Often you want to execute or skip a task based on facts. Facts are attributes of
See :ref:`commonly_used_facts` for a list of facts that frequently appear in conditional statements. Not all facts exist for all hosts. For example, the 'lsb_major_release' fact used in an example below only exists when the lsb_release package is installed on the target host. To see what facts are available on your systems, add a debug task to your playbook::
- debug: var=ansible_facts
- name: Show facts available on the system
ansible.builtin.debug:
var: ansible_facts
Here is a sample conditional based on a fact:
.. code-block:: yaml
tasks:
- name: shut down Debian flavored systems
command: /sbin/shutdown -t now
- name: Shut down Debian flavored systems
ansible.builtin.command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian"
If you have multiple conditions, you can group them with parentheses:
@ -57,16 +62,16 @@ If you have multiple conditions, you can group them with parentheses:
.. code-block:: yaml
tasks:
- name: shut down CentOS 6 and Debian 7 systems
command: /sbin/shutdown -t now
- name: Shut down CentOS 6 and Debian 7 systems
ansible.builtin.command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
You can use `logical operators <https://jinja.palletsprojects.com/en/master/templates/#logic>`_ to combine conditions. When you have multiple conditions that all need to be true (that is, a logical ``and``), you can specify them as a list::
tasks:
- name: shut down CentOS 6 systems
command: /sbin/shutdown -t now
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
@ -74,7 +79,7 @@ You can use `logical operators <https://jinja.palletsprojects.com/en/master/temp
If a fact or variable is a string, and you need to run a mathematical comparison on it, use a filter to ensure that Ansible reads the value as an integer::
tasks:
- shell: echo "only on Red Hat 6, derivatives, and later"
- ansible.builtin.shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
.. _conditionals_registered_vars:
@ -89,29 +94,31 @@ Often in a playbook you want to execute or skip a task based on the outcome of a
You create the name of the registered variable using the ``register`` keyword. A registered variable always contains the status of the task that created it as well as any output that task generated. You can use registered variables in templates and action lines as well as in conditional ``when`` statements. You can access the string contents of the registered variable using ``variable.stdout``. For example::
- name: test play
- name: Test play
hosts: all
tasks:
- shell: cat /etc/motd
- name: Register a variable
ansible.builtin.shell: cat /etc/motd
register: motd_contents
- shell: echo "motd contains the word hi"
- name: Use the variable in conditional statement
ansible.builtin.shell: echo "motd contains the word hi"
when: motd_contents.stdout.find('hi') != -1
You can use registered results in the loop of a task if the variable is a list. If the variable is not a list, you can convert it into a list, with either ``stdout_lines`` or with ``variable.stdout.split()``. You can also split the lines by other fields::
- name: registered variable usage as a loop list
- name: Registered variable usage as a loop list
hosts: all
tasks:
- name: retrieve the list of home directories
command: ls /home
- name: Retrieve the list of home directories
ansible.builtin.command: ls /home
register: home_dirs
- name: add home dirs to the backup spooler
file:
- name: Add home dirs to the backup spooler
ansible.builtin.file:
path: /mnt/bkspool/{{ item }}
src: /home/{{ item }}
state: link
@ -127,12 +134,12 @@ The string content of a registered variable can be empty. If you want to run ano
tasks:
- name: list contents of directory
command: ls mydir
- name: List contents of directory
ansible.builtin.command: ls mydir
register: contents
- name: check contents for emptiness
debug:
- name: Check contents for emptiness
ansible.builtin.debug:
msg: "Directory is empty"
when: contents.stdout == ""
@ -141,17 +148,21 @@ Ansible always registers something in a registered variable for every host, even
.. code-block:: yaml
tasks:
- command: /bin/false
- name: Register a variable, ignore errors and continue
ansible.builtin.command: /bin/false
register: result
ignore_errors: True
ignore_errors: true
- command: /bin/something
- name: Run only if the task that registered the "result" variable fails
ansible.builtin.command: /bin/something
when: result is failed
- command: /bin/something_else
- name: Run only if the task that registered the "result" variable succeeds
ansible.builtin.command: /bin/something_else
when: result is succeeded
- command: /bin/still/something_else
- name: Run only if the task that registered the "result" variable is skipped
ansible.builtin.command: /bin/still/something_else
when: result is skipped
.. note:: Older versions of Ansible used ``success`` and ``fail``, but ``succeeded`` and ``failed`` use the correct tense. All of these options are now valid.
@ -173,10 +184,12 @@ With the variables above, Ansible would run one of these tasks and skip the othe
.. code-block:: yaml
tasks:
- shell: echo "This certainly is epic!"
- name: Run the command if "epic" or "monumental" is true
ansible.builtin.shell: echo "This certainly is epic!"
when: epic or monumental | bool
- shell: echo "This certainly isn't epic!"
- name: Run the command if "epic" is false
ansible.builtin.shell: echo "This certainly isn't epic!"
when: not epic
If a required variable has not been set, you can skip or fail using Jinja2's `defined` test. For example:
@ -184,10 +197,12 @@ If a required variable has not been set, you can skip or fail using Jinja2's `de
.. code-block:: yaml
tasks:
- shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
- name: Run the command if "foo" is defined
ansible.builtin.shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
when: foo is defined
- fail: msg="Bailing out. this play requires 'bar'"
- name: Fail if "bar" is undefined
ansible.builtin.fail: msg="Bailing out. This play requires 'bar'"
when: bar is undefined
This is especially useful in combination with the conditional import of vars files (see below).
@ -203,7 +218,8 @@ If you combine a ``when`` statement with a :ref:`loop <playbooks_loops>`, Ansibl
.. code-block:: yaml
tasks:
- command: echo {{ item }}
- name: Run with items greater than 5
ansible.builtin.command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
@ -211,7 +227,8 @@ If you need to skip the whole task when the loop variable is undefined, use the
.. code-block:: yaml
- command: echo {{ item }}
- name: Skip the whole task when a loop variable is undefined
ansible.builtin.command: echo {{ item }}
loop: "{{ mylist|default([]) }}"
when: item > 5
@ -219,7 +236,8 @@ You can do the same thing when looping over a dict:
.. code-block:: yaml
- command: echo {{ item.key }}
- name: The same as above using a dict
ansible.builtin.command: echo {{ item.key }}
loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5
@ -233,9 +251,11 @@ You can provide your own facts, as described in :ref:`developing_modules`. To r
.. code-block:: yaml
tasks:
- name: gather site specific fact data
- name: Gather site specific fact data
action: site_facts
- command: /usr/bin/thingy
- name: Use a custom fact
ansible.builtin.command: /usr/bin/thingy
when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
.. _when_with_reuse:
@ -258,19 +278,24 @@ When you add a conditional to an import statement, Ansible applies the condition
when: x is not defined
# other_tasks.yml
- set_fact:
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- debug:
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of::
- set_fact:
- name: Set a variable if not defined
ansible.builtin.set_fact:
x: foo
when: x is not defined
# this task sets a value for x
- debug:
- name: Do the task if "x" is not defined
ansible.builin.debug:
var: x
when: x is not defined
# Ansible skips this task, because x is now defined
@ -293,22 +318,29 @@ When you use a conditional on an ``include_*`` statement, the condition is appli
when: x is not defined
# other_tasks.yml
- set_fact:
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- debug:
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of::
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# if condition is met, Ansible includes other_tasks.yml
- set_fact:
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
# no condition applied to this task, Ansible sets the value of x to foo
- debug:
- name: Print a variable
ansible.builtin.debug:
var: x
# no condition applied to this task, Ansible prints the debug statement
@ -337,10 +369,11 @@ When you incorporate a role in your playbook statically with the ``roles`` keywo
Selecting variables, files, or templates based on facts
-------------------------------------------------------
Sometimes the facts about a host determine the values you want to use for certain variables or even the file or template you want to select for that host. For example, the names of packages are different on CentOS and on Debian. The configuration files for common services are also different on different OS flavors and versions. To load different variables file, templates, or other files based on a fact about the hosts you are managing:
Sometimes the facts about a host determine the values you want to use for certain variables or even the file or template you want to select for that host. For example, the names of packages are different on CentOS and on Debian. The configuration files for common services are also different on different OS flavors and versions. To load different variables file, templates, or other files based on a fact about the hosts:
# Name your vars files, templates, or files to match the Ansible fact that differentiates them
# Select the correct vars file, template, or file for each host with a variable based on that Ansible fact
1) name your vars files, templates, or files to match the Ansible fact that differentiates them
2) select the correct vars file, template, or file for each host with a variable based on that Ansible fact
Ansible separates variables from tasks, keeping your playbooks from turning into arbitrary code with nested conditionals. This approach results in more streamlined and auditable configuration rules because there are fewer decision points to track.
@ -363,10 +396,12 @@ Then import those variables files based on the facts you gather on the hosts in
- "vars/common.yml"
- [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ]
tasks:
- name: make sure apache is started
service: name={{ apache }} state=started
- name: Make sure apache is started
ansible.builtin.service:
name: '{{ apache }}'
state: started
Ansible gathers facts on the hosts in the webservers group, then interpolates the variable "ansible_facts['os_family']" into a list of filenames. If you have hosts with Red Hat operating systems ('CentOS', for example), Ansible looks for 'vars/RedHat.yml'. If that file does not exist, Ansible attempts to load 'vars/os_defaults.yml'. For Debian hosts, Ansible first looks for 'vars/Debian.yml', before falling back on 'vars/os_defaults.yml'. If no files in the list are found, Ansible raises an error.
Ansible gathers facts on the hosts in the webservers group, then interpolates the variable "ansible_facts['os_family']" into a list of filenames. If you have hosts with Red Hat operating systems (CentOS, for example), Ansible looks for 'vars/RedHat.yml'. If that file does not exist, Ansible attempts to load 'vars/os_defaults.yml'. For Debian hosts, Ansible first looks for 'vars/Debian.yml', before falling back on 'vars/os_defaults.yml'. If no files in the list are found, Ansible raises an error.
Selecting files and templates based on facts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -375,14 +410,14 @@ You can use the same approach when different OS flavors or versions require diff
For example, you can template out a configuration file that is very different between, say, CentOS and Debian::
- name: template a file
template:
- name: Template a file
ansible.builtin.template:
src: "{{ item }}"
dest: /etc/myapp/foo.conf
loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"
vars:
myfiles:
- "{{ansible_facts['distribution']}}.conf"
- "{{ ansible_facts['distribution'] }}.conf"
- default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/']

View file

@ -18,39 +18,39 @@ Some tasks always execute on the controller. These tasks, including ``include``,
Delegating tasks
----------------
If you want to perform a task on one host with reference to other hosts, use the 'delegate_to' keyword on a task. This is ideal for managing nodes in a load balanced pool or for controlling outage windows. You can use delegation with the :ref:`serial <rolling_update_batch_size>` keyword to control the number of hosts executing at one time::
If you want to perform a task on one host with reference to other hosts, use the ``delegate_to`` keyword on a task. This is ideal for managing nodes in a load balanced pool or for controlling outage windows. You can use delegation with the :ref:`serial <rolling_update_batch_size>` keyword to control the number of hosts executing at one time::
---
- hosts: webservers
serial: 5
tasks:
- name: take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
- name: Take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: actual steps would go here
yum:
- name: Actual steps would go here
ansible.builtin.yum:
name: acme-web-stack
state: latest
- name: add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
- name: Add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
The first and third tasks in this play run on 127.0.0.1, which is the machine running Ansible. There is also a shorthand syntax that you can use on a per-task basis: 'local_action'. Here is the same playbook as above, but using the shorthand syntax for delegating to 127.0.0.1::
The first and third tasks in this play run on 127.0.0.1, which is the machine running Ansible. There is also a shorthand syntax that you can use on a per-task basis: ``local_action``. Here is the same playbook as above, but using the shorthand syntax for delegating to 127.0.0.1::
---
# ...
tasks:
- name: take out of load balancer pool
local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }}
- name: Take out of load balancer pool
local_action: ansible.builtin.command /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ...
- name: add back to load balancer pool
local_action: command /usr/bin/add_back_to_pool {{ inventory_hostname }}
- name: Add back to load balancer pool
local_action: ansible.builtin.command /usr/bin/add_back_to_pool {{ inventory_hostname }}
You can use a local action to call 'rsync' to recursively copy files to the managed servers::
@ -58,11 +58,10 @@ You can use a local action to call 'rsync' to recursively copy files to the mana
# ...
tasks:
- name: recursively copy files from management server to target
local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
- name: Recursively copy files from management server to target
local_action: ansible.builtin.command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync
will need to ask for a passphrase.
Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync asks for a passphrase.
To specify more arguments, use the following syntax::
@ -72,7 +71,7 @@ To specify more arguments, use the following syntax::
tasks:
- name: Send summary mail
local_action:
module: mail
module: community.general.mail
subject: "Summary Mail"
to: "{{ mail_recipient }}"
body: "{{ mail_body }}"
@ -85,17 +84,17 @@ The `ansible_host` variable reflects the host a task is delegated to.
Delegating facts
----------------
Delegating Ansible tasks is like delegating tasks in the real world - your groceries belong to you, even if someone else delivers them to your home. Similarly, any facts gathered by a delegated task are assigned by default to the `inventory_hostname` (the current host), not to the host which produced the facts (the delegated to host). To assign gathered facts to the delegated host instead of the current host, set `delegate_facts` to `True`::
Delegating Ansible tasks is like delegating tasks in the real world - your groceries belong to you, even if someone else delivers them to your home. Similarly, any facts gathered by a delegated task are assigned by default to the `inventory_hostname` (the current host), not to the host which produced the facts (the delegated to host). To assign gathered facts to the delegated host instead of the current host, set ``delegate_facts`` to ``true``::
---
- hosts: app_servers
tasks:
- name: gather facts from db servers
setup:
delegate_to: "{{item}}"
delegate_facts: True
loop: "{{groups['dbservers']}}"
- name: Gather facts from db servers
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
loop: "{{ groups['dbservers'] }}"
This task gathers facts for the machines in the dbservers group and assigns the facts to those machines, even though the play targets the app_servers group. This way you can lookup `hostvars['dbhost1']['ansible_default_ipv4']['address']` even though dbservers were not part of the play, or left out by using `--limit`.
@ -107,7 +106,7 @@ Local playbooks
It may be useful to use a playbook locally on a remote host, rather than by connecting over SSH. This can be useful for assuring the configuration of a system by putting a playbook in a crontab. This may also be used
to run a playbook inside an OS installer, such as an Anaconda kickstart.
To run an entire playbook locally, just set the "hosts:" line to "hosts: 127.0.0.1" and then run the playbook like so::
To run an entire playbook locally, just set the ``hosts:`` line to ``hosts: 127.0.0.1`` and then run the playbook like so::
ansible-playbook playbook.yml --connection=local

View file

@ -23,7 +23,7 @@ You can set the environment directly at the task level::
tasks:
- name: Install cobbler
package:
ansible.builtin.package:
name: cobbler
state: present
environment:
@ -42,7 +42,7 @@ You can re-use environment settings by defining them as variables in your play a
tasks:
- name: Install cobbler
package:
ansible.builtin.package:
name: cobbler
state: present
environment: "{{ proxy_env }}"
@ -94,19 +94,19 @@ Some language-specific version managers (such as rbenv and nvm) require you to s
PATH: /var/local/nvm/versions/node/v4.2.1/bin:{{ ansible_env.PATH }}
tasks:
- name: check for package.json
stat:
- name: Check for package.json
ansible.builtin.stat:
path: '{{ node_app_dir }}/package.json'
register: packagejson
- name: npm prune
command: npm prune
- name: Run npm prune
ansible.builtin.command: npm prune
args:
chdir: '{{ node_app_dir }}'
when: packagejson.stat.exists
- name: npm install
npm:
- name: Run npm install
community.general.npm:
path: '{{ node_app_dir }}'
when: packagejson.stat.exists
@ -119,8 +119,8 @@ Some language-specific version managers (such as rbenv and nvm) require you to s
You can also specify the environment at the task level::
---
- name: install ruby 2.3.1
command: rbenv install {{ rbenv_ruby_version }}
- name: Install ruby 2.3.1
ansible.builtin.command: rbenv install {{ rbenv_ruby_version }}
args:
creates: '{{ rbenv_root }}/versions/{{ rbenv_ruby_version }}/bin/ruby'
vars:

View file

@ -16,36 +16,36 @@ Ignoring failed commands
By default Ansible stops executing tasks on a host when a task fails on that host. You can use ``ignore_errors`` to continue on in spite of the failure::
- name: this will not count as a failure
command: /bin/false
- name: Do not count this as a failure
ansible.builtin.command: /bin/false
ignore_errors: yes
The ``ignore_errors`` directive only works when the task is able to run and returns a value of 'failed'. It will not make Ansible ignore undefined variable errors, connection failures, execution issues (for example, missing packages), or syntax errors.
The ``ignore_errors`` directive only works when the task is able to run and returns a value of 'failed'. It does not make Ansible ignore undefined variable errors, connection failures, execution issues (for example, missing packages), or syntax errors.
Ignoring unreachable host errors
================================
.. versionadded:: 2.7
You may ignore task failure due to the host instance being 'UNREACHABLE' with the ``ignore_unreachable`` keyword. Ansible ignores the task errors, but continues to execute future tasks against the unreachable host. For example, at the task level::
You can ignore a task failure due to the host instance being 'UNREACHABLE' with the ``ignore_unreachable`` keyword. Ansible ignores the task errors, but continues to execute future tasks against the unreachable host. For example, at the task level::
- name: this executes, fails, and the failure is ignored
command: /bin/true
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
ignore_unreachable: yes
- name: this executes, fails, and ends the play for this host
command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
And at the playbook level::
- hosts: all
ignore_unreachable: yes
tasks:
- name: this executes, fails, and the failure is ignored
command: /bin/true
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
- name: this executes, fails, and ends the play for this host
command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
ignore_unreachable: no
.. _resetting_unreachable:
@ -84,21 +84,21 @@ Ansible lets you define what "failure" means in each task using the ``failed_whe
You may check for failure by searching for a word or phrase in the output of a command::
- name: Fail task when the command error output prints FAILED
command: /usr/bin/example-command -x -y -z
ansible.builtin.command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
or based on the return code::
- name: Fail task when both files are identical
raw: diff foo/file1 bar/file2
ansible.builtin.raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
You can also combine multiple conditions for failure. This task will fail if both conditions are true::
- name: Check if a file exists in temp and fail task if it does
command: ls /tmp/this_should_not_be_here
ansible.builtin.command: ls /tmp/this_should_not_be_here
register: result
failed_when:
- result.rc == 0
@ -111,7 +111,7 @@ If you want the task to fail when only one condition is satisfied, change the ``
If you have too many conditions to fit neatly into one line, you can split it into a multi-line yaml value with ``>``::
- name: example of many failed_when conditions with OR
shell: "./myBinary"
ansible.builtin.shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
@ -127,17 +127,19 @@ Ansible lets you define when a particular task has "changed" a remote node using
tasks:
- shell: /usr/bin/billybass --mode="take me to the river"
- name: Report 'changed' when the return code is not equal to 2
ansible.builtin.shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
# this will never report 'changed' status
- shell: wall 'beep'
- name: This will never report 'changed' status
ansible.builtin.shell: wall 'beep'
changed_when: False
You can also combine multiple conditions to override "changed" result::
- command: /bin/fake_command
- name: Combine multiple conditions to override 'changed' result
ansible.builtin.command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
@ -149,11 +151,11 @@ See :ref:`controlling_what_defines_failure` for more conditional syntax examples
Ensuring success for command and shell
======================================
The :ref:`command <command_module>` and :ref:`shell <shell_module>` modules care about return codes, so if you have a command whose successful exit code is not zero, you may wish to do this::
The :ref:`command <command_module>` and :ref:`shell <shell_module>` modules care about return codes, so if you have a command whose successful exit code is not zero, you can do this::
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true
- name: Run this command and ignore the result
ansible.builtin.shell: /usr/bin/somecommand || /bin/true
Aborting a play on all hosts
@ -181,25 +183,26 @@ You can use this feature when all tasks must be 100% successful to continue play
---
- hosts: load_balancers_dc_a
any_errors_fatal: True
any_errors_fatal: true
tasks:
- name: 'shutting down datacenter [ A ]'
command: /usr/bin/disable-dc
- name: Shut down datacenter 'A'
ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: 'stopping service'
command: /usr/bin/stop-software
- name: 'updating software'
command: /usr/bin/upgrade-software
- name: Stop service
ansible.builtin.command: /usr/bin/stop-software
- name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: 'Starting datacenter [ A ]'
command: /usr/bin/enable-dc
- name: Start datacenter 'A'
ansible.builtin.command: /usr/bin/enable-dc
In this example Ansible starts the software upgrade on the front ends only if all of the load balancers are successfully disabled.

View file

@ -14,49 +14,54 @@ Handler example
This playbook, ``verify-apache.yml``, contains a single play with a handler::
---
- name: verify apache installation
- name: Verify apache installation
hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest
- name: write the apache config file
template:
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service:
- Restart apache
- name: Ensure apache is running
ansible.builtin.service:
name: httpd
state: started
handlers:
- name: restart apache
service:
- name: Restart apache
ansible.builtin.service:
name: httpd
state: restarted
In this example playbook, the second task notifies the handler. A single task can notify more than one handler::
- name: template configuration file
template:
- name: Template configuration file
ansible.builtin.template:
src: template.j2
dest: /etc/foo.conf
notify:
- restart memcached
- restart apache
- Restart memcached
- Restart apache
handlers:
- name: restart memcached
service:
- name: Restart memcached
ansible.builtin.service:
name: memcached
state: restarted
- name: restart apache
service:
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
@ -68,9 +73,14 @@ By default, handlers run after all the tasks in a particular play have been comp
If you need handlers to run before the end of the play, add a task to flush them using the :ref:`meta module <meta_module>`, which executes Ansible actions::
tasks:
- shell: some tasks go here
- meta: flush_handlers
- shell: some other tasks
- name: Some tasks go here
ansible.builtin.shell: ...
- name: Flush handlers
meta: flush_handlers
- name: Some other tasks
ansible.builtin.shell: ...
The ``meta: flush_handlers`` task triggers any handlers that have been notified at that point in the play.
@ -80,8 +90,8 @@ Using variables with handlers
You may want your Ansible handlers to use variables. For example, if the name of a service varies slightly by distribution, you want your output to show the exact name of the restarted service for each target machine. Avoid placing variables in the name of the handler. Since handler names are templated early on, Ansible may not have a value available for a handler name like this::
handlers:
# this handler name may cause your play to fail!
- name: restart "{{ web_service_name }}"
# This handler name may cause your play to fail!
- name: Restart "{{ web_service_name }}"
If the variable used in the handler name is not available, the entire play fails. Changing that variable mid-play **will not** result in newly created handler.
@ -94,28 +104,29 @@ Instead, place variables in the task parameters of your handler. You can load th
include_vars: "{{ ansible_facts.distribution }}.yml"
handlers:
- name: restart web service
service:
- name: Restart web service
ansible.builtin.service:
name: "{{ web_service_name | default('httpd') }}"
state: restarted
Handlers can also "listen" to generic topics, and tasks can notify those topics as follows::
handlers:
- name: restart memcached
service:
- name: Restart memcached
ansible.builtin.service:
name: memcached
state: restarted
listen: "restart web services"
- name: restart apache
service:
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
listen: "restart web services"
tasks:
- name: restart everything
command: echo "this task will restart the web services"
- name: Restart everything
ansible.builtin.command: echo "this task will restart the web services"
notify: "restart web services"
This use makes it much easier to trigger multiple handlers. It also decouples handlers from their names,
@ -132,6 +143,6 @@ a shared source like Galaxy).
When using handlers within roles, note that:
* handlers notified within ``pre_tasks``, ``tasks``, and ``post_tasks`` sections are automatically flushed in the end of section where they were notified.
* handlers notified within ``roles`` section are automatically flushed in the end of ``tasks`` section, but before any ``tasks`` handlers.
* handlers notified within ``pre_tasks``, ``tasks``, and ``post_tasks`` sections are automatically flushed at the end of section where they were notified.
* handlers notified within ``roles`` section are automatically flushed at the end of ``tasks`` section, but before any ``tasks`` handlers.
* handlers are play scoped and as such can be used outside of the role they are defined in.