Reputation: 161
I have two plays (1 and 2) in my playbook. First play play1 has two tasks (A/B). If task A fails, I need task B also be executed and then playbook exits. In otherwords, play 2 will be skipped. So I used block/always method. It works fine when the host is single host. But when I specify multiple hosts to plays, play2 still got executed. Although play2 was only executed against one host, I expect the playbook to exit before play2.
I tried to add any_errors_fatal to task A, however it doesn't work.
name: Test Block 1
hosts: pltB
gather_facts: no
tasks:
block:
register: hostname_res
any_errors_fatal: true
always:
name: Test Block 2
hosts: pltB
gather_facts: no
tasks:
block:
always:
ansible-playbook test.yml -i ../inventory/serverhosts
PLAY [Test Block 1] **************************************************************************************
TASK [command] *************************************************************************************** fatal: [192.168.111.25]: FAILED! => {"changed": false, "cmd": "/usr/bin/hostname1", "msg": "[Errno 2] No such file or directory", "rc": 2}
TASK [debug] ************************************************************************************* ok: [192.168.111.25] => { "msg": "from always block 1" } to retry, use: --limit @/home/playbooks/test.retry
PLAY RECAP ************************************************************************************* 192.168.111.25 : ok=1 changed=0 unreachable=0 failed=1
name: Test Block 1
hosts: pltB,pltA
gather_facts: no
tasks:
block:
register: hostname_res
any_errors_fatal: true
always:
name: Test Block 2
hosts: pltB,pltA
gather_facts: no
tasks:
block:
always:
PLAY [Test Block 1] ***********************************************************************************
TASK [command] *************************************************************************************** fatal: [192.168.111.25]: FAILED! => {"changed": false, "cmd": "/usr/bin/hostname1", "msg": "[Errno 2] No such file or directory", "rc": 2} changed: [192.168.111.24]
TASK [debug] *************************************************************************************** ok: [192.168.111.25] => { "msg": "from always block 1" } ok: [192.168.111.24] => { "msg": "from always block 1" }
PLAY [Test Block 2] *********************************************************************************
TASK [debug] *************************************************************************************** ok: [192.168.111.24] => { "msg": "result is plt001 " }
TASK [debug] ************************************************************************************** ok: [192.168.111.24] => { "msg": "from always block 2" } to retry, use: --limit @/home/playbooks/test.retry
PLAY RECAP ******************************************************************************************************* 192.168.111.24 : ok=4 changed=1 unreachable=0 failed=0 192.168.111.25 : ok=1 changed=0 unreachable=0 failed=1
Upvotes: 3
Views: 4010
Reputation: 75
I think this behaviour is a bug in Ansible, however I'll start with a workaround to get the behaviour I think you're after.
Register a fact with the number of hosts in ansible_play_batch
prior to the block
, and have a task after the block/always with a task to fail when the number of active hosts reduces.
From the Ansible Special Variables documentation
ansible_play_batch
List of active hosts in the current play run limited by the serial, aka ‘batch’. Failed/Unreachable hosts are not considered ‘active’.
https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html
---
- hosts: all
gather_facts: no
tasks:
- name: set fact with play batch size
local_action:
module: set_fact
play_batch_size: "{{ ansible_play_batch|length|int }}"
run_once: yes
- block:
- name: failing task on some hosts
command: "cat test.txt"
any_errors_fatal: true
always:
- name: message from always section
debug:
msg: "Debug task success"
- name: fail if the batch size has decreased
local_action:
module: fail
msg: "Halting playbook execution due to prior error, when {{ ansible_play_batch|length|int }} < {{ play_batch_size }}"
when: ansible_play_batch|length|int < play_batch_size
run_once: true
- name: this should never run
debug:
msg: "Epic fail"
This playbook works as expected with the exception of correctly identifying the failed hosts in the console PLAY RECAP.
The behaviour you're seeing and I'm reproducing with Ansible v2.8.1 is that the always
section completing successfully is erasing the error status and not triggering the any_errors_fatal condition.
Erasing the error status is a documented feature of using a rescue
block. As per the current latest Ansible documentation on Blocks,
The
always
section runs no matter what previous error did or did not occur in theblock
andrescue
sections. It should be noted that the play continues if arescue
section completes successfully as it ‘erases’ the error status (but not the reporting), this means it won’t triggermax_fail_percentage
norany_errors_fatal
configurations but will appear in the playbook statistics.
https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html#id4
However, as per the observed behaviour by us both, the documentation should either read "...the play continues if a rescue
or always
section completes successfully" or more likely the behaviour of Ansible is incorrect, or at least inconsistent with block/rescue/always not behaving like java's try/catch/finally. With the always section equating to the java's finally I wouldn't expect anything in the always section to prevent the failure from halting the playbook when any_errors_fatal
is enabled. Enabling any_errors_fatal
at the playbook and Ansible defaults level made no difference to the observed behaviour.
Upvotes: 4