Parsing JSON output from Juniper switch using Ansible - json

I have this JSON output from Juniper switch where I need to get the remote system name associated with lldp_local_parent_interface_name having the value ae0.
So, I only know the value ae0 and I need to get the remote system name A_B_C_D in order to start targeting this host to execute some other tasks.
The data, inside the file juniper-data.json looks like this:
{
"output": {
"lldp_neighbors_information": [{
"lldp_neighbor_information": [{
"lldp_local_parent_interface_name": [{
"data": "ae0"
}],
"lldp_local_port_id": [{
"data": "xe_0/2/0"
}],
"lldp_remote_chassis_id": [{
"data": "00:00:00:00:00:00:00"
}],
"lldp_remote_chassis_id_subtype": [{
"data": "Mac address"
}],
"lldp_remote_port_description": [{
"data": "xe_1/0/1"
}],
"lldp_remote_system_name": [{
"data": "A_B_C_D"
}]
},
{
"lldp_local_parent_interface_name": [{
"data": "_"
}],
"lldp_local_port_id": [{
"data": "ge_0/0/23"
}],
"lldp_remote_chassis_id": [{
"data": "xx:xx:xx:xx:xx:xx"
}],
"lldp_remote_chassis_id_subtype": [{
"data": "Mac address"
}],
"lldp_remote_port_description": [{
"data": "bond0"
}]
}
]
}]
}
}
Here are my tasks:
- name: load data to var
set_fact:
remote_sys_name: "{{ lookup('file', 'juniper-data.json') | from_json }}"
- name: view loaded data
debug:
var: item
loop: "{{ remote_sys_name | community.general.json_query('output.lldp_neighbors_information[0].lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0'].lldp_remote_system_name[0].data') }}"
Expected results
"item": "A_B_C_D"
What I got is
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: expected token ',', got 'ae0'. String: {{ remote_sys_name | community.general.json_query('output.lldp_neighbors_information[0].lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0'].lldp_remote_system_name[0].data') }}"}

If you do not care about the exact hierarchy of nodes and their names beside lldp_neighbor_information, you can use a cohort of object projections and flatten projection to simplify it:
*.*[][].*[][?
lldp_local_parent_interface_name[0].data == 'ae0'
][].lldp_remote_system_name[].data
As for your current attempt, the issue lies in the fact that your condition is wrongly located in this bit:
lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0']
The condition should actual replace the star:
lldp_neighbor_information[?lldp_local_parent_interface_name[0].data=='ae0']
Ending up with the query:
output
.lldp_neighbors_information[0]
.lldp_neighbor_information[?
lldp_local_parent_interface_name[0].data=='ae0'
]
.lldp_remote_system_name[0]
.data
Given the task:
- debug:
var: item
loop: >-
{{
lookup('file', 'juniper-data.json')
| from_json
| json_query('
output
.lldp_neighbors_information[0]
.lldp_neighbor_information[?
lldp_local_parent_interface_name[0].data==`ae0`
]
.lldp_remote_system_name[0]
.data
')
}}
This yields:
ok: [localhost] => (item=A_B_C_D) =>
ansible_loop_var: item
item: A_B_C_D
Bonus: If know you before hand that you will only have one element, you can also end your query with | [0] to stop the projection and only get the first element.
This way, you can get rid of the loop:
- debug:
var: remote_sys_name
vars:
remote_sys_name: >-
{{
lookup('file', 'juniper-data.json')
| from_json
| json_query('
output
.lldp_neighbors_information[0]
.lldp_neighbor_information[?
lldp_local_parent_interface_name[0].data==`ae0`
]
.lldp_remote_system_name[0]
.data | [0]
')
}}
Does yields the name right away:
ok: [localhost] =>
remote_sys_name: A_B_C_D
Bonus bis: if your interface name is in a variable, here would be the task. Mind that it is in a variable local to the task here, but you might define it in any place your requirements call for.
- debug:
var: remote_sys_name
vars:
_interface_name: ae0
remote_sys_name: >-
{{
lookup('file', 'juniper-data.json')
| from_json
| json_query('
output
.lldp_neighbors_information[0]
.lldp_neighbor_information[?
lldp_local_parent_interface_name[0]
.data==`' ~ _interface_name ~ '`
]
.lldp_remote_system_name[0]
.data | [0]
')
}}

Related

JSON query in Ansible Playbook failing to select desired data

I am new to writing Ansible playbooks and have hit a roadblock. I am trying to use the Site24x7 API to schedule maintenance and need to get a specific ID from a list of Monitor Groups. I created the following:
- name: Download Monitor Groups from Site24x7
uri:
url: https://www.site24x7.com/api/monitor_groups
method: GET
return_content: yes
headers:
Accept: "application/json; version=2.1"
Authorization: "Zoho-oauthtoken {{authtoken.json.access_token}}"
body:
return_content: yes
register: monitor_groups
- name: Get Monitor Group ID
set_fact:
monitorGroupID_query: "[?display_name=='{{hostname.stdout}}'].group_id"
- name: Assign Monitor Group ID
set_fact:
monitorGroupID: "{{ monitor_groups.json | json_query(monitor_group_id_query) }}"
- debug:
var: monitorGroupID
My API call returns data that looks like the following
"monitor_groups.json": {
"code": 0,
"data": [
{
"description": "System Generated",
"display_name": "server1",
"group_id": "319283000000505864",
"group_type": 1,
"health_threshold_count": 1,
"monitors": [
"319283000000483017"
]
},
{
"display_name": "server2",
"group_id": "319283000004701003",
"group_type": 3,
"health_threshold_count": 1,
"monitors": [
"319283000003989345",
"319283000004061005"
]
}
],
"message": "success"
}
My query constantly returns an empty string.
TASK [Assign Monitor Group ID] *****************************************************************************************
ok: [server1.fdu.edu] => {"ansible_facts": {"monitorGroupID": ""}, "changed": false}
TASK [debug] *****************************************************************************************
ok: [server1.fdu.edu] => {
"monitorGroupID": ""
}
Thank you in advance for your help
Single quotes don't work in JMESPath the way it does in almost any other "query language" -- you'll want ` characters wrapped around the JSON literal values. Also, the data:[] in your response is not implied, so if you meant to use it you'll need to either .json.data | json_query or alter your JMESPath to add the data[?display... part
- name: Assign Monitor Group ID
set_fact:
monitorGroupID: "{{ monitor_groups.json | json_query( monitorGroupID_query ) }}"
vars:
monitorGroupID_query: 'data[?display_name==`"{{hostname.stdout}}"`].group_id'
given hostname.stdout=server2 yields
ok: [localhost] => {"ansible_facts": {"monitorGroupID": ["319283000004701003"]}, "changed": false}
For example, given the data below
monitor_groups:
json:
code: 0
data:
- description: System Generated
display_name: server1
group_id: '319283000000505864'
group_type: 1
health_threshold_count: 1
monitors:
- '319283000000483017'
- display_name: server2
group_id: '319283000004701003'
group_type: 3
health_threshold_count: 1
monitors:
- '319283000003989345'
- '319283000004061005'
message: success
Create a dictionary of the hostnames and their group_id. Use it to evaluate the variable monitorGroupID
name_id: "{{ monitor_groups.json.data|
items2dict(key_name='display_name', value_name='group_id') }}"
monitorGroupID: "{{ name_id[hostname.stdout] }}"
gives
name_id:
server1: '319283000000505864'
server2: '319283000004701003'
Then, the task below
- debug:
var: monitorGroupID
gives
TASK [debug] **********************************************************
ok: [server1] =>
monitorGroupID: '319283000000505864'
ok: [server2] =>
monitorGroupID: '319283000004701003'

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 obtain values from changing key

I would like to get value from json, but one of the key can be differet.
here is example json
{
"json": {
"id": "9758b1e5-442e-4545-9364-45f28477edfb",
"results": [{
"code": 200,
"host": "localhost",
"message": "no change",
"runTime": 1233,
"tenant": "http-validate-2.usa-dc.com"
}],
"traces": {
"http-validate-2.usa-dc.comCurrent": {
"/Common/10.10.100.10": {
"command": "ltm node"
},
"http-validate-2.usa-dc.comDiff": [{
"command": "ltm virtual",
"kind": "D",
"lhs": {
"default": "yes"
},
"lhsCommand": "ltm virtual",
"path": [
"/http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80",
"properties",
"persist",
"/Common/cookie"
],
"rhsCommand": "ltm virtual",
"tags": [
"tmsh"
]
}]
}
}
}
}
my ansible playbook
tasks:
- name : deploy json file AS3 to F5
debug:
msg: "{{ lookup('file', 'parse.json') }}"
register: atc_AS3_status
- name: debug
debug:
msg: "{{ atc_AS3_status.msg.json['traces']['.*Diff']}}"
I would like to reach key "path" but the key above "http-validate-2.usa-dc.comDiff" can be different like "http-validate-3.can-dc.comDiff" but always finish with Diff
Use json_query, e.g.
- debug:
msg: "{{ json.traces|json_query('*.*[][].path') }}"
should give the list of the paths (there might be more of them)
msg:
- - /http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80
- properties
- persist
- /Common/cookie
Q: "I need path only in the key which ends Diff."
A: JMESPath is not able to search key wildcards, AFAIK. Instead, use select and create the list of the nested keys that match the regex, e.g.
- debug:
msg: "{{ json.traces|json_query('*.keys(#)')|flatten|
select('match', '^.*Diff$')|list }}"
gives
msg:
- http-validate-2.usa-dc.comDiff
Then iterate this list, select path and concatenate the list paths, e.g.
- set_fact:
paths: "{{ paths|d([]) + json.traces|json_query(query) }}"
loop: "{{ json.traces|json_query('*.keys(#)')|flatten|
select('match', '^.*Diff$')|list }}"
vars:
query: '*."{{ item }}"[].path'
gives the list of paths for the keys that match the regex
paths:
- - /http-validate-2.usa-dc.com/app/vs_http-validate-2.usa-dc.com_80
- properties
- persist
- /Common/cookie

Ansible extract var from JSON

I am using the URI module and I get a JSON response back. This is my playbook so far.
.. some playbook ..
register: output
- debug: msg="{{ output }}"
- name: get job id
set_fact:
job_id: "{{ output.json.results }}"
- debug: msg="{{ job_id }}"
Here’s the output of job_id from the debug above:
ok: [localhost] => {
"msg": [
{
"approval_state": "pending_approval",
"created_on": "2018-12-18T22:48:40Z",
"description": "Provision from [tpl] to [test]",
"href": "https://foo.ca/api/provision_requests/1000000000143",
"id": "1000000000143",
"message": "VM Provisioning - Request Created",
"options": {
"addr_mode": [
"dhcp",
"DHCP"
],
"auto_approve": false,
"cluster_filter": [
null,
null
],
"cores_per_socket": [
2,
"2"
],
I want to extract the “id” in the 5th line of the JSON output above. Any ideas?
I tried output.json.results.id but that errors out with no object id found
If you look at the JSON that has been produced, you actually have a list of dictionaries, albeit in this particular case, you have a list containing a single dictionary. So given the exact example:
- debug:
msg: "{{ job_id[0].id }}"
will extract the id field.
If you want to extract the id fields from every dictionary in the list (assuming multiple dictionaries are possible), you could instead do:
- debug:
msg: "{{ job_id | map(attribute='id') | list }}"
which will produce a simple list containing the contents of the id field in each dictionary.

ansible parse json with several keys with the same name to one list variable

I have an issue to parse a json using ansible
I have a task that connected to rancher and get a json file
task:
- uri:
url: http://rancher.local:8080/v1/hosts
method: GET
user: ##################
password: ################
body_format: json
register: hosts_json
- name: test
set_fact:
rancher_env_hosts: "{{ item.hostname }}"
#when: item.hostname == "*-i-*"
with_items: "{{hosts_json.json.data}}"
- name: output
debug:
msg: "hosts: {{rancher_env_hosts}}"
and I get the following json (after edit it to be more readable):
{
"json": {
"data": [
{
"hostname": "rancher-i-host-02",
"id": "adsfsa"
},
{
"hostname": "rancher-i-host-01",
"id": "gfdgfdg"
},
{
"hostname": "rancher-q-host-01",
"id": "dfgdg"
},
{
"hostname": "rancher-q-host-02",
"id": "dfgdg"
}
]
}
}
When I start the playbook I get only the last host name in the variable and not all the list of hostname. can I register all the list to the same variable?
In addition, I also added a line with the a comment "#" in order to get only the host names that match the string "-i-" bit it's not working. any idea?
This is what filters (and this) for:
- set_fact:
hosts_all: "{{ hosts_json.json.data | map(attribute='hostname') | list }}"
hosts_i: "{{ hosts_json.json.data | map(attribute='hostname') | map('regex_search','.*-i-.*') | select('string') | list }}"
host_all will contain all hostnames, host_i will contain only .*-i-.* matching hostnames.
Try this
- uri:
url: http://rancher.local:8080/v1/hosts
method: GET
user: ##################
password: ################
body_format: json
register: hosts_json
- name: init fact
set_fact:
rancher_env_hosts: "[]"
- name: test
set_fact:
rancher_env_hosts: "{{rancher_env_hosts}} + [ {{item.hostname}} ]"
when: item.hostname | search(".*-i-.*")
with_items: "{{hosts_json.json.data}}"
- name: output
debug:
msg: "hosts: {{rancher_env_hosts}}"
About search you can read here http://docs.ansible.com/ansible/playbooks_tests.html
UPD:
About adding values to array here: Is it possible to set a fact of an array in Ansible?