Ansible: how to filter using varible - ansible

i have ansible-playbook that gives list of lines in debug output.
I am able to filter debug OUTPUT using a string (exp: CUST) but I am struggling to filter the list using a variable.
- debug:
msg: "{{ List.msg | select('match', '^(CUST)[0-9]+') | list }}"
List msg output:
CUST1
CUST2
NEW1
NEW2
from the above debug command, i get CUST1, CUST2 in filtered output.
- set_fact:
filter: "{{ fileout.results[0].content }}"
above filter generates "CUST" and i want to use this filter variable in above debug command.
using below syntax i get nothing, may be ansible is NOT taking it as appropriate variable.
- debug:
msg: "{{ List.msg | select('match', '^("{{ filter }}")[0-9]+') | list }}"
Please help.
thanks in advance.

It's possible to isolate the declaration of the regex filter and simplify the quotation. For example
vars:
List:
msg: ['CUST1','CUST2','NEW1','NEW2']
Patterns: ['CUST','NEW']
tasks:
- debug:
msg: "{{ List.msg | select('match', filter) | list }}"
vars:
filter: '^{{ item }}[0-9]+'
loop: "{{ Patterns }}"
gives
"msg": [
"CUST1",
"CUST2"
]
"msg": [
"NEW1",
"NEW2"
]

Related

Ansible | delete files from a directory if the filename doesn't contains any of the strings from a list

I'm creating vm-s with libvirt, and I would like to do a housekeeping, if I delete a host (in this example a VM) from my inventory, at the next run of the playbook, it should delete that VM's qcow2 disk from the disk pool.
I don't really get, how could I create a nested loop that iterates through the file list of that specific directory and the list of vms in my inventory, checks if the name of the vm is part of any file in the filelist, and deletes the files whose have no connection into the inventory.
Here is an example from the many things I already tried:
- name: "Housekeeping: list qcow2 disks in libvirt-pool"
find:
paths: /mnt/hdd/libvirt-pool
depth: 1
patterns:
- "*.qcow2"
register: qcow_disks
- name: debug
debug:
msg: "{{item[0]}}"
with_nested:
- "{{ qcow_disks.files | map(attribute='path') | list }}"
- "{{ groups.vm }}"
when: item[1] in item[0]
register: valid_disks
- name: debug1
debug:
msg: "invalid disks: {{ valid_disks.results | difference(all_disk) }}"
variable:
all_disk: "{{ qcow_disks.files | map(attribute='path') | list }}"
Hope you can help me out!
Thanks in advance!
I assume you have in groups.vm a list of names of VMs, without the extension .qcow2.
So the list groups.vm could looks like e.g:
['vm1', 'vm5', 'test']
The find command returns files like:
[
"/mnt/hdd/libvirt-pool/bob.qcow2",
"/mnt/hdd/libvirt-pool/daniel.qcow2",
"/mnt/hdd/libvirt-pool/test.qcow2",
"/mnt/hdd/libvirt-pool/vm1.qcow2",
"/mnt/hdd/libvirt-pool/vm5.qcow2"
]
With the following command you can reduce this list to the name without extension, then you can easily compare the lists.
{{ qcow_disks.files | map(attribute='path') | map('basename') | map('splitext') | map('first') }}
basename returns the filename, without preceding path
splitext splits the filename into a list: [name, extension]
first takes the first element from the list, i.e. the name
More on basename and splitext in the Ansible docs.
{{ found_disks | reject('in', current_vms) }}
With the reject filter you can then discard the current elements, so that you contain a list with all old VMs.
The following tasks:
- name: "Housekeeping: list qcow2 disks in libvirt-pool"
find:
paths: /mnt/hdd/libvirt-pool
depth: 1
patterns:
- "*.qcow2"
register: qcow_disks
- debug:
msg: "{{ qcow_disks.files | map(attribute='path') }}"
- debug:
msg: "{{ old_disks }}"
vars:
current_vms: ['vm1', 'vm5', 'test']
found_disks: "{{ qcow_disks.files | map(attribute='path') | map('basename') | map('splitext') | map('first') }}"
old_disks: "{{ found_disks | reject('in', current_vms) }}"
Note: current_vms corresponds to the list you have via groups.vm.
return this result:
TASK [Housekeeping: list qcow2 disks in libvirt-pool] ************************
ok: [localhost]
TASK [debug] *****************************************************************
ok: [localhost] => {
"msg": [
"/mnt/hdd/libvirt-pool/bob.qcow2",
"/mnt/hdd/libvirt-pool/daniel.qcow2",
"/mnt/hdd/libvirt-pool/test.qcow2",
"/mnt/hdd/libvirt-pool/vm1.qcow2",
"/mnt/hdd/libvirt-pool/vm5.qcow2"
]
}
TASK [debug] *****************************************************************
ok: [localhost] => {
"msg": [
"bob",
"daniel"
]
}
I hope this helps you.

