Search code examples
jsoncsvparsingansiblenested-loops

Ansible build report from json


A PyATS Ansible playbook output is saved to a json file:

- name: Export variable to file
  copy:
    content: "{{ output }}"
    dest: "report.json"

The json content is as follows:

{
  "results": [
    {
      "structured": {
        "interfaces": {
          "Port-channel11": {
            "ipv4": {
              "neighbors": {
                "1.1.0.1": {
                  "ip": "1.1.0.1",
                  "link_layer_address": "0000.0000.00ca"
                }
              }
            }
          },
          "GigabitEthernet1": {
            "ipv4": {
              "neighbors": {
                "1.1.1.2": {
                  "ip": "1.1.1.2",
                  "link_layer_address": "0000.0000.00cb"
                },
                "1.1.1.3": {
                  "ip": "1.1.1.3",
                  "link_layer_address": "0000.0000.00cc"
                }
              }
            }
          }
        }
      },
      "item": {
        "key": "vrf1",
        "value": {
          "route_distinguisher": "1:1",
          "interfaces": [
            "GigabitEthernet1",
            "Port-channel11"
          ]
        }
      },
      "ansible_loop_var": "item"
    },
    {
      "structured": {
        "interfaces": {
          "Port-channel2": {
            "ipv4": {
              "neighbors": {
                "2.2.2.200": {
                  "ip": "2.2.2.200",
                  "link_layer_address": "0000.0000.00dd"
                }
              }
            }
          }
        }
      },
      "item": {
        "key": "vrf2",
        "value": {
          "route_distinguisher": "2:2",
          "interfaces": [
            "Port-channel2"
          ]
        }
      },
      "ansible_loop_var": "item"
    }
  ],
  "skipped": false,
  "msg": "All items completed",
  "changed": false
}

I would like to create a csv file from this json file that contains the following data:

{{ ansible_host/inventory_host }},vrf1,Port-channel11,1.1.0.1,0000.0000.00ca
{{ ansible_host/inventory_host }},vrf1,GigabitEthernet1,1.1.1.2,0000.0000.00cb
{{ ansible_host/inventory_host }},vrf1,GigabitEthernet1,1.1.1.3,0000.0000.00cc
{{ ansible_host/inventory_host }},vrf2,Port-channel2,2.2.2.200,0000.0000.00dd

The closest I could reach has been:

    - name: Set vrflength variable
      set_fact:
        vrflength: "{{ output.results | length }}"

    - name: Set vrfmaxindex variable
      set_fact:
        vrfmaxindex: "{{ (vrflength | int) - 1 }}"

    - name: Create file
      lineinfile:
        insertafter: EOF
        dest: "report1.csv"
        line: "{{ inventory_hostname }},{{ output.results[ item | int ].item.key }},{{ output.results[ item | int ].structured.interfaces }}"
      with_sequence: start=0 end="{{ vrfmaxindex }}"
`device1,vrf1,{"Port-channel11":{"ipv4":{"neighbors":{"1.1.0.1":{"ip":"1.1.0.1","link_layer_address":"0000.0000.00ca"}}}},"GigabitEthernet1":{"ipv4":{"neighbors":{"1.1.1.2":{"ip":"1.1.1.2","link_layer_address":"0000.0000.00cb"},"1.1.1.3":{"ip":"1.1.1.3","link_layer_address":"0000.0000.00cc"}}}}}

device1,vrf2,{"Port-channel2":{"ipv4":{"neighbors":{"2.2.2.200":{"ip":"2.2.2.200","link_layer_address":"0000.0000.00dd"}}}}}`

I'm unable to figure out how to build the required nested loops (for each VRF, for each interface and, for each neighbor, print the device name, the VRF name, the neighbour IP and the MAC on independent CSV lines).

Would really appreciate any assistance.


Solution

  • Use Jinja. For example, the play

    - hosts: all
    
      vars:
    
        _csv: |
          {% for i in output.results %}
          {% set key=i.item.key %}
          {% for k,v in i.structured.interfaces.items() %}
          {% for n,l in v.ipv4.neighbors.items() %}
          {{ key }},{{ k }},{{ n }},{{ l.link_layer_address }}
          {% endfor %}
          {% endfor %}
          {% endfor %}
    
      tasks:
    
        - include_vars:
            file: report.json
            name: output
    
        - debug:
            var: _csv
    

    gives (abridged)

      _csv: |-
        vrf1,Port-channel11,1.1.0.1,0000.0000.00ca
        vrf1,GigabitEthernet1,1.1.1.2,0000.0000.00cb
        vrf1,GigabitEthernet1,1.1.1.3,0000.0000.00cc
        vrf2,Port-channel2,2.2.2.200,0000.0000.00dd