Ansible task that ends all plays defined in playbook yaml file

65 Views Asked by At

To start with I am using:

ansible --version
ansible [core 2.16.3]
  config file = None
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/.local/lib/python3.11/site-packages/ansible
  ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/ansible/.local/bin/ansible
  python version = 3.11.6 (main, Feb  7 2024, 22:18:53) [GCC 7.3.1 20180712 (Red Hat 7.3.1-17)] (/usr/local/bin/python3.11)
  jinja version = 3.1.3
  libyaml = True

And I have an Ansible role that I use in 2 different plays in one playbook transfer-files.yml as such:

- name: CMS - Transfer CSV files 1
  become: true
  hosts: host1
  roles:
    - { role: transfer_files, TRANSFER_FILES_MODE: "cms" }

- name: COMMERCE - Transfer CSV files 2
  become: true
  hosts: host2
  roles:
    - { role: transfer_files, TRANSFER_FILES_MODE: "commerce" }

In my role I use the TRANSFER_FILES_MODE to decide which tasks get executed cms.yml or commerce.yml. In cms.yml I have the following task:

...

- name: If directory is empty end the play silently
  ansible.builtin.meta: end_play
  when: files_found.matched < 1

...

So with all this said when my play gets to this task in cms.yml and the directory is empty it nicely ends the first play but it still executes the second play.

Is there something I can do to end all executions of all plays listed in a playbook in one task?

2

There are 2 best solutions below

0
Alexander Pletnev On

Generally, no, because Ansible runs the plays sequentially and the next play knows nothing about the previous one.

But you can set some fact before that meta task, delegate it to localhost, and use include_role with a corresponding when condition that checks hostvars.localhost.<your_fact> on all the subsequent plays.

0
Vladimir Botka On

It's not possible to terminate a playbook gracefully. Only end_host and end_play are available in the module meta. The option end_playbook is missing. As a consequence, it's up to you to share the termination flag among all hosts.

  • The solution is trivial if the flag (in this case the variable terminate) is created in a single host play. For example, delegate the variable to localhost in the first play and test it in all consequent plays
- hosts: host1
  tasks:
    - debug:
        msg: play 1 running ...
    - set_fact:
        terminate: true
      when: files_found|d(1)|int < 1
      delegate_to: localhost
      delegate_facts: true

 hosts: host2
  tasks:
    - meta: end_play
      when: hostvars.localhost.terminate|d(false)
    - debug:
        msg: play 2 running ...

- hosts: host3
  tasks:
    - meta: end_play
      when: hostvars.localhost.terminate|d(false)
    - debug:
        msg: play 3 running ...
  • The situation will be more complicated if there are more hosts in the plays and any of them can set the termination flag. In this case, test all of them
    - meta: end_play
      when: hostvars|json_query('*.terminate')|length > 0

This solution benefits from the fact that hostvars of all hosts in the inventory (yes, in the inventory, not only the hosts in a playbook) are available across the whole playbook. This makes the termination condition rather fragile.


Example of a complete playbook for testing

- hosts: host1,host11
  tasks:
    - debug:
        msg: play 1 running ...
    - set_fact:
        terminate: true
      when: inventory_hostname == 'host1'

- hosts: host2,host12
  tasks:
    - block:
        - debug:
            msg: "{{ hostvars|json_query('*.terminate') }}"
        - meta: end_play
          when: hostvars|json_query('*.terminate')|length > 0
      run_once: true
    - debug:
        msg: play 2 running ...

- hosts: host3,host13
  tasks:
    - block:
        - debug:
            msg: "{{ hostvars|json_query('*.terminate') }}"
        - meta: end_play
          when: hostvars|json_query('*.terminate')|length > 0
      run_once: true
    - debug:
        msg: play 3 running ...