I have the below json data file:
[
{
"?xml": {
"attributes": {
"encoding": "UTF-8",
"version": "1.0"
}
}
},
{
"domain": [
{
"name": "mydom"
},
{
"domain-version": "12.2.1.3.0"
},
{
"server": [
{
"name": "AdminServer"
},
{
"ssl": {
"name": "AdminServer"
}
},
{
"listen-port": "12400"
},
{
"listen-address": "mydom.myserver1.mybank.com"
}
]
},
{
"server": [
{
"name": "SERV01"
},
{
"log": [
{
"name": "SERV01"
},
{
"file-name": "/web/bea_logs/domains/mydom/SERV01/SERV01.log"
}
]
},
{
"listen-port": "12401"
},
{
"listen-address": "mydom.myserver1.mybank.com"
},
{
"server-start": [
{
"java-vendor": "Sun"
},
{
"java-home": "/web/bea/platform1221/jdk"
}
]
}
]
},
{
"server": [
{
"name": "SERV02"
},
{
"log": [
{
"name": "SERV02"
},
{
"file-name": "/web/bea_logs/domains/mydom/SERV02/SERV02.log"
}
]
},
{
"listen-port": "12401"
},
{
"listen-address": "mydom.myhost2.mybank.com"
},
{
"server-start": [
{
"java-home": "/web/bea/platform1221/jdk"
} ]
}
]
}
]
}
]
I wish to display all the server names and their respective port numbers.
Below is my failed attempt to display all the server names viz
AdminServer
SERV01
SERV02
My playbook:
tasks:
- name: Read the JSON file content in a variable
shell: "cat {{ playbook_dir }}/tmpfiles/{{ Latest_Build_Number }}/testme.json"
register: result
- name: Server Names
set_fact:
servernames: "{{ jsondata | json_query(jmesquery) }}"
vars:
jmesquery: '*.domain.server[*].name'
- name: Server Names and Ports
set_fact:
serverinfo: "{{ jsondata | json_query(jmesquery) }}"
vars:
jmesquery: '*.server[*].[name, port]'
- name: Print all server names
debug:
msg: "{{ item}}"
with_items:
- "{{ servernames }}"
I also tried the below:
jmesquery: 'domain.server[*].name'
There is no error but no data in the output as well. Output below:
TASK [Print all server names] *********************************************************************************
Monday 21 February 2022 03:07:47 -0600 (0:00:00.129) 0:00:03.590 *******
ok: [localhost] => (item=) => {
"msg": ""
}
Can you please suggest how can I get the desired data?
CodePudding user response:
lot of solutions, one with jmespath, you could try this:
tasks:
- name: Read the JSON file content in a variable
shell: "cat testme.json"
register: result
- name: jsondata
set_fact:
jsondata: "{{ result.stdout | from_json }}"
- name: Server Names
set_fact:
servernames: "{{ servernames | default([]) [dict(name=item[0], port=item[1])] }}"
loop: "{{ jsondata | json_query(jmesquery0) | zip(jsondata | json_query(jmesquery1)) | list }}"
vars:
jmesquery0: '[].domain[].server[].name'
jmesquery1: '[].domain[].server[]."listen-port"'
- name: debug result
debug:
msg: "{{ servernames }}"
result:
ok: [localhost] => {
"msg": [
{
"name": "AdminServer",
"port": "12400"
},
{
"name": "SERV01",
"port": "12401"
},
{
"name": "SERV02",
"port": "12401"
}
]
}
CodePudding user response:
Due to the nature of your data being in lists, you'll have to resort to conditionals in order to get rid of the empty objects and single item lists that would otherwise pollute your data:
[].domain[?server].server
, to get the objects having a propertyserver
[?name].name | [0]
to get the name[?"listen-port"]."listen-port" | [0]
to get the port
So, a valid JMESPath query on your data would be
[].domain[?server]
.server[]
.{
name: [?name].name | [0],
port: [?"listen-port"]."listen-port" | [0]
}
And in Ansible, with that single JMESPath query, given that the file is on the controller:
- debug:
var: >-
lookup(
'file',
playbook_dir ~ '/tmpfiles/' ~ Latest_Build_Number ~ '/testme.json'
)
| from_json
| json_query('
[].domain[?server]
.server[]
.{
name: [?name].name | [0],
port: [?"listen-port"]."listen-port" | [0]
}
')
vars:
Latest_Build_Number: 1
This would yield
TASK [debug] *************************************************************************
ok: [localhost] =>
? |-
lookup(
'file',
playbook_dir ~ '/tmpfiles/' ~ Latest_Build_Number ~ '/testme.json'
) | from_json | json_query('
[].domain[?server]
.server[]
.{
name: [?name].name | [0],
port: [?"listen-port"]."listen-port" | [0]
}
')
: - name: AdminServer
port: '12400'
- name: SERV01
port: '12401'
- name: SERV02
port: '12401'
If the file is on the nodes and not on the controller, then, you can either slurp
the files first or resort to cat
, as you did before applying the same JMESPath query.