ansible implements handlers and handles failed tasks

1. Implement handling procedures

1.1 ansible handler

Ansible modules are designed to be idempotent. This means that in a properly written playbook, the playbook and its tasks can run multiple times without changing the managed host unless changes are needed to bring the managed host into the desired state.

However, when the task does change the system, it may be necessary to run further tasks. For example, changing a service profile may require that the service be reloaded for its changed configuration to take effect.

A handler is a task that responds to notifications triggered by other tasks. The task notifies its handler only when it changes something on the managed host. Each handler has a globally unique name and is triggered at the end of the task block in the playbook. If no task notifies the handler by name, the handler will not run. If one or more tasks notify the handler, the handler runs once after all other tasks in play are completed. Because handlers are tasks, you can use the modules they will use for any other task in the handler. Typically, the handler is used to reboot the host and restart the service.

A handler can be considered an inactive task and is triggered only when explicitly invoked with a notify statement. In the following code snippet, the restart httpd handler will restart the Apache server only if the configuration file is updated and the task is notified:

---
- host: 192.168.101.200
  tasks:
    - name: install apache
      yum:
        name: httpd
        state: present
    
    - name: service httpd
      service:
        name: httpd
        state: restarted
        enabled: yes

    - name: config httpd
      copy:
        src: files/httpd.conf
        dest: /etc/httpd/conf.d/httpd.conf
      notify:
        - restart httpd

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

tasks:
  - name: config httpd
    template:
      src: files/httpd.conf
      dest: /etc/httpd/conf.d/httpd.conf
    notify:
      - restart mysql
      - restart apache

handlers:
  - name: restart mysql
    service:
      name: mariadb
      state: restarted
      
  - name: restart apache
    service:
      name: httpd
      state: restarted

1.2 benefits of using handlers

There are several important things to keep in mind when using handlers:

  • Handlers always run in the order specified in the handlers section of play. They do not run in the order listed by the notify statement in the task, or in the order notified to them by the task.
  • The handler typically runs after all other tasks in the associated play have been completed. The handler called by a task in the tasks section of the playbook will not run until all tasks under the tasks have been processed.
  • The handler name exists in each play namespace. If two handlers are mistakenly given the same name, only one will run.
  • Even if there are multiple task notification handlers, the handler runs only once. If there is no task notification handler, it will not run.
  • If the task that contains the notify statement does not report the changed result (for example, the package is installed and the task reports ok), the handler is not notified. The handler will be skipped until it is notified by another task. Ansible notifies the handler only if the related task reports the changed status.
  • The handler is used to perform additional actions when the task makes changes to the managed host. They should not be used as a substitute for normal tasks.

2. Failed to process task

2.1 manage error tasks in play

Ansible evaluates the return code of the task to determine whether the task succeeded or failed. Generally speaking, when a task fails, ansible will immediately abort the rest of the play on the host and skip all subsequent tasks.

But sometimes, you may want to continue play ing even if the task fails. For example, you might expect a pending task to fail and want to fix it by conditionally running some other task.

Ansible has several functions to manage task errors.

2.2 ignore task failure

By default, play aborts when a task fails. However, you can override this behavior by ignoring failed tasks. Ignore can be used in tasks_ Errors keyword to achieve this.

The following code snippet demonstrates how to use ignore in a task_ Errors to continue to execute playbook on the host if the task fails. For example, if the httpd package does not exist, the yum module will fail, but ignore_ If errors is set to yes, execution will continue.

---
- host: 192.168.101.200
  tasks:
    - name: install apache
      yum:
         name: httpd
         state: present
       ignore_errors: yes
       

3.3 enforce handler after task failure

In general, if a task fails and play is aborted on that host, the handler that receives notification of an earlier task in play will not run. If force is set in play_ Handlers: Yes keyword, the notified handler will be called even if play is aborted due to subsequent task failure.

The following code snippet demonstrates how to use force in play_ Handlers keyword. Even if the installation fails because there is no abcdefg such service, the restart task in handlers will not stop:

