Get Odd or Even Index Value from Variable in Ansible Playbook - ansible

I need to get odd or even index value from a variable list:
For example:
- hosts: myhost
vars:
- var1: ["test1","test2","test3","test4","test5"]
- odd_var: []
- even_var: []
I need odd_var to be ["test1","test3","test5"] and even_var to be ["test2","test4"] and also concatenate each variable string of odd_var and even_var to be one string like:
odd_string: "test1,test3,test5"
even_string: "test2,test4"
What should i do to achieve this?
I have tried :
- name: test
set_fact:
odd_list: "{{ odd_list | default([]) + [item] }}"
loop: "{{ var1 }}"
when: "{{ lookup('ansible.utils.index_of', var1, 'eq', item) is even }}
it works but i wonder if i can get more eficient way to do this

Since you said "index value", I'm going to take you at your word and base it on position in the list, not the numbers contained in the strings.
- hosts: localhost
vars:
var1:
- test1
- test2
- test3
- test4
- test5
odd_var: "{{ var1[::2] | join(',') }}"
even_var: "{{ var1[1::2] | join(',') }}"
tasks:
- debug:
msg: "{{ odd_var }} / {{ even_var }}"
Output:
TASK [debug] *******************************************************************
ok: [localhost] => {
"msg": "test1,test3,test5 / test2,test4"
}

Using regex, checking the last character.
- var1: ["test1","test2","test3","test4","test5"]
- even: "{{ var1 | select('search','.*[02468]$') | join(',') }}"
- odd: "{{ var1 | select('search','.*[13579]$') | join(',') }}"

Related

How to change a value assigned to a variable into integer in Ansible

I am trying to compare two filesystem values /var and /tmp and as per the result need to execute the corresponding remediation playbook.
- name: check top utilized files.
shell: du -sx --exclude=/proc --exclude=/sys* --exclude=/dev* --exclude=/u02* --exclude=/usr* --exclude=/boot* --exclude=/swapfile* --exclude=/run* -t 200M "{{fs_input}}"* | sort -n -r
register: top_files
- debug:
msg: "{{ top_files.stdout_lines }}"
- debug:
msg: "{{ item.split('\t/')[0]}}"
with_items: "{{ top_files.stdout_lines[0] }}"
register: value1
- debug:
msg: "{{ value1 |int * 1024 }}"
- debug:
msg: "{{ item.split('\t/')[0]}}"
with_items: "{{ top_files.stdout_lines[1] }}"
register: value2
- debug:
msg: "{{ value2 |int * 1024 }}"
The debug value gives 0 as output.
There's a lot going on here. register saves the result of a task, but you are trying to treat the registered variable as though it contains the single value you want. You're looping over single items, which will make the registered result more complicated than it needs to be.
debug + register is not a good way to do variable manipulation, but you can make it work:
# Removing the unnecessary loop
- debug:
msg: "{{ top_files.stdout_lines.0.split('\t/')[0] }}"
register: value1
- debug:
msg: "{{ value1.msg | int * 1024 }}"
# Or you can leave it in, if you really want
- debug:
msg: "{{ item.split('\t/')[0]}}"
loop:
- "{{ top_files.stdout_lines[1] }}"
register: value2
- debug:
msg: "{{ value2.results.0.msg | int * 1024 }}"
However, it's better to use the Ansible features that are actually intended for this type of work.
- set_fact:
value1: "{{ top_files.stdout_lines.0.split('\t/')[0] }}"
value2: "{{ top_files.stdout_lines.1.split('\t/')[0] }}"
- debug:
msg: "{{ value1 | int * 1024 }} / {{ value2 | int * 1024 }}"
or
- debug:
msg: "{{ value1 | int * 1024 }} / {{ value2 | int * 1024 }}"
vars:
value1: "{{ top_files.stdout_lines.0.split('\t/')[0] }}"
value2: "{{ top_files.stdout_lines.1.split('\t/')[0] }}"
Personally, I might write it more like this:
- debug:
msg: "{{ values.0 | int * 1024 }} / {{ values.1 | int * 1024 }}"
vars:
values: "{{ top_files.stdout_lines | map('split', '\t/') | map('first') }}"

Iterate Over 2 dictionary in ansible

I Have 2 dictionary:
- Test1:
1: pass
2: fail
3: pass
- Test2:
1.1.1.1: val1
2.2.2.2: val2
3.3.3.3: val3
Condition is when Test1.value contians fail
- name: test
debug:
msg: "{{item.1.value}} {{item.1.key}} {{item.0.key}} {{item.0.value}}"
with_together:
- "{{Test1}}"
- "{{Test2}}"
when: item.0.value == "fail"
This is not working as expected unable to get both key and value of 2 dict in one loop
In when statement you must to use item.0 or item.1 to evaluate the condition. And I recommend you use a list in with_together loop and if you are using a variable you have to use braces {{ variable }} .
Try as below:
- name: test
debug:
msg: "{{item.1 }}"
with_together:
- "{{ Test1.values() | list }}"
- "{{ Test2.values() | list }}"
when: item.0 == "fail"
You'll get
TASK [test] *******************************************************************************************************************************************************************************************************
skipping: [127.0.0.1] => (item=['pass', 'val1'])
ok: [127.0.0.1] => (item=['fail', 'val2']) => {
"msg": "val2"
}
skipping: [127.0.0.1] => (item=['pass', 'val3'])
I achieved this by :
converting dict to list using filter -> |list
since
both dict of same size I was able to get data of both dict in single loop:
- name: test
debug:
msg: "{{item.0}} {{item.1}} {{item.2}} {{item.3}}"
with_together:
- "{{ Test1.values() | list }}"
- "{{ Test2.values() | list }}"
- "{{ Test1.keys() | list }}"
- "{{ Test2.keys() | list }}"
when: item.0 == "fail"

