Handlers
Using Handlers
- A task that is triggered and is executed by a successful task.
Working with Handlers
- Define a notify statement at the level where the task is defined.
- The notify statement should list the name of the handler that is to be executed
- Handlers are listed at the end of the play.
- Make sure the name of the handler matches the name of the item that is called in the notify statement, because that is what the handler is looking for.
- Handlers can be specified as a list, so one task can call multiple handlers.
Lab
- Define the file index.html on localhost. Use this file in the second play to set up the web server.
- The handler is triggered from the task where the copy module is used to copy the index.html file.
- If this task is successful, the notify statement calls the handler.
- A second task is defined, which is intended to fail.
-
All tasks up to copy index.html run successfully. However, the task copy nothing fails, which is why the handler does not run. The solution seems easy: the handler doesn’t run because the task that copies the file /tmp/nothing fails as the source file doesn’t exist.
-
Create the source file using
touch /tmp/nothingon the control host and run the task again. -
After creating the source file and running the playbook again, the handler still doesn’t run.
-
Handlers run only if the task that triggers them gives a changed status.
Run an ad hoc command to remove the /var/www/html/index.html file on the managed hosts and run the playbook again:
ansible ansible2 -m file -a "name=/var/www/html/index.html state=absent"
Run the playbook again and you’ll see the handler runs.
Understanding Handler Execution and Exceptions
When a task fails, none of the following tasks run. How does that make handlers different? A handler runs only on the success of a task, but the next task in the list also runs only if the previous task was successful. What, then, is so special about handlers?
The difference is in the nature of the handler.
- Handlers are meant to perform an extra action when a task makes a change to a host.
- Handler should be considered an extension to the regular task.
- A conditional task that runs only upon the success of a previous task.
Two methods to get Handlers to run even if a subsequent task fails:
force_handlers: true (More specific and preferred)
- Used in the play header to ensure that the handler will run even if a task fails.
ignore_errors: true
- Used in the play header to accomplish the same thing.
• Handlers are specified in a handlers section at the end of the play. • Handlers run in the order they occur in the handlers section and not in the order as triggered. • Handlers run only if the task calling them generates a changed status. • Handlers by default will not run if any task in the same play fails, unless force_handlers or ignore_errors are used. • Handlers run only after all tasks in the play where the handler is activated have been processed. You might want to define multiple plays to avoid this behavior.
Lab: Working with Handlers
1. Open a playbook with the name exercise73.yaml.
2. Define the play header:
3. Add a task that updates the current kernel:
4. Add a handler that reboots the server in case the kernel was successfully updated:
5. Run the playbook using ansible-playbook exercise73.yaml andobserve its result. Notice that the handler runs only if the kernel was updated. If the kernel already was at the latest version, nothing has changed and the handler does not run. Also notice that it wasn’t really necessary to use force_handlers in the play header, but by using it anyway, at least you now know where to use it.
Dealing with Failures
Understanding Task Execution
- Tasks in Ansible playbooks are executed in the order they are specified.
- If a task in the playbook fails to execute on a host, the task generates an error and the play does not further execute on that specific host.
- This also goes for handlers: if any task that follows the task that triggers a handler fails, the handlers do not run.
- In both of these cases, it is important to know that the tasks that have run successfully still generate their result. Because this can give an unexpected result, it is important to always restore the original situation if that happens.
any_errors_fatal
- Used in the play header or on a block.
- Stop executing on all hosts when a failing task is encountered
Managing Task Errors
Generically, tasks can generate three different types of results. ok
- The tasks has run successfully but no changes were applied changed
- The task has run successfully and changes have been applied failed
- While running the task, a failure condition was encountered
ignore_errors: yes
- Keep running the playbook even if a task fails
force_handlers. If
- can be used to ensure that handlers will be executed, even if a failing task was encountered.
Lab: ignore_errors
Lab: Forcing Handlers to Run
Specifying Task Failure Conditions
failed_when
- conditional used to evaluate some expression.
- Set a failure condition on a task
Lab: failed_when
fail module
- specify when a task fails.
- Using this module makes sense only if when is used to define the exact condition when a failure should occur.
Lab: Using the fail Module
- The ignore_errors statement has movedfrom the task definition to the play header.
- Without this move, the message “second task executed” would never be shown because the fail module always generates a failure message.
- The main advantage of using the fail module instead of using failed_when is that the fail module can easily be used to set a clear failure message, which is not possible when using failed_when.
Managing Changed Status
In Ansible, there are commands that change something and commands that don’t. Some commands, however, are not very obvious in reporting their status.
Lab: Change status
-
Reports a changed status, even if nothing really was changed!
-
Managing the changed status can be useful in avoiding unexpected results while running a playbook.
changed_when
- If you set changed_when to false, the playbook reports only an ok or failed status and never reports a changed status.
Lab: Using changed_when
Using Blocks
- Useful when working with conditional statements.
- A group of tasks to which a when statement can be applied.
- As a result, if a single condition is true, multiple tasks can be executed.
- To do so, between the tasks: statement in the play header and the actual tasks that run the specific modules, you can insert a block: statement.
Lab: Using Blocks
- The when statement is applied at the same level as the block definition.
- When you define it this way, the tasks in the block are executed only if the when statement is true.
Using Blocks with rescue and always Statements
- Blocks can be used for simple error handling as well, in such a way that if any task that is defined in the block statement fails, the tasks that are defined in the rescue section are executed.
- Besides that, an always section can be used to define tasks that should always run, regardless of the success or failure of the tasks in the block.
Lab: Using Blocks, rescue, and always
- Run this twice to see the rescue. (The file is already created so a task in the block fails)
command_warnings=False
-
Setting in ansible.cfg to avoid seeing command module warning message.
-
you cannot use a block on a loop.
-
If you need to iterate over a list of values, think of using a different solution.