Search code examples
ansiblejinja2

Get only key from nested object with Jinja filter


I am using a Jinja filter in ansible to extract the value I need in the right format to process it.

This is the data in JSON format (I have shortened the output, usually there are much more variables per item and not all item have an IPv4 variable et all):

"interfaces": {
    "GigabitEthernet0": {
        "arp_timeout": "00:20:00",
        "arp_type": "arpa",
        "auto_negotiate": true,
        "bandwidth": 1000000
    },
    "GigabitEthernet0/0/0": {
        "arp_timeout": "00:20:00",
        "arp_type": "arpa",
        "auto_negotiate": true,
        "bandwidth": 10000
    },
    "GigabitEthernet0/0/0.3": {
        "arp_timeout": "04:00:00",
        "arp_type": "arpa",
        "bandwidth": 10000,
        "delay": 10,
        "description": "Private1 MPLS",
        "enabled": true,
        "encapsulations": {
            "encapsulation": "dot1q",
            "first_dot1q": "3"
        },
        "ipv4": {
            "10.10.84.2/30": {
                "ip": "10.10.84.2",
                "prefix_length": "30"
            }

That simple Jinja filer I use then to extract the information I need like the interface name and the IPv4:

[
{% for interface in interfaces if interfaces[interface]['ipv4'] is defined %}
{
"name": "{{ interface }}",
{% if interfaces[interface]['ipv4'] is defined %}
"prefix": "{{ interfaces[interface]['ipv4'] }}",
{% endif %}
"hostname": "{{ hostname }}"
}{{ ", " if not loop.last else "" }}
{% endfor %}
]

My problem is now that the parse data looks like this:

{
"name": "GigabitEthernet0/0/0.3",
"prefix": "{'10.10.84.2/30': {'ip': '10.10.84.2', 'prefix_length': '30'}}",
"hostname": "Horst1"
},

But I wanted to have only the key from the nested dict like this:

{
"name": "GigabitEthernet0/0/0.3",
"prefix": "10.10.84.2/30",
"hostname": "Horst1"
},

Isn't there a simple method in Jinja to get just the key from the nested object?


Solution

  • Here is for a possibly simpler template, using the for key, value in dict.items() construct:

    [
    {% for name, interface in interfaces.items() if interface.ipv4 is defined %}
      {
        "name": "{{ name }}",
        "prefix": "{{ (interface.ipv4.keys() | list).0 }}",
        "hostname": "{{ hostname }}"
      }{{ ", " if not loop.last }}
    {% endfor %}
    ]
    

    The keys() method is the one from Python, that return a view representing a list of the keys of that dictionary. Cast it back to a list and take the first element of it, an you should be good to go.

    Another option would be to use dict2items, once again, take the first element of the generated list and get its key:

        "prefix": "{{ (interface.ipv4 | dict2items).0.key }}",