Define Views for Junos PyEZ Operational Tables that Parse Unstructured Output
Junos PyEZ operational (op) Tables for unstructured output extract data from the text output of a
CLI command executed on a Junos device or a vty command executed on a given Flexible PIC
Concentrator (FPC). A Table is associated with a View, which is used to access fields
in the Table items and map them to user-defined Python variables. You associate a Table with a
particular View by including the view property in the Table definition, which
takes the View name as its argument.
A View maps your user-defined variables to data in the selected Table items. A View enables you to access specific fields in the output as variables with properties that can be manipulated in Python. Junos PyEZ handles the extraction of the data into Python as well as any type conversion or data normalization. The keys defined in the View must be valid Python variable names.
This topic discusses the different components of the View.
Summary of Parameters in Views for Parsing Unstructured Output
Junos PyEZ Views, like Tables, are formatted using YAML. Views that parse unstructured output can include a number of parameters, which are summarized in Table 1.
View Parameter |
Description |
|---|---|
View Name |
User-defined identifier for the View. |
|
(Optional) List of column titles in the command output. |
|
(Optional) Associative array, or dictionary, of one or
more key-value pairs that map a user-defined key to a string containing
a mathematical expression. For each iteration of the data, the expression
is evaluated using the Python |
|
(Optional) Associative array, or dictionary, of key-value pairs that map a user-defined key to a string. If the string is present in the output, the variable is set to True, otherwise, the variable is set to False. |
|
(Optional) Associative array, or dictionary, of key-value pairs that map a user-defined key to the name of a nested Table that parses a specific section of the command output. |
|
(Optional) List of one or more keys defined under |
|
(Optional) List of regular expressions to match desired content. |
View Name
The View name is a user-defined identifier for the View. You
associate a Table with a particular View by including the view property in the Table definition and providing
the View name as its argument.
The following example defines a View named ChassisFanView, which is referenced by the Table’s view parameter:
---
ChassisFanTable:
command: show chassis fan
key: fan-name
view: ChassisFanView
ChassisFanView:
columns:
fan-name: Item
fan-status: Status
fan-rpm: RPM
fan-measurement: Measurementcolumns
You can use the columns parameter
in a View to extract and parse command output that is formatted in
rows and columns.
Consider the following show ospf neighbor command output:
Address Interface State ID Pri Dead 198.51.100.2 ge-0/0/0.0 Full 192.168.0.2 128 37 198.51.100.6 ge-0/0/1.0 Full 192.168.0.3 128 34
To extract the data, include the columns parameter in the View, and map your Python variable name to the
column name. The application stores the key and the value extracted
from the command output for that column as a key-value pair in the
dictionary for the given item.
The following View extracts the data from each column
in the show ospf neighbor command output:
---
OspfNeighborTable:
command: show ospf neighbor
key: Address
view: OspfNeighborView
OspfNeighborView:
columns:
neighbor_address: Address
interface: Interface
neighbor_state: State
neighbor_id: ID
neighbor_priority: Pri
activity_timer: DeadWhen you retrieve and print the data in the Junos PyEZ application, the data for each neighbor includes the column keys and corresponding data.
from jnpr.junos import Device from jnpr.junos.command.ospf_neighbor import OspfNeighborTable from pprint import pprint import json with Device(host='router1.example.com') as dev: stats = OspfNeighborTable(dev) stats.get() pprint(json.loads(stats.to_json()))
user@host:~$ python3 junos-pyez-ospf-neighbors.py
{'198.51.100.2': {'activity_timer': 39,
'interface': 'ge-0/0/0.0',
'neighbor_address': '198.51.100.2',
'neighbor_id': '192.168.0.2',
'neighbor_priority': 128,
'neighbor_state': 'Full'},
'198.51.100.6': {'activity_timer': 36,
'interface': 'ge-0/0/1.0',
'neighbor_address': '198.51.100.6',
'neighbor_id': '192.168.0.3',
'neighbor_priority': 128,
'neighbor_state': 'Full'}}
To filter the data to include only data from selected columns, include the
filters parameter in the View. For more information, see filters.
Some command output includes column titles that span multiple lines, for example:
FI interrupt statistics
-----------------------
--------------------------------------------------------------------------------
Stream Total RLIM Total Cell timeout Total Reorder Total cell Total number
request PT/MALLOC Ignored cell timeout drops in of times
counter Usemeter errors secure mode entered into
saturation Drops secure mode
--------------------------------------------------------------------------------
36 0 0 1 1 0 0
128 0 0 1 49 0 0
142 0 0 1 53 0 0
--------------------------------------------------------------------------------
...To define a multiline column title in a View, set the column key element equal to a list of the words in each row for that title. The following View defines the columns for the previous command output:
CChipFiStatsTable:
command: show mqss {{ chip_instance }} fi interrupt-stats
target: fpc8
args:
chip_instance: 0
key: Stream
view: CChipFiStatsView
CChipFiStatsView:
columns:
stream: Stream
req_sat:
- Total RLIM
- request
- counter
- saturation
cchip_fi_malloc_drops:
- Total
- PT/MALLOC
- Usemeter
- Drops
cell_timeout_ignored:
- Cell timeout
- Ignored
cchip_fi_cell_timeout:
- Total Reorder
- cell timeout
- errors
drops_in_secure:
- Total cell
- drops in
- secure mode
times_in_secure:
- Total number
- of times
- entered into
- secure modeEval Expression (eval)
You can use the optional eval parameter
to add or modify key-value pairs in the final data returned by the
Table and View. The eval parameter maps
a key name to a string containing a mathematical expression that gets
evaluated by the Python eval function.
You can include the eval parameter in both
Tables and Views, and eval can define and
calculate multiple values.
When you use eval in a Table, it
references the full data dictionary for the calculation, and the key
and calculated value are added as a single additional item to the
dictionary. When you use eval in a View,
the expression is calculated on each iteration of the data, and the
calculated value is added to the data for that iteration. If the eval key name matches a key defined in the View, eval replaces the value of that key with the calculated
value. If the eval key name does not match
a key defined in the View, eval adds the
new key and calculated value to the data.
The eval expression can reference
the data dictionary returned by the View.
The expression must reference data using
a Jinja template variable, so that Junos PyEZ can replace the variable
with the dictionary when the expression is evaluated.
The following example uses eval in the View definition. The cpu entry
modifies the existing value of the cpu field
for each item in the data dictionary, and the max entry creates a new key-value pair for each item in the data dictionary.
---
FPCThread:
command: show threads
target: Null
key: Name
view: FPCThreadView
FPCThreadView:
columns:
pid-pr: PID PR
state: State
name: Name
stack: Stack Use
time: Time (Last/Max/Total)
cpu: cpu
eval:
cpu: "'{{ cpu }}'[:-1]"
max: "('{{ time }}'.split('/'))[1]"Consider the following sample output for the show
threads vty command:
PID PR State Name Stack Use Time (Last/Max/Total) cpu --- -- ------- --------------------- --------- --------------------- 1 H asleep Maintenance 680/32768 0/5/5 ms 0% 2 L running Idle 1452/32768 0/22/565623960 ms 80% 3 H asleep Timer Services 1452/32768 0/7/1966 ms 0% ...
The View’s eval parameter
modifies each cpu entry to omit the percent
sign (%). As a result, the data includes '0' instead of '0%'. In addition,
it adds a new key, max, and its calculated
value for each item.
'Maintenance': {'cpu': '0',
'max': '5',
'name': 'Maintenance',
'pid-pr': '1 H',
'stack': '680/32768',
'state': 'asleep',
'time': '0/5/5 ms'},
'Timer Services': {'cpu': '0',
'max': '7',
'name': 'Timer Services',
'pid-pr': '3 H',
'stack': '1452/32768',
'state': 'asleep',
'time': '0/7/1966 ms'},
...For examples that use eval in the Table definition, see Eval Expression (eval).
exists
You can use the optional exists parameter
in a View to indicate if a string is present in the command output. exists is a dictionary of key-value pairs that map
a user-defined Python variable name to the string to match in the
command output. If the string is present in the output, the variable
is set to True. Otherwise, the variable
is set to False.
Consider the show host_loopback status-summary vty command output.
SENT: Ukern command: show host_loopback status-summary Host Loopback Toolkit Status Summary: No detected wedges No toolkit errors
The following Table defines exists to test if the command output includes a No detected
wedges string or a No toolkit errors string:
---
HostlbStatusSummaryTable:
command: show host_loopback status-summary
target: fpc1
view: HostlbStatusSummaryView
HostlbStatusSummaryView:
exists:
no_detected_wedges: No detected wedges
no_toolkit_errors: No toolkit errorsWhen you use the Table and View to test for the strings
and print the resulting values in your Junos PyEZ application, both
variables are set to True in this case.
{'no_detected_wedges': True, 'no_toolkit_errors': True}fields
Command output can be lengthy and complex, and you might need
different logic to parse different sections of the output. In some
cases, you cannot adequately parse the command output using a single
Table and View. To parse this type of output, you can include the
optional fields parameter in the View. fields is a dictionary of key-value pairs that map
a user-defined key to the name of a nested Table that selects a specific
section of the command output. Each nested Table can reference its
own View, which is used to parse the data selected by that Table.
Consider the show xmchip 0 pt stats vty command
output, which has two different sections of data:
SENT: Ukern command: show xmchip 0 pt stats WAN PT statistics (Index 0) --------------------------- PCT entries used by all WI-1 streams : 0 PCT entries used by all WI-0 streams : 0 PCT entries used by all LI streams : 0 CPT entries used by all multicast packets : 0 CPT entries used by all WI-1 streams : 0 CPT entries used by all WI-0 streams : 0 CPT entries used by all LI streams : 0 Fabric PT statistics (Index 1) ------------------------------ PCT entries used by all FI streams : 0 PCT entries used by all WI (Unused) streams : 0 PCT entries used by all LI streams : 0 CPT entries used by all multicast packets : 0 CPT entries used by all FI streams : 0 CPT entries used by all WI (Unused) streams : 0 CPT entries used by all LI streams : 0
The following XMChipStatsView View uses the fields parameter to define
two additional Tables, which are used to parse the two different sections
of the command output. The _WANPTStatTable and _FabricPTStatTable Tables extract
the data from the WAN PT statistics and the Fabric PT statistics sections,
respectively. In this case, the Tables use the delimiter parameter to extract and split the data, so they do not need to
reference a separate View.
XMChipStatsTable:
command: show xmchip 0 pt stats
target: fpc1
view: XMChipStatsView
XMChipStatsView:
fields:
wan_pt_stats: _WANPTStatTable
fabric_pt_stats: _FabricPTStatTable
_WANPTStatTable:
title: WAN PT statistics (Index 0)
delimiter: ":"
_FabricPTStatTable:
title: Fabric PT statistics (Index 1)
delimiter: ":"
When you retrieve and print the data in the Junos PyEZ
application, each key defined under fields contains the data selected and parsed by the corresponding Table.
{'fabric_pt_stats': {'CPT entries used by all FI streams': 0,
'CPT entries used by all LI streams': 0,
'CPT entries used by all WI (Unused) streams': 0,
'CPT entries used by all multicast packets': 0,
'PCT entries used by all FI streams': 0,
'PCT entries used by all LI streams': 0,
'PCT entries used by all WI (Unused) streams': 0},
'wan_pt_stats': {'CPT entries used by all LI streams': 0,
'CPT entries used by all WI-0 streams': 0,
'CPT entries used by all WI-1 streams': 0,
'CPT entries used by all multicast packets': 0,
'PCT entries used by all LI streams': 0,
'PCT entries used by all WI-0 streams': 0,
'PCT entries used by all WI-1 streams': 0}}
As another example, consider the show ttp statistics vty command output:
TTP Statistics:
Receive Transmit
---------- ----------
L2 Packets 4292 1093544
L3 Packets 542638 0
Drops 0 0
Netwk Fail 0 0
Queue Drops 0 0
Unknown 0 0
Coalesce 0 0
Coalesce Fail 0 0
TTP Transmit Statistics:
Queue 0 Queue 1 Queue 2 Queue 3
---------- ---------- ---------- ----------
L2 Packets 1093544 0 0 0
L3 Packets 0 0 0 0
TTP Receive Statistics:
Control High Medium Low Discard
---------- ---------- ---------- ---------- ----------
L2 Packets 0 0 4292 0 0
L3 Packets 0 539172 3466 0 0
Drops 0 0 0 0 0
Queue Drops 0 0 0 0 0
Unknown 0 0 0 0 0
Coalesce 0 0 0 0 0
Coalesce Fail 0 0 0 0 0
TTP Receive Queue Sizes:
Control Plane : 0 (max is 4473)
High : 0 (max is 4473)
Medium : 0 (max is 4473)
Low : 0 (max is 2236)
TTP Transmit Queue Size: 0 (max is 6710)
The FPCTTPStatsView View uses
the fields parameter to reference multiple
nested Tables, which extract the data in the different sections of
the output. Each Table references its own View or uses the delimiter parameter to parse the data in that section.
---
FPCTTPStatsTable:
command: show ttp statistics
target: fpc2
view: FPCTTPStatsView
FPCTTPStatsView:
fields:
TTPStatistics: _FPCTTPStatisticsTable
TTPTransmitStatistics: _FPCTTPTransmitStatisticsTable
TTPReceiveStatistics: _FPCTTPReceiveStatisticsTable
TTPQueueSizes: _FPCTTPQueueSizesTable
_FPCTTPStatisticsTable:
title: TTP Statistics
view: _FPCTTPStatisticsView
_FPCTTPStatisticsView:
columns:
rcvd: Receive
tras: Transmit
_FPCTTPTransmitStatisticsTable:
title: TTP Transmit Statistics
view: _FPCTTPTransmitStatisticsView
_FPCTTPTransmitStatisticsView:
columns:
queue0: Queue 0
queue1: Queue 1
queue2: Queue 2
queue3: Queue 3
filters:
- queue2
_FPCTTPReceiveStatisticsTable:
title: TTP Receive Statistics
key: name
key_items:
- Coalesce
view: _FPCTTPReceiveStatisticsView
_FPCTTPReceiveStatisticsView:
columns:
control: Control
high: High
medium: Medium
low: Low
discard: Discard
_FPCTTPQueueSizesTable:
title: TTP Receive Queue Sizes
delimiter: ":"
When you retrieve and print the data in the Junos PyEZ
application, each fields key contains the
data that was extracted and parsed by the corresponding Table.
{'TTPQueueSizes': {'Control Plane': '0 (max is 4473)',
'High': '0 (max is 4473)',
'Low': '0 (max is 2236)',
'Medium': '0 (max is 4473)'},
'TTPReceiveStatistics': {'Coalesce': {'control': 0,
'discard': 0,
'high': 0,
'low': 0,
'medium': 0,
'name': 'Coalesce'}},
'TTPStatistics': {'Coalesce': {'name': 'Coalesce', 'rcvd': 0, 'tras': 0},
'Coalesce Fail': {'name': 'Coalesce Fail',
'rcvd': 0,
'tras': 0},
'Drops': {'name': 'Drops', 'rcvd': 0, 'tras': 0},
'L2 Packets': {'name': 'L2 Packets',
'rcvd': 4292,
'tras': 1093544},
'L3 Packets': {'name': 'L3 Packets',
'rcvd': 542638,
'tras': 0},
'Netwk Fail': {'name': 'Netwk Fail',
'rcvd': 0,
'tras': 173},
'Queue Drops': {'name': 'Queue Drops',
'rcvd': 0,
'tras': 0},
'Unknown': {'name': 'Unknown', 'rcvd': 0, 'tras': 0}},
'TTPTransmitStatistics': {'L2 Packets': {'queue2': 0},
'L3 Packets': {'queue2': 0}}}
filters
The columns parameter extracts data from command output that is formatted in
rows and columns. When you include the columns parameter in a View, you
can optionally include the filters parameter to filter which column data
is included in the final output. The filters parameter defines a list of
one or more keys defined under columns. The final set of data includes
only data from the selected columns. You can provide default filters in the View
definition, and you can also define or override filter values in the Junos PyEZ
application.
Consider the show ospf neighbor command output:
Address Interface State ID Pri Dead 198.51.100.2 ge-0/0/0.0 Full 192.168.0.2 128 37 198.51.100.6 ge-0/0/1.0 Full 192.168.0.3 128 34
In the following View, the columns parameter defines keys for all of the columns in the corresponding
command output, but the filters parameter
only includes the data from the Address and State columns in the data dictionary.
---
OspfNeighborFiltersTable:
command: show ospf neighbor
key: Address
view: OspfNeighborFiltersView
OspfNeighborFiltersView:
columns:
neighbor_address: Address
interface: Interface
neighbor_state: State
neighbor_id: ID
neighbor_priority: Pri
activity_timer: Dead
filters:
- neighbor_address
- neighbor_state
The following Junos PyEZ code first calls get() without any arguments, which retrieves the data
using the default list of filters defined in the View. The second
call to get() includes the filters argument, which overrides the filter list defined
in the View.
from jnpr.junos import Device from Tables.show_ospf_neighbor_filter import OspfNeighborFiltersTable from pprint import pprint import json with Device(host='router1.example.com') as dev: stats = OspfNeighborFiltersTable(dev) stats.get() pprint(json.loads(stats.to_json())) print('\n') stats.get(filters=['neighbor_address', 'neighbor_id', 'neighbor_state']) pprint(json.loads(stats.to_json()))
When you execute the application, the first call to get() uses the filters defined in the View, and the
second call uses the filters defined in the call, which override those
defined in the View.
user@host:~$ python3 junos-pyez-ospf-filters.py
{'198.51.100.2': {'neighbor_address': '198.51.100.2', 'neighbor_state': 'Full'},
'198.51.100.6': {'neighbor_address': '198.51.100.6', 'neighbor_state': 'Full'}}
{'198.51.100.2': {'neighbor_address': '198.51.100.2',
'neighbor_id': '192.168.0.2',
'neighbor_state': 'Full'},
'198.51.100.6': {'neighbor_address': '198.51.100.6',
'neighbor_id': '192.168.0.3',
'neighbor_state': 'Full'}}
regex
You can use the optional regex parameter
in a View to match and extract specific fields in the command output. regex is a dictionary that maps keys to regular expressions.
If the corresponding Table does not define item: '*', Junos PyEZ combines the regular expressions and matches the result
against each line of output. However, if the Table defines item: '*' to extract the data as a single text string,
Junos PyEZ instead matches each individual regular expression against
the entire text string.
The capturing group defined in the regular expression determines
the data that is extracted from the field and stored in the data dictionary.
If you define a capturing group, only the value for that group is
stored in the data. Otherwise, Junos PyEZ stores the value that matches
the full expression. For example, (d+.d+) retrieves and stores a float value from the string search expression,
whereas (d+).d+ only stores the integer
portion of the data. If you define multiple groups, only the value
for the first group is stored.
Junos PyEZ leverages the pyparsing module to define a number of built-in keywords that you can use
in place of a regular expression. Table 2 lists
the keyword, a brief description, and the corresponding expression,
where pp is derived from import
pyparsing as pp.
Keyword |
Description |
Expression |
|---|---|---|
|
Word containing only hexadecimal characters |
hex_numbers = pp.OneOrMore(pp.Word(pp.nums, min=1))
& pp.OneOrMore(pp.Word('abcdefABCDEF', min=1)) |
|
Word consisting of an integer or float value |
numbers = (pp.Word(pp.nums) + pp.Optional(pp.Literal('.') +
pp.Word(pp.nums))).setParseAction(lambda i: ''.join(i)) |
|
Word composed of digits and a trailing percentage sign (%) |
percentage = pp.Word(pp.nums) + pp.Literal('%') |
|
One or more words composed of printable characters (any non-whitespace characters) |
printables = pp.OneOrMore(pp.Word(pp.printables)) |
|
Word composed of alpha or alphanumeric characters |
word = pp.Word(pp.alphanums) | pp.Word(pp.alphas) |
|
One or more |
words = (pp.OneOrMore(word)).setParseAction(lambda i: ' '.join(i)) |
Consider the following show icmp statistics command output. Each section of output is under a specific section
title, and the data consists of a number and one or more words.
ICMP Statistics:
0 requests
0 throttled
0 network unreachables
0 ttl expired
0 redirects
0 mtu exceeded
0 source route denials
0 filter prohibited
0 other unreachables
0 parameter problems
0 ttl captured
0 icmp/option handoffs
0 igmp v1 handoffs
0 tag te requests
0 tag te to RE
ICMP Errors:
0 unknown unreachables
0 unsupported ICMP type
0 unprocessed redirects
0 invalid ICMP type
0 invalid protocol
0 bad input interface
0 bad route lookup
0 bad nh lookup
0 bad cf mtu
0 runts
ICMP Discards:
0 multicasts
0 bad source addresses
0 bad dest addresses
0 IP fragments
0 ICMP errors
0 unknown originators
ICMP Debug Messages:
0 throttled
ICMP Rate Limit Settings:
500 pps per iff
1000 pps total
To parse the previous output, the main View defines fields, which references nested Tables and Views that
parse each section of output. The nested Views define the regex parameter to match against the data extracted
by the corresponding Table.
---
ICMPStatsTable:
command: show icmp statistics
target: fpc1
view: ICMPStatsView
ICMPStatsView:
fields:
discards: _ICMPDiscardsTable
errors: _ICMPErrorsTable
rate: _ICMPRateTable
_ICMPDiscardsTable:
title: ICMP Discards
key: name
view: _ICMPDiscardsView
_ICMPDiscardsView:
regex:
value: \d+
name: '(\w+(\s\w+)*)'
_ICMPErrorsTable:
title: ICMP Errors
key: name
view: _ICMPErrorsView
_ICMPErrorsView:
regex:
error: numbers
name: words
_ICMPRateTable:
title: ICMP Rate Limit Settings
key: name
view: _ICMPRateView
_ICMPRateView:
regex:
rate: numbers
name: words
For example, the _ICMPDiscardsTable Table selects the data under the ICMP Discards section in the command output. The _ICMPDiscardsView View defines two keys, value and name, which map to regular expressions. value matches one or more digits, and name matches one or more words. Because the Table does
not define item: '*', the regular expressions
are combined and matched against each line of data in that section.
_ICMPDiscardsTable:
title: ICMP Discards
key: name
view: _ICMPDiscardsView
_ICMPDiscardsView:
regex:
value: \d+
name: '(\w+(\s\w+)*)'
The _ICMPErrorsTable Table
selects the data under the ICMP Errors section in the command output. The _ICMPErrorsView View defines the error and name keys and uses the built-in keywords numbers and words in place
of explicitly defining regular expressions.
_ICMPErrorsTable:
title: ICMP Errors
key: name
view: _ICMPErrorsView
_ICMPErrorsView:
regex:
error: numbers
name: words
If the Table defines item: '*', the
extracted data is considered to be one text string. In this case,
each regular expression in the corresponding view matches against
the entire string.
Consider the show ithrottle id 0 command
output.
SENT: Ukern command: show ithrottle id 0
ID Usage % Cfg State Oper State Name
-- ------- --------- ---------- --------
0 50.0 1 1 TOE ithrottle
Throttle Times: In hptime ticks In ms
--------------- ------
Timer Interval 333333 5.000
Allowed time 166666 2.500
Allowed excess 8333 0.125
Start time 488655082 n/a
Run time this interval 0 0.000
Deficit 0 0.000
Run time max 17712 0.266
Run time total 144154525761 2162317
Min Usage Perc: 25.0
Max Usage Perc: 50.0
AdjustUsageEnable: 1
Throttle Stats:
Starts : 65708652
Stops : 65708652
Checks : 124149442
Enables : 0
Disables : 0
AdjUp : 6
AdjDown : 4
The following Table uses item: '*' to extract the data as a single string. The View’s regex parameter defines three regular expressions.
Each regex pattern is matched against the entire string. Because the
regular expressions define capturing groups, Junos PyEZ only stores
the data that matches the group.
IthrottleIDTable:
command: show ithrottle id {{ id }}
args:
id: 0
item: '*'
target: fpc1
view: IthrottleIDView
IthrottleIDView:
regex:
min_usage: 'Min Usage Perc: (\d+\.\d+)'
max_usage: 'Max Usage Perc: (\d+\.\d+)'
usg_enable: 'AdjustUsageEnable: (\d)'
fields:
throttle_stats: _ThrottleStatsTable
_ThrottleStatsTable:
title: Throttle Stats
delimiter: ":"
When you retrieve and print the data in the Junos PyEZ application,
the data includes the three regex items,
which contain the value matched by the capturing group for that expression.
{'max_usage': 50.0,
'min_usage': 25.0,
'throttle_stats': {'AdjDown': 4,
'AdjUp': 6,
'Checks': 124149442,
'Disables': 0,
'Enables': 0,
'Starts': 65708652,
'Stops': 65708652},
'usg_enable': 1}