kaceymusgraves
kaceymusgraves

Reputation: 57

Need help parsing Cisco output

I am having some issue trying to parse the mrib table of a router. I have been able to parse some of it out but having issue. For example I have the following output:

(192.168.1.1,232.0.6.8) RPF nbr: 55.44.23.1 Flags: RPF
  Up: 4w1d
  Incoming Interface List
    TenGigE0/0/0/1 Flags: A, Up: 4w1d
  Outgoing Interface List
    TenGigE0/0/0/10 Flags: A, Up: 4w1d

(192.168.55.3,232.0.10.69) RPF nbr: 66.76.44.130 Flags: RPF
  Up: 4w1d
  Incoming Interface List
    TenGigE0/0/0/0 Flags: A, Up: 4w1d
    TenGigE0/1/0/0 Flags: A, Up: 4w1d
    TenGigE0/2/0/0 Flags: A, Up: 4w1d
  Outgoing Interface List
    TenGigE0/0/0/10 Flags: A, Up: 4w1d
    TenGigE0/3/0/0 Flags: A, Up: 4w1d
    TenGigE0/4/0/0 Flags: A, Up: 4w1d

I am trying to build a data structure with the above output. For clarity I want it to look like this:

{'192.168.1.1,232.0.6.8': {'incoming': ['TenGigE0/0/0/1'],
                           'outgoing': ['TenGigE0/0/0/10']}}

The above is sounds simple. One of the main issues I am having is with the second block. I am trying to figure a way to iterate through the interfaces after the Incoming and Outgoing interface.

Not necessarily asking for the code but what would be the best way to do something like this?

Upvotes: 3

Views: 1337

Answers (2)

Jan
Jan

Reputation: 43189

Well, if you're able to use the newer regex module in Python, you can define subpatterns and use the following approach:

  1. Define subpatterns for the IP address in the beginning
  2. ... as well as the Incoming and Outgoing Interface
  3. Parse the Interfaces separately
  4. See a demo on regex101.com.


Define subpatterns

Define subpatterns for the Incoming and Outgoing Interface strings, the IP adress and the end.

(?(DEFINE)
    (?<ips>[^()]+)
    (?<incoming>Incoming\ Interface \ List)
    (?<outgoing>Outgoing\ Interface \ List)
    (?<end>^$|\Z)
)

Put the regex together

Anchor the IP part to the beginning of the line and use a tempered greedy token with negative lookaheads for the incoming/outgoing part.

    ^\((?P<ip>(?&ips))\)
    (?:(?!(?&incoming))[\s\S]+?)
    (?&incoming)[\r\n]
    (?P<in>(?!(?&outgoing))[\s\S]+?) # tempered greedy token
    (?&outgoing)[\r\n]
    (?P<out>(?!^$)[\s\S]+?)
    (?&end)

Parse the incoming/outgoing parts

As you only need the interfaces types/names, you can simply come up with:

TenGig\S+ # TenGig, followed by anything NOT a whitespace

Hints

You do not really need to define subpatterns but then you'll need to repeat yourself a lot (because of the neg. lookaheads). So if you need to stick with the original re module, you can very well use this as well.

Glued together

All glued together in code, this will be:

import regex as re

string = """
(192.168.1.1,232.0.6.8) RPF nbr: 55.44.23.1 Flags: RPF
  Up: 4w1d
  Incoming Interface List
    TenGigE0/0/0/1 Flags: A, Up: 4w1d
  Outgoing Interface List
    TenGigE0/0/0/10 Flags: A, Up: 4w1d

(192.168.55.3,232.0.10.69) RPF nbr: 66.76.44.130 Flags: RPF
  Up: 4w1d
  Incoming Interface List
    TenGigE0/0/0/0 Flags: A, Up: 4w1d
    TenGigE0/1/0/0 Flags: A, Up: 4w1d
    TenGigE0/2/0/0 Flags: A, Up: 4w1d
  Outgoing Interface List
    TenGigE0/0/0/10 Flags: A, Up: 4w1d
    TenGigE0/3/0/0 Flags: A, Up: 4w1d
    TenGigE0/4/0/0 Flags: A, Up: 4w1d
"""

rx = re.compile(r"""
            (?(DEFINE)
                (?<ips>[^()]+)
                (?<incoming>Incoming\ Interface \ List)
                (?<outgoing>Outgoing\ Interface \ List)
                (?<end>^$|\Z)
            )
            ^\((?P<ip>(?&ips))\)
            (?:(?!(?&incoming))[\s\S]+?)
            (?&incoming)[\r\n]
            (?P<in>(?!(?&outgoing))[\s\S]+?)
            (?&outgoing)[\r\n]
            (?P<out>(?!^$)[\s\S]+?)
            (?&end)
""", re.MULTILINE|re.VERBOSE)

rxiface = re.compile(r'TenGig\S+')

result = dict()
for match in rx.finditer(string):
    key = match.group('ip')
    incoming = rxiface.findall(match.group('in'))
    outgoing = rxiface.findall(match.group('out'))

    result[key] = {'incoming': incoming, 'outgoing': outgoing}

print result
# {'192.168.1.1,232.0.6.8': {'outgoing': ['TenGigE0/0/0/10'], 'incoming': ['TenGigE0/0/0/1']}, '192.168.55.3,232.0.10.69': {'outgoing': ['TenGigE0/0/0/10', 'TenGigE0/3/0/0', 'TenGigE0/4/0/0'], 'incoming': ['TenGigE0/0/0/0', 'TenGigE0/1/0/0', 'TenGigE0/2/0/0']}}

Upvotes: 2

strippenzieher
strippenzieher

Reputation: 316

Assumes your input is complete and well formatted:

matcher = re.compile(
    r'\((?P<range>[^\)]+)\)|'
    r'(?P<incoming>\s+Incoming Interface List)|'
    r'(?P<outgoing>\s+Outgoing Interface List)|'
    r'\s+(?P<interface>TenGigE0[^\s]+)'
)

with open('router_table.txt', 'r') as f:
    routing_table = []
    current_range = ''
    direction = ''
    for line in f:
        match = matcher.search(line)
        if match:
            if match.group('interface'):
                routing_table[-1][current_range][direction].append(match.group('interface'))
            if match.group('range'):
                current_range = match.group('range')
                routing_table.append(
                    {
                        current_range: {
                            'incoming': [],
                            'outgoing': []
                        }
                    }
                )
            if match.group('incoming'):
                direction = 'incoming'
            if match.group('outgoing'):
                direction = 'outgoing'

Upvotes: 0

Related Questions