How to compare lists in Ansible? - ansible

I have created 2 lists in Ansible:
{{actual_event_list}}
task1
task2
task3
task4
task5
{{expected_event_list}}
completed
completed
completed
completed
completed
I want to compare both lists in a way that if "task1" == "completed" then it's a success, else it should fail. Same goes for all items (task2 == completed, task3 == completed and so on...).

Try map. There might be more options on the filter. For example replace
shell> cat map-replace.yml
- hosts: localhost
vars:
actual_event_list:
- "{{ task1|default('running') }}"
- "{{ task2|default('running') }}"
- "{{ task3|default('running') }}"
tasks:
- debug:
msg: "{{ actual_event_list|
map('replace', 'completed', 'true')|map('bool')|
list }}"
vars:
task1: completed
task3: completed
gives
msg:
- true
- false
- true

I would first combine the list to get a dict and then loop over it. Here is an example with an assert.
playbook.yml:
---
- name: Stackoverflow demo
hosts: localhost
tasks:
- name: Set facts
set_fact:
actual_event_list:
- task1
- task2
- task3
- task4
- task5
expected_event_list:
- completed
- completed
- completed
- completed
- completed
# https://docs.ansible.com/ansible/latest/user_guide/complex_data_manipulation.html#id10
- name: Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists
set_fact:
events: "{{ events | default({}) | combine({item[0]: item[1]}) }}"
loop: "{{ (actual_event_list | zip(expected_event_list)) | list }}"
- name: Assert
assert:
that: item.value == "completed"
msg: "{{ item.key }} has status `{{ item.value }}` instead of `completed`"
loop: "{{ events | dict2items }}"
Output:
PLAY [Stackoverflow demo] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Set facts] **********************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists] **************************************************************************************************************************************************************
ok: [localhost] => (item=['task1', 'completed'])
ok: [localhost] => (item=['task2', 'completed'])
ok: [localhost] => (item=['task3', 'completed'])
ok: [localhost] => (item=['task4', 'completed'])
ok: [localhost] => (item=['task5', 'completed'])
TASK [Assert] *************************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'task1', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task1",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task2', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task2",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task3', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task3",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task4', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task4",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task5', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task5",
"value": "completed"
},
"msg": "All assertions passed"
}
PLAY RECAP ****************************************************************************************************************************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0
Output when something goes wrong:
PLAY [Stackoverflow demo] *************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Set facts] **********************************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists] **************************************************************************************************************************************************************
ok: [localhost] => (item=['task1', 'completed'])
ok: [localhost] => (item=['task2', 'completed'])
ok: [localhost] => (item=['task3', 'completed'])
ok: [localhost] => (item=['task4', 'not_completed'])
ok: [localhost] => (item=['task5', 'completed'])
TASK [Assert] *************************************************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'key': 'task1', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task1",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task2', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task2",
"value": "completed"
},
"msg": "All assertions passed"
}
ok: [localhost] => (item={'key': 'task3', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task3",
"value": "completed"
},
"msg": "All assertions passed"
}
failed: [localhost] (item={'key': 'task4', 'value': 'not_completed'}) => {
"assertion": "item.value == \"completed\"",
"changed": false,
"evaluated_to": false,
"item": {
"key": "task4",
"value": "not_completed"
},
"msg": "task4 has status `not_completed` instead of `completed`"
}
ok: [localhost] => (item={'key': 'task5', 'value': 'completed'}) => {
"changed": false,
"item": {
"key": "task5",
"value": "completed"
},
"msg": "All assertions passed"
}
PLAY RECAP ****************************************************************************************************************************************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=1
You can later enhance that script making sure the list have the same size.

Related

ansible: how to parse output of an Oracle script (JSON_OBJECT)

