Parsing complex BIG-IP json structures made easy with Ansible filters like json_query
JMESPath and json_query
JMESPath (JSON Matching Expression paths) is a query language for searching JSON documents. It allows you to declaratively extract elements from a JSON document. Have a look at this tutorial to learn more.
The json_query filter lets you query a complex JSON structure and iterate over it using a loop structure.This filter is built upon jmespath, and you can use the same syntax as jmespath. Click here to learn more about the json_query filter and how it is used in Ansible.
In this article we are going to use the bigip_device_info module to get various facts from the BIG-IP and then use the json_query filter to parse the output to extract relevant information.
Ansible bigip_device_info module
Playbook to query the BIG-IP and gather system based information.
- name: "Get BIG-IP Facts" hosts: bigip gather_facts: false connection: local tasks: - name: Query BIG-IP facts bigip_device_info: provider: validate_certs: False server: "xxx.xxx.xxx.xxx" user: "*****" password: "*****" gather_subset: - system-info register: bigip_facts - set_fact: facts: '{{bigip_facts.system_info}}' - name: debug debug: msg="{{facts}}"
To view the output on a different subset, below are a few examples to change the gather_subset and set_fact values in the above playbook from gather_subset: system-info, facts: bigip_facts.system_info to any of the below:
- gather_subset: vlans , facts: bigip_facts.vlans
- gather_subset: self-ips, facts: bigip_facts.self_ips
- gather_subset: nodes. facts: bigip_facts.nodes
- gather_subset: software-volumes, facts: bigip_facts.software_volumes
- gather_subset: virtual-servers, facts: bigip_facts.virtual_servers
- gather_subset: system-info, facts: bigip_facts.system_info
- gather_subset: ltm-pools, facts: bigip_facts.ltm_pools
Click here to view all the information that can be obtained from the BIG-IP using this module.
Parse the JSON output
Once we have the output lets take a look at how to parse the output. As mentioned above the jmespath syntax can be used by the json_query filter.
Step 1: We will get the jmespath syntax for the information we want to extract
Step 2: We will see how the jmespath syntax and then be used with json_query in an Ansible playbook
The website used in this article to try out the below syntax: https://jmespath.org/
Some BIG-IP sample outputs are attached to this article as well (Check the attachments section after the References). The attachment file is a combined output of a few configuration subsets.Copy paste the relevant information from the attachment to test the below examples if you do not have a BIG-IP.
System information
The output for this section is obtained with above playbook using parameters: gather_subset: system-info, facts: bigip_facts.system_info
Once the above playbook is run against your BIG-IP or if you are using the sample configuration attached, copy the output and paste it in the relevant text box. Try different queries by placing them in the text box next to the magnifying glass as shown in image below
# Get MAC address, serial number, version information msg.[base_mac_address,chassis_serial,platform,product_version] # Get MAC address, serial number, version information and hardware information msg.[base_mac_address,chassis_serial,platform,product_version,hardware_information[*].[name,type]]
Software volumes
The output for this section is obtained with above playbook using parameters:
- gather_subset: software-volumes
- facts: bigip_facts.software_volumes
# Get the name and version of the software volumes installed and its status msg[*].[name,active,version] # Get the name and version only for the software volume that is active msg[?active=='yes'].[name,version]
VLANs and Self-Ips
The output for this section is obtained with above playbook using parameters
- gather_subset: vlans and self-ips
- facts: bigip_facts
Look at the following example to define more than one subset in the playbook
# Get all the self-ips addresses and vlans assigned to the self-ip # Also get all the vlans and the interfaces assigned to the vlan [msg.self_ips[*].[address,vlan], msg.vlans[*].[full_path,interfaces[*]]]
Nodes
The output for this section is obtained with above playbook using parameters
- gather_subset: nodes,
- facts: bigip_facts.nodes
# Get the address and availability status of all the nodes msg[*].[address,availability_status] # Get availability status and reason for a particular node msg[?address=='192.0.1.101'].[full_path,availability_status,status_reason]
Pools
The output for this section is obtained with above playbook using parameters
- gather_subset: ltm-pools
- facts: bigip_facts.ltm_pools
# Get the name of all pools msg[*].name # Get the name of all pools and their associated members msg[*].[name,members[*]] # Get the name of all pools and only address of their associated members msg[*].[name,members[*].address] # Get the name of all pools along with address and status of their associated members msg[*].[name,members[*].address,availability_status] # Get status of pool members of a particular pool msg[?name=='/Common/pool'].[members[*].address,availability_status] # Get status of pool # Get address, partition, state of pool members msg[*].[name,members[*][address,partition,state],availability_status] # Get status of a particular pool and particular member (multiple entries on a member) msg[?full_name=='/Common/pool'].[members[?address=='192.0.1.101'].[address,partition],availability_status]
Virtual Servers
The output for this section is obtained with above playbook using parameters
- gather_subset: virtual-servers
- facts: bigip_facts.virtual_servers
# Get destination IP address of all virtual servers msg[*].destination # Get destination IP and default pool of all virtual servers msg[*].[destination,default_pool] # Get me all destination IP of all virtual servers that a particular pool as their default pool msg[?default_pool=='/Common/pool'].destination # Get me all profiles assigned to all virtual servers msg[*].[destination,profiles[*].name]
Loop and display using Ansible
We have seen how to use the jmespath syntax and extract information, now lets see how to use it within an Ansible playbook
- name: Parse the output hosts: localhost connection: local gather_facts: false tasks: - name: Setup provider set_fact: provider: server: "xxx.xxx.xxx.xxx" user: "*****" password: "*****" server_port: "443" validate_certs: "no" - name: Query BIG-IP facts bigip_device_info: provider: "{{provider}}" gather_subset: - system_info register: bigip_facts - debug: msg="{{bigip_facts.system_info}}" # Use json query filter. The query_string will be the jmespath syntax # From the jmespath query remove the 'msg' expression and use it as it is - name: "Show relevant information" set_fact: result: "{{bigip_facts.system_info | json_query(query_string)}}" vars: query_string: "[base_mac_address,chassis_serial,platform,product_version,hardware_information[*].[name,type]]" - debug: "msg={{result}}"
Another example of what would change if you use a different query (only highlighting the changes that need to made below from the entire playbook)
- name: Query BIG-IP facts bigip_device_info: provider: "{{provider}}" gather_subset: - ltm-pools register: bigip_facts - debug: msg="{{bigip_facts.ltm_pools}}" - name: "Show relevant information" set_fact: result: "{{bigip_facts.ltm_pools | json_query(query_string)}}" vars: query_string: "[*].[name,members[*][address,partition,state],availability_status]"
The key is to get the jmespath syntax for the information you are looking for and then its a simple step to incorporate it within your Ansible playbook
References
- Try the queries - https://jmespath.org/
- Learn more jmespath syntax and example - https://jmespath.org/tutorial.html
- Ansible lab that can be used as a sandbox - https://clouddocs.f5.com/training/automation-sandbox/
- MalliKosuriEmployee
Thanks I haven't heard about jmespath. I used jq for my json parsing, it is another tool will parse long JSON documents, More info https://stedolan.github.io/jq/
- Payal_SRet. Employee
Yup jq is great as well. json_query is a filter supported by Ansilbe. Not sure if how jq would be used within ansible.