cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
563
Views
0
Helpful
6
Replies

Issue in Genie parser for commands "show bgp {address-family}"

Manu05
Level 1
Level 1

I'm getting error ('Parser Output is empty',) when the command specifies the address family (ipv4,ipv6, vpnv4, etc)
- show bgp {address-family}
- show bgp vrf {vrf}  {address-family}

Below my code:

def get_bgp_routing_table(host, username, password, vrf='', filter='ipv4_unicast'):
global TESTBED_YAML_FILE
testbed = loader.load(TESTBED_YAML_FILE)
if host not in testbed.devices:
raise ValueError(f"Device {host} not found in testbed")
device = testbed.devices[host]

device.credentials['default'] = {
'username': username,
'password': password
}
device.connect(
timeout=60,
init_exec_commands=[], # Prevents exec commands
init_config_commands=[] # Prevents config commands
)
extracted_data = []
filter = filter.replace('_', ' ') # Adjust filter for command syntax
try:

if filter == 'ipv4 unicast':
command = f"show bgp vrf {vrf}" if vrf else "show bgp"
else:
command = f"show bgp vrf {vrf} {filter}" if vrf else f"show bgp {filter}"

raw_output = device.execute(command)
print("Raw output received from the device:\n", raw_output)

output = device.parse(command)

filter = filter.replace(' ', '_')

if 'vrf' in output:
vrf_output = output['vrf']
if vrf in vrf_output:
address_family = vrf_output[vrf]['address_family']
if filter in address_family:
for prefix, details in address_family[filter]['prefix'].items():
for index, info in details['index'].items():
extracted_data.append({
'network': prefix,
'next_hop': info['next_hop'],
'status_codes': info['status_codes'],
'origin_codes': info['origin_codes'],
'locprf': info['locprf'],
'weight': info['weight'],
'path': info.get('path', None) # Safe to use .get() here
})
else:
raise ValueError(f"Address family '{filter}' not found in the VRF '{vrf}' output.")
else:
raise ValueError(f"VRF '{vrf}' not found in the output.")
else:
raise ValueError("VRF information missing in the parsed output")
except Exception as e:
results = {
'status': 400,
'message': str(e)
}
return results, 400
finally:
device.disconnect()
return extracted_data


The exception is raised when execution reaches line "output = device.parse(command)"

When the command has the ipv4 unicast filter, the raw output is:


Tue Mar 4 11:49:15.027 GMT
BGP VRF lab, state: Active
BGP Route Distinguisher: 12345:92
VRF ID: 0x6000013d
BGP router identifier 1.1.1.1, local AS number 12345
Non-stop routing is enabled
BGP table state: Active
Table ID: 0xe000013d RD version: 54879
BGP table nexthop route policy:
BGP main routing table version 54879
BGP NSR Initial initsync version 53406 (Reached)
BGP NSR/ISSU Sync-Group versions 54879/0

Status codes: s suppressed, d damped, h history, * valid, > best
i - internal, r RIB-failure, S stale, N Nexthop-discard
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
Route Distinguisher: 12345:92 (default for vrf lab)
Route Distinguisher Version: 54879
*>i2.122.208.0/24 2.120.0.2 0 100 0 ?
*> 2.216.0.0/13 3.3.3.3.234 0 32768 ?
*>i8.8.8.8/32 76.145.128.249 100 0 ?
*> 10.0.0.0/16 172.16.110.2 0 32768 ?
*> 10.100.0.0/16 3.3.3.3.226 0 32768 ?
*>i3.3.3.3.32/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.64/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.128/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.160/28 76.145.128.41 0 100 0 ?
*> 3.3.3.3.224/30 3.3.3.3.248 0 32768 ?
*> 3.3.3.3.232/30 3.3.3.3.249 0 32768 ?
* 3.3.3.3.248/31 0.0.0.0 0 32768 ?
*> 0.0.0.0 0 32768 ?
*> 3.3.3.3.250/32 0.0.0.0 0 32768 ?
*> 11.0.0.0/16 172.16.110.2 0 32768 ?
*> 192.168.0.0/15 3.3.3.3.234 0 32768 ?
*>i80.238.8.0/23 76.145.128.249 100 0 ?
* i108.207.236.0/23 2.120.0.2 0 100 0 ?
*>i 76.145.128.249 100 0 ?
*>i91.103.34.0/24 2.120.0.99 10000 180 0 ?
*>i94.119.8.0/21 2.120.0.99 10000 180 0 ?
*> 172.16.110.0/30 3.3.3.3.249 0 32768 ?
*> 172.16.111.0/30 172.16.110.2 0 32768 ?
*>i20.6.14.0/24 76.145.128.249 100 0 ?
*>i20.58.18.0/23 76.145.128.249 100 0 ?

