Reputation: 30013
I would like to use the following handler with Ansible:
- name: force ntp update
shell: ntpdate {{item}}
with_lines: /etc/ntpd.serverlist
But I want it to end execution after the first successful execution (the list contains ntpd servers with which you can attempt to sync. One is enough). How would I do that?
Upvotes: 4
Views: 7703
Reputation: 1486
At least on Ansible 2.2.1.0 a when
statement can break the loop, and the following works skipping all evaluations after the first success:
---
# test.yml
# run this playbook with: ansible-playbook -i localhost, test.yml
- hosts: localhost
connection: local
gather_facts: no
tasks:
- name: check
shell: "[ {{item}} -ne 2 ]"
register: checks
ignore_errors: yes
changed_when: false
when: checks is not defined or checks.rc != 0
with_items: [2,3,2,4]
- name: set
set_fact: first_working={{ item.item }}
when: "'rc' in item and item.rc == 0"
with_items: "{{ checks.results }}"
- debug: var=first_working
# Note that first_working will be undefined if no check succeeded
This is the output:
PLAY [localhost] ***************************************************************
TASK [check] *******************************************************************
failed: [localhost] (item=2) => {"changed": false, "cmd": "[ 2 -ne 2 ]", "delta": "0:00:00.001735", "end": "2017-03-13 16:14:00.515372", "failed": true, "item": 2, "rc": 1, "start": "2017-03-13 16:14:00.513637", "stderr": "", "stdout": "", "stdout_lines": [], "warnings": []}
ok: [localhost] => (item=3)
skipping: [localhost] => (item=2)
skipping: [localhost] => (item=4)
...ignoring
TASK [set] *********************************************************************
skipping: [localhost] => (item={'_ansible_parsed': True, u'cmd': u'[ 2 -ne 2 ]', u'end': u'2017-03-13 16:14:00.515372', '_ansible_no_log': False, u'stdout': u'', '_ansible_item_result': True, u'changed': False, 'item': 2, u'delta': u'0:00:00.001735', u'stderr': u'', u'rc': 1, 'invocation': {'module_name': u'command', u'module_args': {u'creates': None, u'executable': None, u'_uses_shell': True, u'_raw_params': u'[ 2 -ne 2 ]', u'removes': None, u'warn': True, u'chdir': None}}, 'stdout_lines': [], u'start': u'2017-03-13 16:14:00.513637', u'warnings': [], 'failed': True})
ok: [localhost] => (item={'_ansible_parsed': True, u'changed': False, u'stdout': u'', '_ansible_no_log': False, u'warnings': [], '_ansible_item_result': True, u'rc': 0, u'end': u'2017-03-13 16:14:00.615658', u'start': u'2017-03-13 16:14:00.613978', u'cmd': u'[ 3 -ne 2 ]', 'item': 3, u'delta': u'0:00:00.001680', 'invocation': {'module_name': u'command', u'module_args': {u'creates': None, u'executable': None, u'_uses_shell': True, u'_raw_params': u'[ 3 -ne 2 ]', u'removes': None, u'warn': True, u'chdir': None}}, 'stdout_lines': [], u'stderr': u''})
skipping: [localhost] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': 2, 'changed': False})
skipping: [localhost] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': 4, 'changed': False})
TASK [debug] *******************************************************************
ok: [localhost] => {
"first_working": "3"
}
Upvotes: 4
Reputation: 20759
That's a very interesting situation you have. I haven't tried this personally, but I wonder if something like this would work:
- name: force ntp update
shell: ntpdate {{item}}
with_lines: /etc/ntpd.serverlist
register: ntp_result
when: ntp_result is not defined or ntp_result.rc != 0
ignore_errors: yes
So in a nutshell, each call to ntpdate should populate the ntp_result
variable with the return code of the call to ntpdate
. The when
clause then ensures the loop continues if the variable doesn't exist (as it wouldn't have been populated during the first iteration), or if the ntpdate
call failed (rc != 0). Telling Ansible to ignore any errors ensures that it continues looping if any of the calls to ntpdate
does return an error.
The only real downside to this is that it won't directly notify you if none of the calls to ntpdate
succeed. However you can probably follow this task with something along the lines of:
- name: fail if ntpdate fails
fail: msg="All calls to ntpdate failed"
when: ntp_result.rc != 0
If the last call resulted in a non-zero result from ntpdate
then it means none of them succeeded.
Upvotes: 3