Regex match for key in jinja2 selectattr()

Given the data:
"serverName", [
serverData: [
{
"internal_ip": "10.1.1.100",
"external_ip": "172.16.1.10",
"name": "dns-1"
},
],
]
This extracts the name value dns-1 when the internal_ip matches the equalto. So far, so good.
- debug:
msg: "{{ mydict | selectattr('internal_ip', 'equalto', '10.1.1.100') |
map(attribute='name') | list }}"
In the real problem, I do not know which type of *_ip will the ip address I'm searching for will reside. It could be under internal_ip, it could be under external_ip and for all I know, there could be even more options, the only thing that will always be there - is the actual IP address I'm searching for: '10.1.1.100`.
So I need to regex match like so:
- debug:
msg: "{{ mydict | selectattr('^.*$', 'equalto', '10.1.1.100') |
map(attribute='name') | list }}"
I'm not sure if this is possible, but it seems to be one of the ways out of this jam.
For example, the playbook
shell> cat playbook.yml
- hosts: localhost
vars:
mylist:
- {internal_ip: 10.1.1.101, external_ip: 172.16.1.10, name: dns-1}
- {internal_ip: 10.1.1.102, external_ip: 172.16.1.10, name: dns-2}
- {internal_ip: 10.1.1.103, external_ip: 172.16.1.10, name: dns-3}
tasks:
- set_fact:
sel: "{{ sel|default([]) + [item.name] }}"
loop: "{{ mylist }}"
when: sel_ip|default('') in item.values()|list
- debug:
var: sel
gives
shell> ansible-playbook playbook.yml -e sel_ip=172.16.1.10
...
sel:
- dns-1
- dns-2
- dns-3
shell> ansible-playbook playbook.yml -e sel_ip=10.1.1.103
...
sel:
- dns-3

Ansible: How to filter dict2items and run playbook only for the matched values

I have a dict playbook which looks like this:
x_php_versions_installed:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
- pecl-memcached
- pecl-imagick
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
- pecl-imagick
I would like to filter them, to write me each item.value which contains 'ea' string but not everything else. My task looks like this:
- name: Write out only the ea packages
debug:
msg: '{{ item.value }}'
when: item.value | selectattr(item.value, 'contains', 'ea')
loop: '{{ x_php_versions_installed | dict2items }}
But it does not work, because it will list all of the packages, not only the ea ones. The expected answer should look like this:
...
"msg": [
"ea-php71-php-bcmath",
"ea-php71-php-xmlrpc",
"ea-php71-php-zip"
]
...
"msg": [
"ea-php72-php-cli",
"ea-php72-php-common",
"ea-php72-php-curl"
]
...
Another possibility is to filter out the 'pecl' string, it will gave me the same result and it also works fine.
Q: "Filter item.value which contains ea string."
A: The task below does the job
- debug:
msg: "{{ item.value|select('match','^ea-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
gives (abridged)
msg:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
msg:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
Note: The test match by default "succeeds if it finds the pattern at the beginning of the string". The task below gives the same result
- debug:
msg: "{{ item.value|select('match', 'ea-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
Q: "Filter out the pecl string."
A: Change the filter to reject and fit the regex. For example, the task below gives the same result
- debug:
msg: "{{ item.value|reject('match','^pecl-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
Notes:
Select the lists without iteration. Declare the variables
x_php_versions_installed_keys: "{{ x_php_versions_installed.keys()|list }}"
x_php_versions_installed_ea_vals: "{{ x_php_versions_installed|dict2items|
map(attribute='value')|
map('select', 'match', 'ea-')|list }}"
x_php_versions_installed_ea: "{{ dict(x_php_versions_installed_keys|
zip(x_php_versions_installed_ea_vals)) }}"
gives
x_php_versions_installed_ea:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
Example of a complete playbook for testing
- hosts: localhost
vars:
x_php_versions_installed:
ea-php71:
- ea-php71-php-bcmath
- ea-php71-php-xmlrpc
- ea-php71-php-zip
- pecl-memcached
- pecl-imagick
ea-php72:
- ea-php72-php-cli
- ea-php72-php-common
- ea-php72-php-curl
- pecl-imagick
x_php_versions_installed_keys: "{{ x_php_versions_installed.keys()|list }}"
x_php_versions_installed_ea_vals: "{{ x_php_versions_installed|dict2items|
map(attribute='value')|
map('select', 'match', 'ea-')|list }}"
x_php_versions_installed_ea: "{{ dict(x_php_versions_installed_keys|
zip(x_php_versions_installed_ea_vals)) }}"
tasks:
- debug:
msg: "{{ item.value|select('match','^ea-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|select('match', 'ea-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|reject('match','^pecl-(.*)$')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
msg: "{{ item.value|reject('match','pecl-')|list }}"
loop: "{{ x_php_versions_installed|dict2items }}"
- debug:
var: x_php_versions_installed_ea

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.

Resources