[2.10] Doc backports: 71753, 71754, 71769, 71770, 71771, 71772 (#71807)
* Docsite: misc fixes of playbooks_delegation (#71753) (cherry picked from commit05a45f63ff
) * Docsite: fix playbooks_conditionals (#71754) (cherry picked from commitaa1f0bd062
) * Docsite: fix user_guide/playbooks_blocks (#71769) (cherry picked from commit79dc6fa948
) * Docsite: fix user_guide/playbooks_handlers (#71770) (cherry picked from commit1cf42897d2
) * Docsite: fix user_guide/playbooks_error_handling (#71771) (cherry picked from commit2c6661d4c1
) * Docsite: fix user_guide/playbooks_environment (#71772) (cherry picked from commita204f5f955
)
This commit is contained in:
parent
aacff5e35f
commit
47cabb1d32
6 changed files with 256 additions and 184 deletions
|
@ -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'
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
src: "{{ item }}"
|
||||
dest: /etc/myapp/foo.conf
|
||||
- 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/']
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue