ansible get result from role and append to a list - ansible

I have a play like this
---
- name: List images in ACRs
any_errors_fatal: true
hosts:
- localhost
gather_facts: false
vars:
acrs: ["registry1", "registry2"]
tasks:
- name: list repos
with_items: "{{ acrs }}"
include_role:
name: list_docker_image_repos
vars:
registry_name: "{{ item }}"
list_docker_image_repos will do set_fact which a list of dicts.
How can I append all the facts (from every iteration) to a list?
Or is there are different way to do this?
thanks

In each iteration put the list into a dictionary. For example, given the role
shell> cat roles/list_docker_image_repos/tasks/main.yml
- set_fact:
docker_image_repos: "{{ ['repo1', 'repo2', 'repo3']|
product([registry_name])|
map('join', '-')|
list }}"
- set_fact:
my_lists: "{{ my_lists|
combine({registry_name: docker_image_repos}) }}"
the playbook
- hosts: localhost
vars:
acrs: [reg1, reg2]
my_lists: {}
tasks:
- name: list repos
include_role:
name: list_docker_image_repos
loop: "{{ acrs }}"
vars:
registry_name: "{{ item }}"
- debug:
var: my_lists
gives the dictionary
my_lists:
reg1:
- repo1-reg1
- repo2-reg1
- repo3-reg1
reg2:
- repo1-reg2
- repo2-reg2
- repo3-reg2
A dictionary is a more suitable structure for this purpose compared with a list. There are many ways how to use it. For example, you can
extract the lists
- debug:
msg: "{{ acrs|map('extract', my_lists)|list }}"
gives
msg:
- - repo1-reg1
- repo2-reg1
- repo3-reg1
- - repo1-reg2
- repo2-reg2
- repo3-reg2
Use the filter flatten to put all items into a single list
- debug:
msg: "{{ acrs|map('extract', my_lists)|flatten }}"
gives
msg:
- repo1-reg1
- repo2-reg1
- repo3-reg1
- repo1-reg2
- repo2-reg2
- repo3-reg2
Use the filter dict2items to iterate items
- debug:
var: item
loop: "{{ my_lists|dict2items }}"
gives (abridged)
item:
key: reg1
value:
- repo1-reg1
- repo2-reg1
- repo3-reg1
item:
key: reg2
value:
- repo1-reg2
- repo2-reg2
- repo3-reg2
Or, use the lookup plugin subelements to iterate the items of the lists as well
- debug:
var: item
with_subelements:
- "{{ my_lists|dict2items }}"
- value
gives (abridged)
item:
- key: reg1
- repo1-reg1
item:
- key: reg1
- repo2-reg1
item:
- key: reg1
- repo3-reg1
item:
- key: reg2
- repo1-reg2
item:
- key: reg2
- repo2-reg2
item:
- key: reg2
- repo3-reg2
Example of a complete playbook for testing
- hosts: localhost
vars:
acrs: [reg1, reg2]
my_lists: {}
tasks:
- name: list repos
include_role:
name: list_docker_image_repos
loop: "{{ acrs }}"
vars:
registry_name: "{{ item }}"
- debug:
var: my_lists
- debug:
msg: "{{ acrs|map('extract', my_lists)|list }}"
- debug:
msg: "{{ acrs|map('extract', my_lists)|flatten }}"
- debug:
var: item
loop: "{{ my_lists|dict2items }}"
- debug:
var: item
with_subelements:
- "{{ my_lists|dict2items }}"
- value

Related

Ansible filter dict based on key presence and list values

