Is it possible to flatten list of lists in Ansible? - list

Need to flatten the array list FROM
[
[
[
"10.11.33.11"
]
],
[
[
"10.11.88.88"
]
]
]
TO
[ "10.11.33.11", "10.11.88.88" ]

I understand your question as "How to manage list variables?
Since you have list of lists,
---
- hosts: localhost
become: false
gather_facts: false
vars:
LIST: [ [ ['10.11.33.11'] ],[ ['10.11.88.88'] ] ]
tasks:
- name: Show LIST flattened
debug:
msg: "{{ LIST | flatten(levels=2) }}"
you need need to address the level. Resulting into an output of
TASK [Show LIST flattened] *******
ok: [localhost] =>
msg:
- 10.11.33.11
- 10.11.88.88
Background Information
How to make a flat list out of a list of lists?
How do I flatten a list of lists/nested lists?
if someone may be interested.

Related

Ansible filter item in list using variables+wildcard

I have the below lists stored in variable results_one.msg
[
{
"IP": [
"192.168.1.100",
"192.168.1.101"
],
"Pool": "lan_pool_sftp",
"Members": [
"sftpnode01:5425",
"sftpnode02:5425"
]
},
{
"IP": [
"192.168.1.103",
"192.168.1.104"
],
"Pool": "icmp-net-pool",
"Members": [
"icmpnet01:8443",
"icmpnet02:8443"
]
}
]
I have another variable node_name
I would like to get the Pool and Members information from above output, by querying one of the members name.
For example, if I assign the variable node_name: icmpnet02
I want to get the output stored as in respective variable names as below.
pool_name: icmp-net-pool
pool_members: [ icmpnet01:8443,icmpnet02:8443 ]
I tried as below and I'm unable to get it
- set_fact:
pool_name: "{{ item.Pool }}"
pool_members: "{{ item.Members }}"
with_items: "{{results_one.msg }}"
when: 'item.Members.0 is defined and "node_name:*" in item.Members'
Create a list of lists of members with their names only:
_members_hostnames: "{{ results_one.msg | map(attribute='Members')
| map('map', 'regex_replace', '^(.*):.*$', '\\1') }}"
Gives:
"_members_hostnames": [
[
"sftpnode01",
"sftpnode02"
],
[
"icmpnet01",
"icmpnet02"
]
]
select the matching entry from your relevant variable, i.e.
create a list of tuples associating each original element with its counterpart calculated members hostnames
retain only element where hostname is present in the list
keep only the first element of tupple (i.e. the orginal entry)
keep only the first element from list
_matching_entry: "{{ results_one.msg | zip(_members_hostnames)
| selectattr(1, 'contains', node_name) | map(attribute=0) | first }}"
gives
"_matching_entry": {
"IP": [
"192.168.1.103",
"192.168.1.104"
],
"Members": [
"icmpnet01:8443",
"icmpnet02:8443"
],
"Pool": "icmp-net-pool"
}
use the matching entry to extract whatever variable you need:
pool_name: "{{ _matching_entry.Pool }}"
pool_members: "{{ _matching_entry.Members }}"
Putting it all together in a test playbook:
---
- hosts: localhost
gather_facts: false
vars:
# Your orig data on a single line for legibility
results_one: {"msg":[{"IP":["192.168.1.100","192.168.1.101"],"Pool":"lan_pool_sftp","Members":["sftpnode01:5425","sftpnode02:5425"]},{"IP":["192.168.1.103","192.168.1.104"],"Pool":"icmp-net-pool","Members":["icmpnet01:8443","icmpnet02:8443"]}]}
node_name: icmpnet02
_members_hostnames: "{{ results_one.msg | map(attribute='Members')
| map('map', 'regex_replace', '^(.*):.*$', '\\1') }}"
_matching_entry: "{{ results_one.msg | zip(_members_hostnames)
| selectattr(1, 'contains', node_name) | map(attribute=0) | first }}"
pool_name: "{{ _matching_entry.Pool }}"
pool_members: "{{ _matching_entry.Members }}"
tasks:
- debug:
msg:
- Pool name is {{ pool_name }}
- Pool members are {{ pool_members }}
Which gives:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [debug] ***************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"Pool name is icmp-net-pool",
"Pool members are ['icmpnet01:8443', 'icmpnet02:8443']"
]
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Ansible - remove empty elements from the list

I have an ansible output from which I want to remove all empty (none) elements for a specific attribute. Here is the example list:
"resources": [
{
"id": "9c40900909",
"name": "some_name1"
},
{
"id": "pc4b09090"
},
{
"id": "8lknkkn45"
},
{
"id": "9df40900909",
"name": "some_name2"
}
]
Here is how I reduced the list to be just "resources" with attribute "name":
- set_fact:
resources_names: "{{ output.resources | map(attribute='name') }}"
Problem with that is that I get the elements that does not have any value for the name. These are "AnsibleUndefined" in the following output:
- debug:
msg: resources_names list is "{{ resources_names }}"
ok: [localhost] => {
"msg": "resources_names list is \"['some_name1', AnsibleUndefined, AnsibleUndefined, 'some_name2']\""
}
I tried to remove it with reject and regexp but that's not working.
- set_fact:
list2: "{{ resources_names | reject('match', '^$') | list }}"
Same with this one:
- set_fact:
resources_names: "{{ output.resources | map(attribute='name') | rejectattr('name', 'none') }}"
Any idea?
Thanks.
In a nutshell:
---
- hosts: localhost
gather_facts: false
vars:
resources: [
{
"id": "9c40900909",
"name": "some_name1"
},
{
"id": "pc4b09090"
},
{
"id": "8lknkkn45"
},
{
"id": "9df40900909",
"name": "some_name2"
}
]
tasks:
- debug:
msg: "{{ resources | selectattr('name', 'defined') | map(attribute='name') }}"
which gives:
$ ansible-playbook /tmp/play.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
PLAY [localhost] *******************************************************************************************************************************************************************************
TASK [debug] ***********************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"some_name1",
"some_name2"
]
}
PLAY RECAP *************************************************************************************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Note for your next question: please make sure you double check your examples and paste valid json/yaml data in your question. Thanks
Use the filter json_query. This filter ignores missing attributes
resources_names: "{{ output.resources|json_query('[].name') }}"
gives
resources_names:
- some_name1
- some_name2

