Loop over multiple JSON arrays to filter the relevant JSON object based on a key value in Ansible - json

I am trying to loop over 2 different JSON array and filter only if the 2 key values are same.
I am comparing cidr from po-orig.json/po file and subnet from pocooling json. If the key values are same in both file, then print the pertaining details of pocooling json file as a list.
- name: Combine GP MGMT
vars:
pocidr: >-
{{
po
| json_query('ansible_facts.policyobject[].json[]')
}}
poname: >-
{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| list
}}
set_fact:
result: >-
{{
result | default([])
+
[poname]
}}
with_items: "{{ pocooling }}"
Updated code
- name: Compare the Current & Actual GP policy
set_fact:
pocidr: "{{ po | json_query(\"ansible_facts.policyobject[].json[]\") }}"
- name: Combine GP MGMT
vars:
poname:
"{{
pocidr
| selectattr('cidr', '==', item.subnet)
| map(attribute='name')
| first
}}"
set_fact:
result:
"{{
result | default([])
+
[poname]
}}"
with_items: "{{ pocooling }}"
pocooling.json
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
},
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "123456789",
"reservedIpRanges": [],
"subnet": "10.11.11.0/28"
}
]
po-orig.json(po)
{
"ansible_facts":{
"policyobject":[
{
"invocation":{
"module_args":{
"status_code":[
200,
201,
202
],
"validate_certs":false
}
},
"item":{
"id":"123456",
"name":"DC"
},
"json":[
{
"category":"network",
"cidr":"10.1.1.1/28",
"createdAt":"2021-11-23T19:48:21Z",
"groupIds":[
],
"id":"545649843651365",
"name":"JM-Privat",
"networkIds":[
"545649843651365"
],
"type":"cidr",
"updatedAt":"2021-11-23T19:48:21Z"
},
{
"category":"network",
"cidr":"10.10.10.0/28",
"createdAt":"2021-12-07T13:54:05Z",
"groupIds":[
"897987654689854"
],
"id":"564678984565465",
"name":"PO-L-DE-MCC-COOLING_DMZ_LAB",
"networkIds":[
],
"type":"cidr",
"updatedAt":"2021-12-07T13:54:05Z"
}
]
}
]
}
}
Error:
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: {{ pocidr\n | selectattr('cidr', '==', item.subnet)\n | map(attribute='name')\n | list\n}}: 'dict object' has no attribute 'cidr
Expected result
[
{
"applianceIp": "10.10.10.10",
"dhcpBootOptionsEnabled": false,
"dhcpHandling": "Run a DHCP server",
"dhcpLeaseTime": "1 day",
"dhcpOptions": [],
"dnsNameservers": "upstream_dns",
"fixedIpAssignments": {},
"id": 2222,
"name": "Cooling",
"networkId": "78984654989",
"reservedIpRanges": [],
"subnet": "10.10.10.0/28"
}
]
Complete Playbook
- name: Test
hosts: localhost
connection: local
gather_facts: false
vars:
pocooling: "{{ lookup('file', 'pocooling.json') | from_json }}"
tasks:
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"

Given the list
pocooling:
- networkId: '78984654989'
subnet: 10.10.10.0/28
- networkId: '123456789'
subnet: 10.11.11.0/28
Select list of cidr
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
_cidr:
- 10.1.1.1/28
- 10.10.10.0/28
Then select the items that fit the condition
- debug:
msg: "{{ pocooling|selectattr('subnet', 'in' , _cidr) }}"
vars:
_cidr: "{{ ansible_facts.policyobject|json_query('[].json[].cidr') }}"
gives
msg:
- networkId: '78984654989'
subnet: 10.10.10.0/28
Your code, fixed and simplified
- include_vars:
file: po-orig.json
name: po
- name: Combine GP MGMT
vars:
poname: "{{ po|json_query('ansible_facts.policyobject[].json[]')|
selectattr('cidr', '==', item.subnet)|
map(attribute='name')|
list }}"
set_fact:
result: "{{ result|default([]) + [poname] }}"
loop: "{{ pocooling }}"
gives
result:
- - PO-L-DE-MCC-COOLING_DMZ_LAB
- []

