Score:1

Best way to reformat a JSON with Ansible?

cn flag
raw

I have this JSON in a variable :

{
"device_vlans": {
    "1": {
        "name": "default",
        "interfaces": [
            "GigabitEthernet1/1",
            "GigabitEthernet1/2",
            "GigabitEthernet1/3"
        ]
    },
    "20": {
        "name": "VLAN20",
        "interfaces": [
            "GigabitEthernet1/2"
        ]
    },
    "30": {
        "name": "VLAN30",
        "interfaces": [
            "GigabitEthernet1/3"
        ]
    }
}

But I need it to look more like this :

{
"device_vlans": {
    "GigabitEthernet1/1": {
        "vlans": [
            "1"
        ]
    },
    "GigabitEthernet1/2": {
        "vlans": [
            "1",
            "20"
        ]
    },
    "GigabitEthernet1/3": {
        "vlans": [
            "1",
            "30"
        ]
    }
}

Currently I'm looping over all interfaces of the device and inside that loop I'm looping over all items in the device_vlans variable with a when: item == interface. It's really slow and causing me problems..

Is there any better way of doing that with ansible ?

I thought of a custom filter would that be the solution ?

Score:2
br flag

Create a list of devices and interfaces

    - set_fact:
        dev_ifc: "{{ dev_ifc|d([]) + [{'dev': item.1, 'ifc': item.0.key}] }}"
      with_subelements:
        - "{{ device_vlans|dict2items }}"
        - value.interfaces

gives

  dev_ifc:
    - {dev: GigabitEthernet1/1, ifc: '1'}
    - {dev: GigabitEthernet1/2, ifc: '1'}
    - {dev: GigabitEthernet1/3, ifc: '1'}
    - {dev: GigabitEthernet1/2, ifc: '20'}
    - {dev: GigabitEthernet1/3, ifc: '30'}

Then, group the list by the devices and create the dictionary

    - set_fact:
        device_vlans: "{{ dict(key|zip(val)) }}"
      vars:
        arr: "{{ dev_ifc|groupby('dev') }}"
        key: "{{ arr|map('first')|list }}"
        val: "{{ arr|map('last')|
                     map('json_query', '[].ifc')|
                     map('community.general.dict_kv', 'interfaces')|
                     list }}"

gives

  device_vlans:
    GigabitEthernet1/1:
      interfaces: ['1']
    GigabitEthernet1/2:
      interfaces: ['1', '20']
    GigabitEthernet1/3:
      interfaces: ['1', '30']

It's possible to avoid iteration in a task. Instead, put the iteration into Jinja2. For example, put the declarations below as appropriate

    dev_ifc_str: |-
      {% for ifc in device_vlans.keys() %}
      {% for dev in device_vlans[ifc]['interfaces'] %}
      - {dev: {{ dev }}, ifc: {{ ifc }}}
      {% endfor %}
      {% endfor %}
    dev_ifc: "{{ dev_ifc_str|from_yaml }}"
    device_vlans2: "{{ dict(_key|zip(_val)) }}"
    _arr: "{{ dev_ifc|groupby('dev') }}"
    _key: "{{ _arr|map('first')|list }}"
    _val: "{{ _arr|map('last')|
                   map('json_query', '[].ifc')|
                   map('community.general.dict_kv', 'interfaces')|
                   list }}"
cn flag
raw
I'm getting an error with the `val` var its returning `<generator object sync_do_map at 0x7f87fdec0048>` do you have a solution ?
br flag
Add the *list* filter to the pipe. I updated the code.
cn flag
raw
Thank you ! @vladimir-botka
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.