Python ciscoconfparse to find PE configuration (VRF, INTERFACE, BGP Setup)

73 views Asked by At

I'm trying to use CiscoConfParse on Cisco IOS config an parse a full PE configurations.
The goal is to collect all the essential information from a PE Config (VRF, INTERFACE, BGP Setup).
I found out this ciscoconfparse library (https://pypi.python.org/pypi/ciscoconfparse).
The code only partially works and the regex match correctly but only the first match (see also output below).

Code:

import ciscoconfparse

def parse_config(config_file):
    # Create a CiscoConfParse object from the configuration file
    config = ciscoconfparse.CiscoConfParse(config_file)

    # Parse VRF configurations
    vrf_configs = []
    for vrf_obj in config.find_objects(r'^vrf definition (\S+)'):
        vrf_config = {
            'name': vrf_obj.re_match(r'vrf definition (\S+)'),
            'rd': vrf_obj.re_match(r'rd (\S+)'),
            'export_rt': vrf_obj.re_match(r'route-target export (\S+)'),
            'import_rt': vrf_obj.re_match(r'route-target import (\S+)'),
        }
        vrf_configs.append(vrf_config)

    # Parse Interface configurations
    interface_configs = []
    for intf_obj in config.find_objects(r'^interface'):
        interface_config = {
            'name': intf_obj.re_match(r'interface (\S+)'),
            'description': intf_obj.re_match(r'description (.+)'),
            'vrf': intf_obj.re_match(r'vrf forwarding (\S+)'),
            'interface_ip': intf_obj.re_match(r'ip address (\S+) (\S+)'),
            'interface_dot1q': intf_obj.re_match(r'encapsulation dot1q (\S+)'),
        }
        interface_configs.append(interface_config)

    # Parse BGP configurations
    bgp_configs = []
    for bgp_obj in config.find_objects(r'^router bgp (\S+)'):
        bgp_config = {
            'as_number PE': bgp_obj.re_match(r'router bgp (\d+)'),
            'bgp_address-family': bgp_obj.re_match(r'address-family ipv4 vrf (\S+)'),
            'bgp_neighbor_ip': bgp_obj.re_match(r'neighbor (\S+) remote-as (\d+)'),
            'bgp_neighbor_as': bgp_obj.re_match(r'remote-as (\d+)'),
        }
        bgp_configs.append(bgp_config)

    return vrf_configs, interface_configs, bgp_configs

# Parse the PE router configuration
vrf_configs, interface_configs, bgp_configs = parse_config('pe_config.txt')

# Print parsed data
print("Parsed VRF configurations from PE config:")
for vrf in vrf_configs:
    print(vrf)

print("\nParsed Interface configurations from PE config:")
for intf in interface_configs:
    print(intf)

print("\nParsed BGP configurations from PE config:")
for bgp in bgp_configs:
    print(bgp)

Output:

Parsed VRF configurations from PE config:
{'name': 'BLUE', 'rd': '', 'export_rt': '', 'import_rt': ''}
{'name': 'RED', 'rd': '', 'export_rt': '', 'import_rt': ''}

Parsed Interface configurations from PE config:
{'name': 'FastEthernet2/0.2', 'description': '', 'vrf': '', 'interface_ip': '', 'interface_dot1q': ''}
{'name': 'FastEthernet3/0.3', 'description': '', 'vrf': '', 'interface_ip': '', 'interface_dot1q': ''}

Parsed BGP configurations from PE config:
{'as_number PE': '100', 'bgp_address-family': '', 'bgp_neighbor_ip': '', 'bgp_neighbor_as': ''}

PE config (pe_config.txt):

!-- BASIC PE configuration ------------
vrf definition BLUE
rd 108:108
route-target export 108:108
route-target import 108:108
!
address-family ipv4
exit-address-family
!
vrf definition RED
rd 79:79
route-target export 79:79
route-target import 79:79
!
address-family ipv4
exit-address-family
!
interface FastEthernet2/0.2
description Customer RED
encapsulation dot1q 2
vrf forwarding RED
ip address 10.57.1.5 255.255.255.0
!
interface FastEthernet3/0.3
description Customer BLUE
encapsulation dot1q 3
vrf forwarding BLUE
ip address 10.58.1.5 255.255.255.0
!
router bgp 100
bgp log-neighbor-changes
no bgp default ipv4-unicast
neighbor 6.6.6.6 remote-as 100
neighbor 6.6.6.6 update-source Loopback0
!
address-family ipv4
exit-address-family
!
address-family vpnv4
neighbor 6.6.6.6 activate
neighbor 6.6.6.6 send-community extended
exit-address-family
!
address-family ipv4 vrf RED
neighbor 10.57.1.7 remote-as 79
neighbor 10.57.1.7 activate
exit-address-family
!
address-family ipv4 vrf BLUE
neighbor 10.58.1.8 remote-as 8
neighbor 10.58.1.8 activate
exit-address-family
!
!-- EOF -------------------------------

Maybe someone has a good idea?

Thank you for your input.

I expect all information about the VRFs, interfaces and BGP configuration to be listed.

Parsed VRF configurations from PE config:
{'name': 'VRF xx', 'rd': 'xx:xx', 'export_rt': 'xx:xx', 'import_rt': 'xx:xx'}

Parsed Interface configurations from PE config:
{'name': 'FastEthernet2/0.2', 'description': 'xxx', 'vrf': 'VRF xx', 'interface_ip': 'A.B.C.D 255.255.255.252', 'interface_dot1q': 'x'}

Parsed BGP configurations from PE config:
{'as_number PE': '100', 'bgp_address-family': 'VRF xx', 'bgp_neighbor_ip': 'A.B.C.D', 'bgp_neighbor_as': 'xx'}
1

There are 1 answers

0
Mike Pennington On

I expect all information about the VRFs, interfaces and BGP configuration to be listed.

Your Cisco IOS config is not indented correctly, and you can't parse a flat config the way you've tried... specifically, this is broken...

    for vrf_obj in config.find_objects(r'^vrf definition (\S+)'):
        vrf_config = {
            'name': vrf_obj.re_match(r'vrf definition (\S+)'),
            'rd': vrf_obj.re_match(r'rd (\S+)'),
            'export_rt': vrf_obj.re_match(r'route-target export (\S+)'),
            'import_rt': vrf_obj.re_match(r'route-target import (\S+)'),
        }

vrf_obj.re_match() cannot simultaneously handle vrf definition AND route-target because vrf_obj only hits the vrf definition line, even in your flat (un-indented) IOS config.

Indent your configuration as Cisco IOS does, and use re_match_iter_typed() to capture first-level children of parents... you should iterate individually for multi-level children... this works...

from ciscoconfparse import CiscoConfParse

CONFIG = """!
vrf definition BLUE
 rd 108:108
 route-target export 108:108
 route-target import 108:108
 !
 address-family ipv4
 exit-address-family
!
vrf definition RED
 rd 79:79
 route-target export 79:79
 route-target import 79:79
 !
 address-family ipv4
 exit-address-family
!
interface FastEthernet2/0.2
 description Customer RED
 encapsulation dot1q 2
 vrf forwarding RED
 ip address 10.57.1.5 255.255.255.0
!
interface FastEthernet3/0.3
 description Customer BLUE
 encapsulation dot1q 3
 vrf forwarding BLUE
 ip address 10.58.1.5 255.255.255.0
!
router bgp 100
 bgp log-neighbor-changes
 no bgp default ipv4-unicast
 neighbor 6.6.6.6 remote-as 100
 neighbor 6.6.6.6 update-source Loopback0
!
 address-family ipv4
 exit-address-family
!
 address-family vpnv4
  neighbor 6.6.6.6 activate
  neighbor 6.6.6.6 send-community extended
 exit-address-family
!
 address-family ipv4 vrf RED
  neighbor 10.57.1.7 remote-as 79
  neighbor 10.57.1.7 activate
 exit-address-family
!
 address-family ipv4 vrf BLUE
  neighbor 10.58.1.8 remote-as 8
  neighbor 10.58.1.8 activate
 exit-address-family
!
end"""

def parse_config(config_file):
    # Create a CiscoConfParse object from the configuration file
    config = CiscoConfParse(config_file)

    # Parse VRF configurations
    vrf_configs = []
    for vrf_obj in config.find_objects(r'^vrf definition (\S+)'):
        vrf_config = {
            'name': vrf_obj.re_match(r'vrf definition (\S+)'),
            'rd': vrf_obj.re_match_iter_typed(r'rd (\S+)'),
            'export_rt': vrf_obj.re_match_iter_typed(r'route-target export (\S+)'),
            'import_rt': vrf_obj.re_match_iter_typed(r'route-target import (\S+)'),
        }
        vrf_configs.append(vrf_config)

    # Parse Interface configurations
    interface_configs = []
    for intf_obj in config.find_objects(r'^interface'):
        interface_config = {
            'name': intf_obj.re_match(r'interface (\S+)'),
            'description': intf_obj.re_match_iter_typed(r'description (.+)'),
            'vrf': intf_obj.re_match_iter_typed(r'vrf forwarding (\S+)'),
            'interface_ip': intf_obj.re_match_iter_typed(r'ip address (\S.+)'),
            'interface_dot1q': intf_obj.re_match_iter_typed(r'encapsulation dot1q (\S+)'),
        }
        interface_configs.append(interface_config)

    # Parse BGP configurations
    bgp_configs = []
    pe_asn = config.find_objects('^router bgp')[0].re_match(r'router bgp (\d+)')
    for af_config in config.find_objects(r'^router bgp (\S+)')[0].children:
        if 'address-family ipv4' in af_config.text:
            af_vrf_name = af_config.re_match(r'address-family ipv4 vrf (\S+)')
            for cobj in af_config.children:
                if 'remote-as' in cobj.text:
                    bgp_config = {
                        'PE_local_as_number': pe_asn,
                        'PE_vrf': af_vrf_name,
                        'bgp_remote_neighbor_ip': cobj.re_match(r'neighbor (\S+) remote-as \d+'),
                        'bgp_remote_neighbor_as': cobj.re_match(r'remote-as (\d+)'),
                    }
                    bgp_configs.append(bgp_config)

    return vrf_configs, interface_configs, bgp_configs

# Parse the PE router configuration
vrf_configs, interface_configs, bgp_configs = parse_config(CONFIG.splitlines())

# Print parsed data
print("Parsed VRF configurations from PE config:")
for vrf in vrf_configs:
    print(vrf)

print("\nParsed Interface configurations from PE config:")
for intf in interface_configs:
    print(intf)

print("\nParsed BGP configurations from PE config:")
for bgp in bgp_configs:
    print(bgp)

The exact output is:

$ python stackoverflow.py
Parsed VRF configurations from PE config:
{'name': 'BLUE', 'rd': '108:108', 'export_rt': '108:108', 'import_rt': '108:108'}
{'name': 'RED', 'rd': '79:79', 'export_rt': '79:79', 'import_rt': '79:79'}

Parsed Interface configurations from PE config:
{'name': 'FastEthernet2/0.2', 'description': 'Customer RED', 'vrf': 'RED', 'interface_ip': '10.57.1.5 255.255.255.0', 'interface_dot1q': '2'}
{'name': 'FastEthernet3/0.3', 'description': 'Customer BLUE', 'vrf': 'BLUE', 'interface_ip': '10.58.1.5 255.255.255.0', 'interface_dot1q': '3'}

Parsed BGP configurations from PE config:
{'PE_local_as_number': '100', 'PE_vrf': 'RED', 'bgp_remote_neighbor_ip': '10.57.1.7', 'bgp_remote_neighbor_as': '79'}
{'PE_local_as_number': '100', 'PE_vrf': 'BLUE', 'bgp_remote_neighbor_ip': '10.58.1.8', 'bgp_remote_neighbor_as': '8'}