Ansible - create list out of with_items

How can I create a list out of with_items? I tried the following but it records only the last group ID instead of creating a list.
- name: "Generate list"
set_fact:
my_new_list: "{{ [ item.group_id ] }}"
with_items:
- "{{ec2_info.instances[0].security_groups}}"
ec2_info.instances[0].security_groups has multiple group_id's.
"security_groups": [
{
"group_id": "sg-0500c5b20f7c152b4",
"group_name": "ManageIQ"
},
{
"group_id": "sg-062178ea5fabaf350",
"group_name": "launch-wizard-1"
}
],
this playbook traps all the list:
- name: "Generate list"
set_fact:
my_new_list: "{{ my_new_list|d([]) + [ item.group_id ] }}"
with_items:
- "{{ec2_info.instances[0].security_groups}}"

Scrolling through a list with sublist in Ansible

I need to simultaneously traverse a data structure to align the values, I get this structure with the code below :
- name: Set instances
set_fact:
instance_db:
- 'db2inst1'
- 'db2inst2'
- name: Get Dialog Path
shell: db2 get dbm cfg | grep -i "Current member resolved DIAGPATH" | awk {'print $6'}
become: true
become_method: sudo
become_flags: -i
become_user: "{{ item }}"
loop: "{{ instance_db }}"
register: kud_path
- name: set_fact
set_fact:
db2_store: "[{{ instance_db | list }}] + [{{ kud_path.results|map(attribute='stdout')|list }}]"
vars:
db2_store: []
Result
{
"changed": false,
"ansible_facts": {
"db2_store": [
"db2inst1",
"db2inst2",
[
"/db2home/db2inst1/sqllib/db2dump/DIAG0000/",
"/home/db2inst2/sqllib/db2dump/DIAG0000/"
]
]
},
"_ansible_no_log": false
}
Now I need to automatically traverse these indexes where I put [*]. Because as it is, I can only access the data like this item[0][1]
- name: Creating silent config
template:
src: template.txt.j2
dest: '/tmp/template{{ item[0][*] | lower }}.txt'
mode: '0775'
loop:
- "{{ db2_store }}"
This workaround was necessary to be able to pass both values to the template
Template
################## Database connection config ##################
INSTANCE={{ item[0][*] }}
DIAGLOG_PATH={{ item[1][*] }}db2diag.log
Any suggestions on how to do this or a more elegant way to get the same result?
Assuming the following:
instance_db = ["db2inst1", "db2inst2"]
kud_path.results|map(attribute='stdout')|list = ["/db2home/db2inst1/sqllib/db2dump/DIAG0000/", "/home/db2inst2/sqllib/db2dump/DIAG0000/"]
Then you can use the zip filter which will pair the Nth element of the first list with the Nth element of the second list.
- debug:
msg: "{{ instance_db | zip(kud_path.results|map(attribute='stdout')|list) }}"
Outputs:
TASK [debug] ********************************************************************
ok: [localhost] => {
"msg": [
[
"db2inst1",
"/db2home/db2inst1/sqllib/db2dump/DIAG0000/"
],
[
"db2inst2",
"/home/db2inst2/sqllib/db2dump/DIAG0000/"
]
]
}
This makes it easy for you to loop over:
- name: Creating silent config
template:
src: template.txt.j2
dest: '/tmp/template{{ item[0] | lower }}.txt'
mode: '0775'
loop: "{{ instance_db | zip(kud_path.results|map(attribute='stdout')|list) }}"
################## Database connection config ##################
INSTANCE={{ item[0] }}
DIAGLOG_PATH={{ item[1] }}db2diag.log

Ansible list concat contains only the last element when used with a loop

[Closed] It is a bug in Ansible v2.5.1, see comment below.
I want to build a new list based on a dictionary. So I try with set_fact and a loop but the variable only contains the last value (not the list)
I try simpler example without dictionary. I use this web site: https://ttl255.com/ansible-appending-to-lists-and-dictionaries/. And it doesn't work has expected.
---
- name: Append to list
hosts: localhost
vars:
devices: []
cisco:
- CiscoRouter01
- CiscoRouter02
- CiscoRouter03
- CiscoSwitch01
arista:
- AristaSwitch01
- AristaSwitch02
- AristaSwitch03
tasks:
- name: Add Cisco and Airsta devices to the list
set_fact:
devices: "{{ devices + [item] }}"
with_items:
- "{{ cisco }}"
- "{{ arista }}"
- name: Debug list
debug:
var: devices
verbosity: 0
Extract of the output:
TASK [Debug list] *********************************************************************************************************
ok: [localhost] => {
"devices": [
"AristaSwitch03"
]
}
Expected:
TASK [Debug list] *********************************************************************************************************
ok: [localhost] => {
"devices": [
"CiscoRouter01",
"CiscoRouter02",
"CiscoRouter03",
"CiscoSwitch01",
"AristaSwitch01",
"AristaSwitch02",
"AristaSwitch03"
]
}
I use the ansible version: 2.5.1
its a bug of 2.5.1, you will have to upgrade.
check this question for more info.