[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: tasks:
- name: Install, configure, and start Apache - name: Install, configure, and start Apache
block: block:
- name: install httpd and memcached - name: Install httpd and memcached
yum: ansible.builtin.yum:
name: name:
- httpd - httpd
- memcached - memcached
state: present state: present
- name: apply the foo config template
template: - name: Apply the foo config template
ansible.builtin.template:
src: templates/src.j2 src: templates/src.j2
dest: /etc/foo.conf dest: /etc/foo.conf
- name: start service bar and enable it
service: - name: Start service bar and enable it
ansible.builtin.service:
name: bar name: bar
state: started state: started
enabled: True enabled: True
@ -62,14 +64,19 @@ Rescue blocks specify tasks to run when an earlier task in a block fails. This a
tasks: tasks:
- name: Handle the error - name: Handle the error
block: block:
- debug: - name: Print a message
ansible.builtin.debug:
msg: 'I execute normally' msg: 'I execute normally'
- name: i force a failure
command: /bin/false - name: Force a failure
- debug: ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-(' msg: 'I never execute, due to the above task failing, :-('
rescue: rescue:
- debug: - name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error, can do stuff here to fix it, :-)' 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. 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 - name: Always do X
block: block:
- debug: - name: Print a message
ansible.builtin.debug:
msg: 'I execute normally' msg: 'I execute normally'
- name: i force a failure
command: /bin/false - name: Force a failure
- debug: ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute :-(' msg: 'I never execute :-('
always: always:
- debug: - name: Always do this
ansible.builtin.debug:
msg: "This always executes, :-)" msg: "This always executes, :-)"
Together, these elements offer complex error handling. 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 - name: Attempt and graceful roll back demo
block: block:
- debug: - name: Print a message
ansible.builtin.debug:
msg: 'I execute normally' msg: 'I execute normally'
- name: i force a failure
command: /bin/false - name: Force a failure
- debug: ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-(' msg: 'I never execute, due to the above task failing, :-('
rescue: rescue:
- debug: - name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error' msg: 'I caught an error'
- name: i force a failure in middle of recovery! >:-)
command: /bin/false - name: Force a failure in middle of recovery! >:-)
- debug: ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I also never execute :-(' msg: 'I also never execute :-('
always: always:
- debug: - name: Always do this
ansible.builtin.debug:
msg: "This always executes" 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. 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: 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: tasks:
- name: Attempt and graceful roll back demo - name: Attempt and graceful roll back demo
block: block:
- debug: - name: Print a message
ansible.builtin.debug:
msg: 'I execute normally' msg: 'I execute normally'
changed_when: yes changed_when: yes
notify: run me even after an error notify: run me even after an error
- command: /bin/false
- name: Force a failure
ansible.builtin.command: /bin/false
rescue: rescue:
- name: make sure all handlers run - name: Make sure all handlers run
meta: flush_handlers meta: flush_handlers
handlers: handlers:
- name: run me even after an error - name: Run me even after an error
debug: ansible.builtin.debug:
msg: 'This handler runs even on error' 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: tasks:
- name: Configure SELinux to start mysql on any port - 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" when: ansible_selinux.status == "enabled"
# all variables can be used directly in conditionals without double curly braces # 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:: 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: Here is a sample conditional based on a fact:
.. code-block:: yaml .. code-block:: yaml
tasks: tasks:
- name: shut down Debian flavored systems - name: Shut down Debian flavored systems
command: /sbin/shutdown -t now ansible.builtin.command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian" when: ansible_facts['os_family'] == "Debian"
If you have multiple conditions, you can group them with parentheses: 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 .. code-block:: yaml
tasks: tasks:
- name: shut down CentOS 6 and Debian 7 systems - name: Shut down CentOS 6 and Debian 7 systems
command: /sbin/shutdown -t now ansible.builtin.command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7") (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:: 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: tasks:
- name: shut down CentOS 6 systems - name: Shut down CentOS 6 systems
command: /sbin/shutdown -t now ansible.builtin.command: /sbin/shutdown -t now
when: when:
- ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6" - 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:: 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: 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 when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
.. _conditionals_registered_vars: .. _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:: 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 hosts: all
tasks: tasks:
- shell: cat /etc/motd - name: Register a variable
ansible.builtin.shell: cat /etc/motd
register: motd_contents 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 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:: 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 hosts: all
tasks: tasks:
- name: retrieve the list of home directories - name: Retrieve the list of home directories
command: ls /home ansible.builtin.command: ls /home
register: home_dirs register: home_dirs
- name: add home dirs to the backup spooler - name: Add home dirs to the backup spooler
file: ansible.builtin.file:
path: /mnt/bkspool/{{ item }} path: /mnt/bkspool/{{ item }}
src: /home/{{ item }} src: /home/{{ item }}
state: link state: link
@ -127,12 +134,12 @@ The string content of a registered variable can be empty. If you want to run ano
tasks: tasks:
- name: list contents of directory - name: List contents of directory
command: ls mydir ansible.builtin.command: ls mydir
register: contents register: contents
- name: check contents for emptiness - name: Check contents for emptiness
debug: ansible.builtin.debug:
msg: "Directory is empty" msg: "Directory is empty"
when: contents.stdout == "" when: contents.stdout == ""
@ -141,17 +148,21 @@ Ansible always registers something in a registered variable for every host, even
.. code-block:: yaml .. code-block:: yaml
tasks: tasks:
- command: /bin/false - name: Register a variable, ignore errors and continue
ansible.builtin.command: /bin/false
register: result 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 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 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 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. .. 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 .. code-block:: yaml
tasks: 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 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 when: not epic
If a required variable has not been set, you can skip or fail using Jinja2's `defined` test. For example: 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 .. code-block:: yaml
tasks: 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 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 when: bar is undefined
This is especially useful in combination with the conditional import of vars files (see below). 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 .. code-block:: yaml
tasks: tasks:
- command: echo {{ item }} - name: Run with items greater than 5
ansible.builtin.command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ] loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5 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 .. 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([]) }}" loop: "{{ mylist|default([]) }}"
when: item > 5 when: item > 5
@ -219,7 +236,8 @@ You can do the same thing when looping over a dict:
.. code-block:: yaml .. 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({})) }}" loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5 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 .. code-block:: yaml
tasks: tasks:
- name: gather site specific fact data - name: Gather site specific fact data
action: site_facts 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: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
.. _when_with_reuse: .. _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 when: x is not defined
# other_tasks.yml # other_tasks.yml
- set_fact: - name: Set a variable
ansible.builtin.set_fact:
x: foo x: foo
- debug:
- name: Print a variable
ansible.builtin.debug:
var: x var: x
Ansible expands this at execution time to the equivalent of:: 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 x: foo
when: x is not defined when: x is not defined
# this task sets a value for x # this task sets a value for x
- debug: - name: Do the task if "x" is not defined
ansible.builin.debug:
var: x var: x
when: x is not defined when: x is not defined
# Ansible skips this task, because x is now 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 when: x is not defined
# other_tasks.yml # other_tasks.yml
- set_fact: - name: Set a variable
ansible.builtin.set_fact:
x: foo x: foo
- debug:
- name: Print a variable
ansible.builtin.debug:
var: x var: x
Ansible expands this at execution time to the equivalent of:: Ansible expands this at execution time to the equivalent of::
# main.yml
- include_tasks: other_tasks.yml - include_tasks: other_tasks.yml
when: x is not defined when: x is not defined
# if condition is met, Ansible includes other_tasks.yml # if condition is met, Ansible includes other_tasks.yml
- set_fact: # other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo x: foo
# no condition applied to this task, Ansible sets the value of x to 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 var: x
# no condition applied to this task, Ansible prints the debug statement # 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 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 1) 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
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. 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/common.yml"
- [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ] - [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ]
tasks: tasks:
- name: make sure apache is started - name: Make sure apache is started
service: name={{ apache }} state=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 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:: For example, you can template out a configuration file that is very different between, say, CentOS and Debian::
- name: template a file - name: Template a file
template: ansible.builtin.template:
src: "{{ item }}" src: "{{ item }}"
dest: /etc/myapp/foo.conf dest: /etc/myapp/foo.conf
loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}" loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"
vars: vars:
myfiles: myfiles:
- "{{ansible_facts['distribution']}}.conf" - "{{ ansible_facts['distribution'] }}.conf"
- default.conf - default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/'] 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 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 - hosts: webservers
serial: 5 serial: 5
tasks: tasks:
- name: take out of load balancer pool - name: Take out of load balancer pool
command: /usr/bin/take_out_of_pool {{ inventory_hostname }} ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1 delegate_to: 127.0.0.1
- name: actual steps would go here - name: Actual steps would go here
yum: ansible.builtin.yum:
name: acme-web-stack name: acme-web-stack
state: latest state: latest
- name: add back to load balancer pool - name: Add back to load balancer pool
command: /usr/bin/add_back_to_pool {{ inventory_hostname }} ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1 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: tasks:
- name: take out of load balancer pool - name: Take out of load balancer pool
local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }} local_action: ansible.builtin.command /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ... # ...
- name: add back to load balancer pool - name: Add back to load balancer pool
local_action: command /usr/bin/add_back_to_pool {{ inventory_hostname }} 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:: 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: tasks:
- name: recursively copy files from management server to target - name: Recursively copy files from management server to target
local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/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 Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync asks for a passphrase.
will need to ask for a passphrase.
To specify more arguments, use the following syntax:: To specify more arguments, use the following syntax::
@ -72,7 +71,7 @@ To specify more arguments, use the following syntax::
tasks: tasks:
- name: Send summary mail - name: Send summary mail
local_action: local_action:
module: mail module: community.general.mail
subject: "Summary Mail" subject: "Summary Mail"
to: "{{ mail_recipient }}" to: "{{ mail_recipient }}"
body: "{{ mail_body }}" body: "{{ mail_body }}"
@ -85,17 +84,17 @@ The `ansible_host` variable reflects the host a task is delegated to.
Delegating facts 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 - hosts: app_servers
tasks: tasks:
- name: gather facts from db servers - name: Gather facts from db servers
setup: ansible.builtin.setup:
delegate_to: "{{item}}" delegate_to: "{{ item }}"
delegate_facts: True delegate_facts: true
loop: "{{groups['dbservers']}}" 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`. 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 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 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 ansible-playbook playbook.yml --connection=local

View file

@ -23,7 +23,7 @@ You can set the environment directly at the task level::
tasks: tasks:
- name: Install cobbler - name: Install cobbler
package: ansible.builtin.package:
name: cobbler name: cobbler
state: present state: present
environment: environment:
@ -42,7 +42,7 @@ You can re-use environment settings by defining them as variables in your play a
tasks: tasks:
- name: Install cobbler - name: Install cobbler
package: ansible.builtin.package:
name: cobbler name: cobbler
state: present state: present
environment: "{{ proxy_env }}" 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 }} PATH: /var/local/nvm/versions/node/v4.2.1/bin:{{ ansible_env.PATH }}
tasks: tasks:
- name: check for package.json - name: Check for package.json
stat: ansible.builtin.stat:
path: '{{ node_app_dir }}/package.json' path: '{{ node_app_dir }}/package.json'
register: packagejson register: packagejson
- name: npm prune - name: Run npm prune
command: npm prune ansible.builtin.command: npm prune
args: args:
chdir: '{{ node_app_dir }}' chdir: '{{ node_app_dir }}'
when: packagejson.stat.exists when: packagejson.stat.exists
- name: npm install - name: Run npm install
npm: community.general.npm:
path: '{{ node_app_dir }}' path: '{{ node_app_dir }}'
when: packagejson.stat.exists 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:: You can also specify the environment at the task level::
--- ---
- name: install ruby 2.3.1 - name: Install ruby 2.3.1
command: rbenv install {{ rbenv_ruby_version }} ansible.builtin.command: rbenv install {{ rbenv_ruby_version }}
args: args:
creates: '{{ rbenv_root }}/versions/{{ rbenv_ruby_version }}/bin/ruby' creates: '{{ rbenv_root }}/versions/{{ rbenv_ruby_version }}/bin/ruby'
vars: 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:: 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 - name: Do not count this as a failure
command: /bin/false ansible.builtin.command: /bin/false
ignore_errors: yes 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 Ignoring unreachable host errors
================================ ================================
.. versionadded:: 2.7 .. 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 - name: This executes, fails, and the failure is ignored
command: /bin/true ansible.builtin.command: /bin/true
ignore_unreachable: yes ignore_unreachable: yes
- name: this executes, fails, and ends the play for this host - name: This executes, fails, and ends the play for this host
command: /bin/true ansible.builtin.command: /bin/true
And at the playbook level:: And at the playbook level::
- hosts: all - hosts: all
ignore_unreachable: yes ignore_unreachable: yes
tasks: tasks:
- name: this executes, fails, and the failure is ignored - name: This executes, fails, and the failure is ignored
command: /bin/true ansible.builtin.command: /bin/true
- name: this executes, fails, and ends the play for this host - name: This executes, fails, and ends the play for this host
command: /bin/true ansible.builtin.command: /bin/true
ignore_unreachable: no ignore_unreachable: no
.. _resetting_unreachable: .. _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:: 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 - 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 register: command_result
failed_when: "'FAILED' in command_result.stderr" failed_when: "'FAILED' in command_result.stderr"
or based on the return code:: or based on the return code::
- name: Fail task when both files are identical - 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 register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2 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:: 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 - 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 register: result
failed_when: failed_when:
- result.rc == 0 - 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 ``>``:: 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 - name: example of many failed_when conditions with OR
shell: "./myBinary" ansible.builtin.shell: "./myBinary"
register: ret register: ret
failed_when: > failed_when: >
("No such file or directory" in ret.stdout) or ("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: 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 register: bass_result
changed_when: "bass_result.rc != 2" changed_when: "bass_result.rc != 2"
# this will never report 'changed' status - name: This will never report 'changed' status
- shell: wall 'beep' ansible.builtin.shell: wall 'beep'
changed_when: False changed_when: False
You can also combine multiple conditions to override "changed" result:: 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 register: result
ignore_errors: True ignore_errors: True
changed_when: changed_when:
@ -149,11 +151,11 @@ See :ref:`controlling_what_defines_failure` for more conditional syntax examples
Ensuring success for command and shell 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: tasks:
- name: run this command and ignore the result - name: Run this command and ignore the result
shell: /usr/bin/somecommand || /bin/true ansible.builtin.shell: /usr/bin/somecommand || /bin/true
Aborting a play on all hosts 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 - hosts: load_balancers_dc_a
any_errors_fatal: True any_errors_fatal: true
tasks: tasks:
- name: 'shutting down datacenter [ A ]' - name: Shut down datacenter 'A'
command: /usr/bin/disable-dc ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a - hosts: frontends_dc_a
tasks: tasks:
- name: 'stopping service' - name: Stop service
command: /usr/bin/stop-software ansible.builtin.command: /usr/bin/stop-software
- name: 'updating software'
command: /usr/bin/upgrade-software - name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a - hosts: load_balancers_dc_a
tasks: tasks:
- name: 'Starting datacenter [ A ]' - name: Start datacenter 'A'
command: /usr/bin/enable-dc 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. 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:: This playbook, ``verify-apache.yml``, contains a single play with a handler::
--- ---
- name: verify apache installation - name: Verify apache installation
hosts: webservers hosts: webservers
vars: vars:
http_port: 80 http_port: 80
max_clients: 200 max_clients: 200
remote_user: root remote_user: root
tasks: tasks:
- name: ensure apache is at the latest version - name: Ensure apache is at the latest version
yum: ansible.builtin.yum:
name: httpd name: httpd
state: latest state: latest
- name: write the apache config file
template: - name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2 src: /srv/httpd.j2
dest: /etc/httpd.conf dest: /etc/httpd.conf
notify: notify:
- restart apache - Restart apache
- name: ensure apache is running
service: - name: Ensure apache is running
ansible.builtin.service:
name: httpd name: httpd
state: started state: started
handlers: handlers:
- name: restart apache - name: Restart apache
service: ansible.builtin.service:
name: httpd name: httpd
state: restarted state: restarted
In this example playbook, the second task notifies the handler. A single task can notify more than one handler:: In this example playbook, the second task notifies the handler. A single task can notify more than one handler::
- name: template configuration file - name: Template configuration file
template: ansible.builtin.template:
src: template.j2 src: template.j2
dest: /etc/foo.conf dest: /etc/foo.conf
notify: notify:
- restart memcached - Restart memcached
- restart apache - Restart apache
handlers: handlers:
- name: restart memcached - name: Restart memcached
service: ansible.builtin.service:
name: memcached name: memcached
state: restarted state: restarted
- name: restart apache
service: - name: Restart apache
ansible.builtin.service:
name: apache name: apache
state: restarted 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:: 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: tasks:
- shell: some tasks go here - name: Some tasks go here
- meta: flush_handlers ansible.builtin.shell: ...
- shell: some other tasks
- 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. 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:: 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: handlers:
# this handler name may cause your play to fail! # This handler name may cause your play to fail!
- name: restart "{{ web_service_name }}" - 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. 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" include_vars: "{{ ansible_facts.distribution }}.yml"
handlers: handlers:
- name: restart web service - name: Restart web service
service: ansible.builtin.service:
name: "{{ web_service_name | default('httpd') }}" name: "{{ web_service_name | default('httpd') }}"
state: restarted state: restarted
Handlers can also "listen" to generic topics, and tasks can notify those topics as follows:: Handlers can also "listen" to generic topics, and tasks can notify those topics as follows::
handlers: handlers:
- name: restart memcached - name: Restart memcached
service: ansible.builtin.service:
name: memcached name: memcached
state: restarted state: restarted
listen: "restart web services" listen: "restart web services"
- name: restart apache
service: - name: Restart apache
ansible.builtin.service:
name: apache name: apache
state: restarted state: restarted
listen: "restart web services" listen: "restart web services"
tasks: tasks:
- name: restart everything - name: Restart everything
command: echo "this task will restart the web services" ansible.builtin.command: echo "this task will restart the web services"
notify: "restart web services" notify: "restart web services"
This use makes it much easier to trigger multiple handlers. It also decouples handlers from their names, 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: 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 ``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 in the end of ``tasks`` section, but before any ``tasks`` handlers. * 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. * handlers are play scoped and as such can be used outside of the role they are defined in.