You are using the select filter wrongly with rather weird argument (I suspect a copy/paste error but I'm not sure). select will apply a test to each object in the list. I don't know any search test that can be applied to a hashmap (The closest I can think of is the python search method of an re -i.e. regexp- object which would not be appropriate anyway)
In your case, you are looking for a specific value of an attribute of your hashmap. This can be done with the selectattr filter which will apply a test to a given attribute of the objects in the list and return only the ones passing the test.
There is a different approach to your problem which is more compact IMO using the json_query filter
Below is an example playbook using both approaches leading to the same result.
---
- name: Sum size of FS
hosts: localhost
gather_facts: false
vars:
FS:
- nom_FS: /appm/oracle/product
nom_LV: lv_product
size_FS: 5
owner_FS: oracle
group_FS: dba
vg_name: vgapplis
- nom_FS: /appm/oracle/product/12.1.0.2
nom_LV: lv_12102
size_FS: 15
owner_FS: oracle
group_FS: dba
vg_name: vgapplis
- nom_FS: /apps/oracle/logs
nom_LV: lvlogs
size_FS: 5
owner_FS: oracle
group_FS: dba
vg_name: vglogs
tasks:
- name: Calculate with selectattr, map and sum
debug:
msg: "{{ FS | selectattr('vg_name', '==', 'vgapplis') | map(attribute='size_FS') | list | sum }}"
- name: Calculate with json_query
vars:
sum_query: "[?vg_name=='vgapplis'].size_FS | sum(@)"
debug:
msg: "{{ FS | json_query(sum_query) }}"
And the result
PLAY [Sum size of FS] ****************************************************************
TASK [Calculate with selectattr, map and sum] ****************************************
ok: [localhost] => {
"msg": "20"
}
TASK [Calculate with json_query] *****************************************************
ok: [localhost] => {
"msg": "20"
}
PLAY RECAP ***************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Answer from Zeitounator on serverfault.comjinja2 - Ansible variable selectattr then map then equality - Stack Overflow
python - Ansible - how to use selectattr with yaml of different keys - Stack Overflow
How do I exclude 'null" from selectattr and map?
selectattr of list type, using the intersection of two lists
Is
selectattrfilter expecting all the dicts in the list to have the same keys?
More precisely, it is expecting all dicts in the list to have the attribute you are selecting on. If not all dict in the list have it, you will have to first filter out the items where it is not defined. This can be done with selectattr as well. (thanks @Randy for making this clearer since my initial answer).
In your situation, the json_query filter (which implements jmespath) can also do the job in sometimes a more compact manner. But it is not a core filter and requires to have the community.general collection installed.
Here are a few examples taken from your above requirements solved with both core filters and json_query solutions.
The playbook:
---
- name: "Filter data with core filters or json query"
hosts: "localhost"
gather_facts: false
vars:
# Your initial data on a single line for legibility
test_var: [{"vm":"vm1","ip":"10.10.10.1"},{"vm":"vm2","ip":"10.10.10.2"},{"test_vm":"something","process_1":"X","process_2":"Y","process_3":"Z"},{"another_vm":"something_other"}]
tasks:
- name: Get objects having vm==vm1
vars:
msg: |-
With core filters: {{ test_var | selectattr('vm', 'defined') | selectattr('vm', '==', 'vm1') | list }}
With json_query: {{ test_var | json_query("[?vm=='vm1']") | list }}
debug:
msg: "{{ msg.split('\n') }}"
- name: Get all objects having vm attribute
vars:
msg: |-
With core filters: {{ test_var | selectattr('vm', 'defined') | list }}
With json_query: {{ test_var | json_query("[?vm]") | list }}
debug:
msg: "{{ msg.split('\n') }}"
- name: Get all objects having process_2 attribute
vars:
msg: |-
With core filters: {{ test_var | selectattr('process_2', 'defined') | list }}
With json_query: {{ test_var | json_query("[?process_2]") | list }}
debug:
msg: "{{ msg.split('\n') }}"
- name: Get only a list of process_2 attributes
vars:
msg: |-
With core filters: {{ test_var | selectattr('process_2', 'defined') | map(attribute='process_2') | list }}
With json_query: {{ test_var | json_query("[].process_2") | list }}
debug:
msg: "{{ msg.split('\n') }}"
which gives:
PLAY [Filter data with core filters or json query] *********************************************************************
TASK [Get objects having vm==vm1] *********************************************************************
ok: [localhost] => {
"msg": [
"With core filters: [{'vm': 'vm1', 'ip': '10.10.10.1'}]",
"With json_query: [{'vm': 'vm1', 'ip': '10.10.10.1'}]"
]
}
TASK [Get all objects having vm attribute] *********************************************************************
ok: [localhost] => {
"msg": [
"With core filters: [{'vm': 'vm1', 'ip': '10.10.10.1'}, {'vm': 'vm2', 'ip': '10.10.10.2'}]",
"With json_query: [{'vm': 'vm1', 'ip': '10.10.10.1'}, {'vm': 'vm2', 'ip': '10.10.10.2'}]"
]
}
TASK [Get all objects having process_2 attribute] *********************************************************************
ok: [localhost] => {
"msg": [
"With core filters: [{'test_vm': 'something', 'process_1': 'X', 'process_2': 'Y', 'process_3': 'Z'}]",
"With json_query: [{'test_vm': 'something', 'process_1': 'X', 'process_2': 'Y', 'process_3': 'Z'}]"
]
}
TASK [Get only a list of process_2 attributes] *********************************************************************
ok: [localhost] => {
"msg": [
"With core filters: ['Y']",
"With json_query: ['Y']"
]
}
PLAY RECAP *********************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
To complement this edit of Zeitounator's answer:
More precisely, it is expecting all dicts in the list to have the attribute you are selecting on.
It is not 100% true for all filter functions, to select objects by an attribute not defined by all elements:
{{ test_var | selectattr('vm','defined') |selectattr('vm','equalto','vm1') | list }}
Got the following from using selectattr and map. I would like to just get the drive letters themselves from some windows facts. My end goal is to match the drive letter to the physical drive on the server. The null part is stumping me at this point.
"windows_disk_facts['ansible_facts.disks']|selectattr('partitions','defined')|map(attribute='partitions')|flatten|selectattr('drive_letter','defined')|map(attribute='drive_letter')|reject('==',0)|list": [
null,
"C",
null,
null,
"E",
null,
"F"
]
}I've tried using various combinations, but null is throwing me off. If someone already has some existing code to match drive to physical device I'll gladly take that. I'd really like to know how to skip null for the future in general as well though. TIA.
I want to extract certain inventory_hostnames from my inventory, as I need to render templates based on the "server_roles" that I have assigned. Now one could argue that a good architecture requires only one role per server. However in reality that is not the case, as often economics call for multiple roles on one server. So I want to compare the "server_roles" list to a list of arbitrary other server_roles.
If I am using "subset", which is available, this could give false results, as what I am really interested in the intersection. But unfortunatly, selectattr does not support a "intersect" filter - my dream would be something like this:
some_servers: >-
{{
groups.all
| map('extract', hostvars)
| selectattr('server_roles', 'defined')
| selectattr('server_roles', 'intersect', ['harvester', 'mower', 'storage'])
| map(attribute='inventory_hostname')
}}So short of the "intersect" filter - how can I achieve my goal?