Skip to content

Add NetDevOps automation toolkit: Python SDK + CLI, in-app Automation tab, CI gate - #1

Merged
joxorsayan merged 2 commits into
mainfrom
devin/1782060391-netdevops-automation
Jun 21, 2026
Merged

joxorsayan merged 2 commits into
mainfrom
devin/1782060391-netdevops-automation

Conversation

@joxorsayan

Copy link
Copy Markdown
Owner

Summary

Adds a network-automation / NetDevOps layer on top of NetSim so the project visibly demonstrates Python network automation (config-as-code, validation, drift) — not just a clickable simulator. The engine already exposed an automation surface (app/engine/automation.py: Jinja-style templating, intent↔CLI, snapshot/drift, Batfish-style validation) wired to REST endpoints, but there was no SDK, no CLI, no CI, and no UI for it. This PR fills that gap; no engine behavior changes.

The whole network is described as one declarative file (automation/labs/two_router_ospf.yaml — topology + per-device vars + intended-state assertions) and a single command deploys and verifies it: a real OSPF adjacency between R1/R2 and validated cross-subnet reachability (PC1↔PC2), 8/8 assertions passing.

What's added

1. Python SDK + CLI (automation/)

  • client.NetSimClient — typed, stdlib-only (urllib) REST SDK, one method per primitive (apply_lab, push_config, render, snapshot, drift, validate, get_intent, …).
  • netdevops.py — the render_configs → topology_spec → deploy_and_verify pipeline. Configs render with real Jinja2 (templates/router.j2, host.j2) from per-device vars, then push; deploy_and_verify returns (report, ok) where ok requires clean topology + clean pushes + all assertions passing.
  • cli.py — Typer CLI: render | deploy | validate | drift | intent. deploy exits non-zero on failure, so it doubles as a CI gate.

2. In-app Automation tab (app/static/index.html + app.js)

  • New "Automation" panel over the existing /api/automation/* endpoints: config-as-code (template + vars → render / render&push), intended-state Validate (PASS/FAIL + score), drift (snapshot baseline → diff), a "Copy as Python" button that emits an equivalent SDK script, and a one-click "Load OSPF demo" that provisions PC1–R1=OSPF=R2–PC2 so the tab is immediately usable in the live demo.

3. NetDevOps CI (.github/workflows/netdevops.yml)

  • Boots NetSim, runs automation.cli deploy … then the example script (render → push → validate → drift), failing the build on any misconfiguration. Existing ci.yml now also installs automation/requirements.txt.

4. Tests + docs

  • tests/test_automation_toolkit.py (6 tests: pure render/spec/report functions + full pipeline against an in-thread uvicorn server, drift, intent). 197 tests pass (was 191).
  • examples/automation/provision_and_verify.py + README; README gains a "Network automation (NetDevOps)" section, a CI badge, and SEO keywords.

Dependency note

automation/requirements.txt pins typer==0.12.5 and click==8.1.7 — newer Click (8.2+) changed make_metavar()'s signature and breaks Typer 0.12.5. App runtime deps (requirements.txt) are untouched; these are tooling-only.

Verified locally

  • python -m automation.cli deploy automation/labs/two_router_ospf.yaml → validation: 8/8 passed (score 100/100), exit 0.
  • Example script provisions, validates 8/8, and detects injected drift.
  • python -m pytest -q → 197 passed.

harryroger798 and others added 2 commits June 21, 2026 16:46
…ab, CI gate

- automation/ package: typed NetSimClient SDK, Typer CLI, Jinja2 config
  templating, render->push->validate pipeline, declarative network definitions
- in-app Automation tab: config-as-code, intended-state validation, drift
  detection, copy-as-Python, OSPF demo loader
- examples/automation/provision_and_verify.py end-to-end demo
- .github/workflows/netdevops.yml: render->push->validate CI gate
- tests/test_automation_toolkit.py (6 tests); 197 passing total

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The in-app seeded Jinja template (AUTO_TPL_DEFAULT) omitted the per-block
'exit' lines, so 'Render & push' failed with '% Invalid input' when entering
a second interface / the OSPF block (NetSim's CLI requires leaving a config
sub-mode before entering another). Matches automation/templates/router.j2.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@joxorsayan

Copy link
Copy Markdown
Owner Author

Automation tab — end-to-end test results

Tested the new Automation/NetDevOps tab through the browser against the exact PR-branch code (local uvicorn, identical to what deploys). All 5 flows pass. One real bug was found and fixed during testing (commit e77f1d0).

Result: T1 demo provision ✓ · T2 validate 100/100 (healthy) & 25/100 (broken) ✓ · T3 render & push ✓ (after fix) · T4 drift detection ✓ · T5 copy-as-Python ✓
CI on e77f1d0: test ✓ · provision-and-verify ✓ · live site HTTP 200.

🔧 Bug found & fixed: Render & push emitted % Invalid input

The in-app default Jinja template (AUTO_TPL_DEFAULT in app/static/app.js) was missing the per-block exit statements, so Render & push failed when entering the second interface and the router ospf block — NetSim's CLI requires leaving a config sub-mode before entering another. The committed automation/templates/router.j2 and the AUTO_DEMO_CONFIG constant both already had exit; only the seeded UI template was inconsistent.

Fixed by adding exit after each interface block and after the OSPF block to match router.j2. After the fix: Pushed 13 lines to R1 … ✓ applied cleanly.

T1 — Load OSPF demo

Canvas shows PC1–R1–R2–PC2 (4 devices, 3 links); status "Provisioned PC1–R1=OSPF=R2–PC2 via automation push."

T2 — Intended-state validation (healthy + adversarial)
  • Healthy: ✓ COMPLIANT — 4/4 passed (score 100/100), with reachable PC1->192.168.2.10 — received 2 (real cross-subnet OSPF routing).
  • Adversarial: after shutting R1 Gig0/0, ✗ NON-COMPLIANT — 1/4 passed (score 25/100) with FAIL on interface_up / route_present / reachable (received 0). Validator reflects live state, not a hardcoded pass.
T4 — Drift detection

Save baseline → ✓ in sync; after shutting Gig0/0 via console → ✗ drift detected vs baseline: + R1: shutdown.

T5 — Copy as Python

Emits a runnable SDK snippet (from automation import NetSimClient, NetSimClient(...), current template + variables, nc.render(..., device="R1", push=True)) and copies it to the clipboard. Mirrors current UI state.

Tested by Devin — session: https://app.devin.ai/sessions/850f5eff01f640b9b3f6079509dce508

@joxorsayan
joxorsayan merged commit fb07a80 into main Jun 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants