Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loop over json objects in Ansible

I have been stuck to get a particular json object if the value of a key matches a variable (string).

My json file looks like this:

    "totalRecordsWithoutPaging": 1234,
    "jobs": [
        {
            "jobSummary": {

                "totalNumOfFiles": 0,
                "jobId": 8035,
                "destClientName": "BOSDEKARLSSP010",
                "destinationClient": {
                    "clientId": 10,
                    "clientName": "BOSDEKARLSSP010"
                }
            }
        },
        {
            "jobSummary": {
                "totalNumOfFiles": 0,
                "jobId": 9629,
                "destClientName": "BOSDEKARLSSP006",
                "destinationClient": {
                    "clientId": 11,
                    "clientName": "BOSDEKARLSSP006"
                }
            }
        },
                .....
    ]
}

I read this json with result: "{{ lookup('file','CVExport-short.json') | from_json }}" and I can get only one value of destClientName key with the following code:

- name: Iterate JSON
  set_fact:
    app_item: "{{ item.jobSummary }}"
  with_items: "{{ result.jobs }}"
  register: app_result

- debug:
    var: app_result.results[0].ansible_facts.app_item.destClientName

My goal is to get the value of jobIdif the value of destClientName matches some other variable or string in any jobSummary. I don't still have much knowledge in Ansible. So, any help would be much appreciated.

Update

Ok, I have found one solution.

- name: get job ID
  set_fact: 
    job_id: "{{ item.jobSummary.jobId }}"
  with_items: "{{ result.jobs}}"
  when: item.jobSummary.destClientName == '{{ target_vm }}'

- debug: 
    msg: "{{job_id}}"

But I think there might be a better solution than this. Any idea how?

like image 256
sumion Avatar asked May 27 '19 09:05

sumion


1 Answers

Ansible's json_query filter let's you perform complex filtering of JSON documents by applying JMESPath expressions. Rather than looping over the jobs in the the result, you can get the information you want in a single step.

We want to query all jobs in which have a destClientName that matches the value in target_vm. Using literal values, the expression yielding that list of jobs would look like this:

jobs[?jobSummary.destClientName == `BOSDEKARLSSP006`]

The result of this, when applied to your sample data, would be:

[
  {
    "jobSummary": {
      "totalNumOfFiles": 0,
      "jobId": 9629,
      "destClientName": "BOSDEKARLSSP006",
      "destinationClient": {
        "clientId": 11,
        "clientName": "BOSDEKARLSSP006"
      }
    }
  }
]

From this result, you want to extract the jobId, so we rewrite the expression like this:

jobs[?jobSummary.destClientName == `BOSDEKARLSSP006`]|[0].jobSummary.jobId

Which gives us:

9629

To make this work in a playbook, you'll want to replace the literal hostname in this expression with the value of your target_vm variable. Here's a complete playbook that demonstrates the solution:

---
- hosts: localhost
  gather_facts: false

  # This is just the sample data from your question.
  vars:
    target_vm: BOSDEKARLSSP006
    results:
      totalRecordsWithoutPaging: 1234
      jobs:
      - jobSummary:
          totalNumOfFiles: 0
          jobId: 8035
          destClientName: BOSDEKARLSSP010
          destinationClient:
            clientId: 10
            clientName: BOSDEKARLSSP010
      - jobSummary:
          totalNumOfFiles: 0
          jobId: 9629
          destClientName: BOSDEKARLSSP006
          destinationClient:
            clientId: 11
            clientName: BOSDEKARLSSP006

  tasks:
    - name: get job ID
      set_fact:
        job_id: "{{ results|json_query('jobs[?jobSummary.destClientName == `{}`]|[0].jobSummary.jobId'.format(target_vm)) }}"

    - debug:
        var: job_id

Update re: your comment

The {} in the expression is a Python string formatting sequence that is filled in by the call to .format(target_vm). In Python, the expression:

'The quick brown {} jumped over the lazy {}.'.format('fox', 'dog')

Would evaluate to:

The quick brown fox jumped over the lazy dog.

And that's exactly what we're doing in that set_fact expression. I could instead have written:

job_id: "{{ results|json_query('jobs[?jobSummary.destClientName == `' ~ target_vm ~ '`]|[0].jobSummary.jobId') }}"

(Where ~ is the Jinja stringifying concatenation operator)

like image 154
larsks Avatar answered Oct 24 '22 20:10

larsks