Processed 23 prefixes, 25 paths

 

When the command does not have any filters, the raw output is:


Tue Mar 4 11:50:17.130 GMT
BGP VRF lab, state: Active
BGP Route Distinguisher: 12345:92
VRF ID: 0x6000013d
BGP router identifier 1.1.1.1, local AS number 12345
Non-stop routing is enabled
BGP table state: Active
Table ID: 0xe000013d RD version: 54879
BGP table nexthop route policy:
BGP main routing table version 54879
BGP NSR Initial initsync version 53406 (Reached)
BGP NSR/ISSU Sync-Group versions 54879/0

Status codes: s suppressed, d damped, h history, * valid, > best
i - internal, r RIB-failure, S stale, N Nexthop-discard
Origin codes: i - IGP, e - EGP, ? - incomplete
Network Next Hop Metric LocPrf Weight Path
Route Distinguisher: 12345:92 (default for vrf lab)
Route Distinguisher Version: 54879
*>i2.122.208.0/24 2.120.0.2 0 100 0 ?
*> 2.216.0.0/13 3.3.3.3.234 0 32768 ?
*>i8.8.8.8/32 76.145.128.249 100 0 ?
*> 10.0.0.0/16 172.16.110.2 0 32768 ?
*> 10.100.0.0/16 3.3.3.3.226 0 32768 ?
*>i3.3.3.3.32/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.64/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.128/28 76.145.128.41 0 100 0 ?
*>i3.3.3.3.160/28 76.145.128.41 0 100 0 ?
*> 3.3.3.3.224/30 3.3.3.3.248 0 32768 ?
*> 3.3.3.3.232/30 3.3.3.3.249 0 32768 ?
* 3.3.3.3.248/31 0.0.0.0 0 32768 ?
*> 0.0.0.0 0 32768 ?
*> 3.3.3.3.250/32 0.0.0.0 0 32768 ?
*> 11.0.0.0/16 172.16.110.2 0 32768 ?
*> 192.168.0.0/15 3.3.3.3.234 0 32768 ?
*>i80.238.8.0/23 76.145.128.249 100 0 ?
* i108.207.236.0/23 2.120.0.2 0 100 0 ?
*>i 76.145.128.249 100 0 ?
*>i91.103.34.0/24 2.120.0.99 10000 180 0 ?
*>i94.119.8.0/21 2.120.0.99 10000 180 0 ?
*> 172.16.110.0/30 3.3.3.3.249 0 32768 ?
*> 172.16.111.0/30 172.16.110.2 0 32768 ?
*>i20.6.14.0/24 76.145.128.249 100 0 ?
*>i20.58.18.0/23 76.145.128.249 100 0 ?

Processed 23 prefixes, 25 paths

 

So the output is the same.

1 Accepted Solution

Accepted Solutions

Nice, so i guess this confirms the parsing issue specifically happens when an address family (like ipv6 unicast) is added to the command here. I guess to get deeper into this, you could update the code to print more, some more debugging outputs, or/and a print statement for your full output structure if no matching family is found ...

You could ask directly to the team over at pyats-support-ext@cisco.com if they are aware of known issues on versions etc..

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

View solution in original post

6 Replies 6

@Manu05 your error ('Parser Output is empty',) indicates that the parser is unable to interpret the raw output from your device, this i think the parser is unable to recognize the command structure when specific address family filters are used. 

What device is this for?

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

It's an IOS XR NCS5500 device.

When the command is "show bgp vrf {vrf} ipv4 unicast" the parsing fails. It's the same for other address families. When it's omitted like for "show bgp vrf {vrf}" the parsing is successful. Although the two commands give the same output from the device.

Please test this, it might be a bit rough, its using some command variations, some error handling, and i am attemting to handle the different address family key formats

 

