The Non-Tabular Parser¶
parsergen
is a generic parser for show commands. The goal is to make
it simple to create a parser for any given show command one time, and then
reuse this parser to create tests for any values found within the output.
The main functions of parsergen
are:
Find and verify expected values in show command output.
Provide an OS agnostic common interface to OS specific show commands.
Create once (per command), use many times (throughout all of your pyATS test suites).
Maintain a per command cache for fast processing.
Only parse the parts of the output that the user is interested in, thus avoiding wasting precious real-time.
How It Works¶
parsergen
tries to match a given set of data using a set of
regular expressions
that describe the values found within show command
output. The main idea is to exploit the structured form of the output. The
regular expression
matching is tried through a hierarchical process.
Consider some hypothetical output from a show command:
interface A
value_1 : 10, value_2 : 20
value_3 : 30
interface B
value_1 : 100, value_2 : 200
value_3 : 300
interface C
value_1 : 1000, value_2 : 2000
value_3 : 3000
To access the value_2
of interface A
parsergen
needs a list
of regular expressions
to narrow down the scope of the matching. The
order of the regexes
in the list correspond to the order of the values in
the output.
Note that parsergen
has a best effort algorithm to catch false
positives. For instance, if value_2
is not matched for interface B
,
parsergen
stops the execution as it exits from the inner level.
How To Specify the Regex¶
Note
Please see extend_markup
and mkpgen on
how to automatically generate the set of regular expressions
for
any given show command.
- The
regular expression
must: Be identified by a unique key (tag) in the
regex
dictionary
.Must have no more than one value extracted per line.
For example:
regex = {
'iox': {
'regex1' : "i want to access (this) value in XR"
},
'nxos': {
'regex1' : "i want to access (this) value in NXOS"
}
}
To teach parsergen
a new set of regular expressions
use the
extend
or extend_markup
methods.
How To Specify Show Commands¶
In order to allow for OS agnostic support, and also to allow for migration of
show command syntax, one should also teach parsergen
about show
command syntax. To do this one creates a dictionary
that maps a user
chosen key value for the show command to the actual show command syntax for a
given OS.
For example:
show_cmds = {
'iox': {
'SHOW_IP_INTF' : "show {=ipv4} interface {}"
},
'nxos': {
'SHOW_IP_INTF' : "show {=ip} interface {}"
}
}
The above example now maps the key ‘SHOW_IP_INTF
’ to the correct syntax for
both XR and NXOS. Additionally the command strings can contain python
str.format()
arguments. The standard python format function has been
extended to allow for specifying default values if the argument is not provided
to parsergen
. The default values can be seen above for selecting IPv4
interfaces using =ipv4
for XR and =ip
for NXOS. If a default value is
not provided an empty string is substituted. For requests involving multiple
format fields, specify a format argument of None
to indicate this is a
placeholder for the default field value.
To teach parsergen a new set of show commands pass the dictionary
to
parsergen.extend
along with the set of regular expressions
.
parsergen
can render CLI commands in an OS-agnostic manner.
Here’s an example
cli_command_formatting_example.py
.
Specifying parsing details using Marked Up Input¶
The regular expression
and show command details mentioned in previous
sections can either be specified manually, or can be autogenerated via use of a
special CLI command markup format. Please see Output Generated from Marked Up Input for an
example of what this autogenerated output looks like.
A Full Example¶
Warning
Please ensure that all tags are defined for each marked-up value. Do not allow the tool to infer these values from surrounding text. Any rework arising from minor syntax changes in CLI “show” command output from release to release is then isolated to a single parser and will not affect user code.
Warning
Please identify all parts of the router output that could potentially change from run to run and explicitly assign markup tags. Any untagged portions of the markup text is expected to match the router output exactly, otherwise parse failure is expected.
In our full example we will create a parser for the show command show
interface
. The marked-up output is placed into
a file (for example,
parsergen_demo_mkpg.py
).
from genie import parsergen as pg
marked_up_show_interface_xrvr_output = '''\
OS: iosxr
CMD: show_interface_<WORD>
SHOWCMD: show interface {ifname}
PREFIX: show.intf
ACTUAL:
show interface MgmtEth0/0/CPU0/0
Fri Mar 6 12:03:11.409 EST
MgmtEth0/0/CPU0/0 is up, line protocol is up
Interface state transitions: 1
Hardware is Management Ethernet, address is 5254.00d6.36c9 (bia 5254.00d6.36c9)
Internet address is 10.30.108.132/23
MTU 1514 bytes, BW 0 Kbit
reliability 255/255, txload Unknown, rxload Unknown
Encapsulation ARPA,
Duplex unknown, 0Kb/s, unknown, link type is autonegotiation
output flow control is off, input flow control is off
Carrier delay (up) is 10 msec
loopback not set,
ARP type ARPA, ARP timeout 04:00:00
Last input 00:00:00, output 00:00:48
Last clearing of "show interface" counters never
5 minute input rate 79000 bits/sec, 32 packets/sec
5 minute output rate 0 bits/sec, 0 packets/sec
2459211 packets input, 774707935 bytes, 0 total input drops
0 drops for unrecognized upper-level protocol
Received 2216135 broadcast packets, 233738 multicast packets
0 runts, 0 giants, 0 throttles, 0 parity
0 input errors, 0 CRC, 0 frame, 0 overrun, 0 ignored, 0 abort
349 packets output, 58930 bytes, 0 total output drops
Output 4 broadcast packets, 0 multicast packets
0 output errors, 0 underruns, 0 applique, 0 resets
0 output buffer failures, 0 output buffers swapped out
1 carrier transitions
MARKUP:
show interface MgmtEth0/0/CPU0/0
Fri Mar 6 12:03:11.409 EST
XI<if_name>XMgmtEth0/0/CPU0/0 is XC<admin_state>Xup, line protocol is XW<line_protocol>Xup
Interface state transitions: XN<intf_trans>X1
Hardware is XXX<[^,]+><hardware>XXXManagement Ethernet, address is XA<mac_address>X5254.00d6.36c9 (bia 5254.00d6.36c9)
Internet address is XA<ip_address>X10.30.108.132/23
MTU XN<mtu>X1514 bytes, BW XNX<bw>0 Kbit
reliability 255/255, txload Unknown, rxload Unknown
Encapsulation XW<encap>XARPA,
Duplex unknown, 0Kb/s, unknown, link type is XW<link_type>Xautonegotiation
output flow control is XW<output_flowcontrol>Xoff, input flow control is XW<input_flowcontrol>Xoff
Carrier delay (up) is XN<carrier_delay_up>X10 msec
loopback XXX<[^,]+><loopback_status>XXXnot set,
ARP type ARPA, ARP timeout XW<arp_timeout>X04:00:00
Last input XT<last_input>X00:00:00, output XT<last_output>X00:00:48
Last clearing of "show interface" counters XR<last_clear_counter>Xnever
5 minute input rate XN<input_rate_bits>X79000 bits/sec, XN<input_rate>X32 packets/sec
5 minute output rate XN<output_rate_bits>X0 bits/sec, XN<output_rate>X0 packets/sec
XN<input_pkts>X2459211 packets input, XN<input_bytes>X774707935 bytes, XN<input_total_drops>X0 total input drops
XN<drops_unrec_upper_level_proto>X0 drops for unrecognized upper-level protocol
Received XN<broadcasts>X2216135 broadcast packets, XN<multicasts>X233738 multicast packets
XN<runts>X0 runts, XN<giants>X0 giants, XN<throttles>X0 throttles, XN<parity>X0 parity
XN<input_errors>X0 input errors, XN<crc>X0 CRC, XN<frame>X0 frame, XN<overrun>X0 overrun, XN<ignored>X0 ignored, XN<abort>X0 abort
XN<output_pkts>X349 packets output, XN<output_bytes>X58930 bytes, XN<output_total_drops>X0 total output drops
Output XN<output_broadcast>X4 broadcast packets, XN<output_multicast>X0 multicast packets
XN<output_errors>X0 output errors, XN<output_underruns>X0 underruns, XN<output_applique>X0 applique, XN<output_resets>X0 resets
XN<output_buf_failures>X0 output buffer failures, XN<output_buf_swapped>X0 output buffers swapped out
XN<carrier_trans>X1 carrier transitions
'''
pg.extend_markup(marked_up_show_interface_xrvr_output)
Registering With Parsergen¶
Now we extend the current set of regular expressions
and show commands recognized by parsergen
.
from genie.parsergen.examples.pyAts import parsergen_demo_mkpg
Requesting a Parse and Comparison of Select Tags¶
In many cases, users are only interested in parsing select portions of the CLI command output.
parsergen
offers a comparison mode which allows a list of regex tags to
be specified, along with their expected values. A parse is done on these
tags only, and if the parsed values are not as expected, parse failure will
result.
Here is an example of a parse and compare operation:
device.connect()
attrValPairsToCheck = [
('show.intf.if_name', 'MgmtEth0/0/CPU0/0'),
('show.intf.line_protocol', 'up'),
('show.intf.ip_address', '10.30.108.132'),
('show.intf.mtu', 1514),
('show.intf.admin_state', 'up'),
]
pgcheck = pg.oper_check (
device,
('show_interface_<WORD>', [], {'ifname':'MgmtEth0/0/CPU0/0'}),
attrValPairsToCheck,
refresh_cache=True)
if pgcheck.parse():
log.info("Parse succeeded.")
log.info("Parsing details : {}".format(pg.ext_dictio[device.name]))
else:
log.info("Parse failed.")
log.error(str(pgcheck))
This API is also predicate-compatible. The above snippet can also be written as such:
from genie.parsergen.predicate import PredicateTestedFalseSignal
attrValPairsToCheck = [
('show.intf.if_name', 'MgmtEth0/0/CPU0/0'),
('show.intf.line_protocol', 'up'),
('show.intf.ip_address', '10.30.108.132'),
('show.intf.mtu', 1514),
('show.intf.admin_state', 'up'),
]
pgCheckPred = pg.oper_check (
device,
('show_interface_<WORD>', [], {'ifname':'MgmtEth0/0/CPU0/0'}),
attrValPairsToCheck,
refresh_cache=True)
try:
pgCheckPred.assert_test("Parse failed")
log.info("Parse succeeded.")
log.info("Parsing details : {}".format(pg.ext_dictio[device.name]))
except PredicateTestedFalseSignal as e:
log.error(e)
Requesting a Full Parse¶
Finally we have parsergen
generate an object for us that we can ask to
parse values from the show output. In this case, we are asking for all
the regexp tags that contain the text show.intf
to be parsed.
from genie import parsergen as pg
device.connect(alias='myalias')
attrValPairsToParse = [
('show.intf.if_name', 'MgmtEth0/0/CPU0/0'),
]
pgfill = pg.oper_fill (
device=device,
show_command=\
('show_interface_<WORD>', [], {'ifname':'MgmtEth0/0/CPU0/0'}),
attrvalpairs=attrValPairsToParse,
refresh_cache=True, regex_tag_fill_pattern='show\.intf',
device_conn_alias='myalias')
result = pgfill.parse()
if result:
print("Test passed. Result :")
pprint(pg.ext_dictio[device.name]
else:
print("Parse failed. Diagnosis follows:")
print(str(pgfill))
Skip Mode¶
oper_fill
has a skip
option that can make it possible to parse text
that contains optional lines that may or may not be present.
Parsing Output¶
Here is a sample parsing output obtained by running the command from the previous section:
{'show.intf.abort': '0',
'show.intf.admin_state': 'up',
'show.intf.arp_timeout': '04:00:00',
'show.intf.broadcasts': '16015275',
'show.intf.bw': '0',
'show.intf.carrier_delay_up': '10',
'show.intf.carrier_trans': '1',
'show.intf.crc': '0',
'show.intf.drops_unrec_upper_level_proto': '0',
'show.intf.encap': 'ARPA',
'show.intf.frame': '0',
'show.intf.giants': '0',
'show.intf.hardware': 'Management Ethernet',
'show.intf.if_name': 'MgmtEth0/0/CPU0/0',
'show.intf.ignored': '0',
'show.intf.input_bytes': '5613119704',
'show.intf.input_errors': '0',
'show.intf.input_flowcontrol': 'off',
'show.intf.input_pkts': '17890219',
'show.intf.input_rate': '33',
'show.intf.input_rate_bits': '84000',
'show.intf.input_total_drops': '0',
'show.intf.intf_trans': '1',
'show.intf.ip_address': '10.30.108.132',
'show.intf.last_clear_counter': 'never',
'show.intf.last_input': '00:00:00',
'show.intf.last_output': '00:00:48',
'show.intf.line_protocol': 'up',
'show.intf.link_type': 'autonegotiation',
'show.intf.loopback_status': 'not set',
'show.intf.mac_address': '5254.00d6.36c9',
'show.intf.mtu': '1514',
'show.intf.multicasts': '1792005',
'show.intf.output_applique': '0',
'show.intf.output_broadcast': '4',
'show.intf.output_buf_failures': '0',
'show.intf.output_buf_swapped': '0',
'show.intf.output_bytes': '1027241',
'show.intf.output_errors': '0',
'show.intf.output_flowcontrol': 'off',
'show.intf.output_multicast': '0',
'show.intf.output_pkts': '15398',
'show.intf.output_rate': '0',
'show.intf.output_rate_bits': '0',
'show.intf.output_resets': '0',
'show.intf.output_total_drops': '0',
'show.intf.output_underruns': '0',
'show.intf.overrun': '0',
'show.intf.parity': '0',
'show.intf.runts': '0',
'show.intf.throttles': '0'}
Attribute Value Ordering¶
When using oper_fill
or oper_check
, ordering of (key,value) pairs is
extremely important.
Specifying (key,value) pairs in the wrong order can result in parse failure.
If you choose to register using extend_markup
, then subsequent (key,value)
pairs passed to oper_fill
or oper_check
will be auto-ordered for you,
meaning that you may specify them in any order you prefer.
If you choose to register using extend
then you must provide an ordered
list of regexp tags if you want the auto-ordering functionality.
Otherwise, if you register using extend
but do not provide this ordered
list, you must adhere to a strict ordering of (key,value) pairs or face
parse failure.
Output Generated from Marked Up Input¶
Although extend_markup
automates the production of all inputs to the
extend
API, the user still may wish to see this intermediate output for
debugging purposes. This is done by running the
mkpgen
script. For example:
> mkpgen examples/parsergen/pyAts/parsergen_demo_mkpg.py
from genie import parsergen as pgen
show_commands = {
'iosxr': {
'show_interface_<WORD>': 'show interface {ifname}',
},
}
regex = {
'iosxr': {
#
# show_interface_<WORD> ('show interface {ifname}')
#
'show.intf.if_name' : r'([-A-Za-z0-9\._/:]+) is\s+\w+, line protocol is\s+\w+\s+',
'show.intf.admin_state' : r'[-A-Za-z0-9\._/:]+ is\s+(\w+), line protocol is\s+\w+\s+',
'show.intf.line_protocol' : r'[-A-Za-z0-9\._/:]+ is\s+\w+, line protocol is\s+(\w+)\s+',
'show.intf.intf_trans' : r'\s+Interface state transitions:\s+(\d+)',
'show.intf.hardware' : r'\s+Hardware is\s+([^,]+), address is\s+[A-Fa-f0-9:\.]+ \(bia 5254.00d6.36c9\)',
'show.intf.mac_address' : r'\s+Hardware is\s+[^,]+, address is\s+([A-Fa-f0-9:\.]+) \(bia 5254.00d6.36c9\)',
'show.intf.ip_address' : r'\s+Internet address is\s+([A-Fa-f0-9:\.]+)/23',
'show.intf.mtu' : r'\s+MTU\s+(\d+) bytes, BW\s+\d+ Kbit',
'show.intf.bw' : r'\s+MTU\s+\d+ bytes, BW\s+(\d+) Kbit',
'show.intf.encap' : r'\s+Encapsulation\s+(\w+),',
'show.intf.link_type' : r'\s+Duplex unknown, 0Kb/s, unknown, link type is\s+(\w+)',
'show.intf.output_flowcontrol' : r'\s+output flow control is\s+(\w+), input flow control is\s+\w+',
'show.intf.input_flowcontrol' : r'\s+output flow control is\s+\w+, input flow control is\s+(\w+)',
'show.intf.carrier_delay_up' : r'\s+Carrier delay \(up\) is\s+(\d+) msec',
'show.intf.loopback_status' : r'\s+loopback\s+([^,]+),',
'show.intf.last_clear_counter' : r'\s+Last clearing of "show interface" counters\s+([^\r\n]+)',
'show.intf.input_rate_bits' : r'\s+5 minute input rate\s+(\d+) bits/sec,\s+\d+ packets/sec',
'show.intf.input_rate' : r'\s+5 minute input rate\s+\d+ bits/sec,\s+(\d+) packets/sec',
'show.intf.output_rate_bits' : r'\s+5 minute output rate\s+(\d+) bits/sec,\s+\d+ packets/sec',
'show.intf.output_rate' : r'\s+5 minute output rate\s+\d+ bits/sec,\s+(\d+) packets/sec',
'show.intf.input_pkts' : r'\s+(\d+) packets input,\s+\d+ bytes,\s+\d+ total input drops',
'show.intf.input_bytes' : r'\s+\d+ packets input,\s+(\d+) bytes,\s+\d+ total input drops',
'show.intf.input_total_drops' : r'\s+\d+ packets input,\s+\d+ bytes,\s+(\d+) total input drops',
'show.intf.drops_unrec_upper_level_proto' : r'\s+(\d+) drops for unrecognized upper-level protocol',
'show.intf.broadcasts' : r'\s+Received\s+(\d+) broadcast packets,\s+\d+ multicast packets',
'show.intf.multicasts' : r'\s+Received\s+\d+ broadcast packets,\s+(\d+) multicast packets',
'show.intf.runts' : r'\s+(\d+) runts,\s+\d+ giants,\s+\d+ throttles,\s+\d+ parity',
'show.intf.giants' : r'\s+\d+ runts,\s+(\d+) giants,\s+\d+ throttles,\s+\d+ parity',
'show.intf.throttles' : r'\s+\d+ runts,\s+\d+ giants,\s+(\d+) throttles,\s+\d+ parity',
'show.intf.parity' : r'\s+\d+ runts,\s+\d+ giants,\s+\d+ throttles,\s+(\d+) parity',
'show.intf.input_errors' : r'\s+(\d+) input errors,\s+\d+ CRC,\s+\d+ frame,\s+\d+ overrun,\s+\d+ ignored,\s+\d+ abort',
'show.intf.crc' : r'\s+\d+ input errors,\s+(\d+) CRC,\s+\d+ frame,\s+\d+ overrun,\s+\d+ ignored,\s+\d+ abort',
'show.intf.frame' : r'\s+\d+ input errors,\s+\d+ CRC,\s+(\d+) frame,\s+\d+ overrun,\s+\d+ ignored,\s+\d+ abort',
'show.intf.overrun' : r'\s+\d+ input errors,\s+\d+ CRC,\s+\d+ frame,\s+(\d+) overrun,\s+\d+ ignored,\s+\d+ abort',
'show.intf.ignored' : r'\s+\d+ input errors,\s+\d+ CRC,\s+\d+ frame,\s+\d+ overrun,\s+(\d+) ignored,\s+\d+ abort',
'show.intf.abort' : r'\s+\d+ input errors,\s+\d+ CRC,\s+\d+ frame,\s+\d+ overrun,\s+\d+ ignored,\s+(\d+) abort',
'show.intf.output_pkts' : r'\s+(\d+) packets output,\s+\d+ bytes,\s+\d+ total output drops',
'show.intf.output_bytes' : r'\s+\d+ packets output,\s+(\d+) bytes,\s+\d+ total output drops',
'show.intf.output_total_drops' : r'\s+\d+ packets output,\s+\d+ bytes,\s+(\d+) total output drops',
'show.intf.output_broadcast' : r'\s+Output\s+(\d+) broadcast packets,\s+\d+ multicast packets',
'show.intf.output_multicast' : r'\s+Output\s+\d+ broadcast packets,\s+(\d+) multicast packets',
'show.intf.output_errors' : r'\s+(\d+) output errors,\s+\d+ underruns,\s+\d+ applique,\s+\d+ resets',
'show.intf.output_underruns' : r'\s+\d+ output errors,\s+(\d+) underruns,\s+\d+ applique,\s+\d+ resets',
'show.intf.output_applique' : r'\s+\d+ output errors,\s+\d+ underruns,\s+(\d+) applique,\s+\d+ resets',
'show.intf.output_resets' : r'\s+\d+ output errors,\s+\d+ underruns,\s+\d+ applique,\s+(\d+) resets',
'show.intf.output_buf_failures' : r'\s+(\d+) output buffer failures,\s+\d+ output buffers swapped out',
'show.intf.output_buf_swapped' : r'\s+\d+ output buffer failures,\s+(\d+) output buffers swapped out',
'show.intf.carrier_trans' : r'\s+(\d+) carrier transitions',
},
}
regex_tags = {
'iosxr': [
#
# show_interface_<WORD> ('show interface {ifname}')
#
'show.intf.if_name' ,
'show.intf.admin_state' ,
'show.intf.line_protocol' ,
'show.intf.intf_trans' ,
'show.intf.hardware' ,
'show.intf.mac_address' ,
'show.intf.ip_address' ,
'show.intf.mtu' ,
'show.intf.bw' ,
'show.intf.encap' ,
'show.intf.link_type' ,
'show.intf.output_flowcontrol' ,
'show.intf.input_flowcontrol' ,
'show.intf.carrier_delay_up' ,
'show.intf.loopback_status' ,
'show.intf.last_clear_counter' ,
'show.intf.input_rate_bits' ,
'show.intf.input_rate' ,
'show.intf.output_rate_bits' ,
'show.intf.output_rate' ,
'show.intf.input_pkts' ,
'show.intf.input_bytes' ,
'show.intf.input_total_drops' ,
'show.intf.drops_unrec_upper_level_proto' ,
'show.intf.broadcasts' ,
'show.intf.multicasts' ,
'show.intf.runts' ,
'show.intf.giants' ,
'show.intf.throttles' ,
'show.intf.parity' ,
'show.intf.input_errors' ,
'show.intf.crc' ,
'show.intf.frame' ,
'show.intf.overrun' ,
'show.intf.ignored' ,
'show.intf.abort' ,
'show.intf.output_pkts' ,
'show.intf.output_bytes' ,
'show.intf.output_total_drops' ,
'show.intf.output_broadcast' ,
'show.intf.output_multicast' ,
'show.intf.output_errors' ,
'show.intf.output_underruns' ,
'show.intf.output_applique' ,
'show.intf.output_resets' ,
'show.intf.output_buf_failures' ,
'show.intf.output_buf_swapped' ,
'show.intf.carrier_trans' ,
],
}
pgen.extend (regex, show_commands, regex_tags)