Search code examples
jsondictionaryansibleconditional-statementsjinja2

How to search for a specific value in a multiple dictionary list, then extract another value from the same list?


After debugging results of a task, I want to be able to get the corresponding uuid just by entering a specific snapshot_name.

This is the output of the "result" variable:

ok: [localhost] => {
    "msg": {
        "date": "Mon, 22 May 2023 03:39:34 GMT",
        "elapsed": 0,
        "expires": "0",
        "failed": false,
        "json": {
            "entities": [
                {
                    "created_time": 1684493611741165,
                    "deleted": false,
                    "logical_timestamp": 1,
                    "snapshot_name": "Snapshot 1",
                    "uuid": "ffa972f7-25b7-4d0c-8e92-2e7afef863c4"
                },
                {
                    "created_time": 1684502215147173,
                    "deleted": false,
                    "logical_timestamp": 1,
                    "snapshot_name": "vm snapshot",
                    "uuid": "6f1kc72a-8916-4edd-9639-c2823b57a1cb"
                },
                {
                    "created_time": 1684725225721634,
                    "deleted": false,
                    "logical_timestamp": 1,
                    "snapshot_name": "new",
                    "uuid": "ad872bde-3a94-411f-8f89-5f5ad55e4ffa"
                }
            ],
            "metadata": {
                "grand_total_entities": 3,
                "total_entities": 3
            }
        },
        "msg": "OK (unknown bytes)",
        "pragma": "no-cache",
        "redirected": false,
        "server": "envoy"
    }
}

This is my playbook:

- name: debug
  debug:
    msg: "{{ result }}"

- name: Snapshot name to look for
  set_fact:
    snapname: "{{ snapname }}"

- name: Getting the uuid of the above snapname
  debug:
    msg: "{{ result.json.entities[*].uuid }}"
  when: result.json.entities[*].snapshot_name == snapname 

Snapname is a variable as I want to be able to be able to enter different snapname values in ansible controller itself, so it is meant to be a changeable variable. Then let ansible search for the snapname value and get its corresponding uuid.

I got this error for the last task in my playbook:

fatal: [localhost]: FAILED! => {
    "msg": "The conditional check 'result.json.entities[*].snapshot_name == snapname' failed. The error was: template error while templating string: unexpected '*'. String: {% if result.json.entities[*].snapshot_name == snapname %} True {% else %} False {% endif %}\n\nThe error appears to be in '/var/lib/awx/projects/Nutanix/roles/vm_restore/tasks/vm_restore.yml': line 46, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: debug\n  ^ here\n"
}

How do I write my playbook in a way that when the value of snapname == "vm snapshot", it will search for "vm snapshot" in "snapshot_name" in all the lists, then get the corresponding uuid?

For example, search which list has "vm_snapshot", then output the uuid, "6f1kc72a-8916-4edd-9639-c2823b57a1cb".


Solution

  • You can use either json_query

         - debug:
             msg: "{{ result.json.entities|
                      json_query('[?snapshot_name == `vm snapshot`].uuid') }}"
    

    , or selectattr and map

         - debug:
             msg: "{{ result.json.entities|
                      selectattr('snapshot_name', '==', 'vm snapshot')|
                      map(attribute='uuid') }}"
    

    Both tasks give the same result

      msg:
      - 6f1kc72a-8916-4edd-9639-c2823b57a1cb
    

    The code below shows how to substitute a variable

         - debug:
             msg: "{{ result.json.entities|json_query(_query) }}"
           vars:
             snapname: vm snapshot
             _query: '[?snapshot_name == `{{ snapname }}`].uuid'
    
         - debug:
             msg: "{{ result.json.entities|
                      selectattr('snapshot_name', '==', snapname)|
                      map(attribute='uuid') }}"
           vars:
             snapname: vm snapshot
    

    But, if the values of snapshot_name are unique, you can create a dictionary first

      snapshot_uuid: "{{ result.json.entities|
                         items2dict(key_name='snapshot_name', value_name='uuid') }}"
    

    gives

      snapshot_uuid:
        Snapshot 1: ffa972f7-25b7-4d0c-8e92-2e7afef863c4
        new: ad872bde-3a94-411f-8f89-5f5ad55e4ffa
        vm snapshot: 6f1kc72a-8916-4edd-9639-c2823b57a1cb
    

    The search is trivial now.


    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        result:
          date: Mon, 22 May 2023 03:39:34 GMT
          elapsed: 0
          expires: '0'
          failed: false
          json:
            entities:
            - created_time: 1684493611741165
              deleted: false
              logical_timestamp: 1
              snapshot_name: Snapshot 1
              uuid: ffa972f7-25b7-4d0c-8e92-2e7afef863c4
            - created_time: 1684502215147173
              deleted: false
              logical_timestamp: 1
              snapshot_name: vm snapshot
              uuid: 6f1kc72a-8916-4edd-9639-c2823b57a1cb
            - created_time: 1684725225721634
              deleted: false
              logical_timestamp: 1
              snapshot_name: new
              uuid: ad872bde-3a94-411f-8f89-5f5ad55e4ffa
            metadata:
              grand_total_entities: 3
              total_entities: 3
          msg: OK (unknown bytes)
          pragma: no-cache
          redirected: false
          server: envoy
    
        snapshot_uuid: "{{ result.json.entities|
                           items2dict(key_name='snapshot_name', value_name='uuid') }}"
          
      tasks:
    
         - debug:
             var: snapshot_uuid
    
         - debug:
             msg: "{{ result.json.entities|
                      selectattr('snapshot_name', '==', 'vm snapshot')|
                      map(attribute='uuid') }}"
    
         - debug:
             msg: "{{ result.json.entities|
                      json_query('[?snapshot_name == `vm snapshot`].uuid') }}"