A modular, extensible Python library for converting gNMI JSON responses into human-readable SONiC CLI output format.
- Version: 2.7.0
- Author: SONiC Team
- License: Apache-2.0 (See the SONiC project license)
- Last Updated: February 2026
- Status: Production-Ready ✅
- Overview
- Installation
- Quick Start
- Architecture
- Unified Command Parser
- Command Reference
- Error Handling
- Testing
- API Reference
This library provides a clean, extensible framework for transforming gNMI JSON responses into the human-readable format used by SONiC CLI show commands. It supports 78 commands across 21 command categories.
- Unified Command Parser: Single entry point for parsing CLI commands
- Consistent Naming: All commands follow
show <command> [subcommand] [optional_arg|<required_arg>] [options] - Modular Design: Easy to extend with new commands
- Production-Ready: 100% pass rate verified against 704 real device test cases across 6 devices
- Self-Contained: Only requires Python 3.8+ and
tabulatelibrary - Comprehensive Testing: Unit + integration tests under
python/tests/
The library is self-contained within the gnmi_cli_lib/ directory:
# Option 1: Install from source directory
cd /path/to/gnmi_cli_lib
pip install -e .
# Option 2: Install dependencies only (for development)
pip install -r requirements.txt
# Option 3: Minimal install (just the required dependency)
pip install tabulate
# Verify installation
python3 -c "from gnmi_cli_lib import CommandParser; print('OK')"| Dependency | Version | Purpose |
|---|---|---|
| Python | 3.8+ | Runtime |
| tabulate | >=0.9.0 | Table formatting (required) |
| pytest | >=7.0.0 | Testing (optional, dev only) |
The library has no external dependencies except tabulate. All functionality is implemented using:
- Python standard library modules:
json,re,typing,dataclasses,abc,datetime,ipaddress - Single external package:
tabulatefor table formatting
The CommandParser class provides a single, consistent entry point for all CLI command parsing and conversion.
from gnmi_cli_lib import CommandParser, parse_and_convert
# Create a parser instance
parser = CommandParser()
# Parse and convert with a CLI command string
json_data = {"Ethernet0": {"admin_status": "up", "oper_status": "up", "speed": "100G"}}
output = parser.parse_and_convert("show interfaces status", json_data)
print(output)
# Or use the convenience function
output = parse_and_convert("show interfaces status", json_data)
print(output)| Category | File | Description |
|---|---|---|
| Core | parser.py | Unified CommandParser (single entry point) |
| registry.py | CommandRegistry (command configurations) | |
| commands.py | Command registrations (78 CLI mappings) | |
| cli.py | Command-line interface | |
| Types | common/types.py | CommandConfig, CliMapping dataclasses |
| common/constants.py | Column orderings, field mappings | |
| Utils | utils/sorting.py | Natural sorting (Ethernet0, Ethernet4, ...) |
| utils/parsing.py | JSON and string parsing utilities | |
| Formatters | formatters/base.py | Abstract OutputFormatter, TableFormatter |
| formatters/interface.py | Interface status/counters formatters | |
| formatters/transceiver.py | Transceiver EEPROM/status formatters | |
| formatters/ipv6.py | IPv6 BGP/routing formatters | |
| formatters/system.py | Version, uptime, memory formatters | |
| formatters/network.py | LLDP, VLAN, MAC, NDP formatters | |
| formatters/queue.py | Queue counter formatters | |
| formatters/dropcounters.py | Drop counter formatters | |
| formatters/watermark.py | Watermark formatters | |
| Testing | ../tests/unit/ | Unit test suite |
| ../tests/fixtures/sample_data.py | Test data fixtures | |
| ../tests/integration/ | Integration verification suite | |
| ../tests/reports/ALL_COMMANDS_ANALYSIS.md | Auto-generated analysis report | |
| Config | pyproject.toml | Package configuration |
| requirements.txt | Dependencies |
gnmi_cli_lib/
├── __init__.py # Public API exports
├── parser.py # Unified CommandParser (single entry point)
├── registry.py # CommandRegistry (command configurations)
├── commands.py # Command registrations (78 CLI mappings)
├── cli.py # Command-line interface
├── pyproject.toml # Package configuration
├── requirements.txt # Dependencies
├── .gitignore # Git ignore patterns
├── common/
│ ├── types.py # CommandConfig, CliMapping dataclasses
│ └── constants.py # Column orderings, field mappings
├── utils/
│ ├── sorting.py # Natural sorting (Ethernet0, Ethernet4, ...)
│ └── parsing.py # JSON and string parsing utilities
├── formatters/
│ ├── base.py # Abstract OutputFormatter, TableFormatter
│ ├── interface.py # Interface status/counters formatters
│ ├── transceiver.py # Transceiver EEPROM/status formatters
│ ├── ipv6.py # IPv6 BGP/routing formatters
│ ├── system.py # Version, uptime, memory formatters
│ ├── network.py # LLDP, VLAN, MAC, NDP formatters
│ ├── queue.py # Queue counter formatters
│ ├── dropcounters.py # Drop counter formatters
│ └── watermark.py # Watermark formatters
└── ../tests/ # Tests live under python/tests (unit + integration + reports)
The CommandParser class provides a single, consistent entry point for all CLI command parsing and conversion.
All commands follow the format:
show <command> <subcommand> [optional_argument|<required_argument>]
[optional_argument]- Square brackets indicate optional<required_argument>- Angle brackets indicate required
from gnmi_cli_lib import CommandParser
parser = CommandParser()
# Parse a command to get its components
parsed = parser.parse("show interfaces status Ethernet0")
print(parsed.command) # "interfaces"
print(parsed.subcommand) # "status"
print(parsed.argument) # "Ethernet0"
print(parsed.is_valid) # True
# Parse and convert in one step
output = parser.parse_and_convert("show buffer_pool watermark", json_data)
# Get help for a command
help_text = parser.get_command_help("interfaces", "status")
print(help_text) # "show interfaces status [interface_name]"
# List all supported commands
commands = parser.list_commands()
for cmd, subcommands in commands.items():
print(f"{cmd}: {subcommands}")All commands use consistent naming:
show <command> <subcommand> [optional_arg]for commands with optional argumentsshow <command> <subcommand> <required_arg>for commands with required arguments
Shows buffer pool watermarks.
Input JSON:
{
"egress_lossless_pool": {"Bytes": "2057328"},
"egress_lossy_pool": {"Bytes": "2056704"},
"ingress_lossless_pool": {"Bytes": "0"}
}Expected Output:
Shared pool maximum occupancy:
Pool Bytes
-------------------- -------
egress_lossless_pool 2057328
egress_lossy_pool 2056704
ingress_lossless_pool 0
Usage:
from gnmi_cli_lib import CommandParser, parse_and_convert
# Method 1: CommandParser class (recommended)
parser = CommandParser()
output = parser.parse_and_convert("show buffer_pool watermark", data)
# Method 2: Convenience function
output = parse_and_convert("show buffer_pool watermark", data)Shows persistent buffer pool watermarks.
Input JSON:
{
"egress_lossless_pool": {"Bytes": "3057328"},
"egress_lossy_pool": {"Bytes": "3056704"}
}Usage:
from gnmi_cli_lib import parse_and_convert
# Using parse_and_convert
output = parse_and_convert("show buffer_pool persistent-watermark", data)Shows headroom pool watermarks.
Note: Uses hyphen (
headroom-pool) as per consistent naming convention.
Input JSON:
{
"ingress_lossless_pool": {"Bytes": "1024000"}
}Expected Output:
Headroom pool maximum occupancy:
Pool Bytes
-------------------- ------
ingress_lossless_pool 1024000
Usage:
from gnmi_cli_lib import parse_and_convert
# Using unified parser
output = parse_and_convert("show headroom-pool watermark", data)Shows persistent headroom pool watermarks.
Usage:
output = parse_and_convert("show headroom-pool persistent-watermark", data)Shows current date and time.
Input JSON:
{
"date": "Mon 20 Jan 2025 07:42:51 UTC"
}Expected Output:
Mon 20 Jan 2025 07:42:51 UTC
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show clock", data)Shows available timezones.
Input JSON:
{
"timezones": ["Africa/Abidjan", "Africa/Accra", "America/New_York", "Asia/Tokyo", "Europe/London", "UTC"]
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show clock timezones", data)Shows drop counter capabilities by type.
Input JSON:
{
"PORT_INGRESS_DROPS": ["L2_ANY", "SMAC_MULTICAST", "INGRESS_VLAN_FILTER"],
"SWITCH_EGRESS_DROPS": ["L3_EGRESS_LINK_DOWN"]
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show dropcounters capabilities", data)Shows drop counter configuration.
Input JSON:
{
"DEBUG_0": {
"alias": "PORT_DROPS",
"group": "DEBUG",
"type": "PORT_INGRESS_DROPS",
"reason": "L2_ANY,SMAC_MULTICAST",
"description": "Port ingress drops"
}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show dropcounters configuration", data)Shows port drop counter values.
Input JSON:
{
"Ethernet0": {"DEBUG_0": "100", "DEBUG_1": "0"},
"Ethernet4": {"DEBUG_0": "50", "DEBUG_1": "25"}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show dropcounters counts", data)Shows interface aliases.
Optional argument: interface_name to show specific interface.
Input JSON:
{
"Ethernet0": {"alias": "fortyGigE0/0"},
"Ethernet4": {"alias": "fortyGigE0/4"}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces alias", data)
output = parse_and_convert("show interfaces alias Ethernet0", data)Shows interface counters.
Optional argument: interface_name to show specific interface.
Input JSON:
{
"Ethernet0": {
"SAI_PORT_STAT_IF_IN_UCAST_PKTS": "1000000",
"SAI_PORT_STAT_IF_OUT_UCAST_PKTS": "2000000",
"SAI_PORT_STAT_IF_IN_ERRORS": "0",
"SAI_PORT_STAT_IF_OUT_ERRORS": "0"
}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters", data)
output = parse_and_convert("show interfaces counters Ethernet0", data)Shows detailed interface counters for a specific interface.
Required argument: interface_name
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters detailed Ethernet0", data)Shows interface counter errors.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters errors", data)Shows FEC histogram counters.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters fec-histogram", data)Shows FEC statistics.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters fec-stats", data)Shows interface counter rates.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters rates", data)Shows RIF (Router Interface) counters.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters rif", data)Shows interface trim counters.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces counters trim", data)Shows interface descriptions.
Input JSON:
{
"Ethernet0": {"admin_status": "up", "description": "Server1", "oper_status": "up"},
"Ethernet4": {"admin_status": "up", "description": "Server2", "oper_status": "down"}
}Expected Output:
Interface Oper Admin Alias Description
----------- ------ ------- ------- -------------
Ethernet0 up up Server1
Ethernet4 down up Server2
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces description", data)Shows interface errors.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces errors", data)Shows FEC status.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces fec-status", data)Shows interface flap counts.
Input JSON:
{
"Ethernet0": {"flap_count": "5"},
"Ethernet4": {"flap_count": "0"}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces flap", data)Shows interface naming mode.
Note: Uses hyphen (
naming-mode) as per consistent naming convention.
Input JSON:
{
"naming_mode": "default"
}Expected Output:
default
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces naming-mode", data)Shows expected interface neighbors.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces neighbor expected", data)Shows port channel information.
Input JSON:
{
"PortChannel0001": {
"admin_status": "up",
"oper_status": "up",
"members": ["Ethernet0", "Ethernet4"],
"mtu": "9100",
"min_links": "1"
}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces portchannel", data)Shows interface status.
Optional argument: interface_name to show specific interface.
Input JSON:
{
"Ethernet0": {
"admin_status": "up",
"oper_status": "up",
"speed": "100G",
"mtu": "9100",
"fec": "rs",
"alias": "fortyGigE0/0"
}
}Sample CLI Output:
Interface Lanes Speed MTU FEC Alias Oper Admin
----------- ------- ------- ----- ----- -------------- ------ -------
Ethernet0 33,34,35,36 100G 9100 rs fortyGigE0/0 up up
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces status", data)
output = parse_and_convert("show interfaces status Ethernet0", data)Shows switchport configuration.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces switchport-config", data)Shows switchport status.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces switchport-status", data)All transceiver commands support optional [interface_name] argument.
| Command | Description |
|---|---|
show interfaces transceiver-eeprom [interface_name] |
Transceiver EEPROM |
show interfaces transceiver-error-status [interface_name] |
Transceiver error status |
show interfaces transceiver-info [interface_name] |
Transceiver info |
show interfaces transceiver-lpmode [interface_name] |
Transceiver LP mode |
show interfaces transceiver-pm [interface_name] |
Transceiver PM |
show interfaces transceiver-presence [interface_name] |
Transceiver presence |
show interfaces transceiver-status [interface_name] |
Transceiver status |
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show interfaces transceiver-info", data)Shows IPv6 BGP neighbors.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 bgp-neighbors", data)Shows IPv6 BGP network.
Usage:
output = parse_and_convert("show ipv6 bgp-network", data)Shows IPv6 BGP summary.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 bgp-summary", data)Shows IPv6 FIB.
Usage:
output = parse_and_convert("show ipv6 fib", data)Shows IPv6 interfaces.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 interfaces", data)Shows IPv6 link local mode.
Note: Uses hyphens (
link-local-mode) as per consistent naming convention.
Input JSON:
{
"link_local_mode": "default"
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 link-local-mode", data)Shows IPv6 prefix list.
Note: Uses hyphen (
prefix-list) as per consistent naming convention.
Optional argument: name to show specific prefix list.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 prefix-list", data)Shows IPv6 protocol.
Usage:
output = parse_and_convert("show ipv6 protocol", data)Shows IPv6 routes.
Optional argument: prefix to show specific route.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ipv6 route", data)Shows LLDP neighbors.
Optional argument: interface_name to show specific interface.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show lldp neighbors", data)Shows LLDP table.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show lldp table", data)Shows MAC address table.
Optional argument: address to show specific MAC.
Input JSON:
{
"FDB_TABLE:Vlan1000:00:11:22:33:44:55": {
"port": "Ethernet0",
"type": "dynamic"
}
}Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show mac", data)Shows MAC aging time.
Note: Uses hyphen (
aging-time) as per consistent naming convention.
Input JSON:
{
"fdb_aging_time": "600"
}Expected Output:
600
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show mac aging-time", data)Shows MMU configuration.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show mmu", data)Shows NDP table.
Input JSON:
{
"2001:db8::1": {
"mac": "00:11:22:33:44:55",
"interface": "Ethernet0",
"vlan": "1000",
"status": "REACHABLE"
}
}Sample CLI Output:
Address MacAddress Iface Vlan Status
----------------- ----------------- ---------- ------ ---------
2001:db8::1 00:11:22:33:44:55 Ethernet0 1000 REACHABLE
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show ndp", data)Shows process summary.
Input JSON:
[
{"PID": "1", "PPID": "0", "CMD": "/sbin/init", "MEM%": "0.1", "CPU%": "0.0"},
{"PID": "100", "PPID": "1", "CMD": "orchagent", "MEM%": "5.2", "CPU%": "10.5"}
]Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show processes summary", data)Shows processes sorted by CPU usage.
Usage:
output = parse_and_convert("show processes cpu", data)Shows processes sorted by memory usage.
Usage:
output = parse_and_convert("show processes memory", data)Shows queue counters.
Optional argument: interface_name to show specific interface.
Input JSON:
{
"Ethernet0:UC0": {"Counter/pkts": "1000", "Counter/bytes": "128000", "Drop/pkts": "0"},
"Ethernet0:UC1": {"Counter/pkts": "2000", "Counter/bytes": "256000", "Drop/pkts": "5"}
}Sample CLI Output:
Port TxQ Counter/pkts Counter/bytes Drop/pkts Drop/bytes
----------- ----- -------------- --------------- ----------- ------------
Ethernet0 UC0 1000 128000 0 0
Ethernet0 UC1 2000 256000 5 0
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show queue counters", data)
output = parse_and_convert("show queue counters Ethernet0", data)| Command | Description |
|---|---|
show queue watermark-unicast |
Unicast queue watermarks |
show queue watermark-multicast |
Multicast queue watermarks |
show queue watermark-all |
All queue watermarks |
show queue persistent-watermark-unicast |
Persistent unicast watermarks |
show queue persistent-watermark-multicast |
Persistent multicast watermarks |
show queue persistent-watermark-all |
Persistent all watermarks |
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show queue watermark-unicast", data)Shows WRED drop counters.
Optional argument: interface_name to show specific interface.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show queue wredcounters", data)Shows current reboot cause.
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show reboot-cause", data)Shows reboot cause history.
Input JSON:
{
"REBOOT_CAUSE|2025_07_10_20_06_34": {
"cause": "reboot",
"comment": "N/A",
"time": "Thu Jul 10 08:05:33 PM UTC 2025",
"user": "admin"
}
}Sample CLI Output:
Name Cause Time User Comment
------------------- ------- ------------------------------- -------------- ---------
2025_07_10_20_06_34 reboot Thu Jul 10 08:05:33 PM UTC 2025 admin N/A
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show reboot-cause history", data)Shows running Docker services and their processes.
Input JSON:
[
{
"dockerProcessName": "snmp",
"processes": [
{"pid": "123", "user": "root", "cpuPercentage": "0.1",
"memPercentage": "0.5", "command": "python snmpd.py"}
]
}
]Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show services", data)Shows SRv6 counter statistics.
Optional argument: sid to show specific SID.
Input JSON:
{
"2001:db8:1::/48": {"packets": "12345", "bytes": "67890"},
"2001:db8:2::/48": {"packets": "23456", "bytes": "78901"}
}Sample CLI Output:
MySID Packets Bytes
---------------- --------- -------------
2001:db8:1::/48 12345 67890
2001:db8:2::/48 23456 78901
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show srv6 stats", data)Shows system memory usage.
Input JSON:
[
{"type": "Mem", "total": "64252", "used": "15017", "free": "7526",
"shared": "563", "buff/cache": "41708", "available": "46228"},
{"type": "Swap", "total": "0", "used": "0", "free": "0"}
]Sample CLI Output:
total used free shared buff/cache available
Mem: 64252 15017 7526 563 41708 46228
Swap: 0 0 0
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show system-memory", data)Shows system uptime.
Input JSON:
{
"uptime": "up 3 weeks, 4 days, 10 hours, 15 minutes"
}Expected Output:
up 3 weeks, 4 days, 10 hours, 15 minutes
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show uptime", data)Shows SONiC version information.
Input JSON:
{
"sonic_software_version": "SONiC.20241200-12345",
"sonic_os_version": "11",
"distribution": "Debian 11.4",
"kernel": "5.10.0-18-2-amd64",
"build_commit": "abc123def",
"build_date": "2025-01-20",
"built_by": "Azure Pipelines",
"platform": "x86_64-mlnx_msn2700-r0",
"hwsku": "Mellanox-SN2700",
"asic": "mellanox",
"asic_count": "1",
"serial_number": "MT1234567890",
"uptime": "07:42:51 up 16 days",
"date": "Mon 20 Jan 2025 18:00:00"
}Sample CLI Output:
SONiC Software Version: SONiC.20241200-12345
SONiC OS Version: 11
Distribution: Debian 11.4
Kernel: 5.10.0-18-2-amd64
Build commit: abc123def
Build date: 2025-01-20
Built by: Azure Pipelines
Platform: x86_64-mlnx_msn2700-r0
HwSKU: Mellanox-SN2700
ASIC: mellanox
ASIC Count: 1
Serial Number: MT1234567890
Uptime: 07:42:51 up 16 days
Date: Mon 20 Jan 2025 18:00:00
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show version", data)Shows VLAN brief information.
Input JSON:
{
"Vlan1000": {
"vlan_id": "1000",
"ip_address": ["192.168.0.1/24"],
"ports": [
{"name": "Ethernet0", "port_tagging": "untagged"},
{"name": "Ethernet4", "port_tagging": "tagged"}
],
"proxy_arp": "disabled",
"dhcp_helper_addresses": ["192.0.0.1"]
}
}Sample CLI Output:
+-----------+-----------------+---------------+-----------+----------------+
| VLAN ID | IP Address | Ports | Proxy ARP | DHCP Helper |
+===========+=================+===============+===========+================+
| 1000 | 192.168.0.1/24 | Ethernet0(U) | disabled | 192.0.0.1 |
| | | Ethernet4(T) | | |
+-----------+-----------------+---------------+-----------+----------------+
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show vlan brief", data)Shows watermark telemetry interval.
Input JSON:
{
"interval": "120"
}Expected Output:
Telemetry interval 120 second(s)
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show watermark telemetry", data)| Command | Description |
|---|---|
show priority-group watermark-headroom |
Priority group headroom watermark |
show priority-group watermark-shared |
Priority group shared watermark |
show priority-group persistent-watermark-headroom |
Persistent headroom watermark |
show priority-group persistent-watermark-shared |
Persistent shared watermark |
Usage:
from gnmi_cli_lib import parse_and_convert
output = parse_and_convert("show priority-group watermark-headroom", data)from gnmi_cli_lib import CommandParser
parser = CommandParser()
# Invalid command
parsed = parser.parse("show invalid_command something")
if not parsed.is_valid:
print(f"Error: {parsed.error_message}")
# Parse and convert with error handling
try:
output = parser.parse_and_convert("show invalid_command something", {})
except ValueError as e:
print(f"Unknown command: {e}")Unit and integration tests live under python/tests/.
| Resource | Description |
|---|---|
| ../tests/unit/ | Unit test suite |
| ../tests/integration/ | Integration verification suite |
| ../tests/fixtures/sample_data.py | Test data fixtures |
| ../tests/reports/ALL_COMMANDS_ANALYSIS.md | Auto-generated analysis report |
cd ../
python -m pytest tests/unit -v
# With coverage (terminal report)
python -m pytest tests/unit --cov=gnmi_cli_lib --cov-report=term-missing
# With coverage (HTML interactive report)
python -m pytest tests/unit --cov=gnmi_cli_lib --cov-report=html
# With coverage (XML report for CI)
python -m pytest tests/unit --cov=gnmi_cli_lib --cov-report=xml
# Fail if coverage drops below 80%
python -m pytest tests/unit --cov=gnmi_cli_lib --cov-fail-under=80cd ../
python -m pytest tests/integration -vThe report in tests/reports/ALL_COMMANDS_ANALYSIS.md is generated by the verification tools:
cd ../gnmi_cli_lib_verifying
python generate_all_commands_analysis.pyOverall: 93% (2833 statements, 203 missed) — Threshold: 80% ✅
| Module | Stmts | Miss | Cover | Note |
|---|---|---|---|---|
__init__.py |
6 | 0 | 100% | |
cli.py |
98 | 30 | 69% | |
commands.py |
103 | 0 | 100% | |
common/constants.py |
10 | 0 | 100% | |
common/types.py |
34 | 0 | 100% | |
formatters/base.py |
108 | 1 | 99% | |
formatters/dropcounters.py |
84 | 1 | 99% | |
formatters/interface.py |
333 | 12 | 96% | |
formatters/ipv6.py |
780 | 99 | 87% | |
formatters/network.py |
275 | 11 | 96% | |
formatters/queue.py |
112 | 3 | 97% | |
formatters/system.py |
274 | 15 | 95% | |
formatters/transceiver.py |
254 | 12 | 95% | |
formatters/watermark.py |
56 | 1 | 98% | |
parser.py |
160 | 15 | 91% | |
registry.py |
68 | 0 | 100% | |
utils/parsing.py |
56 | 3 | 95% | |
utils/sorting.py |
8 | 0 | 100% | |
| TOTAL | 2833 | 203 | 93% |
Coverage configuration is defined in pyproject.toml under [tool.coverage.*] sections.
The TestCommandCoverage class ensures:
- Every registered CLI command has input JSON test data
- Every registered CLI command has expected CLI output
- Every command converts successfully without errors
The TestEveryCommandWithExpectedOutput class provides individual tests for each command category, verifying:
- Input JSON from
GNMI_JSON_TEST_DATA - Expected output patterns from
EXPECTED_CLI_OUTPUTS - Key data elements are present in the converted output
Unified command parser providing single entry point.
from gnmi_cli_lib import CommandParser
parser = CommandParser(table_format="simple")
# Parse a command
parsed = parser.parse("show interfaces status")
# Parse and convert
output = parser.parse_and_convert("show interfaces status", json_data)
# List all commands
commands = parser.list_commands()
# Get command help
help_text = parser.get_command_help("interfaces", "status")Central registry for command configurations.
from gnmi_cli_lib import CommandRegistry, get_default_registry
registry = get_default_registry()
# Get command configuration
config = registry.get_config("interfaces", "status")
# List all commands
commands = registry.list_commands()
# List all CLI mappings
mappings = registry.list_cli_mappings()# Convert JSON file using CLI command
python -m gnmi_cli_lib.cli -i data.json -c "show interfaces status"
# Read from stdin
cat data.json | python -m gnmi_cli_lib.cli -c "show version"
# List all supported commands
python -m gnmi_cli_lib.cli --list-commands
# Get gNMI path for a command
python -m gnmi_cli_lib.cli --get-path "show interfaces status"- Test Architecture Refactor: Migrated from
gnmi_cli_lib_verifying/totests/with proper pytest structure- Created
tests/core/module (data_loader, runner, test_case, compare) for reusable test infrastructure - Created
tests/unit/with parametrized tests,tests/integration/for real device verification - Created
tests/reports/withgenerate_all_commands_analysis.pyreport generator - Created
tests/tools/with consolidatedimport_device_data.py(mergedimport_new_devices.py)
- Created
- Test Optimization: Merged duplicate classes, lifted shared imports, parametrized edge cases
- Coverage: Configured pytest-cov with 93% coverage (threshold 80%)
- Test Expansion: 1014 unit tests, 704 integration test cases across 6 devices
- Report Update: SKIP status renamed to NO_ACCEPTABLE_DATA; removed from summary tables
- New Devices: Added str3-8101-02 and str4-sn5640-7 to test suite (now 6 devices total)
- Bug Fix: Fixed
VlanBriefFormatternull-handling for ports (.get("ports") or []instead of.get("ports", [])) - Bug Fix: Fixed
IPv6BgpNeighborsFormatterweek-format timestamp conversion forbgpTimerUpMsec - Bug Fix: Fixed
IPv6BgpNeighborsFormatterempty routes now returns empty string instead of header - Bug Fix: Fixed
IPv6BgpNeighborsFormatteraddressFamiliesByPeerstring iteration bug - Bug Fix: Fixed
IPv6BgpNeighborsFormatterbgpConnectioncamelCase to spaced format - Bug Fix: Fixed
parser._filter_by_argument()substring matching bug (Ethernet8 no longer matches Ethernet80/84/88) - Bug Fix: Fixed
TransceiverErrorStatusFormatterreturns "OK" whenstatus == "1"regardless of cmis_state - Bug Fix: Fixed
IPv6RouteFormatterVRF line and weight output conditional logic - Verification Enhancement: Added internal metadata key filtering (internalFlags, interfaceIndex, etc.) in coverage checks
- Test Expansion: 704 total test cases across 6 devices
- Production Ready: Self-contained library with 100% pass rate verification
- Bug Fix: Fixed
show interfaces transceiver error-status <interface>single-interface output formatting - Bug Fix: Fixed
show ipv6 interfaceslink-local addresses now include zone identifier (%interface_name) - Bug Fix: Fixed
show queue countersempty JSON no longer outputs spurious "For namespace :" header - Bug Fix: Fixed
show ipv6 fibnow outputs header and "Total number of entries 0" when no routes match (instead of empty string) - Verification Enhancement: Added IPv6 link-local zone ID transformation recognition in verification logic
- Production Ready: Self-contained library with automated unit and integration verification
- Enhanced Test Data: Fixed test data formats for processes/cpu, processes/memory, and lldp/neighbors
- Improved Test Comparison: Added timestamp/uptime normalization to handle CLI vs gNMI time drift
- Production Ready: Self-contained library with automated unit and integration verification
- Integrated Real Device Tests: Moved verification suite into
real_device_tests/within the library - Production Ready: Self-contained library with 100% pass rate verification
- Package Configuration: Added
pyproject.toml,requirements.txt,.gitignore - Documentation: Updated README to reflect latest status and structure
- Bug Fixes: Fixed multiple formatter and parser issues discovered during real device verification
- Fixed BGP routes output formatting (spacing, total paths handling)
- Fixed queue watermark help text option ordering
- Fixed transceiver EEPROM DOM threshold formatting
- Fixed IPv6 prefix-list sequence number handling
- Fixed LLDP neighbors capability code formatting
- New Formatters: Added
transceiver.pyandipv6.pyformatters - Testing: Achieved 100% pass rate (406/406 tests) across 696 real device test cases on 4 devices
- Verification Logic: Implemented 6-step multi-step comparison for robust verification
- Enhanced command coverage with 70 CLI command mappings
- Improved error handling and edge case support
- Added comprehensive unit test suite (285+ tests)
- Major refactoring with modular formatter architecture
- Added
CommandParseras unified entry point - Introduced convenience functions for common operations
Apache-2.0. See the SONiC project license.