Определение

Хендлер (handler) — это тип таски в Ansible, который предназначен для выполнения после вызова другими тасками

Основные отличия таски от хендлера:

  1. Хендлеры выполняются в самом конце группы тасок и могут быть вызваны до 3 раз (см. предыдущий пост про pre_tasks/post_tasks), при этом порядок выполнения будет соответствовать порядку записи
  2. Хендлеры вызываются директивой notify: <имя_хендлера> и выполняются только если результат таски имеет статус CHANGED
  3. Хендлер будет выполнен один раз, даже если он был вызвал несколько раз

Согласно официальной документации хендлеры стоит применять для перезапуска или перезагрузки служб, но на этом область их применения не заканчивается, в общем виде использование хендлера выглядит так:

tasks:
  - name: Configure network
    ansible.builtin.template:
      dest: /etc/network/interfaces
      src: networking.j2
      owner: root
      group: root
      mode: "0644"
      backup: true
    notify: Restart networking

handlers:
  - name: Restart networking
    ansible.builtin.systemd:
      name: networking
      state: restarted

Практика

Логически таска ниже равносильна таске выше, но использование when: <some_var>.changed — очень плохая практика:

tasks:
  - name: Configure network
    ansible.builtin.template:
      dest: /etc/network/interfaces
      src: networking.j2
      owner: root
      group: root
      mode: "0644"
      backup: true
    register: network_config

  - name: Restart networking
    ansible.builtin.systemd:
      name: networking       
      state: restarted
    when: network_config.changed

Но на этом не все, хендлеры умеют многое другое, например:

Хендлер может вызывать другие хендлеры или даже группу задач через include_tasks или import_tasks:

tasks:
  - name: Always changed
    ansible.builtin.command: /bin/true
    notify: 
      - First handler

handlers:
  - name: First handler
    ansible.builtin.command: /bin/true
    notify: Inlcude tasks

  - name: Inlcude tasks
    ansible.builtin.include_tasks: tasks/main.

Можно изменить имя для вызова хендлера путем использования Jinja2 шаблона в имени хендлера, либо использовав директиву listen:, но для listen: можно использовать только статичные строки:

vars:
  service: apache2
tasks:
  - name: Always changed
    ansible.builtin.command: /bin/true
    notify: 
      - Restart {{ service }}
      - Reboot

handlers:
  - name: Restart {{ service }}
    ansible.builtin.systemd:
      name: "{{ service }}"
      state: restarted

  - name: Reboot task
    ansible.builtin.reboot:
    listen: Reboot

Используя модуль ansible.builtin.meta: flush_handlers можно выполнить все ранее вызванные хендлеры и сбросить их счетчик, что позволит вызвать их снова:

tasks:
  - name: Configure network
    ansible.builtin.template:
      dest: /etc/network/interfaces
      src: networking.j2
      owner: root
      group: root
      mode: "0644"
      backup: true
    notify: Restart networking

  - name: Restart right away
    ansible.builtin.meta: flush_handlers

  - name: Notify again
    ansible.builtin.command: /bin/true
    notify: Restart networking

handlers:
  - name: Restart networking
    ansible.builtin.systemd:
      name: networking
      state: restarted

Поскольку roles просто “включается” в tasks, то можно вызвать хендлеры из роли (пример можно также найти в предыдущем посте), при этом для удобства можно использовать конструкцию имя_роли/FQCN_роли : имя хендлера, например:

- name: Always change
  ansible.builtin.command: /bin/true
  notify: 'k3s.orchestration.raspberrypi : Reboot Pi'

Исходя из 3 пунтка единственное условия для вызова хендлера это статус CHANGED, и, как следствие, можно самому определить условие изменение путем changed_when: <условие>:

tasks:
  - name: Run sample task
    ansible.builtin.command: /bin/true
    changed_when: false
    notify: Say hello

# Не будет вызван из-за 'changed_when: false'
handlers:
  - name: Say hello
    ansible.builtin.debug:
      msg: Hello guys