Search code examples
ansiblehandler

Ansible rolling execution block of handlers on targets


I have an ansible playbook that calls a role, containing many tasks and handlers. the handlers will be triggered if my desired configuration of service is changed.

What I am looking for is a way to trigger multiple handlers on the hosts sequently. for example, if I have to targets target1 and target2 and I have also two handlers handler1 and handler2, What I want in execution of handlers on targets would be something like below:

RUNNING HANDLER [myrole : handler 1] ********************************************
changed: [target1]

RUNNING HANDLER [myrole : handler2] *************************************************
changed: [target1]

RUNNING HANDLER [myrole : handler 1] ********************************************
changed: [target2]

RUNNING HANDLER [myrole : handler2] *************************************************
changed: [target2]

But as is known, the normal execution of handlers on targets are as below:

RUNNING HANDLER [myrole : handler 1] ********************************************
changed: [target1]
changed: [target2]

RUNNING HANDLER [myrole : handler 2] ********************************************
changed: [target1]
changed: [target2]

That it is not what I want.

I know that with using of serial option in playbook level we can restrict parallelism, but this option will bring the cost of huge time consuming because all of my tasks would be executed in serial as well.

The ways I have tried was using both of throttle option and block directive on handlers but it wasn't usefull.


Solution

  • flush_handlers on each host separately. Dynamically create and include the file. For example, the playbook

    shell> cat pb.yml
    - hosts: target1,target2
    
      tasks:
    
        - debug:
            msg: Notify handler1
          changed_when: true
          notify: handler1
        - debug:
            msg: Notify handler2
          changed_when: true
          notify: handler2
    
        - block:
            - copy:
                dest: "{{ playbook_dir }}/flush_handlers_serial.yml"
                content: |
                  {% for host in ansible_play_hosts_all %}
                  - meta: flush_handlers
                    when: inventory_hostname == '{{ host }}'
                  {% endfor %}
              delegate_to: localhost
            - include_tasks: flush_handlers_serial.yml
          run_once: true
          when: flush_handlers_serial|d(false)|bool
          
      handlers:
    
        - name: handler1
          debug:
            msg: Run handler1
        - name: handler2
          debug:
            msg: Run handler2
    

    by default runs the handlers in parallel (see linear strategy)

    shell> ansible-playbook pb.yml
    
    PLAY [target1,target2] ***************************************************************************************
    
    TASK [debug] *************************************************************************************************
    changed: [target1] => 
      msg: Notify handler1
    changed: [target2] => 
      msg: Notify handler1
    
    TASK [debug] *************************************************************************************************
    changed: [target2] => 
      msg: Notify handler2
    changed: [target1] => 
      msg: Notify handler2
    
    TASK [copy] **************************************************************************************************
    skipping: [target1]
    
    TASK [include_tasks] *****************************************************************************************
    skipping: [target1]
    
    RUNNING HANDLER [handler1] ***********************************************************************************
    ok: [target1] => 
      msg: Run handler1
    ok: [target2] => 
      msg: Run handler1
    
    RUNNING HANDLER [handler2] ***********************************************************************************
    ok: [target1] => 
      msg: Run handler2
    ok: [target2] => 
      msg: Run handler2
    
    PLAY RECAP ***************************************************************************************************
    target1: ok=4    changed=2    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0   
    target2: ok=4    changed=2    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
    

    When you enable flush_handlers_serial=true the file below will be created and included

    shell> cat flush_handlers_serial.yml 
    - meta: flush_handlers
      when: inventory_hostname == 'target1'
    - meta: flush_handlers
      when: inventory_hostname == 'target2'
    

    This will run the handlers serially, similarly to the strategy host_pinned

    shell> ansible-playbook pb.yml -e flush_handlers_serial=true
    
    PLAY [target1,target2] ***************************************************************************************
    
    TASK [debug] *************************************************************************************************
    changed: [target1] => 
      msg: Notify handler1
    changed: [target2] => 
      msg: Notify handler1
    
    TASK [debug] *************************************************************************************************
    changed: [target1] => 
      msg: Notify handler2
    changed: [target2] => 
      msg: Notify handler2
    
    TASK [copy] **************************************************************************************************
    changed: [target1 -> localhost]
    
    TASK [include_tasks] *****************************************************************************************
    included: /export/scratch/tmp7/test-172/flush_handlers_serial.yml for target1
    
    TASK [meta] **************************************************************************************************
    
    RUNNING HANDLER [handler1] ***********************************************************************************
    ok: [target1] => 
      msg: Run handler1
    
    RUNNING HANDLER [handler2] ***********************************************************************************
    ok: [target1] => 
      msg: Run handler2
    
    RUNNING HANDLER [handler1] ***********************************************************************************
    ok: [target2] => 
      msg: Run handler1
    
    TASK [meta] **************************************************************************************************
    skipping: [target1]
    
    RUNNING HANDLER [handler2] ***********************************************************************************
    ok: [target2] => 
      msg: Run handler2
    
    PLAY RECAP ***************************************************************************************************
    target1: ok=6    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    target2: ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0