I have this variable :
components:
comp_one:
name: first_component
remote_server_path: "/tmp/path/one"
binaries:
- test
- test_two
comp_two:
name: second_component
remote_server_path: "/tmp/path/two"
binaries:
- ed_test
- ed_test_2
comp_three:
name: third_component
remote_server_path: "/tmp/path/three"
What I'm trying to achieve, is to only keep sub-dictionaries of components that has key binaries, and the filter those binaries using my group_names. Also, D'd like to only keep dict keys binaries and remote_server_path.
For example, if i have this hosts.yaml :
---
all:
children:
kubernetes_dev:
children:
ed_test:
hosts:
host_one:
ansible_host: XXX.XXX.XXX.XXX
ansible_ssh_user: ansible
test_two:
hosts:
host_one:
ansible_host: XXX.XXX.XXX.XXX
ansible_ssh_user: ansible
test:
hosts:
host_one:
ansible_host: XXX.XXX.XXX.XXX
ansible_ssh_user: ansible
The expected output would be :
comp_one:
remote_server_path: "/tmp/path/one"
binaries:
- test
- test_two
comp_two:
remote_server_path: "/tmp/path/two"
binaries:
- ed_test
I've successfully filtered the comp_three dict using this :
- name: Debug
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ components | dict2items | selectattr('value.binaries', 'defined') }}"
But, I can't figure out a way to only select binariesĀ and remote_server_path keys while filtering with my `group_names.
Does anyone can't help me find a clean way to do this ?
Regards,
Use group_names. This is: "List of groups the current host is part of.". For the host host_one in the inventory hosts.yaml this is
group_names: [ed_test, test, test_two]
Convert the dictionary components to list and test intersect of binaries and group_names. Default to empty list if the attribute binaries is missing
- set_fact:
selected_list: "{{ selected_list|d([]) + [_item] }}"
loop: "{{ components|dict2items }}"
when: my_groups|length > 0
vars:
_item: "{{ item|combine(my_value) }}"
my_value: "{{ {'value': {'binaries': my_groups,
'remote_server_path': item.value.remote_server_path}} }}"
my_groups: "{{ item.value.binaries|d([])|intersect(group_names) }}"
gives
selected_list:
- key: comp_one
value:
binaries:
- test
- test_two
remote_server_path: /tmp/path/one
- key: comp_two
value:
binaries:
- ed_test
remote_server_path: /tmp/path/two
Conver the list into a dictionary
- set_fact:
selected_dict: "{{ selected_list|items2dict }}"
gives the expected result
selected_dict:
comp_one:
binaries:
- test
- test_two
remote_server_path: /tmp/path/one
comp_two:
binaries:
- ed_test
remote_server_path: /tmp/path/two
Example of a complete playbook
- hosts: all
vars:
components:
comp_one:
name: first_component
remote_server_path: /tmp/path/one
binaries:
- test
- test_two
comp_two:
name: second_component
remote_server_path: /tmp/path/two
binaries:
- ed_test
- ed_test_2
comp_three:
name: third_component
remote_server_path: /tmp/path/three
tasks:
- set_fact:
selected_list: "{{ selected_list|d([]) + [_item] }}"
loop: "{{ components|dict2items }}"
when: my_groups|length > 0
vars:
_item: "{{ item|combine(my_value) }}"
my_value: "{{ {'value': {'binaries': my_groups,
'remote_server_path': item.value.remote_server_path}} }}"
my_groups: "{{ item.value.binaries|d([])|intersect(group_names) }}"
- set_fact:
selected_dict: "{{ selected_list|items2dict }}"
- debug:
var: selected_dict

Ansible How do i sort an array in descending order upon the element substring

Below is my array:
- set_fact:
diskout:
- 85_20.198.65.132
- 86_52.140.118.141
- 84_20.198.75.31
- 82_20.204.75.114
- 83_20.204.24.160
I wish to sort this in descending order upon just the first substring separated by _ while ignoring whatever is after the underscore.
Thus, my expected output is:
- 86_52.140.118.141
- 85_20.198.65.132
- 84_20.198.75.31
- 83_20.204.24.160
- 82_20.204.75.114
I tried the below but it did not give me the desired output:
- debug:
msg: "The automation will run on {{ item }}"
with_items: "{{ diskout | reverse | list }}"
Can you please suggest?
Create index, e.g.
- debug:
msg: "{{ _dict|dict2items|
sort(attribute='key', reverse=true)|
map(attribute='value')|
list }}"
vars:
_index: "{{ diskout|map('regex_replace', '^(.*)_(.*)$', '\\1')|list }}"
_dict: "{{ dict(_index|zip(diskout)) }}"
gives
msg:
- 86_52.140.118.141
- 85_20.198.65.132
- 84_20.198.75.31
- 83_20.204.24.160
- 82_20.204.75.114
The next option might be faster
- debug:
msg: "{{ _dict|sort(reverse=true)|map('extract', _dict)|list }}"
vars:
_index: "{{ diskout|map('regex_replace', '^(.*)_(.*)$', '\\1')|list }}"
_dict: "{{ dict(_index|zip(diskout)) }}"

"search" a string in "when:" conditional statement?

