ansible

Ansible URI Response JSON Data Parsing

While most of the vendors and their platforms (including DellEMC) are having Ansible modules published, but there are instances where functionality you’re trying to use isn’t covered in the available module or simply modules aren’t available at all.

In such situations you’ve no choice than using Ansible URI module. It allows you to interact with HTTP and HTTPS web services, in this particular example REST API endpoints to be precise. There are several benefits of using URI module with REST API, including but not limited to

  • Perform automation if there’s no Ansible module available
  • Use functionality that hasn’t been implemented in Ansible modules
  • Easier to redeploy your workflow to another automation tool

While I was using the URI module I came across issue of data parsing for REST API response payload. In case of Dell EMC platforms response payload is in JSON and based on the API endpoint response can be 1000s of lines. It’s very difficult to make sense of this data and also process the same in ansible to extract required information. In this blog post I am trying to list down the process I’ve followed to parse the JSON data and extract the required information.

Below is the sample playbook having uri module. In this example we’re talking about GET call for sample URI endpoint – which is getting device details of the existing server (BMaaS use case).

  tasks:
  - name: listdevices
    uri:
      url: https://api.sample.com/base/version/endpoint
      method: GET
      validate_certs: no
      headers:
        X-Auth-Token: "{{ api_key }}"
      status_code: 200
    register: output
    
  - name: printoutput
    ansible.builtin.debug: 
      var: output

In this playbook there are multiple parameters used under uri task. More on using uri module with REST API coming in another blog post.

In above example you can see that we’ve registering the output and then printing the same. Problem with this is there could be 100s of lines which probably doesn’t make sense. So for parsing this data we will need

Below is the sample of the GET call JSON response.

[
    {
        "id": "2b12858454174f03aece5a71bb382318",
        "name": "Drive_0_0_11",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_Level_1",
            "size": 3840755982336
        }
    },
    {
        "id": "2dd2c43f2582415585a9ce0bbb961a4f",
        "name": "Drive_0_0_0",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_Level_1",
            "size": 3840755982336
        }
    },
    {
        "id": "325e5abc34ed4be398715285cf2e2826",
        "name": "Drive_0_0_8",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "3c9449f55ade4c04a801e804f3872eff",
        "name": "Drive_0_0_5",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "426d706ef7d443a887a450e6ac4abed5",
        "name": "Drive_0_0_23",
        "extra_details": {
            "firmware_version": "3.0.45.6",
            "drive_type": "NVMe_NVRAM",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 8484552704
        }
    },
    {
        "id": "42f2145cc5ef4a5f86ec1dfe8bdc5ca3",
        "name": "Drive_0_0_24",
        "extra_details": {
            "firmware_version": "3.0.45.6",
            "drive_type": "NVMe_NVRAM",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 8484552704
        }
    },
    {
        "id": "4ed0b8b74eff4113ad85ded6f342ab34",
        "name": "Drive_0_0_6",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "5bca4db416c342ebab6d3b795c604963",
        "name": "Drive_0_0_2",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "6eb66e909f4b414193922e4de63cfa9f",
        "name": "Drive_0_0_1",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "b413b5e98e6245b58134c0d5a8d001e1",
        "name": "Drive_0_0_7",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "bae7ed28120e43a8b322b8d8b2fedc0d",
        "name": "Drive_0_0_10",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "bea9d084e49248359e3191d8f648a37c",
        "name": "Drive_0_0_3",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "f3af5ea2e84d4181a9068c6e85c4b750",
        "name": "Drive_0_0_4",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    },
    {
        "id": "fca0678677cf48f5a51c28ea1e47b514",
        "name": "Drive_0_0_9",
        "extra_details": {
            "firmware_version": "GPJ99E5Q",
            "drive_type": "NVMe_SSD",
            "encryption_status": "Supported_Locked_Cluster_PIN",
            "fips_status": "FIPS_Compliance_None",
            "size": 3840755982336
        }
    }
]

In above JSON example if I have to extract only name and id from the JSON, then we will need to parse the data in playbook as a separate task. Sample task is as mentioned below.

  - name: devicenames
    set_fact: 
      devicenames: "{{ devices | json_query(jmesquery) }}"
    vars:
      jmesquery: '*.devices[*].[name, id]'

So, overall the playbook will look like below

---
- hosts: localhost
  gather_facts: no

  tasks:
  - name: getdevices
    uri:
      url: https://<target_api_server>/v1/definition/endpoint
      method: GET
      #body_format: json
      validate_certs: no
      headers:
        X-Auth-Token: "{{ api_key }}"
      #body:
      #- [ name, your_username ]
      #- [ password, your_password ]
      #- [ enter, Sign in ]
      status_code: 200
    register: devices
    
  #- name: save the json as variable
  #  set_fact: 
  #    jsondata: "{{ devices.stdout | from_json }}"
  
  - name: devicenames
    set_fact: 
      devicenames: "{{ devices | json_query(jmesquery) }}"
    vars:
      jmesquery: '*.devices[*].[hostname, id]'
      
  - name: print devicenames
    debug:
      msg: "{{ item }}"
    with_items:
    - "{{ devicenames }}"

In this example I’ve printed the captured device value. But in real life scenario it can be captured as a variable and use as input for subsequent tasks.

Categories: ansible, REST API

Tagged as: , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s