Related

Pass JSON from output for another query Ansible

Please help me figure out what I'm wrong with. I'm getting a JSON from Ansible and filtering it, after which I want to save the output and reuse it. But, unfortunately, I get an error that this attribute does not exist. Where did I go wrong?
playbook code:
var:
query_general: "body.results[].{display_name: display_name, subnets: subnets[]}"
- name: parsing query
set_fact:
myvar: "{{ results | json_query(query_general) }}"
register: output
- name: qwe
set_fact:
scndjson: "{{ output.myvar[].display_name }}"
- name: print
debug:
msg: "{{ scndjson }}"
I tried the json_query second case as well, but that didn't work either.
in register:output i have:
[
{
"display_name": "1test",
"subnets": [
{
"gateway_address": "0.0.0.0/25",
"network": "0.0.0.0/25"
}
]
},
{
"display_name": "test",
"subnets": [
{
"gateway_address": "0.0.0.1/25",
"network": "0.0.0.1/25"
}
]
}
]
error:
The task includes an option with an undefined variable.
it can be: output, display_name, etc
UPD:
I corrected the yaml, there are no errors, but the data is not displayed.
tasks:
- name:
nsxt_rest:
hostname: anyhost
username: anyuser
password: anypass
validate_certs: false
method: get
path: /policy/api/v1/infra/segments
register: nsx_results
- debug:
var: nsx_query_general
vars:
nsx_query_general: "{{ nsx_results | json_query('body.results[].{display_name: display_name, subnets: subnets[]}') }}"
register: output
- debug:
var: secondjson
vars:
secondjson: "{{ output|json_query('[].display_name') }}"
Output from nsx_query_general:
{
"nsx_query_general": [
{
"display_name": "test",
"subnets": [
{
"gateway_address": "0.0.0.0/25",
"network": "0.0.0.0/25"
}
]
},
{
"display_name": "1test",
"subnets": [
{
"gateway_address": "0.0.0.1/25",
"network": "0.0.0.1/25"
}
]
}]}
Output from secondjson:
{
"secondjson": "",
"_ansible_verbose_always": true,
"_ansible_no_log": false,
"changed": false
}
Given the registered variable output
output:
- display_name: 1test
subnets:
- gateway_address: 0.0.0.0/25
network: 0.0.0.0/25
- display_name: test
subnets:
- gateway_address: 0.0.0.1/25
network: 0.0.0.1/25
Either use json_query
scndjson: "{{ output|json_query('[].display_name') }}"
, or map attribute
scndjson: "{{ output|map(attribute='display_name')|list }}"
Both declarations create the list
scndjson: [1test, test]
Example of a complete playbook
- hosts: localhost
vars:
output:
- display_name: 1test
subnets:
- gateway_address: 0.0.0.0/25
network: 0.0.0.0/25
- display_name: test
subnets:
- gateway_address: 0.0.0.1/25
network: 0.0.0.1/25
tasks:
- debug:
var: scndjson
vars:
scndjson: "{{ output|json_query('[].display_name') }}"
- debug:
var: scndjson
vars:
scndjson: "{{ output|map(attribute='display_name')|list }}"

Parse json value when value contains a string in ansible

