ansibleansible-roleansible-handlers

Execute Ansible reboot with handler only when required


I have the following file structure:

$ tree roles/os
roles/os
├── handlers
│   └── main.yaml
└── tasks
    └── main.yaml

tasks/main.yaml:

---
- name: Upgrade all packages
  ansible.builtin.apt:
    update_cache: true
    upgrade: dist

- name: Update bootloader firmware
  ansible.builtin.command: rpi-eeprom-update -ad
  changed_when: eeprom_update.stdout.find('updates pending') != -1
  notify: Perform reboot
  register: eeprom_update

- name: Check reboot status
  ansible.builtin.stat:
    path: /var/run/reboot-required
    get_checksum: false
  changed_when: reboot.stat.exists
  notify: Perform reboot
  register: reboot

handlers/main.yaml:

---
- name: Perform reboot
  ansible.builtin.reboot:
    post_reboot_delay: 30
  when: >
    eeprom_update.stdout.find('updates pending') != -1 or
    reboot.stat.exists

With the above configuration, a reboot is performed, only if a change occurs.

Is that the correct way to perform a conditional reboot with a handler?


Solution

  • You don't have to repeat the conditions in the handler. The conditions were used to notify the handler in the tasks.

      when: >
        eeprom_update.stdout.find('updates pending') != -1 or
        reboot.stat.exists
    

    Remove the condition from the handler

    - name: Perform reboot
      ansible.builtin.reboot:
        post_reboot_delay: 30
    

    Notes:

    • In the handler, the syntax of the condition is wrong. The result of the expression is a string. Test it
        - name: Test the syntax of the condition
          block:
            - name: Wrong. The result of the expression is string.
              debug:
                msg: >
                  eeprom_update.stdout.find('updates pending') != -1 or
                  reboot.stat.exists
            - name: Correct. The result of the expression is boolean.
              debug:
                msg: "{{ eeprom_update.stdout.find('updates pending') != -1 or
                         reboot.stat.exists }}"
    
            - name: Do not use braces "{{ '{{' }}" in the conditions. Variables are
                    expanded automatically in a condition.
              debug:
                msg: Correct. The result of the expression is boolean
              when: eeprom_update.stdout.find('updates pending') != -1 or
                    reboot.stat.exists
    

    gives

    TASK [Wrong. The result of the expression is string.] ****************************************
    ok: [localhost] => 
      msg: |-
        eeprom_update.stdout.find('updates pending') != -1 or reboot.stat.exists
    
    TASK [Correct. The result of the expression is boolean.] *************************************
    ok: [localhost] => 
      msg: true
    
    TASK [Do not use braces "{{" in the conditions. Variables are expanded automatically in a condition.] ***
    ok: [localhost] => 
      msg: Correct. The result of the expression is boolean
    
    shell> tree .
    .
    ├── ansible.cfg
    ├── hosts
    └── pb.yml
    
    0 directories, 3 files
    
    shell> cat ansible.cfg 
    [defaults]
    gathering = explicit
    inventory = $PWD/hosts
    roles_path = $PWD/roles
    remote_tmp = ~/.ansible/tmp
    retry_files_enabled = false
    stdout_callback = yaml
    
    shell> cat hosts 
    localhost
    

    The reboot file exists

    shell> ls -1 /tmp/reboot-required 
    /tmp/reboot-required
    

    The playbook

    shell> cat pb.yml 
    - hosts: localhost
    
      tasks:
    
        - name: Update bootloader firmware
          ansible.builtin.command: echo 'updates pending'
          register: eeprom_update
          changed_when: eeprom_update.stdout.find('updates pending') != -1
          notify: Perform reboot
        - debug:
            var: eeprom_update.stdout
    
        - name: Check reboot status
          ansible.builtin.stat:
            path: /tmp/reboot-required
            get_checksum: false
          register: reboot
          changed_when: reboot.stat.exists
          notify: Perform reboot
        - debug:
            var: reboot.stat.exists
    
        - name: Test the syntax of the condition
          block:
            - name: Wrong. The result of the expression is string.
              debug:
                msg: >
                  eeprom_update.stdout.find('updates pending') != -1 or
                  reboot.stat.exists
            - name: Correct. The result of the expression is boolean.
              debug:
                msg: "{{ eeprom_update.stdout.find('updates pending') != -1 or
                         reboot.stat.exists }}"
    
            - name: Do not use braces '{{' '}}' in the conditions. Variables are
                    expanded automatically in a condition.
              debug:
                msg: Correct. The result of the expression is boolean
              when: eeprom_update.stdout.find('updates pending') != -1 or
                    reboot.stat.exists
            
      handlers:
    
        - name: Perform reboot
          debug:
            msg: Perform reboot
    

    gives

    shell> ansible-playbook pb.yml 
    
    PLAY [localhost] *****************************************************************************
    
    TASK [Update bootloader firmware] ************************************************************
    changed: [localhost]
    
    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      eeprom_update.stdout: updates pending
    
    TASK [Check reboot status] *******************************************************************
    changed: [localhost]
    
    TASK [debug] *********************************************************************************
    ok: [localhost] => 
      reboot.stat.exists: true
    
    TASK [Wrong. The result of the expression is string.] ****************************************
    ok: [localhost] => 
      msg: |-
        eeprom_update.stdout.find('updates pending') != -1 or reboot.stat.exists
    
    TASK [Correct. The result of the expression is boolean.] *************************************
    ok: [localhost] => 
      msg: true
    
    TASK [Do not use braces "{{" in the conditions. Variables are expanded automatically in a condition.] ***
    ok: [localhost] => 
      msg: Correct. The result of the expression is boolean
    
    RUNNING HANDLER [Perform reboot] *************************************************************
    ok: [localhost] => 
      msg: Perform reboot
    
    PLAY RECAP ***********************************************************************************
    localhost: ok=8    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0