There is an Oracle script that runs this query:
select json_object (name, value) from v$parameter where lower(name) in ...
output of this script:
{"name":"processes","value":"1000"}
{"name":"sessions","value":"1522"}
{"name":"sga_target","value":"3221225472"}
{"name":"control_file_record_keep_time","value":"7"}
{"name":"db_block_size","value":"8192"}
{"name":"compatible","value":"19.0.0.0.0"}
...
I register stdout_lines during sql-script execution and then parse line by line:
- name: print fetched database parameters
debug:
msg: "{{ db_params_out.stdout_lines }}"
- name: create list of newdb parameters from fetched result
debug:
msg: "{{ item }}"
with_list: "{{ db_params_out.stdout_lines }}"
when: db_params_out.stdout_lines | length > 0
Output looks good to me:
TASK [db_standby_preinstall : print fetched database parameters] *********************************************************************************************
ok: [localhost -> ol79_1] => {
"msg": [
"",
"{\"name\":\"processes\",\"value\":\"1000\"}",
"{\"name\":\"sessions\",\"value\":\"1522\"}",
"{\"name\":\"sga_target\",\"value\":\"3221225472\"}",
"{\"name\":\"control_file_record_keep_time\",\"value\":\"7\"}",
"{\"name\":\"db_block_size\",\"value\":\"8192\"}",
"{\"name\":\"compatible\",\"value\":\"19.0.0.0.0\"}",
...
TASK [db_standby_preinstall : create list of newdb parameters from fetched result] ***************************************************************************
ok: [localhost -> ol79_1] => (item=) => {
"msg": ""
}
ok: [localhost -> ol79_1] => (item={'name': 'processes', 'value': '1000'}) => {
"msg": {
"name": "processes",
"value": "1000"
}
}
ok: [localhost -> ol79_1] => (item={'name': 'sessions', 'value': '1522'}) => {
"msg": {
"name": "sessions",
"value": "1522"
}
}
ok: [localhost -> ol79_1] => (item={'name': 'sga_target', 'value': '3221225472'}) => {
"msg": {
"name": "sga_target",
"value": "3221225472"
}
}
ok: [localhost -> ol79_1] => (item={'name': 'control_file_record_keep_time', 'value': '7'}) => {
"msg": {
"name": "control_file_record_keep_time",
"value": "7"
}
}
ok: [localhost -> ol79_1] => (item={'name': 'db_block_size', 'value': '8192'}) => {
"msg": {
"name": "db_block_size",
"value": "8192"
}
}
ok: [localhost -> ol79_1] => (item={'name': 'compatible', 'value': '19.0.0.0.0'}) => {
"msg": {
"name": "compatible",
"value": "19.0.0.0.0"
but I cannot address key/value of a fetched item, ansible fails with The error was: 'ansible.utils.unsafe_proxy.AnsibleUnsafeText object' has no attribute 'name' (or 'value') if i try to use item.name or item.value instead of item.
P.S. I can modify Oracle script and provide output in any format. I need to parse output and assign values to corresponding variables:
newdb_processes:1000
newdb_sessions:1522
etc...
Issue is solved. The problem was in the first line of stdout_lines which is empty but still passes via filter
when: db_params_out.stdout_lines | length > 0
I've removed this line but now I use "reject" filter in loop:
with_list: "{{ db_params_out.stdout_lines | reject('match', '^$') }}"
And it works now, no more empty output lines.
Thanks a lot user larsks who pointed me to a test-case with correct data to prove that his solution works properly.
You can pass each line through the from_json filter to get a structured object, like this:
- hosts: localhost
gather_facts: false
vars:
db_params_out:
stdout_lines:
- '{"name":"processes","value":"1000"}'
- '{"name":"sessions","value":"1522"}'
- '{"name":"sga_target","value":"3221225472"}'
- '{"name":"control_file_record_keep_time","value":"7"}'
- '{"name":"db_block_size","value":"8192"}'
- '{"name":"compatible","value":"19.0.0.0.0"}'
tasks:
- name: create list of newdb parameters from fetched result
debug:
msg: "{{ (item|from_json).name }}"
with_list: "{{ db_params_out.stdout_lines }}"
when: db_params_out.stdout_lines | length > 0
The above playbook outputs:
TASK [create list of newdb parameters from fetched result] **********************************************
ok: [localhost] => (item={'name': 'processes', 'value': '1000'}) => {
"msg": "processes"
}
ok: [localhost] => (item={'name': 'sessions', 'value': '1522'}) => {
"msg": "sessions"
}
ok: [localhost] => (item={'name': 'sga_target', 'value': '3221225472'}) => {
"msg": "sga_target"
}
ok: [localhost] => (item={'name': 'control_file_record_keep_time', 'value': '7'}) => {
"msg": "control_file_record_keep_time"
}
ok: [localhost] => (item={'name': 'db_block_size', 'value': '8192'}) => {
"msg": "db_block_size"
}
ok: [localhost] => (item={'name': 'compatible', 'value': '19.0.0.0.0'}) => {
"msg": "compatible"
}

Generating sequences with coma seperated values

I'm trying to achieve something similar like cisco range commandos with ansible. in my case, I have tried the following:
- name: test playbook
hosts: all
connection: local
gather_facts: no
vars:
epg: "{{ lookup('env','EPG').split(',') }}"
task:
- debug:
var:(item|int)
with_sequence_ "{{ epg }}"
the input will be made by the user and can be something like: 10-13,20
in this case what I want to have just like the cisco IOS console : 10,11,12,13,20
instead, I get the following output:
TASK [debug] *******************************************************************
task path: /opt2/jenkins/TEST/TEST interface.yaml:42
ok: [tst] => (item=10) => {
"(item|int)": "10",
"ansible_loop_var": "item",
"item": "10"
}
ok: [tst] => (item=11) => {
"(item|int)": "11",
"ansible_loop_var": "item",
"item": "11"
}
ok: [tst] => (item=12) => {
"(item|int)": "12",
"ansible_loop_var": "item",
"item": "12"
}
ok: [tst] => (item=13) => {
"(item|int)": "13",
"ansible_loop_var": "item",
"item": "13"
}
ok: [tst] => (item=1) => {
"(item|int)": "1",
"ansible_loop_var": "item",
"item": "1"
}
ok: [tst] => (item=2) => {
"(item|int)": "2",
"ansible_loop_var": "item",
"item": "2"
}
ok: [tst] => (item=3) => {
"(item|int)": "3",
"ansible_loop_var": "item",
"item": "3"
... (so on up to 20)
How can I get only 10,11,12,13 and 20 if somebody can help me I will be glad. Thank you in advance.
you have to combine a list of jinja2 and Ansible filters. It's not easy but try this:
- name: test playbook
hosts: all
vars:
epg: "{{ lookup('env','EPG')}}"
task:
- debug:
msg: "{% set LIST_EPG=[] %}{% for it in (epg.split(',')) %}{% set int1=it.split('-')[0] | int() %}{% if it.split('-') |length > 1 %}{% set int2=it.split('-')[1] | int() %}{% for i in range(int1, int2+1) %}{{ LIST_EPG.append(i|int) }}{% endfor %}{% endif %}{{ LIST_EPG.append(int1| int) }}{% endfor %}{{LIST_EPG|flatten|unique|join(',') }}"
By splitting the comma separated list in EPG environment variable, you are creating an array/list in epg. So we need to iterate over this.
Consider environment variable as:
export EPG="10,11,12,13,20"
And reusing your epg variable:
vars:
epg: "{{ lookup('env', 'EPG').split(',') }}"
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ epg }}"
We can get this result:
ok: [localhost] => (item=10) => {
"msg": "10"
}
ok: [localhost] => (item=11) => {
"msg": "11"
}
ok: [localhost] => (item=12) => {
"msg": "12"
}
ok: [localhost] => (item=13) => {
"msg": "13"
}
ok: [localhost] => (item=20) => {
"msg": "20"
}

Ansible append list on the delegated host

I have a requirement to build a list in a delegated host. Here is what I have so far. Under the "set the task" task, this line shut_interface: "{{ hostvars[item.value.hostname].shut_interface + [item.value.interface] }}" under set_fact is not really building a list.
---
- name: Test Play 1
hosts: localhost
gather_facts: no
vars:
devices:
test1:
hostname: host1
interface: Gi1/43
test2:
hostname: host1
interface: Gi1/44
test3:
hostname: host2
interface: Gi1/1
test4:
hostname: host2
interface: Gi1/2
tasks:
- name: Test Task 1
debug:
msg: "{{ devices }}"
- name: Initiate empty list
set_fact:
device_list: []
delegate_to: localhost
- name: Add devices
set_fact:
device_list: "{{ device_list + [item.value.hostname] }}"
delegate_to: localhost
delegate_facts: True
with_dict: "{{ devices }}"
- name: Initialize shut interface
set_fact:
shut_interface: []
delegate_to: "{{ item.value.hostname }}"
delegate_facts: True
with_dict: "{{ devices }}"
- name: set the task
set_fact:
shut_interface: "{{ hostvars[item.value.hostname].shut_interface + [item.value.interface] }}"
delegate_to: "{{ item.value.hostname }}"
delegate_facts: True
with_dict: "{{ devices }}"
- name: Test Play 2
hosts: "{{ hostvars['localhost']['device_list'] }}"
gather_facts: no
tasks:
- name: Tests 2
debug:
msg: "{{ shut_interface }}"
PLAY [Test Play 1] **************************************************************************************************************************************************************************************
TASK [Test Task 1] **************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"test1": {
"hostname": "host1",
"interface": "Gi1/43"
},
"test2": {
"hostname": "host1",
"interface": "Gi1/44"
},
"test3": {
"hostname": "host2",
"interface": "Gi1/1"
},
"test4": {
"hostname": "host2",
"interface": "Gi1/2"
}
}
}
TASK [Initiate empty list] ******************************************************************************************************************************************************************************
ok: [localhost -> localhost]
TASK [Add devices] **************************************************************************************************************************************************************************************
ok: [localhost -> localhost] => (item={'key': 'test1', 'value': {'hostname': 'host1', 'interface': 'Gi1/43'}})
ok: [localhost -> localhost] => (item={'key': 'test2', 'value': {'hostname': 'host1', 'interface': 'Gi1/44'}})
ok: [localhost -> localhost] => (item={'key': 'test3', 'value': {'hostname': 'host2', 'interface': 'Gi1/1'}})
ok: [localhost -> localhost] => (item={'key': 'test4', 'value': {'hostname': 'host2', 'interface': 'Gi1/2'}})
TASK [Initialize shut interface] ************************************************************************************************************************************************************************
ok: [localhost -> host1] => (item={'key': 'test1', 'value': {'hostname': 'host1', 'interface': 'Gi1/43'}})
ok: [localhost -> host1] => (item={'key': 'test2', 'value': {'hostname': 'host1', 'interface': 'Gi1/44'}})
ok: [localhost -> host2] => (item={'key': 'test3', 'value': {'hostname': 'host2', 'interface': 'Gi1/1'}})
ok: [localhost -> host2] => (item={'key': 'test4', 'value': {'hostname': 'host2', 'interface': 'Gi1/2'}})
TASK [set the task] *************************************************************************************************************************************************************************************
ok: [localhost -> host1] => (item={'key': 'test1', 'value': {'hostname': 'host1', 'interface': 'Gi1/43'}})
ok: [localhost -> host1] => (item={'key': 'test2', 'value': {'hostname': 'host1', 'interface': 'Gi1/44'}})
ok: [localhost -> host2] => (item={'key': 'test3', 'value': {'hostname': 'host2', 'interface': 'Gi1/1'}})
ok: [localhost -> host2] => (item={'key': 'test4', 'value': {'hostname': 'host2', 'interface': 'Gi1/2'}})
PLAY [Test Play 2] **************************************************************************************************************************************************************************************
TASK [Tests 2] ******************************************************************************************************************************************************************************************
ok: [host1] => {
"msg": [
"Gi1/44".
]
}
ok: [host2] => {
"msg": [
"Gi1/2".
]
}
I am expecting [Gi1/43, Gi1/44]for host 1 and [Gi1/1, Gi1/2] in host2. Any ideas?
I'm assuming that you don't want to change the device variable's data structure, so you can probably do something like the following:
---
- name: Test Play 1
hosts: 127.0.0.1
connection: local
gather_facts: no
vars:
devices:
test1:
hostname: host1
interface: Gi1/43
test2:
hostname: host1
interface: Gi1/44
test3:
hostname: host2
interface: Gi1/1
test4:
hostname: host2
interface: Gi1/2
tasks:
- name: Test Task 1
debug:
var: devices
- name: Add host_list
set_fact:
shut_interface_list: >-
{{ shut_interface_list|default({})|combine({devices[item].hostname:
(shut_interface_list[devices[item].hostname]|default([])) +
[(devices[item]|default({'interface': ''})).interface]
})
}}
loop: "{{ devices.keys()|list }}"
- name: Test Task 2
debug:
var: shut_interface_list
- name: Add testing hosts
add_host:
name: "{{ item }}"
groups: test_group
ansible_host: 127.0.0.1
loop: "{{ shut_interface_list.keys()|list }}"
- name: Test Play 2
hosts: test_group
vars:
shut_interfaces: "{{ hostvars['localhost']['shut_interface_list'][inventory_hostname] }}"
gather_facts: no
tasks:
- name: Test Task 3
debug:
var: shut_interfaces
Which should give you this result:
TASK [Test Task 3] ***********************************************************************************************************************************
ok: [host1] => {
"shut_interfaces": [
"Gi1/43",
"Gi1/44"
]
}
ok: [host2] => {
"shut_interfaces": [
"Gi1/1",
"Gi1/2"
]
}
Please correct the vars section declaration. Since you are defining the same hostnames twice the ansible reading the last defined vars as final and you are getting task2 output as Gi1/44 and Gi1/2.
vars:
devices:
test1:
hostname: host1
interface:
- Gi1/43
- Gi1/44
test2:
hostname: host2
interface:
- Gi1/1
- Gi1/2

Loops inside inventory files?

Is it possible to use loops in the inventory files? For example in one inv file I got:
---
ISPs:
- name: ISP1
- name: ISP2
networks:
- name: network1
- name: network2
- name: network3
and then I want to create something like this in this same inventory:
from ISP1 to network1 permit
from ISP1 to network2 permit
from ISP1 to network3 permit
from ISP2 to network1 permit
from ISP2 to network2 permit
from ISP2 to network3 permit
but I think that using loops I could do it faster instead of copy-paste and write down all of the possible combinations. Is it even possible?
example:
from {{ item[0] }} to {{ item[1] }} permit
with_nested:
- [ 'ISP1', 'ISP2' ]
- [ 'network1', 'network2', 'network3' ]
will create something like this:
from ISP1 to network1 permit
from ISP1 to network2 permit
from ISP1 to network3 permit
from ISP2 to network1 permit
from ISP2 to network2 permit
from ISP2 to network3 permit
Thanks for any answer!
here is 2 variants,
see https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#with-nested-with-cartesian
---
- hosts: localhost
connection: local
gather_facts: false
vars:
ISPs:
- name: ISP1
- name: ISP2
networks:
- name: network1
- name: network2
- name: network3
tasks:
- name: Using with_nested
debug:
msg: '{{ item.0.name }} - {{item.1.name }} '
with_nested:
- '{{ ISPs }}'
- '{{ networks }}'
- name: Using loop
debug:
msg: '{{ item.0.name }} - {{item.1.name }} '
loop: "{{ ISPs | product(networks) | list }}"
Output:
PLAY [localhost] *******************************************************************************************************************************************************************************************
TASK [Using with_nested] ***********************************************************************************************************************************************************************************
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network1'}]) => {
"msg": "ISP1 - network1 "
}
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network2'}]) => {
"msg": "ISP1 - network2 "
}
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network3'}]) => {
"msg": "ISP1 - network3 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network1'}]) => {
"msg": "ISP2 - network1 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network2'}]) => {
"msg": "ISP2 - network2 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network3'}]) => {
"msg": "ISP2 - network3 "
}
TASK [Using loop] ******************************************************************************************************************************************************************************************
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network1'}]) => {
"msg": "ISP1 - network1 "
}
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network2'}]) => {
"msg": "ISP1 - network2 "
}
ok: [localhost] => (item=[{u'name': u'ISP1'}, {u'name': u'network3'}]) => {
"msg": "ISP1 - network3 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network1'}]) => {
"msg": "ISP2 - network1 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network2'}]) => {
"msg": "ISP2 - network2 "
}
ok: [localhost] => (item=[{u'name': u'ISP2'}, {u'name': u'network3'}]) => {
"msg": "ISP2 - network3 "
}
PLAY RECAP *************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0
It is possible to do, but applying login inside ansible code will be messy, So better idea is to invoke any bash or python script in ansible & pass these two list as an argument to that script & return the expected array from the script at the end & store this value to fact using set_fact
Example:
tasks:
- set_fact: isp_networks="{{ lookup('pipe','python script.py ' + {{ISPs}} + ' ' + {{networks}}) }}"
- debug: var={{isp_networks}}