This ansible playbook works
---
- hosts: localhost
gather_facts: False
vars:
jq: "[?contains(name, 'Pizza')]"
json: |
[{
"name": "Ted's Sub Shop - 720895714701",
"templateid": "24632"
},
{
"name": "Ted's Pizza - 720895714702",
"templateid": "24663"
}]
tasks:
- name: DEBUG
debug:
msg: "{{ json | from_json | json_query(jq) }}"
It returns the following
ok: [localhost] => {
"msg": [
{
"name": "Ted's Pizza - 720895714702",
"templateid": "24663"
}
]
}
I need to take it a few steps further and when the value of name contains Pizza I need it to return just the 12 digit number on the end. So the return output would look like this
ok: [localhost] => {
"msg": "720895714702"
}
Thoughts?
you could try
- name: testplaybook jinja2
hosts: localhost
gather_facts: no
vars:
json: |
[{
"name": "Ted's Sub Shop - 720895714701",
"templateid": "24632"
},
{
"name": "Ted's Pizza - 720895714702",
"templateid": "24663"
}]
tasks:
- name: DEBUG
debug:
msg: "{{ json | from_json | selectattr('name', 'contains' , 'Pizza')
| map(attribute='name')
| map('regex_replace', '^.*?(\\d*)$', '\\1')}}"
result:
ok: [localhost] => {
"msg": [
"720895714702"
]
}
It might be more efficient to add an attribute. For example the declaration below
json_id: "{{ json|zip(id_hash)|map('combine')|list }}"
id_hash: "{{ id_list|
map('community.general.dict_kv', 'id')|list }}"
id_list: "{{ json|
map(attribute='name')|
map('split', '-')|
map('last')|
map('int')|list }}"
expands to
json_id:
- id: 720895714701
name: Ted's Sub Shop - 720895714701
templateid: '24632'
- id: 720895714702
name: Ted's Pizza - 720895714702
templateid: '24663'
Then, the usage is trivial. For example
- debug:
msg: "{{ json_id|
selectattr('name', 'contains' , 'Pizza')|
map(attribute='id')|list }}"
gives
msg:
- 720895714702
The same result gives also json_query below
- debug:
msg: "{{ json_id|
json_query('[?contains(name, `Pizza`)].id') }}"

Ansible parsing json

I am using an Ansible module (vmware_guest_info) with this code:
- name: Get status of VM post-change
vmware_guest_info:
datacenter: "xxxx"
hostname: "{{ result_item.vcenter }}"
username: "{{ xxxx }}"
password: "{{ ansible_password }}"
name: "{{ result_item.vmname }}"
schema: "vsphere"
validate_certs: no
properties: ["config.hardware.memoryMB", "config.hardware.numCPU", "config.hardware.numCoresPerSocket"]
register: postvmname_vminfo
loop: "{{ result.list }}"
loop_control:
loop_var: result_item
It produces the following JSON:
{
"results": [
{
"ansible_loop_var": "result_item",
"changed": false,
"failed": false,
"instance": {
"config": {
"hardware": {
"memoryMB": 4096,
"numCPU": 6,
"numCoresPerSocket": 1
}
}
},
The command that produces the json is looping though an excel file with these contents:
vmname vcenter
Server1 Vcenter1
Server2 Vcenter1
I need to create a dictionary between vmname and numCPU. How can I do this?
I have tried this:
- set_fact:
resultCPU: "{{ instance.config.hardware.numCPU }}"
with_items:
- "{{ postvmname_vminfo.results | json_query(query) }}"
vars:
query: "[?invocation.module_args.name=='{{ result_item.vmname }}']"
loop: "{{ result.list }}"
loop_control:
loop_var: result_item
I get item is undefined from the above. I believe the issue is with my query. I think what I really need to do is use zip to create a dictionary between the servers(vmname) in my Excel file and the value of numCPU.
Does anyone have any suggestions?

Access nested items on a dictionary ansible

I have a json file with server information and the applications related.
Something like this:
{
"Apps": [
{
"AppOwner": "Me",
"AppNm": "Vacations"
}
],
"Hostnm": "some_server",
"Environment": "Prod",
"OSnm": "Windows Server 2008",
"OSManu": "Microsoft",
"SerialNum": "VMware-42 14 e1 37 7a 63 9b 0e-43 07 15 46 64 9c 3c 12"
}
I want to create a list with all the applications and the servers related
For example
{
"Vacations": [server1, server2]
},
{
"other_app": [server2, server3]
},
For that, I have been trying the following.
- set_fact:
apps: "{{ apps }} + {{ item.Apps | map(attribute='AppNm') | list}}"
loop: "{{ server_changes }}"
- set_fact:
apps_revised: "{{apps | unique}}"
- set_fact:
apps_server: "{{ apps_revised | default([]) + [{item : []}] }}"
with_items: "{{ apps_revised }}"
The first part of the code, will obtain all the applications with the duplicates from the json.
The second part of the code, will clean the apps with unique.
The last part its going to create a list of application with a nested list (of servers).
The problem is that I don't understand how to navigate the nested server_changes application list and then add the server to my new list.
Probably there is an easier way to do it, so any help will be appreciated.
Thanks
Given a test data
server_changes:
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
"Hostnm": "server1"
- "Apps":
- {"AppOwner": "Me", "AppNm": "Vacations"}
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server2"
- "Apps":
- {"AppOwner": "Me", "AppNm": "other_app"}
"Hostnm": "server3"
Let's simplify the data. For example
- set_fact:
my_server_changes: "{{ my_server_changes|default([]) +
[{'hostname': item.Hostnm,
'applications': item.Apps|
json_query('[].AppNm')}] }}"
loop: "{{ server_changes }}"
- debug:
var: my_server_changes
gives
"my_server_changes": [
{
"applications": [
"Vacations"
],
"hostname": "server1"
},
{
"applications": [
"Vacations",
"other_app"
],
"hostname": "server2"
},
{
"applications": [
"other_app"
],
"hostname": "server3"
}
]
Create the list of the applications
- set_fact:
my_apps: "{{ server_changes|json_query('[].Apps[].AppNm')|unique }}"
- debug:
var: my_apps
gives
"my_apps": [
"Vacations",
"other_app"
]
Then loop the list of the applications, select records that contains the item and combine the dictionary
- set_fact:
apps_server: "{{ apps_server|default({})|
combine({item: my_server_changes|json_query(query)}) }}"
loop: "{{ my_apps }}"
vars:
query: "[?applications.contains(#, '{{ item }}')].hostname"
- debug:
var: apps_server
gives
"apps_server": {
"Vacations": [
"server1",
"server2"
],
"other_app": [
"server2",
"server3"
]
}