In the following playbook, I have to use search in a when: statement. Can you please tell me what is wrong with the statement when: item.name is search (domain_list)? domain_list is an array variable defined in files.yml as shown below.
---
- hosts: localhost
gather_facts: False
connection: local
vars_files:
- files.yml
vars:
infra:
- name: ironman.vcloud-lab.com
connection_state: CONNECTED
- name: hulk.vcloud-lab.com
connection_state: CONNECTED
- name: captain.vcloud-lab.com
connection_state: DISCONNECTED
- name: hawkeye.vcloud-lab.com
connection_state: DISCONNECTED
tasks:
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{esxilist | default([]) + [item]}}"
with_items: "{{infra}}"
when: item.name is search (domain_list) ## <= please correct me here, it doesn't work
- name: Show connected esxi list information
debug:
var: esxilist
files.yml
---
domain_list:
- ironman
- captain
Select the short name and test the presence in the list. For example, use regex_replace
- name: Filter list of only connected esxi
set_fact:
esxilist: "{{ esxilist|default([]) + [item] }}"
loop: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name|regex_replace('^(.*?)\\.(.*)$', '\\1') }}"
gives
esxilist:
- connection_state: CONNECTED
name: ironman.vcloud-lab.com
- connection_state: DISCONNECTED
name: captain.vcloud-lab.com
The next option is to split the string and select the first item. For example
vars:
_short: "{{ item.name.split('.')|first }}"
gives the same result.
See How to create a Minimal, Reproducible Example. For example
- hosts: localhost
vars:
domain_list: [a, b]
infra:
- {name: a.vcloud-lab.com, state: CONNECTED}
- {name: b.vcloud-lab.com, state: DISCONNECTED}
- {name: c.vcloud-lab.com, state: CONNECTED}
tasks:
- set_fact:
l1: "{{ l1|default([]) + [item] }}"
with_items: "{{ infra }}"
when: _short in domain_list
vars:
_short: "{{ item.name.split('.')|first }}"
- debug:
var: l1

Ansible: Merge list to new list with item.key and item.value

Example:
file: storages.yml
---
storages:
- storageA
- storageB
file: storageA_list_vv.yml
---
vv:
- storageA_lun01
- storageA_lun02
file: storageB_list_vv.yml
---
vv:
- storageB_lun01
Expected new list: vvset.yml
---
vvset:
- key: serverA
value: serverA_lun01
- key: serverA
value: serverA_lun02
- key: serverB
value: serverB_lun01
Appreciate your guidance and advise on how to combine three list storages.yml, storageA_list_vv.yml and storageB_list_vv.yml into new list vvset.yml.
Thanks in advance.
Below is the step by step "brute-force" approach. Custom filter would make it much simpler.
Read the files and create the list of all items
- set_fact:
vv: "{{ vv|default([]) +
(lookup('file', item)|from_yaml).values()|list }}"
loop:
- storageA_list_vv.yml
- storageB_list_vv.yml
- debug:
var: vv|flatten
gives
vv|flatten:
- storageA_lun01
- storageA_lun02
- storageB_lun01
Extract the data
- set_fact:
vvset1: "{{ vvset1|default([]) +
[{'index': index, 'lun': lun}] }}"
loop: "{{ vv|flatten }}"
vars:
index: "{{ item.split('_').0|
regex_replace('^storage(.*)$', '\\1')}}"
lun: "{{ item.split('_').1 }}"
- debug:
var: vvset1
gives
vvset1:
- index: A
lun: lun01
- index: A
lun: lun02
- index: B
lun: lun01
Use groupby
- set_fact:
vvset2: "{{ vvset2|default([]) +
[{'index': item.0, 'lun': item.1|map(attribute='lun')|list}] }}"
loop: "{{ vvset1|groupby('index') }}"
- debug:
var: vvset2
gives
vvset2:
- index: A
lun:
- lun01
- lun02
- index: B
lun:
- lun01
Use subelements
- set_fact:
vvset3: "{{ vvset3|default([]) +
[{'key': key, 'value': val}] }}"
with_subelements:
- "{{ vvset2 }}"
- lun
vars:
key: "{{ 'server' ~ item.0.index }}"
val: "{{ 'server' ~ item.0.index ~ '_' ~ item.1 }}"
- debug:
var: vvset3
gives
vvset3:
- key: serverA
value: serverA_lun01
- key: serverA
value: serverA_lun02
- key: serverB
value: serverB_lun01

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

Resources