Search code examples
ansiblejson-query

Ansible pull data from API response


I am trying to use the JSON response from an API to lookup the UUID of a VM I am trying to back up using the hostname. The API returns a list of hashes which include the hostnames and UUID of that hostname. I'd like to get Ansible to return the UUID of a machine when I feed it the hostname.

Here is an example of the response from the API, which I am storing in networker_vms

    {
        "networker_vms.json.vms": [
            {
                "hostname": ""
            },
            {
                "hostname": ""
            },
            {
                "hostname": "server1.company.com",
                "uuid": "1ef92350-a5e3-8df3-6106-208a88aad352"
            },
            {
                "hostname": "server2.company.com",
                "uuid": "5003006d-23f4-a515-3722-181238489896"
            },
            {
                "hostname": "server3.company.com",
                "uuid": "50640076-07ae-571d-5e69-2183db42d2ee"
            },
            {
                "hostname": "server4.company.com",
                "uuid": "500410b1-505a-7539-5efd-89131d671b0a"
            },
            {
                "hostname": "server5.company.com",
                "uuid": "5024014b-e788-12a9-bcf6-5475ae50bf7c"
            },
            {
                "hostname": "server6.company.com",
                "uuid": "51462200-1abf-1c09-5007-fa72f76854fc"
            }
      ],
}

I am able to get the UUID that coincides with a hostname with the following Ansible snippet, however I cannot get the code to work when switching the "mynames" variable to something like {{ ansible_hostname }} - I get the error "unexpected failure during module execution". I'm guessing it has to do with the stacking of the curly-braces, but am not sure....

- name: debug my_uuid
  debug:
    msg: "{{ mynames | map('extract', dict(networker_vms.json | json_query('vms[].[hostname,uuid]'))) }}"
  vars:
    mynames:
      - server2.company.com

What is the best way for me to search the results of the API call for a specific hostname and get Ansible to put the corresponding uuid into a fact or variable for me?

Thanks!


Solution

  • It sounds like you want to extract the UUID for the current host, as identified by the ansible_hostname variable. You could do that like this:

    - name: debug using ansible_hostname
      debug:
        msg: >-
          {{ [ansible_hostname] |
          map('extract', dict(networker_vms.json |
          json_query('vms[].[hostname,uuid]'))) | first }}
    

    This simply replaces mynames in your example, which is a list, with [ansible_hostname], which is also a list (consisting of a single value, the contents of ansible_hostname). This provides the list context necessary for the map filter.

    However, if you have a single value, you don't need to use the map filter; you can instead just do this:

    - name: debug using ansible_hostname
      debug:
        msg: >-
          {{ (networker_vms.json.vms |
          selectattr('hostname', 'eq', ansible_hostname)|first).uuid }}
    

    Update

    If you have a mix of qualified and unqualified hostnames, you could do something like this:

    - name: get my uuid
      set_fact:
        my_uuid: "{{ host_to_addr[item] }}"
      when: my_uuid is not defined and item in host_to_addr
      vars:
        host_to_addr: "{{ dict(networker_vms.json | json_query('vms[].[hostname,uuid]')) }}"
      loop:
        - "{{ ansible_hostname }}"
        - "{{ ansible_fqdn }}"
    
    - debug:
        var: my_uuid
    

    This will loop over a list of hostnames and will set the my_uuid fact to whichever one matches first.

    You could also write a slightly more complex expression for the json_query filter:

    - name: get my uuid
      debug:
        msg: >-
          {{ networker_vms.json.vms |
          json_query('[?hostname == `%s` || hostname == `%s`].[uuid]|[]' % (
          ansible_hostname, ansible_fqdn))|
          first}}