Structured operational state
Collects hostname, IOS XE version, interface state, IPv4 addressing, and routing data through YANG-backed JSON APIs.
A Python network-automation project for collecting live Cisco IOS XE state, validating expected behavior, comparing management protocols, making a narrowly scoped configuration change, and proving rollback to the original baseline.
The implementation was exercised against Cisco DevNet Catalyst 8000V sandboxes. Read-only collection uses RESTCONF, NETCONF/YANG, and pyATS/Genie; configuration writes are disabled by default and constrained to a single demo loopback on a private/reservable lab.
The project records expected success and failure behavior rather than only showing that an API call returned data.
| Validation | Observed result |
|---|---|
| Stable live policy | PASS |
| Intentional bad default-route next hop | FAIL as expected |
| Two independent live baseline captures | 0 semantic differences |
Guarded Loopback250 creation | Created and read-back verified |
| Semantic post-change diff | Exactly 2 intended changes: interface + connected route |
| Post-change policy validation | 18/18 checks passed |
| Guarded rollback | Delete verified |
| Original baseline vs rollback state | 0 semantic differences |
| RESTCONF vs NETCONF hostname | MATCH |
| RESTCONF vs NETCONF interface state | MATCH |
| RESTCONF vs NETCONF vs pyATS/Genie interface state | MATCH |
| pyATS AEtest validation | 100% section success |
Each management interface is treated as a collector feeding the same internal state model. That separates protocol-specific parsing from validation logic and makes disagreement between sources visible.
Collects hostname, IOS XE version, interface state, IPv4 addressing, and routing data through YANG-backed JSON APIs.
Uses ncclient with the IOS XE device handler to retrieve hostname and operational interface state over NETCONF/XML.
Connects through Unicon, parses CLI state with Genie, and runs reusable AEtest sections with native PASS/FAIL behavior.
Transforms protocol-specific responses into common interface and routing objects before policy checks or semantic comparison.
The same live interface is observed through three separate paths. The comparison uses only fields all three sources expose consistently: interface name, administrative state, operational state, and IPv4 address.
RESTCONF / YANG --------------------┐ NETCONF / YANG ---------------------+--> normalized interface state --> MATCH / FAIL SSH + Genie parser + pyATS ---------┘
The write path is deliberately narrow. It is not a generic configuration shell and is intended only for a private/reservable lab device.
CISCO_ALLOW_WRITES=true must be explicitly set.Loopback250, 192.0.2.250/32, with a fixed ownership description.baseline -> guarded create -> read-after-write verification -> semantic diff: 2 intended changes -> policy validation: 18/18 PASS -> guarded delete -> read-after-delete verification -> compare with original baseline: 0 changes
YAML policies describe expected interfaces, routing behavior, addresses, and other network state independently of the collectors.
Pre/post snapshots compare meaningful network state instead of producing noise from counters or protocol-specific response shapes.
An intentionally incorrect default-route expectation is expected to fail, proving that the validator catches a real policy violation.
Credentials and write permission come from environment variables; missing required configuration does not silently fall back to unsafe behavior.
The repository is structured as a Python package rather than a collection of one-off scripts.
Collectors, normalized models, policy validation, semantic diffing, reporting, RESTCONF, NETCONF, and pyATS integration live in separate modules.
The cisco-validate command exposes collection, validation, diffing, cross-check, and guarded demo-change workflows.
Deterministic fixtures let policy and diff behavior run without Cisco credentials or network connectivity.
GitHub Actions installs the package, runs Ruff, executes pytest, and verifies Python files are not accidentally committed executable.
The project treats the live lab and the public repository as separate trust boundaries.
Device credentials remain outside Git. The checked-in pyATS testbed resolves credentials from environment variables.
TLS verification and NETCONF SSH host-key verification are enabled by default. Disposable DevNet exceptions must be explicitly enabled for that session.
The public repository contains the Python package, policies, fixtures, public-safe pyATS testbed, AEtest job, automated tests, and CI configuration.