Variable in a variable Ansible

I'm trying to use a variable in a variable.
I have one JSON variable :
os: {
"centos_7_5": {
offer: "CentOS",
publisher: "OpenLogic",
sku: "7.5",
version: "latest"
},
"debian_9": {
offer: "Debian",
publisher: "credativ",
sku: "9",
version: "latest"
}
}
If I use
- debug:
msg: " {{ os.debian_9.offer }}"
The output is as desired :
"msg": " Debian"
Now, I'm trying to put the OS name in a variable (so that the variable can be in a config file) as follows :
desired_os: debian_9
I would like to do something like this :
- debug:
msg: " {{ os.desired_os.offer }}"
But I can't find a way to make it work.
I tried some concatenation in a set_fact using '{{ "os."~desired_os~".offer" }}' but the output is not as desired :
"msg": "stuff.os.debian_9.offer"
Thanks.
Hi try use this snippet:
json
{
"os": {
"centos_7_5": {
"offer": "CentOS",
"publisher": "OpenLogic",
"sku": "7.5",
"version": "latest"
},
"debian_9": {
"offer": "Debian",
"publisher": "credativ",
"sku": "9",
"version": "latest"
}
}
}
playbook:
---
- hosts: all
gather_facts: False
vars:
jsonVar: "{{ lookup('file', 'j.json') | from_json }}"
dist: "debian_9"
tasks:
- name: test loop
debug: msg="{{ jsonVar['os'][dist] }}"
You can use the varname[var] notation.
- hosts: localhost
gather_facts: no
vars:
os: {
"centos_7_5": {
offer: "CentOS",
publisher: "OpenLogic",
sku: "7.5",
version: "latest"
},
"debian_9": {
offer: "Debian",
publisher: "credativ",
sku: "9",
version: "latest"
}
}
desired_os: debian_9
tasks:
- debug:
msg: " {{ os['debian_9'].offer }}"
- debug:
msg: " {{ os[desired_os].offer }}"
Simply add the variable within double brackets.
- debug:
msg: " {{ os.{{ desired_os }}.offer }}"
Please try as below
debug: msg= "{{os.vars[desired_os].offer}}"