2

Right now, I can get nearly what I want with a simple re.split("\n([^\s])", data) as shown below, but the problem is that the resulting list contains the single non-whitespace character match as it's own item in the list. Example output below script. Notice how the "V" in "VLAN" has been captured into this as it's own item?

I'm wondering also if there's just a better way to do this, perhaps a library I can include that handles converting tabular data into a dictionary or something.

#!/usr/bin/python
import re
import sys

data = """
VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
1    default                          active    Fa0/2, Fa0/3, Fa0/4, Fa0/5, Fa0/6, Fa0/7
                                                Fa0/8, Fa0/9, Fa0/10, Fa0/11, Fa0/12
                                                Fa0/13, Fa0/14, Fa0/15, Fa0/16, Fa0/17
                                                Fa0/18, Fa0/19, Fa0/20, Fa0/21, Fa0/22
                                                Fa0/23, Fa0/24, Gi0/2
1002 fddi-default                     act/unsup
1003 token-ring-default               act/unsup
1004 fddinet-default                  act/unsup
1005 trnet-default                    act/unsup
"""

lines = re.split("\n([^\s])", data)
print lines

Output:

['', 'V', 'LAN Name Status Ports', '-', '--- -------------------------------- --------- -------------------------------', '1', ' default active Fa0/2, Fa0/3, Fa0/4, Fa0/5, Fa0/6, Fa0/7\n
Fa0/8, Fa0/9, Fa0/10, Fa0/11, Fa0/12\n
Fa0/13, Fa0/14, Fa0/15, Fa0/16, Fa0/17\n
Fa0/18, Fa0/19, Fa0/20, Fa0/21, Fa0/22\n
Fa0/23, Fa0/24, Gi0/2', '1', '002 fddi-default
act/unsup', '1', '003 token-ring-default act/unsup', '1', '004 fddinet-default act/unsup', '1', '005 trnet-default act/unsup\n']

Thanks!

Edit: lines = re.findall(".*[^\n\W]*", data) seems like it's probably a better approach (nm that doesn't work, sorry) but this whole thing still feels pretty hacky so I'd love to hear any alternative suggestions.

Kevin
  • 1,489
  • 2
  • 20
  • 30

5 Answers5

3

NTC Templates provides a set of templates to parse various Cisco IOS command outputs (and other network devices). The templates work with TextFSM to do the actual parsing.

For example:

>>> from ntc_templates.parse import parse_output
>>> vlan_output = (
        "VLAN Name                             Status    Ports\n"
        "---- -------------------------------- --------- -------------------------------\n"
        "1    default                          active    Gi0/1\n"
        "10   Management                       active    \n"
        "50   VLan50                           active    Fa0/1, Fa0/2, Fa0/3, Fa0/4, Fa0/5,\n"
        "                                                Fa0/6, Fa0/7, Fa0/8\n"
    )
>>> vlan_parsed = parse_output(platform="cisco_ios", command="show vlan", data=vlan_output)
>>> vlan_parsed
[
    {
        'vlan_id': '1',
        'name': 'default',
        'status': 'active',
        'interfaces': ['Gi0/1']
    },
    {
        'vlan_id': '10',
        'name': 'Management',
        'status': 'active',
        'interfaces': []
    },
    {
        'vlan_id': '50',
        'name': 'VLan50', 'status': 'active',
        'interfaces': ['Fa0/1', 'Fa0/2', 'Fa0/3', 'Fa0/4', 'Fa0/5', 'Fa0/6', 'Fa0/7', 'Fa0/8']
    }
]
>>> 
Kim
  • 409
  • 3
  • 9
0

This is probably not the best way, but it's at least a solution. Use the regex module instead of the re module.

lines = regex.split("\n(?=[^\s])", data)

Unlike the builtin re module, the regex module allows splitting on zero-width matches, so you can use a lookahead to split at positions where the next character matches.

References:

Community
  • 1
  • 1
Kevin
  • 1,489
  • 2
  • 20
  • 30
0

These days I'd suggest looking at a dedicated existing library to do the parsing. One such example library I found was ciscoconfparse which will parse an IOS configuration (and some others) into line objects which maintain child/parent line relationships useful for configuration manipulation.

Ben Franske
  • 330
  • 2
  • 11
0

I have had similar problems with command outputs in Mellanox Switches(Which is similar to Cisco CLI). But I solved using following method:

Lets say your CLI cmd is

"show interfaces XXXXXXXXXXX".

The output will be in text format. You can get the output directly in json format as below:

"show interfaces XXXXXXXXXXX | json-print"

In your python script once you get the command output in a variable say "output". you can convert it into json object as below:

output = json.loads(output)

Hope this helps.

girkot1307
  • 39
  • 5
0

One more way to parse it and convert to structured data.

Template Text Parser can help to produce results you need, sample code:

from ttp import ttp
from pprint import pprint

template = """
<input load="text">
VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
1    default                          active    Fa0/2, Fa0/3, Fa0/4, Fa0/5, Fa0/6, Fa0/7
                                                Fa0/8, Fa0/9, Fa0/10, Fa0/11, Fa0/12
                                                Fa0/13, Fa0/14, Fa0/15, Fa0/16, Fa0/17
                                                Fa0/18, Fa0/19, Fa0/20, Fa0/21, Fa0/22
                                                Fa0/23, Fa0/24, Gi0/2
1002 fddi-default                     act/unsup
1003 token-ring-default               act/unsup
1004 fddinet-default                  act/unsup
1005 trnet-default                    act/unsup
</input>

<group>
{{ vid | DIGIT | _start_ }}  {{ name }}   {{ status }}
{{ vid | DIGIT | _start_ }}  {{ name }}   {{ status }}    {{ ports | ORPHRASE | replace(" ", "") | split(",") | joinmatches() }}
                                                {{ ports | ORPHRASE | replace(" ", "") | split(",") | joinmatches() }}
</group>
    """

parser = ttp(template=template)
parser.parse()
pprint(parser.result())

# Prints:
# [[[{'name': 'default',
#     'ports': ['Fa0/2',
#               'Fa0/3',
#               'Fa0/4',
#               'Fa0/5',
#               'Fa0/6',
#               'Fa0/7',
#               'Fa0/8',
#               'Fa0/9',
#               'Fa0/10',
#               'Fa0/11',
#               'Fa0/12',
#               'Fa0/13',
#               'Fa0/14',
#               'Fa0/15',
#               'Fa0/16',
#               'Fa0/17',
#               'Fa0/18',
#               'Fa0/19',
#               'Fa0/20',
#               'Fa0/21',
#               'Fa0/22',
#               'Fa0/23',
#               'Fa0/24',
#               'Gi0/2'],
#     'status': 'active',
#     'vid': '1'},
#    {'name': 'fddi-default', 'status': 'act/unsup', 'vid': '1002'},
#    {'name': 'token-ring-default', 'status': 'act/unsup', 'vid': '1003'},
#    {'name': 'fddinet-default', 'status': 'act/unsup', 'vid': '1004'},
#    {'name': 'trnet-default', 'status': 'act/unsup', 'vid': '1005'}]]]

Pay attention to the last line in group, it should have exact number of leading spaces as the actual text to be parsed.

apraksim
  • 191
  • 4