Ansible returns only one item from a dictionary

I'm trying to get a list of IPs from an specific Service, and ansible returns only one item from the loop.
I have tried many things and is always the same result.
Need help.
- name: "Amazon IPs"
include_vars:
file: /home/user1/ansible/AWS/ip-ranges.json
name: amazon
# - set_fact:
# # # test: "{{ (variable.stdout | from_json).prefixes | map(attribute='ip_prefix') | list }}"
# amazonipv4: "{{ item }}"
# # amazonipv6: "{{ amazon.ipv6_prefixes | map(attribute='ipv6_prefix') | list }}"
# loop: "{{amazon.prefixes | map(attribute='ip_prefix') | list }}"
# # when: '"AMAZON" in item.service'
- set_fact:
test3: "{{item.ip_prefix}}"
loop: "{{amazon.prefixes | list }}"
when: '"AMAZON" in item.service'
- debug:
var: test3
I expect to get a list based on the service, but I only get one item.
example:
TASK [debug] ***********************************************
ok: [localhost] => {
"test3": "54.190.198.32/28"
in each iteration in the set_fact loop, you are setting the value, not pushing to a list. you need to change your syntax to:
- set_fact:
test3: "{{ test3 | default([]) + [item.ip_prefix] }}"
hope it helps.

Ansible: Convert two lists into key, value dict

I have 2 lists as set_fact and want to create a dict
I am running ansible 2.8
I have list1 as below
"inventory_devices": [
"device0",
"device1"
]
and list2 as below
"inventory_ips": [
"10.1.1.1",
"10.1.1.2"
]
I want to get an output shows like
"inventory_dict": [
"device0": "10.1.1.1",
"device1": "10.1.1.2"
]
Thanks.
You can do it entirely with jinja2 using the zip filter built into ansible.
To get a list combining the elements of other lists use zip
- name: give me list combo of two lists
debug:
msg: "{{ [1,2,3,4,5] | zip(['a','b','c','d','e','f']) | list }}"
...
Similarly to the output of the items2dict filter mentioned above, these filters can be
used to contruct a dict:
{{ dict(keys_list | zip(values_list)) }}
The zip filter sequentially combines items from pairs of lists and the dict construct creates a dictionary from a list of pairs.
inventory_dict: "{{ dict(inventory_devices | zip(inventory_ips)) }}"
here is the task to do it, populate combined var in the PB below:
---
- hosts: localhost
gather_facts: false
vars:
inventory_devices:
- device0
- device1
inventory_ips:
- 10.1.1.1
- 10.1.1.2
tasks:
- name: populate combined var
set_fact:
combined_var: "{{ combined_var|default({}) | combine({ item.0: item.1 }) }}"
loop: "{{ query('together', inventory_devices, inventory_ips) }}"
- name: print combined var
debug:
var: combined_var
result:
TASK [print combined var] **********************************************************************************************************************************************************************************************
ok: [localhost] => {
"combined_var": {
"device0": "10.1.1.1",
"device1": "10.1.1.2"
}
}
hope it helps

Ansible get first element from list

Suppose I have the following vars_file:
mappings:
- primary: 1.1.1.1
secondary: 2.2.2.2
- primary: 12.12.12.12
secondary: 11.11.11.11
and hosts file
1.1.1.1
12.12.12.12
5.5.5.5
and the following playbook task
- name: Extract secondary from list
debug:
msg: "{{ (mappings | selectattr('primary', 'search', inventory_hostname) | list | first | default({'secondary':None})).secondary }}"
The current task works and will give empty string when no match are found, but I would like to know if there is a better way/cleaner way of doing it without passing a dictionary to the default constructor.
An option would be to use json_query
- debug:
msg: "{{ mappings | json_query(\"[?primary=='\" + inventory_hostname + \"'].secondary\") }}"
, but selectattr works too
- debug:
msg: "{{ mappings | selectattr('primary', 'equalto', inventory_hostname) | map(attribute='secondary') | list }}"

When to use from_json filter in Ansible?

When should I use the from_json filter in Ansible?
I found out that using it sometimes has and sometimes have no effect.
Please consider the following example which illustrates the inconsistency I am getting.
Included in reverse order are: the questions - expected result - actual result - the playbook - the data. The data is taken from this question and the playbook is based on this answer.
The question(s):
Why storing the left part (before json_query) of the following expression in a variable and then using json_query on the variable causes the expression to be evaluated differently?
"{{ lookup('file','test.json') | json_query(query) }}"
Why does adding from_json filter alter the results (but does not if processing a variable):
"{{ lookup('file','test.json') | from_json | json_query(query) }}"
Expected result:
Last four tasks should give the same result. Alternatively, last two tasks should give the same result as previous two tasks.
Actual result (last four tasks only):
One task result differs.
TASK [This query is run against lookup value with from_json stored in a variable] ***
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run against lookup value without from_json stored in a variable] ***
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run directly against lookup value with from_json] **********
ok: [localhost] => {
"msg": [
678
]
}
TASK [This query is run directly against lookup value without from_json - the result is empty - why?] ***
ok: [localhost] => {
"msg": ""
}
The playbook:
---
- hosts: localhost
gather_facts: no
connection: local
tasks:
- set_fact:
from_lookup_with_from_json: "{{ lookup('file','test.json') | from_json }}"
- set_fact:
from_lookup_without_from_json: "{{ lookup('file','test.json') }}"
- name: Save the lookup value stored in a variable in a file for comparison
copy: content="{{ from_lookup_with_from_json }}" dest=./from_lookup_with_from_json.txt
- name: Save the lookup value stored in a variable in a file for comparison (they are the same)
copy: content="{{ from_lookup_without_from_json }}" dest=./from_lookup_without_from_json.txt
- name: This query is run against lookup value with from_json stored in a variable
debug: msg="{{ from_lookup_with_from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run against lookup value without from_json stored in a variable
debug: msg="{{ from_lookup_without_from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run directly against lookup value with from_json
debug: msg="{{ lookup('file','test.json') | from_json | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
- name: This query is run directly against lookup value without from_json - the result is empty - why?
debug: msg="{{ lookup('file','test.json') | json_query(query) }}"
vars:
query: "Foods[].{id: Id, for: (Tags[?Key=='For'].Value)[0]} | [?for=='Tigger'].id"
The data (test.json):
{ "Foods" :
[ { "Id": 456
, "Tags":
[ {"Key":"For", "Value":"Heffalump"}
, {"Key":"Purpose", "Value":"Food"}
]
}
, { "Id": 678
, "Tags":
[ {"Key":"For", "Value":"Tigger"}
, {"Key":"Purpose", "Value":"Food"}
]
}
, { "Id": 911
, "Tags":
[ {"Key":"For", "Value":"Roo"}
, {"Key":"Purpose", "Value":"Food"}
]
}
]
}
json_query requires Python object (dict) as input, if you feed it with string, it gives empty string as result.
You get different result because of Ansible templating engine tricky work.
I should definitely write a post about it on my site...
After evaluating jijna2 expression Ansible try to cast complex types to Python objects (like dict or list). See my other answer.
In your case:
1.
- set_fact:
from_lookup_with_from_json: "{{ lookup('file','test.json') | from_json }}"
from_lookup_with_from_json is a dict, because you manually convert JSON-string from file to dict with from_json filter.
2.
- set_fact:
from_lookup_without_from_json: "{{ lookup('file','test.json') }}"
from_lookup_with_from_json becomes dict, because Ansible converts it when jinja2 expression ends with }}. So from_json is actually unnecessary as the last filter in chain.
3.
debug: msg="{{ lookup('file','test.json') | from_json | json_query(query) }}"
Again, you manually convert JSON-string here. So json_query get dict as input.
4.
debug: msg="{{ lookup('file','test.json') | json_query(query) }}"
In this case you feed JSON-string (not dict) as input to json_query filter. As everything happens inside one jinja2 expression, Ansible doesn't attempt to convert anything in between.
You can also get empty string result with a variable this way:
- set_fact:
from_lookup_force_string: "{{ lookup('file','test.json') | string }}"
In this case from_lookup_force_string will not be converted by Ansible tempating engine, and json_query will give you empty response on it.

Resources