I’ve been meaning to start a thread on this for a while… over the last year or so I have been getting deeper and deeper into generating and searching JSON using Ansible and I’d like to share some of the tools and methods I’ve been using and if anyone else using Ansible (@decentral1se @stephen @matt) have things to share that would be great…
JMESPath
The JMESPath query language for JSON is available as an Ansible filter, community.general.json_query
and can using be used on the command line via jp and tested interactively using jpterm, both jp
and jpterm
can be installed locally using this Ansible role.
This is really handy if you have a big block of JSON and just want to get one thing from it, for example findmnt has the -J
option for outputting JSON, findmnt -J
{
"filesystems": [
{"target":"/", "source":"/dev/xvda2", "fstype":"ext4", "options":"rw,relatime,errors=remount-ro,stripe=512",
"children": [
{"target":"/sys", "source":"sysfs", "fstype":"sysfs", "options":"rw,nosuid,nodev,noexec,relatime",
"children": [
{"target":"/sys/kernel/security", "source":"securityfs", "fstype":"securityfs", "options":"rw,nosuid,nodev,noexec,relatime"},
{"target":"/sys/fs/cgroup", "source":"cgroup2", "fstype":"cgroup2", "options":"rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot"},
{"target":"/sys/fs/pstore", "source":"pstore", "fstype":"pstore", "options":"rw,nosuid,nodev,noexec,relatime"},
{"target":"/sys/fs/bpf", "source":"none", "fstype":"bpf", "options":"rw,nosuid,nodev,noexec,relatime,mode=700"},
{"target":"/sys/kernel/debug", "source":"debugfs", "fstype":"debugfs", "options":"rw,nosuid,nodev,noexec,relatime"},
{"target":"/sys/kernel/tracing", "source":"tracefs", "fstype":"tracefs", "options":"rw,nosuid,nodev,noexec,relatime"},
{"target":"/sys/fs/fuse/connections", "source":"fusectl", "fstype":"fusectl", "options":"rw,nosuid,nodev,noexec,relatime"},
{"target":"/sys/kernel/config", "source":"configfs", "fstype":"configfs", "options":"rw,nosuid,nodev,noexec,relatime"}
]
},
{"target":"/proc", "source":"proc", "fstype":"proc", "options":"rw,relatime",
"children": [
{"target":"/proc/sys/fs/binfmt_misc", "source":"systemd-1", "fstype":"autofs", "options":"rw,relatime,fd=30,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=10400",
"children": [
{"target":"/proc/sys/fs/binfmt_misc", "source":"binfmt_misc", "fstype":"binfmt_misc", "options":"rw,nosuid,nodev,noexec,relatime"}
]
}
]
},
{"target":"/dev", "source":"udev", "fstype":"devtmpfs", "options":"rw,nosuid,relatime,size=187620k,nr_inodes=46905,mode=755",
"children": [
{"target":"/dev/pts", "source":"devpts", "fstype":"devpts", "options":"rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000"},
{"target":"/dev/shm", "source":"tmpfs", "fstype":"tmpfs", "options":"rw,nosuid,nodev"},
{"target":"/dev/mqueue", "source":"mqueue", "fstype":"mqueue", "options":"rw,nosuid,nodev,noexec,relatime"}
]
},
{"target":"/run", "source":"tmpfs", "fstype":"tmpfs", "options":"rw,nosuid,nodev,noexec,relatime,size=48276k,mode=755",
"children": [
{"target":"/run/lock", "source":"tmpfs", "fstype":"tmpfs", "options":"rw,nosuid,nodev,noexec,relatime,size=5120k"},
{"target":"/run/user/1001", "source":"tmpfs", "fstype":"tmpfs", "options":"rw,nosuid,nodev,relatime,size=48272k,nr_inodes=12068,mode=700,uid=1001,gid=1001"}
]
}
]
}
]
}
And if you want to get the physical device for the root filesystem you can use jp
/ community.general.json_query
to extract it like this:
findmnt -J | jp "filesystems[?target == '/'].source | [0]"
"/dev/xvda2"
Using Ansible:
- name: findmnt
ansible.builtin.command: findmnt -J
check_mode: false
changed_when: false
register: findmnt_j
- name: set fact
ansible.builtin.set_fact:
findmnt: "{{ findmnt_j.stdout | from_json }}"
- name: set fact
ansible.builtin.set_fact:
device: "{{ findmnt | community.general.json_query(findmnt_query) }}"
vars:
findmnt_query: "filesystems[?target == '/'].source | [0]"
JSON Convert
JSON Convert, or jc
is a CLI tool for converting file formats and the output of many commands into JSON and YAML, it is also available as an Ansible filter and it can be installed using this Ansible role.
JC has a lot of parsers and the JC developer is open to adding new ones, one I suggested was the update-alternatives --query
parser, for example this text output (which lists the versions of PHP installed and their priority):
update-alternatives --query php
Name: php
Link: /usr/bin/php
Slaves:
php.1.gz /usr/share/man/man1/php.1.gz
Status: manual
Best: /usr/bin/php8.1
Value: /usr/bin/php7.4
Alternative: /usr/bin/php7.4
Priority: 74
Slaves:
php.1.gz /usr/share/man/man1/php7.4.1.gz
Alternative: /usr/bin/php8.0
Priority: 80
Slaves:
php.1.gz /usr/share/man/man1/php8.0.1.gz
Alternative: /usr/bin/php8.1
Priority: 81
Slaves:
php.1.gz /usr/share/man/man1/php8.1.1.gz
Can be piped through jc
:
update-alternatives --query php | jc --update-alt-q -py
To generate YAML like this:
---
name: php
link: /usr/bin/php
slaves:
- name: php.1.gz
path: /usr/share/man/man1/php.1.gz
status: manual
best: /usr/bin/php8.1
value: /usr/bin/php7.4
alternatives:
- alternative: /usr/bin/php7.4
priority: 74
slaves:
- name: php.1.gz
path: /usr/share/man/man1/php7.4.1.gz
- alternative: /usr/bin/php8.0
priority: 80
slaves:
- name: php.1.gz
path: /usr/share/man/man1/php8.0.1.gz
- alternative: /usr/bin/php8.1
priority: 81
slaves:
- name: php.1.gz
path: /usr/share/man/man1/php8.1.1.gz
The jc --update-alt-q
parser has been incorporated into this alternatives role and this role also uses jo
to generate JSON, which is something I intend to post about below when I get around to it…
See the Parsing Command Output in Ansible with JC article for some more on using the community.general.jc
Ansible filter.
I’ve several other things to add to this thread but that will do for today…