Scrolling through a list with sublist in Ansible - list

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

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 returns 'AnibleUnicode' instead of JSON, unable to filter

I'm unable to filter the following output with json_query, even though it works on a jmespath validator website.
Let me explain in detail with the code.
type_debug returns AnsibleUnicode for members value, so when I tried to use json_query to filter output it returned null. I need to get value from key ip, but I don't know how to do it in a good way.
---
- hosts: localhost
gather_facts: false
vars:
- RPN:
stdout: >-
"members": [
{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}
]
tasks:
- debug:
msg: "{{ RPN.stdout | type_debug }}"
From your question I understand that you like to gather all member with a json_query. To do so I've corrected the JSON slightly.
---
- hosts: localhost
become: false
gather_facts: false
vars:
RPN:
stdout: >-
{
"members": [{
"id": 0,
"ip": "x.x.x.x",
"owner": "buzut",
"private_ip": "10.91.154.39",
"speed": 100,
"status": "active"
}]
}
tasks:
- name: Show type(s)
debug:
msg:
- "{{ RPN.stdout | type_debug }}"
- "{{ RPN.stdout | from_json | type_debug }}"
- "{{ RPN.stdout | to_json | type_debug }}"
- name: Show members
debug:
msg: "{{ RPN.stdout | from_json | json_query('members') }}"
resulting into an output of
TASK [Show type(s)] *********
ok: [localhost] =>
msg:
- AnsibleUnicode
- dict
- str
TASK [Show members] *********
ok: [localhost] =>
msg:
- id: 0
ip: x.x.x.x
owner: buzut
private_ip: 10.91.154.39
speed: 100
status: active
I need to get value from key ip
- name: Show value of key 'ip'
debug:
msg: "{{ RPN.stdout | from_json | json_query('members[*].ip') }}"
resulting into an output of
TASK [Show value of key 'ip'] *********
ok: [localhost] =>
msg:
- x.x.x.x
Further Documentation
Selecting JSON data: JSON queries

How do I add to my existing Ansible Dictionary by looking something up from another dictionary?

Been at this about a week and I think it's time to post this. I'm pretty decent at ansible but clearly terrible at dictionaries.
How do I add a sub-key to a dictionary using update_list?
Alright so I have two dictionaries, one is the output of a command to pull AD Creds, the dictionary looks like this called userresult:
{
"objects": [
{
"Name" : "Administrator"
"objectSid": {
"Name" : "DOMAIN\\Administrator"
"Sid": "S-1-5-21"
}
},
{
"Name" : "Guest"
"objectSid": {
"Name" : "DOMAIN\\Guest"
"Sid": "S-1-5-22"
}
]
}
Now, I've got a dictionary that has the names and info I want to lookup (inputdictionary.yml):
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
What I've tried:
---
- name: Update dictionary
hosts: myhosts
gather_facts: no
tasks:
- name: Get SID for user - you can ignore this part it just gets userresult
community.windows.win_domain_object_info:
filter: ObjectClass -eq 'user' -and objectCategory -eq 'Person'
properties:
- objectSid
register: userresult
- name: ready yaml dictionary
delegate_to: localhost
slurp:
path: '/home/inputdictionary.yml'
register: SourceInfo
- name: extract dictionary
set_fact:
myinfo: "{{SourceInfo['content'}] | b64decode | from_yaml ))"
- name: build a list of updates for dictionary
set_fact:
update_list: "{{ update list + update }}"
loop: "{{ myinfo.dictionary.users | dict2items }}"
loop_control:
index_var: idx
vars:
update_list: []
update:
- path: myinfo.dictonary.users[{{idx}}].new_sid
value: "TEST"
- debug:
var: update_list
Expected output:
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
new_sid: TEST
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
new_sid: TEST
Current output:
TASK [build a list of updates for dictinoary]
FAILED! -> {
"msg": "template error while templating string: expected token 'end of print statement', got 'list. String: {{update list + update}}"
}
Test needs to be replaced with the new SID eventually, but I just want this step to work first..
Been trying to follow this but no avail: https://docs.ansible.com/ansible/latest/collections/ansible/utils/update_fact_module.html
Any help would be appreciated while I continue to try to fix this..
TEST needs to be replaced with the new SID eventually, but I just want this step to work first
- name: "make this working"
hosts: localhost
vars:
dictionary:
users:
Nonexistentuser:
old_sid: 1-1-1-1
domain: nonexistentdomain
Administrator:
old_sid: 2-2-2-2
domain: DOMAIN
tasks:
- name: build a list of updates for dictionary
set_fact:
update_list: "{{ update_list | d({}) | combine({ item.key: _v}) }}"
loop: "{{ dictionary.users | dict2items }}"
vars:
_k: "{{ item.key }}"
_v: "{{ item.value | combine({'new_sid': newsid}) }}"
newsid: "TEST"
- debug:
msg: '{{ update_list }}'
result:
ok: [localhost] => {
"msg": {
"Administrator": {
"domain": "DOMAIN",
"new_sid": "TEST",
"old_sid": "2-2-2-2"
},
"Nonexistentuser": {
"domain": "nonexistentdomain",
"new_sid": "TEST",
"old_sid": "1-1-1-1"
}
}
}

ansible identifying values in a list

The below code is attempting to find all the keys associated with the value HWIC-8A. I have tried a few different variations & I can't get the key to print, without doing something really long winded. As i'll be repeating this code with different values, i don't want to search each key/value pair individually within that list.
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
- name: Apply HWIC-8A Build
debug:
msg: "{{ item.key }}"
with_items: "{{ MODULES }}"
when: "{{ item.value }} == HWIC-8A"
Maybe that's something for you:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
tasks:
- debug: var=MODULES
- debug: msg="{{ MODULES | dict2items }}"
- debug: msg="{{ MODULES | dict2items | selectattr('value','match','HWIC-8A') | map(attribute='key')| list }}"
Then if you would like to have multiple matches, you could solve it with an MATCH list:
---
- hosts: localhost
vars:
MODULES:
Slot_0_SubSlot_0: HWIC-8A
Slot_0_SubSlot_1: EHWIC-VA-DSL-M
Slot_0_SubSlot_3: HWIC-8A
Slot_1_SubSlot_3: HWIC-8C
Slot_1_SubSlot_2: HWIC-8C
MATCH:
- HWIC-8A
- HWIC-8C
tasks:
- debug:
msg: "{{ MODULES | dict2items | selectattr('value','match',item) | map(attribute='key')| list }}"
with_items: "{{ MATCH }}"
Output:
TASK [debug] ***********************************************************************************************************************************************************************************
Thursday 27 August 2020 15:08:10 +0200 (0:00:00.042) 0:00:02.037 *******
ok: [localhost] => (item=HWIC-8A) => {
"msg": [
"Slot_0_SubSlot_0",
"Slot_0_SubSlot_3"
]
}
ok: [localhost] => (item=HWIC-8C) => {
"msg": [
"Slot_1_SubSlot_3",
"Slot_1_SubSlot_2"
]
}
I would use use jinja templates to do it. Something like this:
- name: Apply HWIC-8A Build
debug:
msg: '{% for m in MODULES %}{% if MODULES[m] == "HWIC-8A" %}{{ m }} {% endif %}{% endfor %}'
Which will give you this:
ok: [localhost] => {
"msg": "Slot_0_SubSlot_0 Slot_0_SubSlot_3 "
}
There is probably a fancy way using filters as well.

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.