Search code examples
pythonjsonparsingtabular

Convert Tabular CLI output to JSON format in Python


I need to convert he below output to Json format in python.

How can i do it ?

switch# sh mod
Mod  Ports  Module-Type                         Model              Status
---  -----  ----------------------------------- ------------------ ----------
1    48     1/2/4/8 Gbps FC/Supervisor-3        DS-C9148-K9-SUP    active *

Mod  Sw              Hw      World-Wide-Name(s) (WWN)
---  --------------  ------  --------------------------------------------------
1    6.2(17)         1.1     20:01:54:7f:ee:df:88:f8 to 20:30:54:7f:ee:df:88:f8


Mod  MAC-Address(es)                         Serial-Num
---  --------------------------------------  ----------
1    c0-8c-60-65-82-dc to c0-8c-60-65-82-df  JAF1736ALLM

Input 1 : https://i.sstatic.net/EGsY4.jpg

Input 2 : https://i.sstatic.net/aDGcB.jpg


Solution

  • You can use the '---' separators to define the slices of each key and value line to build up each key value. (From your example, I'm guessing there are multiple "Mod"s, with unique Mod values, so I used this field for the overall accumulator key.)

    from collections import defaultdict
    import re
    from itertools import groupby
    
    sample = """\
    Mod  Ports  Module-Type                         Model              Status
    ---  -----  ----------------------------------- ------------------ ----------
    1    48     1/2/4/8 Gbps FC/Supervisor-3        DS-C9148-K9-SUP    active *
    2    48     1/2/4/8 Gbps FC/Supervisor-3        DS-C9148-K9-SUP    active *
    
    Mod  Sw              Hw      World-Wide-Name(s) (WWN)
    ---  --------------  ------  --------------------------------------------------
    1    6.2(17)         1.1     20:01:54:7f:ee:df:88:f8 to 20:30:54:7f:ee:df:88:f8
    2    6.2(17)         1.1     20:01:54:7f:ee:df:88:f8 to 20:30:54:7f:ee:df:88:f8
    
    Mod  MAC-Address(es)                         Serial-Num
    ---  --------------------------------------  ----------
    1    c0-8c-60-65-82-dc to c0-8c-60-65-82-df  JAF1736ALLM
    2    c0-8c-60-65-82-ec to c0-8c-60-65-82-ef  JAF1736AXXX
    
    Xbar Ports Module-Type Model Status
    ---- ----- ----------- ----- ------
    1    0     Fabric 1    ABC   ok
    
    Xbar Sw Hw
    ---- -- ---
    1    NA 1.0
    
    """
    
    all_input_lines = sample.splitlines()
    mod_accum = defaultdict(dict)
    xbar_accum = defaultdict(dict)
    
    for is_blank, input_lines_iter in groupby(all_input_lines, 
                                              key=lambda s: not bool(s.strip())):
        input_lines = list(input_lines_iter)
        if is_blank:
            continue
    
        # assume first two lines are field names and separator dashes
        names, dashes = input_lines[:2]
    
        # make sure dashes line is all '---' separators
        if not all(ss == set('-') for ss in map(set, dashes.split())):
            print("invalid line group found, skipping...")
            print('-'*40)
            print('\n'.join(input_lines))
            print('-'*40)
            continue
    
        # use regex to get start/end of each '---' divider, and make slices
        spans = (match.span() for match in re.finditer('-+', dashes))
        slices = [slice(sp[0], sp[1]+1) for sp in spans]
    
        names = [names[sl].rstrip() for sl in slices]
    
        # is this a module or an xbar?
        if 'Mod' in names:
            key = 'Mod'
            accum = mod_accum
        elif 'Xbar' in names:
            key = 'Xbar'
            accum = xbar_accum
        else:
            raise ValueError("no Mod or Xbar name in row names ({})".format(
                                ",".join(names)))
    
        for line in input_lines:
            # use slices to extract data from values, make into a dict
            row_dict = dict(zip(names, (line[sl].rstrip() for sl in slices)))
    
            # accumulate these values into any previous ones collected for this Mod
            accum[row_dict[key]].update(row_dict)
    
    # print out what we got
    import json
    all_data = {"Modules": mod_accum, "Xbars": xbar_accum}
    print(json.dumps(all_data, indent=2))
    

    Prints:

    {
      "Modules": {
        "2": {
          "World-Wide-Name(s) (WWN)": "20:01:54:7f:ee:df:88:f8 to 20:30:54:7f:ee:df:88:f8",
          "Module-Type": "1/2/4/8 Gbps FC/Supervisor-3",
          "Ports": "48",
          "Sw": "6.2(17)",
          "Hw": "1.1",
          "Model": "DS-C9148-K9-SUP",
          "Status": "active *",
          "Serial-Num": "JAF1736AXXX",
          "MAC-Address(es)": "c0-8c-60-65-82-ec to c0-8c-60-65-82-ef",
          "Mod": "2"
        },
        "1": {
          "World-Wide-Name(s) (WWN)": "20:01:54:7f:ee:df:88:f8 to 20:30:54:7f:ee:df:88:f8",
          "Module-Type": "1/2/4/8 Gbps FC/Supervisor-3",
          "Ports": "48",
          "Sw": "6.2(17)",
          "Hw": "1.1",
          "Model": "DS-C9148-K9-SUP",
          "Status": "active *",
          "Serial-Num": "JAF1736ALLM",
          "MAC-Address(es)": "c0-8c-60-65-82-dc to c0-8c-60-65-82-df",
          "Mod": "1"
        }
      },
      "Xbars": {
        "1": {
          "Module-Type": "Fabric 1",
          "Ports": "0",
          "Sw": "NA",
          "Hw": "1.0",
          "Model": "ABC",
          "Status": "ok",
          "Xbar": "1"
        }
      }
    }