- host: 192.168.101.200
  force_handlers: yes
  tasks:
    - name: config httpd
      copy:
        src: files/httpd.conf
        dest: /etc/httpd/conf.d/httpd.conf
      notify:
        - restart httpd
     
     - name: install abcdefg
      yum:
        name: abcdefg
        state: present

  handlers:
    - name: restart httpd
      service:
        name: httpd
        state: restarted

Remember that the handler is notified when the task reports the changed result, but not when the task reports the ok or failed result.

2.4 failed to specify task conditions

Failed can be used in tasks_ When keyword to specify a condition indicating that the task has failed. This is usually used in conjunction with command modules, which may successfully execute a command, but the output of the command may indicate failure.

For example, you can run a script that outputs an error message and use that message to define the failure status of a task. The following code snippet demonstrates how to use failed in a task_ When keyword:

tasks:
  - name: Run user creation script
    shell: /usr/local/bin/create_users.sh
    register: command_result
    failed_when: "'Password missing' in command_result.stdout"

The fail module can also be used to force a task to fail. The above scenario can also be written as two tasks:

tasks:
  - name: Run user creation script
    shell: /usr/local/bin/create_users.sh
    register: command_result
    ignore_errors: yes
    
  - name: Report script failure
    fail:
      msg: "The password is missing in the output"
      when: "'Password missing' in command_result.stdout"

We can use the fail module to provide a clear failure message for the task. This method also supports deferred failures, allowing intermediate tasks to be run to complete or roll back other changes.

2.5 specify when the task reports "Changed" results

When the task makes changes to the managed host, it reports the changed status and notifies the handler. If the task does not require changes, it reports ok and does not notify the handler.

changed_ The when keyword can be used to control when a task reports that it has changed. For example, the shell module in the next example will be used to obtain Kerberos credentials for subsequent tasks. It usually always reports changed at run time. To resist this change, set changed_when: false so that it only reports ok or failed.

- name: get Kerberos credentials as "admin"
  shell: echo "{{ krb_admin_pass }}" | kinit -f admin
  changed_when: false

The following example uses a shell module to report changed based on the output of the module collected through registered variables:

tasks:
  - shell:
      cmd: /usr/local/bin/upgrade-database
    register: command_result
    changed_when: "'Success' in command_result.stdout"
    notify:
      - restart_database

handlers:
  - name: restart_database
    service:
      name: mariadb
      state: restarted

2.6 ansible blocks and error handling

In playbook, blocks are clauses that logically group tasks, and can be used to control how tasks are executed. For example, a task block can contain the when keyword to apply a condition to multiple tasks:

- name: block example
  hosts: 172.16.103.129
  tasks:
    - name: installing and configuring Yum versionlock plugin
      block:
      - name: package needed by yum
        yum:
          name: yum-plugin-versionlock
          state: present
      - name: lock version of tadata
        lineinfile:
          dest: /etc/yum/pluginconf.d/versionlock.list
          line: tzdata-2020j-1
          state: present
      when: ansible_distribution == "Redhat"

Through the block, you can also combine the rescue and always statements to handle errors. If any task in the block fails, perform the task in its rescue block to recover. After the task in the block clause and the task in the rescue clause (if there is a failure) run, the task in the always clause runs. Summary:

  • block: defines the main task to run
  • rescue: defines the task to run when the task defined in the block clause fails
  • Always: defines tasks that always run independently, regardless of whether the tasks defined in the block and rescue clauses succeed or fail

The following example demonstrates how to implement blocks in playbook. Even if the task defined in the block clause fails, the task defined in the rescue and always clauses will execute.

tasks:
  - name: Upgrade DB
    block:
      - name: upgrade the database
        shell:
          cmd: /usr/local/lib/upgrade-database
    rescue:
      - name: revert the database upgrade
        shell:
          cmd: /usr/local/lib/revert-database
    always:
      - name: always restart the database
        service:
          name: mariadb
          state: restarted

when condition in block is also applied to its rescue and always clauses, if any.

Keywords: Linux

Added by britey on Thu, 13 Jan 2022 22:23:22 +0200