Ansible playbook to handle both Linux and Windows hosts

Trying to write one playbook that checks whether a set of files exist on a set of servers (both Linux and Windows hosts), and if they do, then to replace the files.
Here is what i have so far:
---
- hosts: "{{hosts}}"
vars:
scripts:
- check_blackout.pl
- check_empty.pl
tasks:
- name: windows stat
with_items: "{{scripts}}"
win_stat: path=D:\scripts\{{item}}
register: windows_stat
when: "'windows' in group_names"
- name: other stat
with_items: "{{scripts}}"
stat: path=/usr/local/bin/{{item}}
register: other_stat
remote_user: "{{script_owner | default(ansible_user)}}"
when: "'windows' not in group_names"
- name: windows debug
with_items: "{{windows_stat.results}}"
debug: var={{item.item}}
when: "{{item.stat.exists}}"
- name: other debug
with_items: "{{other_stat.results}}"
debug: var={{item.item}}
when: "{{item.stat.exists}}"
...
When I run this against a Windows and Linux host for testing, I get the following:
[ansible#vmhklftpscdv1 ~]$ ansible-playbook test.yml -e "hosts=vmhkge1jasdev01,jdeesbkup" --ask-vault-pass
Vault password:
PLAY [vmhkge1jasdev01,jdeesbkup] ************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************
ok: [vmhkge1jasdev01]
ok: [jdeesbkup]
TASK [windows stat] *************************************************************************************************************************************************************
skipping: [jdeesbkup] => (item=check_blackout.pl)
skipping: [jdeesbkup] => (item=check_empty.pl)
ok: [vmhkge1jasdev01] => (item=check_blackout.pl)
ok: [vmhkge1jasdev01] => (item=check_empty.pl)
TASK [other stat] ***************************************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item=check_empty.pl)
skipping: [vmhkge1jasdev01] => (item=check_blackout.pl)
ok: [jdeesbkup] => (item=check_blackout.pl)
ok: [jdeesbkup] => (item=check_empty.pl)
TASK [windows debug] ************************************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'check_empty.pl', 'invocation': {'module_name': u'win_stat'}})
skipping: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'check_blackout.pl', 'invocation': {'module_name': u'win_stat'}})
fatal: [jdeesbkup]: FAILED! => {"failed": true, "msg": "The conditional check '{{item.stat.exists}}' failed. The error was: error while evaluating conditional ({{item.stat.exists}}): 'dict object' has no attribute 'stat'\n\nThe error appears to have been in '/home/ansible/test.yml': line 19, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n when: \"'windows' not in group_names\"\n - name: windows debug\n ^ here\n"}
TASK [other debug] **************************************************************************************************************************************************************
fatal: [vmhkge1jasdev01]: FAILED! => {"failed": true, "msg": "The conditional check '{{item.stat.exists}}' failed. The error was: error while evaluating conditional ({{item.stat.exists}}): 'dict object' has no attribute 'stat'\n\nThe error appears to have been in '/home/ansible/test.yml': line 23, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n when: \"{{item.stat.exists}}\"\n - name: other debug\n ^ here\n"}
to retry, use: --limit #/home/ansible/test.retry
PLAY RECAP **********************************************************************************************************************************************************************
jdeesbkup : ok=2 changed=0 unreachable=0 failed=1
vmhkge1jasdev01 : ok=2 changed=0 unreachable=0 failed=1
[ansible#vmhklftpscdv1 ~]$
Any ideas how I can get this working?
I've tried various combinations including checking the group membership before checking the results:
when: "'windows' in group_names and {{item.stat.exists}}"
However Ansible still seems to check the item.stat field even when the first part of the condition is false.
Or is my fundamental approach wrong, should I be splitting these tasks up into different playbooks?
Yes, you are right. with_items is evaluated before when. You can add a default empty list to avoid the error:
- name: other debug
with_items: "{{other_stat.results | default([]) }}"
debug: var={{item}}
when: "{{item.stat.exists | default(false)}}"
Got it working, here is successful playbook and output:
---
- hosts: "{{hosts}}"
vars:
scripts:
- run.pl
tasks:
- name: win_stat
with_items: scripts
win_stat: path="D:\scripts\{{item}}"
register: "win_result"
when: "'windows' in group_names"
- name: other_stat
with_items: scripts
stat: path="/usr/local/bin/{{item}}"
register: "other_result"
when: "'windows' not in group_names"
- debug: var=win_result
- debug: var=other_result
- with_items: "{{ win_result.results }}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
- with_items: "{{ other_result.results}}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
...
[ansible#vmhklftpscdv1 ~]$ ansible-playbook debug.yml -e "hosts=jdeesbkup,vmhkge1jasdev01" --ask-vault-pass
Vault password:
PLAY [jdeesbkup,vmhkge1jasdev01] **************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************
ok: [jdeesbkup]
ok: [vmhkge1jasdev01]
TASK [win_stat] *******************************************************************************************************************************************
skipping: [jdeesbkup] => (item=scripts)
ok: [vmhkge1jasdev01] => (item=scripts)
TASK [other_stat] *****************************************************************************************************************************************
skipping: [vmhkge1jasdev01] => (item=scripts)
ok: [jdeesbkup] => (item=scripts)
TASK [debug] **********************************************************************************************************************************************
ok: [jdeesbkup] => {
"win_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"changed": false,
"item": "scripts",
"skip_reason": "Conditional check failed",
"skipped": true
}
]
}
}
ok: [vmhkge1jasdev01] => {
"win_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_name": "win_stat"
},
"item": "scripts",
"stat": {
"exists": false
}
}
]
}
}
TASK [debug] **********************************************************************************************************************************************
ok: [jdeesbkup] => {
"other_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"_ansible_parsed": true,
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_attributes": true,
"get_checksum": true,
"get_md5": true,
"get_mime": true,
"path": "/usr/local/bin/scripts"
},
"module_name": "stat"
},
"item": "scripts",
"stat": {
"exists": false
}
}
]
}
}
ok: [vmhkge1jasdev01] => {
"other_result": {
"changed": false,
"msg": "All items completed",
"results": [
{
"_ansible_item_result": true,
"_ansible_no_log": false,
"changed": false,
"item": "scripts",
"skip_reason": "Conditional check failed",
"skipped": true
}
]
}
}
TASK [debug] **********************************************************************************************************************************************
skipping: [jdeesbkup] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': u'scripts', 'changed': False})
ok: [vmhkge1jasdev01] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'scripts', 'invocation': {'module_name': u'win_stat'}}) => {
"item": {
"changed": false,
"invocation": {
"module_name": "win_stat"
},
"item": "scripts",
"stat": {
"exists": false
}
},
"msg": "scripts"
}
TASK [debug] **********************************************************************************************************************************************
---
- hosts: "{{hosts}}"
vars:
scripts:
- run.pl
tasks:
- name: win_stat
with_items: scripts
win_stat: path="D:\scripts\{{item}}"
register: "win_result"
when: "'windows' in group_names"
- name: other_stat
with_items: scripts
stat: path="/usr/local/bin/{{item}}"
register: "other_result"
when: "'windows' not in group_names"
- debug: var=win_result
- debug: var=other_result
- with_items: "{{ win_result.results }}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
- with_items: "{{ other_result.results}}"
debug: msg="{{item.item}}"
when: "not (item.skipped | default(false))"
# when: "'windows' in group_names and {{item.stat.exists}}"
# - name: win_test
# with_items: {{win_result.results}}
# debug: var="{{item}}"
# when: "'windows' in group_names and {{item.stat.exists}}"
~
~
~
skipping: [vmhkge1jasdev01] => (item={'skipped': True, '_ansible_no_log': False, 'skip_reason': u'Conditional check failed', '_ansible_item_result': True, 'item': u'scripts', 'changed': False})
ok: [jdeesbkup] => (item={'_ansible_parsed': True, u'stat': {u'exists': False}, '_ansible_item_result': True, '_ansible_no_log': False, u'changed': False, 'item': u'scripts', 'invocation': {'module_name': u'stat', u'module_args': {u'checksum_algorithm': u'sha1', u'get_checksum': True, u'follow': False, u'path': u'/usr/local/bin/scripts', u'get_md5': True, u'get_mime': True, u'get_attributes': True}}}) => {
"item": {
"changed": false,
"invocation": {
"module_args": {
"checksum_algorithm": "sha1",
"follow": false,
"get_attributes": true,
"get_checksum": true,
"get_md5": true,
"get_mime": true,
"path": "/usr/local/bin/scripts"
},
"module_name": "stat"
},
"item": "scripts",
"stat": {
"exists": false
}
},
"msg": "scripts"
}
PLAY RECAP ************************************************************************************************************************************************
jdeesbkup : ok=5 changed=0 unreachable=0 failed=0
vmhkge1jasdev01 : ok=5 changed=0 unreachable=0 failed=0

Resources