2

I am trying to build an array to use for with_nested, but I cannot get past why json_query isn't outputting the actual hosts assigned to each cluster:

Play:

- name: Index clusters.json
  shell: cat {{ tower_var_path }}/clusters.json
  register: result_clusters

- name: Save the Json data to a Variable as a Fact
  set_fact: 
    clusters_jsondata: "{{ result_clusters.stdout | from_json }}"

- debug:
    msg: " {{ clusters_jsondata }}"

- name: Set cluster facts
  set_fact:
    cluster: "{{ cluster|default([]) + [ {
      'name': item,
      'hosts': item | json_query('*.hosts[*].name')
       } ] }}"  
  with_items: "{{ clusters_jsondata.clusters }}"      

- debug: 
    msg: "{{ cluster }}"

clusters.json data:

    {
    "clusters": {
        "Cluster_1": {
            "hosts": [
                {
                    "folder": "/path",
                    "name": "host1.domain.com"
                },
                {
                    "folder": "/path",
                    "name": "host2.domain.com"
                }
            ],

        },
        "Cluster_2": {
            "hosts": [
                {
                    "folder": "/path",
                    "name": "host3.domain.com"
                },
                {
                    "folder": "/path",
                    "name": "host4.domain.com"
                }
            ],

        },
    }

Expected results:

TASK [debug] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "hosts": "host1.domain.com, host2.domain.com",
            "name": "Cluster_1"
        },
        {
            "hosts": "host3.domain.com, host4.domain.com",
            "name": "Cluster_2"
        }
    ]
}

Actual results:

    TASK [debug] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "hosts": null,
            "name": "Cluster_1"
        },
        {
            "hosts": null,
            "name": "Cluster_2"
        }
    ]
}
Paul
  • 3,037
  • 6
  • 27
  • 40
BrillCom
  • 139
  • 2
  • 9

1 Answers1

2

In a nutshell, you can get your result with a single expression without any loops.

The following playbook:

---
- hosts: localhost
  gather_facts: false

  vars:
    # Your original data on a single line for legibility
    result_clusters:
      stdout: >-
        {"clusters":{"Cluster_1":{"hosts":[{"folder":"/path","name":"host1.domain.com"},{"folder":"/path","name":"host2.domain.com"}]},"Cluster_2":{"hosts":[{"folder":"/path","name":"host3.domain.com"},{"folder":"/path","name":"host4.domain.com"}]}}}

  tasks:
    - name: get my expected output from json data in a single task
      vars:
        query: >-
          [].{name: key, hosts: join(', ', value.hosts[].name)}
        clusters: "{{ (result_clusters.stdout | from_json).clusters }}"
      debug:
        msg: "{{ clusters | dict2items | json_query(query) }}"

Gives:

PLAY [localhost] ******************************************************

TASK [get my expected output from json data in a single task] *********
ok: [localhost] => {
    "msg": [
        {
            "hosts": "host1.domain.com, host2.domain.com",
            "name": "Cluster_1"
        },
        {
            "hosts": "host3.domain.com, host4.domain.com",
            "name": "Cluster_2"
        }
    ]
}

PLAY RECAP ************************************************************
localhost: ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Note: although the above gives very precisely the output you asked for, I strongly suspect you are looking for a result slightly different. Replacing the above query with:

        query: >-
          [].{name: key, hosts: value.hosts[].name}

will give this instead:

TASK [get my expected output from json data in a single task] **************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "hosts": [
                "host1.domain.com",
                "host2.domain.com"
            ],
            "name": "Cluster_1"
        },
        {
            "hosts": [
                "host3.domain.com",
                "host4.domain.com"
            ],
            "name": "Cluster_2"
        }
    ]
}

Zeitounator
  • 1,199
  • 5
  • 12
  • Perfect! Thank you. Much different that what I was thinking, so would you be so kind to provide a link to a resource that might explain how you came to this approach? – BrillCom Jun 03 '22 at 11:24
  • I can hardly find a link that points to a summary of my experience dealing with such issues over the past 8+ years ;) Meanwhile, three good starting points related to the subject: [jinja2 builtin filters](https://jinja.palletsprojects.com/en/3.0.x/templates/#list-of-builtin-filters), [ansilble filters](https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html) and [jmespath tutorial](https://jmespath.org/tutorial.html) – Zeitounator Jun 04 '22 at 07:07