Command Parser Reference¶
This guide covers how to configure Command Parsers -- the objects that tell the app how to collect and extract data from your network devices. A Command Parser defines three things:
- Parser -- Which parsing library to use (TEXTFSM, NAPALM, TTP, or JSON)
- Command / NAPALM Getter -- What to run on the device
- Path -- A JMESPath expression that extracts the specific data you want to compare
Each Command Parser is tied to a single Validation Rule and a single Platform. If you have the same rule for multiple platforms (e.g., Cisco IOS and Arista EOS), you need a separate Command Parser for each.
Parser Types¶
The Parser field on the Command Parser form determines how the app collects and structures device output.
TEXTFSM¶
Uses TextFSM templates to parse raw CLI output into structured data. This is the most common parser type for CLI-based devices.
- Command field: The CLI command to run (e.g.,
show interfaces,show ip bgp summary) - NAPALM Getter field: Leave blank
- Template source: The app uses ntc-templates for pre-built templates, or you can provide custom templates via a Git Repository in Nautobot (see Datasource Reference)
Tip
Use the long form of commands (e.g., show interfaces not sh int) to ensure the best chance of matching a parser template.
NAPALM¶
Uses NAPALM getters to retrieve structured data directly from devices. NAPALM provides a consistent, vendor-neutral API -- the same getter works across different platforms.
- Command field: Leave blank
- NAPALM Getter field: Select a getter from the dropdown (e.g.,
get_facts,get_interfaces,get_bgp_neighbors)
Common NAPALM getters:
| Getter | Returns |
|---|---|
get_facts |
Device hostname, model, vendor, OS version, serial number, uptime |
get_interfaces |
Interface names, status, speed, MAC address, description |
get_bgp_neighbors |
BGP peer addresses, state, received prefixes |
get_lldp_neighbors |
LLDP neighbor details per interface |
get_ntp_servers |
Configured NTP servers |
TTP¶
Uses Template Text Parser as an alternative to TextFSM. TTP templates must be provided via a Git Repository in Nautobot (see Datasource Reference).
- Command field: The CLI command to run
- NAPALM Getter field: Leave blank
- Template source: Custom TTP templates from a Git Repository
JSON¶
For devices or commands that return JSON output natively (e.g., show version | json on Arista EOS or NX-OS).
- Command field: The CLI command to run (including the JSON pipe if needed)
- NAPALM Getter field: Leave blank
- Template source: None needed -- the output is parsed directly as JSON
Note
The app converts floating-point numbers to strings to avoid rounding errors that can occur with some database backends when comparing numerical values.
JMESPath Expressions (Path Field)¶
The Path field on the Command Parser form uses JMESPath to extract specific data from the collected output. This determines exactly what data gets stored and compared.
If you set the Path to *, the entire output is captured. For most use cases, you'll want to narrow it down to just the fields that matter.
Basic Expressions¶
Wildcard (*)¶
Returns all values at the current level. Use this when you want the complete output.
// Input (from NAPALM get_facts)
{
"fqdn": "router1.example.com",
"model": "C8000V",
"os_version": "17.12.2",
"serial_number": "ABC123"
}
// Path: *
// Result: ["router1.example.com", "C8000V", "17.12.2", "ABC123"]
Key Selection¶
Extract a specific field by name. Use dot notation to drill into nested objects.
// Input (from NAPALM get_facts)
{
"fqdn": "router1.example.com",
"model": "C8000V",
"os_version": "17.12.2"
}
// Path: os_version
// Result: "17.12.2"
Array Wildcard ([*])¶
Process all elements in an array. This is common with TextFSM output, which typically returns a list of dictionaries.
// Input (from TextFSM parsing "show interfaces")
[
{"interface": "GigabitEthernet1", "link_status": "up", "protocol_status": "up"},
{"interface": "GigabitEthernet2", "link_status": "down", "protocol_status": "down"}
]
// Path: [*].interface
// Result: ["GigabitEthernet1", "GigabitEthernet2"]
Projections¶
Create new arrays by selecting specific fields from each element. This lets you control exactly which fields are compared.
// Input (from TextFSM parsing "show interfaces")
[
{"interface": "GigabitEthernet1", "link_status": "up", "protocol_status": "up", "bandwidth": "1000000"},
{"interface": "GigabitEthernet2", "link_status": "down", "protocol_status": "down", "bandwidth": "1000000"}
]
// Path: [*].[interface, link_status, protocol_status]
// Result: [["GigabitEthernet1", "up", "up"], ["GigabitEthernet2", "down", "down"]]
This extracts only the interface name and status fields, ignoring bandwidth and other fields that might change frequently.
Filters¶
Include only elements that match a condition, using the ? syntax.
// Input (from TextFSM parsing "show interfaces")
[
{"interface": "GigabitEthernet1", "link_status": "up", "protocol_status": "up"},
{"interface": "GigabitEthernet2", "link_status": "down", "protocol_status": "down"},
{"interface": "Loopback0", "link_status": "up", "protocol_status": "up"}
]
// Path: [?starts_with(interface, 'Loopback')].[interface, link_status, protocol_status]
// Result: [["Loopback0", "up", "up"]]
This filters for only Loopback interfaces, then extracts their name and status.
Functions¶
JMESPath includes built-in functions for transforming and aggregating data:
| Function | What It Does | Example |
|---|---|---|
length() |
Count items | length(interfaces[?status == 'up']) |
starts_with() |
Filter by prefix | [?starts_with(name, 'Loopback')] |
contains() |
Filter by substring | [?contains(description, 'WAN')] |
sum() |
Add numeric values | sum([*].prefix_count) |
max() / min() |
Find extremes | max([*].uptime) |
Reference Key Anchoring ($field$)¶
The Operational Compliance app extends JMESPath with reference key anchoring using $ symbols. This tells the comparison engine which field to use as an identifier when generating diffs.
Without reference keys, the diff output uses generic index positions (e.g., index_element[1][1]), which are hard to interpret. With reference keys, the diff uses meaningful identifiers (e.g., 10.1.1.1).
// Input (from TextFSM parsing "show ip bgp summary")
[
{"neighbor": "10.1.1.1", "state_pfxrcd": "150", "as": "65001"},
{"neighbor": "10.1.1.2", "state_pfxrcd": "75", "as": "65002"}
]
// Without reference key:
// Path: [*].[neighbor, state_pfxrcd]
// Diff would show: index_element[0][1] changed from "150" to "145"
// With reference key:
// Path: [*].[$neighbor$, state_pfxrcd]
// Diff would show: 10.1.1.1 state_pfxrcd changed from "150" to "145"
Tip
Always use reference keys ($field$) when your data is a list of objects with a natural identifier (like interface name, neighbor address, or hostname). This makes diff output much easier to read.
Complex Example: BGP Peers with VRF Structure¶
// Input
{
"result": [
{
"vrfs": {
"default": {
"peerList": [
{"peerAddress": "10.1.1.1", "state": "Established", "asn": "65001"},
{"peerAddress": "10.1.1.2", "state": "Idle", "asn": "65002"}
]
}
}
}
]
}
// Path: result[0].vrfs.default.peerList[*].[$peerAddress$, state]
// Result: [["10.1.1.1", "Established"], ["10.1.1.2", "Idle"]]
Exclude Field¶
The Exclude field on the Command Parser form lets you exclude specific keys from the comparison. This is only available for EXACT_MATCH rules when the Path is set to * (raw mode).
Use this when certain fields change frequently but aren't relevant to your compliance check (e.g., interface statistics, counters, timestamps).
// Input
{
"GigabitEthernet1": {
"status": "up",
"speed": "1000",
"interfaceStatistics": {
"inBitsRate": 3582.5,
"outBitsRate": 17327.6
}
}
}
// Exclude: ["interfaceStatistics"]
// Compared data:
{
"GigabitEthernet1": {
"status": "up",
"speed": "1000"
}
}
Tips¶
- Test JMESPath expressions before deploying: Use the JMESPath online tester with sample device output
- Use reference keys (
$field$) for meaningful diffs on list data - Be specific with your Path -- extract only what you need to compare
- Use long-form commands for the best chance of matching parser templates
- One parser per platform -- if you need to check the same rule on IOS and EOS, create two Command Parsers with platform-appropriate commands
Resources¶
- JMESPath Official Documentation
- JMESPath Tutorial
- jdiff Documentation (underlying comparison engine)
- ntc-templates (TextFSM templates)