Search code examples
jsonansibleansible-filter

How to add a key value as a main data object to a existing json query in ansible?


I query some information from cisco apic controller and I cutout the necessary information using json_query filter as follows:

set_fact: 
  my_nodes: "{{ my_nodes| json_query('current[*].fabricNodeIdentP.attributes.{node_name: name, node_id: nodeId, pod_id:podId}') }}"

Result is the following JSON formatted data:

"my_nodes": [
            {
                "node_id": "1", 
                "node_name": "node01", 
                "pod_id": "1"
            }, 
            {
                "node_id": "2", 
                "node_name": "node02", 
                "pod_id": "1"
            }, 
            {
                "node_id": "3", 
                "node_name": "node03", 
                "pod_id": "1"
            }
        ]

What I want to have/achieve is, to have node_name as the main data object which should look like this:

{
    "node01":
    {
        
           "node_id": "1", 
           "pod_id": "1"
        
    },
    "node02":
    { 
        
           "node_id": "2", 
           "pod_id": "1"
    },    
    "node03":
    {
        
           "node_id": "3", 
           "pod_id": "1"
        
    }
}            

I've tried to use the combine filter but I could only add a new key:value pair with it. Is there any way to achieve thin in ansible?


Solution

  • Put the declarations as appropriate

    my_nodes_keys: "{{ my_nodes|map(attribute='node_name')|list }}"
    my_nodes_dict: "{{ dict(my_nodes_keys|zip(my_nodes)) }}"
    

    gives

      my_nodes_dict:
        node01:
          node_id: '1'
          node_name: node01
          pod_id: '1'
        node02:
          node_id: '2'
          node_name: node02
          pod_id: '1'
        node03:
          node_id: '3'
          node_name: node03
          pod_id: '1'
    

    Q: "If I put them under the same set_fact ansible gives an error that my_nodes_keys is not defined."

    A: Unfortunately, it's not possible to 're-use' variables in set_fact. For example,

        - set_fact:
            var1: a
            var2: "{{ var1 }}"
    

    fails with the error:

    The error was: 'var1' is undefined.

    This means var2 is not able to use var1 declared in set_fact. There is an easy solution if you need var1 to declare var2 only. Put the declaration of var1 into task's vars. The task below works as expected

        - set_fact:
            var2: "{{ var1 }}"
          vars:
            var1: a
    

    In your case, if you don't need the keys later in the playbook, put the declaration into task's vars

        - set_fact:
            my_nodes_dict: "{{ dict(my_nodes_keys|zip(my_nodes)) }}"
          vars:
            my_nodes_keys: "{{ my_nodes|map(attribute='node_name')|list }}"