def get_bgp_routing_table(host, username, password, vrf='', filter='ipv4_unicast'):
    global TESTBED_YAML_FILE
    testbed = loader.load(TESTBED_YAML_FILE)
    if host not in testbed.devices:
        raise ValueError(f"Device {host} not found in testbed")
    
    device = testbed.devices[host]

    device.credentials['default'] = {
        'username': username,
        'password': password
    }
    device.connect(
        timeout=60,
        init_exec_commands=[], 
        init_config_commands=[] 
    )
    
    extracted_data = []
    filter = filter.replace('_', ' ') # Adjust filter for command syntax
    
    try:
        # First, try parsing without address family if it fails
        commands_to_try = []
        if vrf:
            if filter == 'ipv4 unicast':
                commands_to_try = [
                    f"show bgp vrf {vrf}",
                    f"show bgp vrf {vrf} ipv4 unicast"
                ]
            else:
                commands_to_try = [
                    f"show bgp vrf {vrf} {filter}",
                    f"show bgp vrf {vrf}"
                ]
        else:
            commands_to_try = [
                f"show bgp {filter}" if filter != 'ipv4 unicast' else "show bgp",
                f"show bgp ipv4 unicast"
            ]

        # Try multiple command variations
        output = None
        for command in commands_to_try:
            try:
                raw_output = device.execute(command)
                print(f"Attempting to parse command: {command}")
                
                output = device.parse(command)
                if output:
                    break
            except Exception as cmd_error:
                print(f"Parsing failed for command {command}: {cmd_error}")
        
        if not output:
            raise ValueError("Could not parse BGP output with any command variation")

        filter = filter.replace(' ', '_')

        # Flexible parsing approach
        if 'vrf' in output:
            vrf_output = output['vrf']
            
            # Determine the VRF to use
            current_vrf = vrf if vrf and vrf in vrf_output else list(vrf_output.keys())[0]
            
            if current_vrf in vrf_output:
                address_family = vrf_output[current_vrf].get('address_family', {})
                
                # Try multiple address family keys
                possible_af_keys = [
                    filter,
                    filter.replace(' ', '_'),
                    'ipv4_unicast',  # Default fallback
                ]
                
                found_af = None
                for af_key in possible_af_keys:
                    if af_key in address_family:
                        found_af = af_key
                        break
                
                if found_af:
                    for prefix, details in address_family[found_af].get('prefix', {}).items():
                        for index, info in details.get('index', {}).items():
                            extracted_data.append({
                                'network': prefix,
                                'next_hop': info.get('next_hop', ''),
                                'status_codes': info.get('status_codes', ''),
                                'origin_codes': info.get('origin_codes', ''),
                                'locprf': info.get('locprf', ''),
                                'weight': info.get('weight', ''),
                                'path': info.get('path', [])
                            })
                else:
                    print(f"No matching address family found. Available families: {list(address_family.keys())}")
            else:
                print(f"VRF {current_vrf} not found in output")
        else:
            print("No VRF information in output")

    except Exception as e:
        print(f"Error occurred: {e}")
        results = {
            'status': 400,
            'message': str(e)
        }
        return results, 400
    finally:
        device.disconnect()
    
    return extracted_data

 

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

Thank you!

I just added a small adjustment on your code:

 

                if output:
                    print(f"Parsing successful for command {command}")
                    break​

 

 

 

Then I tried with two input commands:

1) show bgp vrf lab 

Results:

Attempting to parse command: show bgp vrf lab

Parsing successful for command show bgp vrf lab

2) show bgp vrf lab ipv6 unicast

Results:

Attempting to parse command: show bgp lab ipv6 unicast
Parsing failed for command show bgp vrf lab ipv6 unicast: Parser Output is empty

Attempting to parse command: show bgp vrf lab
Parsing successful for command show bgp vrf lab

 

 

Nice, so i guess this confirms the parsing issue specifically happens when an address family (like ipv6 unicast) is added to the command here. I guess to get deeper into this, you could update the code to print more, some more debugging outputs, or/and a print statement for your full output structure if no matching family is found ...

You could ask directly to the team over at pyats-support-ext@cisco.com if they are aware of known issues on versions etc..

Please mark this as helpful or solution accepted to help others
Connect with me https://bigevilbeard.github.io

Yes. It's clear that whenever an address family is added to the command, the parser fails. After digging further a little bit, I found a more specific Genie parser function can be used for IOS XR: ShowBgpVrf

it's in genieparser module in src/genie/libs/parser/iosxr/show_bgp.py. The address family issue disappeared but the parser fails reading metric, local preference, weight and as path parameters. With IPv6 some prefixes are not parsed at all. I think I'm going to message the team directly to pyats-support-ext@cisco.com and ask for advice.

Thanks!