Ansible Handlers

Handlers are tasks that respond to a notification triggered by other tasks. Each handler has a globally-unique name, and is triggered at the end of a block of tasks in a playbook. If no task notifies the handler by name, it will not run. If one or more tasks notify the handler, it will run exactly once after all other tasks in the play have completed. Because handlers are tasks, administrators can use the same modules in handlers that they would for any other task.
Normally, handlers are used to reboot hosts and restart services.

Handlers can be seen as inactive tasks that only get triggered when explicitly invoked using a notify statement. The following snippet shows how the Apache server is only restarted by the restart_apache task when a configuration file is updated and notifies it:

1. The task that notifies the handler.
2. The notify statement indicates the task needs to trigger a handler.
3. The name of the handler to run.
4. The statement starts the handlers section.
5. The name of the handler invoked by tasks.
6. The module to use for the handler.

In the previous example, the restart_apache handler will trigger when notified by the copy task that a change happened. A task may call more than one handler in their notify section. Ansible treats the notify statement as an array and iterates over the handler names:


Using handlers
As discussed in the Ansible documentation, there are some important things to remember about using handlers:

  • Handlers are always run in the order in which the handlers section is written in the play, not in the order in which they are listed by the notify statement on a particular task.
  • Handlers run after all other tasks in the play complete. A handler called by a task in the tasks: part of the playbook will not run until all of the tasks under tasks: have been processed.
  • Handler names live in a global namespace. If two handlers are incorrectly given the same name, only one will run.
  • Handlers defined inside an include can not be notified.
  • Even if more than one task notifies a handler, the handler will only run once. If no tasks notify it, a handler will not run.
  • If a task that includes a notify does not execute (for example, a package is already installed), the handler will not be notified. The handler will be skipped unless another task notifies it. Ansible notifies handlers only if the task acquires the CHANGED status.

Handlers are meant to perform an action upon the execution of a task; they should not be used as a replacement for tasks.


Use Conditionals to Control Play Execution

The playbook below search for the regular expression DocumentRoot.*$ in the apache conf file /etc/httpd/conf/httpd.conf. The old conf fiile is backuped. This will triger the apache restart, which handler "restart apache" do.

Running playbook2.yml:

Let’s check on the managedhost1 if the playbook2.yml works:

httpd.conf.4262.2020-01-22@19:53:38~ is backup file of old httpd.conf.


Example 1.

Create the configure_db.yml playbook file. This file will install a database server and create some users. When the database server is installed, the playbook restarts the service.  Start the playbook with the initialization of some variables: db_packages, which defines the name of the packages to install for the database service; db_service, which defines the name of the database service; src_file for the URL of the configuration file to install; and dst_file for the location of the installed configuration file on the managed hosts.  Define a task that uses the yum module to install the required database packages as defined by the db_packages variable. Notify the handler start_service in order to start the service. Add a task to download my.cnf.template to /etc/my.cnf on the managed host, using the get_url module. Add a condition that notifies the handler restart_service as well as set_password, to restart the database service and set the administrative password.

Define the three handlers the tasks needs. The start_service handle will start the mariadb service; the restart_service handler will restart the mariadb service; and the set_password handler will set the administrative password for the database service. Define the start_service handler. Define the second handler, restart_service.

Finally, define the handler that sets the administrative password. The handler will use the mysql_user module to perform the command.

When completed, the playbook should look like the following:

Run the configure_db.yml playbook. Notice the output shows the handlers are being executed.

Run the playbook again. This time the handlers are skipped.

Because handlers are executed once, they can help prevent failures. Update the playbook to add a task after installing /etc/my.cnf that sets the MySQL admin password like the set_password handler. This will show you why using a handler in this situation is better than a simple task. The task should read as follows:

Run the playbook again. The task should fail since the MySQL password has already been set.