2

I have this JSON output from Juniper switch where I need to get the remote system name associated with lldp_local_parent_interface_name having the value ae0.
So, I only know the value ae0 and I need to get the remote system name A_B_C_D in order to start targeting this host to execute some other tasks.

The data, inside the file juniper-data.json looks like this:

{
    "output": {
        "lldp_neighbors_information": [{
            "lldp_neighbor_information": [{
                    "lldp_local_parent_interface_name": [{
                        "data": "ae0"
                    }],
                    "lldp_local_port_id": [{
                        "data": "xe_0/2/0"
                    }],
                    "lldp_remote_chassis_id": [{
                        "data": "00:00:00:00:00:00:00"
                    }],
                    "lldp_remote_chassis_id_subtype": [{
                        "data": "Mac address"
                    }],
                    "lldp_remote_port_description": [{
                        "data": "xe_1/0/1"
                    }],
                    "lldp_remote_system_name": [{
                        "data": "A_B_C_D"
                    }]
                },
                {
                    "lldp_local_parent_interface_name": [{
                        "data": "_"
                    }],
                    "lldp_local_port_id": [{
                        "data": "ge_0/0/23"
                    }],
                    "lldp_remote_chassis_id": [{
                        "data": "xx:xx:xx:xx:xx:xx"
                    }],
                    "lldp_remote_chassis_id_subtype": [{
                        "data": "Mac address"
                    }],
                    "lldp_remote_port_description": [{
                        "data": "bond0"
                    }]
                }
            ]
        }]
    }

}

Here are my tasks:

- name: load data to var
  set_fact: 
    remote_sys_name: "{{ lookup('file', 'juniper-data.json') | from_json }}" 
- name: view loaded data
  debug:
    var: item
  loop: "{{ remote_sys_name | community.general.json_query('output.lldp_neighbors_information[0].lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0'].lldp_remote_system_name[0].data') }}"

Expected results

"item": "A_B_C_D"

What I got is

fatal: [localhost]: FAILED! => {"msg": "template error while templating string: expected token ',', got 'ae0'. String: {{ remote_sys_name | community.general.json_query('output.lldp_neighbors_information[0].lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0'].lldp_remote_system_name[0].data') }}"}

β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83
DarkOne00X
  • 23
  • 3
  • About as ugly as the json data structure spitted out by the juniper device... => `{{ output.lldp_neighbors_information | map(attribute='lldp_neighbor_information') | flatten | selectattr('lldp_local_parent_interface_name', 'contains', {'data': 'ae0'}) | map(attribute='lldp_remote_system_name')| flatten | map(attribute='data') }}` <= this gives a list of stings of the found names. – Zeitounator Jun 28 '22 at 17:02
  • IMO this won't get really nicer with `json_query`. If you know a bit of python, [this answer about using a custom filter](https://stackoverflow.com/a/72752544/9401096) might give you some alternate ideas. – Zeitounator Jun 28 '22 at 17:07

1 Answers1

2

If you do not care about the exact hierarchy of nodes and their names beside lldp_neighbor_information, you can use a cohort of object projections and flatten projection to simplify it:

*.*[][].*[][?
  lldp_local_parent_interface_name[0].data == 'ae0'
][].lldp_remote_system_name[].data

As for your current attempt, the issue lies in the fact that your condition is wrongly located in this bit:

lldp_neighbor_information[*].[?lldp_local_parent_interface_name[0].data=='ae0']

The condition should actual replace the star:

lldp_neighbor_information[?lldp_local_parent_interface_name[0].data=='ae0']

Ending up with the query:

output
  .lldp_neighbors_information[0]
  .lldp_neighbor_information[?
    lldp_local_parent_interface_name[0].data=='ae0'
  ]
  .lldp_remote_system_name[0]
  .data

Given the task:

- debug:
    var: item
  loop: >-
      {{
        lookup('file', 'juniper-data.json')
        | from_json
        | json_query('
          output
            .lldp_neighbors_information[0]
            .lldp_neighbor_information[?
              lldp_local_parent_interface_name[0].data==`ae0`
            ]
            .lldp_remote_system_name[0]
            .data
        ')
      }}

This yields:

ok: [localhost] => (item=A_B_C_D) => 
  ansible_loop_var: item
  item: A_B_C_D

Bonus: If know you before hand that you will only have one element, you can also end your query with | [0] to stop the projection and only get the first element.
This way, you can get rid of the loop:

- debug:
    var: remote_sys_name
  vars:
    remote_sys_name: >-
      {{
        lookup('file', 'juniper-data.json')
        | from_json
        | json_query('
          output
            .lldp_neighbors_information[0]
            .lldp_neighbor_information[?
              lldp_local_parent_interface_name[0].data==`ae0`
            ]
            .lldp_remote_system_name[0]
            .data | [0]
        ')
      }}

Does yields the name right away:

ok: [localhost] => 
  remote_sys_name: A_B_C_D

Bonus bis: if your interface name is in a variable, here would be the task. Mind that it is in a variable local to the task here, but you might define it in any place your requirements call for.

- debug:
    var: remote_sys_name
  vars:
    _interface_name: ae0
    remote_sys_name: >-
      {{
        lookup('file', 'juniper-data.json')
        | from_json
        | json_query('
          output
            .lldp_neighbors_information[0]
            .lldp_neighbor_information[?
              lldp_local_parent_interface_name[0]
              .data==`' ~ _interface_name ~ '`
            ]
            .lldp_remote_system_name[0]
            .data | [0]
        ')
      }}
β.εηοιτ.βε
  • 33,893
  • 13
  • 69
  • 83