Skip to content

Commit 77be027

Browse files
committed
Updates v1.3.0
1 parent 231b12f commit 77be027

8 files changed

Lines changed: 107 additions & 23 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ build/
1111
logs/
1212
data/
1313
testing/manual_results/
14+
platform_coverage_results.md
1415
dbl/
1516
clab-dblcheck/
1617
legacy/

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
# Changelog
22

3+
## [1.3.0] — 2026-03-23
4+
5+
### CI Pipeline
6+
7+
- GitHub Actions workflow with 4 parallel jobs: lint (ruff), type-check (pyright), test (pytest), coverage (pytest-cov)
8+
- Consolidated tool configs into `pyproject.toml` (ruff + pyright)
9+
- 85% test coverage baseline
10+
11+
### Live Device Test Suite
12+
13+
- 89 live SSH tests across 5 vendors (IOS-XE, EOS, JunOS, AOS-CX, RouterOS) covering every PLATFORM_MAP query per device
14+
- Automated SSH host key refresh fixture for containerlab restarts (removes stale keys, scans fresh ones before test session)
15+
- Result classifier detects vendor-specific error patterns: IOS `% Invalid input`, EOS `% <msg>`, JunOS `error:` prefix, AOS-CX `Invalid input:` (no `%` prefix), empty output
16+
- Markdown coverage report generated per run (`testing/live/platform_coverage_results.md`)
17+
18+
### Platform Map Fixes
19+
20+
- AOS-CX: replaced bare `show running-config` with targeted commands for ospf/config, bgp/config, policy_based_routing, access_lists
21+
- JunOS: fixed ospf/config, bgp/config, route_maps, prefix_lists commands that caused syntax errors
22+
23+
### Bug Fixes
24+
25+
- Fixed `SSH_STRICT_HOST_KEY=False` not actually disabling strict host key checking (scrapli `BinOptions` defaulted to strict when transport was `None`)
26+
- Fixed `run_tests.sh` not exporting `NO_LAB` variable to pytest subprocess
27+
- Fixed test data paths from gitignored `legacy/` to tracked `testing/mock/resources/`
28+
- Removed duplicate test functions flagged by ruff (F811)
29+
- Linted codebase with ruff: removed unused imports, fixed import ordering across ~30 files
30+
31+
---
32+
333
## [1.2.0] — 2026-03-19
434

535
### Jira Incident Lifecycle

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ✨ dblCheck
22

3-
[![Version](https://img.shields.io/badge/ver.-1.2.0-1a1a2e)](https://github.com/pdudotdev/dblCheck/releases/tag/1.2.0)
3+
[![Version](https://img.shields.io/badge/ver.-1.3.0-1a1a2e)](https://github.com/pdudotdev/dblCheck/releases/tag/1.3.0)
44
![License](https://img.shields.io/badge/license-BSL1.1-1a1a2e)
55
[![Last Commit](https://img.shields.io/github/last-commit/pdudotdev/dblCheck?color=1a1a2e)](https://github.com/pdudotdev/dblCheck/commits/main/)
66

@@ -15,7 +15,7 @@
1515
- 📜 **dblCheck**
1616
- [🔭 Overview](#-overview)
1717
- [🍀 Here's a Quick Demo](#-heres-a-quick-demo)
18-
- [⭐ What's New in v1.2](#-whats-new-in-v12)
18+
- [⭐ What's New in v1.3](#-whats-new-in-v13)
1919
- [⚒️ Core Tech Stack](#️-core-tech-stack)
2020
- [📋 Validation Scope](#-validation-scope)
2121
- [🛠️ Installation & Usage](#️-installation--usage)
@@ -43,7 +43,7 @@ Continuously checks **live network state against design intent** and invokes a C
4343
- [x] **HashiCorp Vault** - All secrets (device creds, NetBox token, Jira key etc.) stored in Vault
4444
- [x] **NetBox** - Network inventory and expected state loaded automatically
4545
- [x] **Jira** - Network state drift and deviations logged to Jira
46-
- [x] **711 tests** - 21 suites (17 unit + 3 integration + 1 live)
46+
- [x] **711 tests** - 21 suites (17 unit + 3 integration + 1 live device SSH), CI via GitHub Actions
4747

4848
▫️ **Supported models:**
4949
- [x] Haiku 4.5
@@ -63,7 +63,7 @@ Continuously checks **live network state against design intent** and invokes a C
6363
## 🍀 Here's a Quick Demo
6464
- [x] *Demo video coming soon...*
6565

66-
## ⭐ What's New in v1.2
66+
## ⭐ What's New in v1.3
6767
- [x] See [**CHANGELOG.md**](CHANGELOG.md)
6868

6969
## ⚒️ Core Tech Stack

platforms/platform_map.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"redistribution": "show running-config section redistribute",
6363
"route_maps": "show route-map",
6464
"prefix_lists": "show ip prefix-list",
65-
"policy_based_routing": "show ip policy",
65+
"policy_based_routing": "show policy-map type pbr",
6666
"access_lists": "show ip access-lists",
6767
},
6868
"interfaces": {
@@ -78,23 +78,23 @@
7878
"neighbors": {"default": "show ospf neighbor", "vrf": "show ospf neighbor instance {vrf}"},
7979
"database": {"default": "show ospf database", "vrf": "show ospf database instance {vrf}"},
8080
"borders": {"default": "show ospf route abr", "vrf": "show ospf route abr instance {vrf}"},
81-
"config": "show configuration protocols ospf",
81+
"config": {"default": "show ospf overview", "vrf": "show ospf overview instance {vrf}"},
8282
"interfaces": {"default": "show ospf interface", "vrf": "show ospf interface instance {vrf}"},
8383
"details": {"default": "show ospf overview", "vrf": "show ospf overview instance {vrf}"},
8484
},
8585
"bgp": {
8686
"summary": {"default": "show bgp summary", "vrf": "show bgp summary instance {vrf}"},
8787
"table": {"default": "show route protocol bgp", "vrf": "show route protocol bgp table {vrf}.inet.0"},
88-
"config": "show configuration protocols bgp",
88+
"config": {"default": "show bgp summary", "vrf": "show bgp summary instance {vrf}"},
8989
"neighbors": {"default": "show bgp neighbor", "vrf": "show bgp neighbor instance {vrf}"},
9090
},
9191
"routing_table": {
9292
"ip_route": {"default": "show route", "vrf": "show route table {vrf}.inet.0"},
9393
},
9494
"routing_policies": {
9595
"redistribution": "show configuration policy-options",
96-
"route_maps": "show configuration policy-options policy-statement",
97-
"prefix_lists": "show configuration policy-options prefix-list",
96+
"route_maps": "show configuration policy-options",
97+
"prefix_lists": "show configuration policy-options",
9898
"policy_based_routing": "show configuration routing-options",
9999
"access_lists": "show configuration firewall",
100100
},
@@ -110,16 +110,16 @@
110110
"aos": {
111111
"ospf": {
112112
"neighbors": {"default": "show ip ospf neighbors", "vrf": "show ip ospf neighbors vrf {vrf}"},
113-
"database": {"default": "show ip ospf database", "vrf": "show ip ospf database vrf {vrf}"},
113+
"database": {"default": "show ip ospf lsdb", "vrf": "show ip ospf lsdb vrf {vrf}"},
114114
"borders": {"default": "show ip ospf border-routers", "vrf": "show ip ospf border-routers vrf {vrf}"},
115-
"config": "show running-config",
115+
"config": {"default": "show ip ospf", "vrf": "show ip ospf vrf {vrf}"},
116116
"interfaces": {"default": "show ip ospf interface", "vrf": "show ip ospf interface vrf {vrf}"},
117117
"details": {"default": "show ip ospf", "vrf": "show ip ospf vrf {vrf}"},
118118
},
119119
"bgp": {
120120
"summary": {"default": "show bgp ipv4 unicast summary", "vrf": "show bgp vrf {vrf} ipv4 unicast summary"},
121121
"table": {"default": "show bgp ipv4 unicast", "vrf": "show bgp vrf {vrf} ipv4 unicast"},
122-
"config": "show running-config",
122+
"config": {"default": "show bgp ipv4 unicast", "vrf": "show bgp vrf {vrf} ipv4 unicast"},
123123
"neighbors": {"default": "show bgp ipv4 unicast neighbors", "vrf": "show bgp vrf {vrf} ipv4 unicast neighbors"},
124124
},
125125
"routing_table": {
@@ -129,8 +129,8 @@
129129
"redistribution": "show running-config",
130130
"route_maps": "show route-map",
131131
"prefix_lists": "show ip prefix-list",
132-
"policy_based_routing": "show running-config",
133-
"access_lists": "show running-config",
132+
"policy_based_routing": "show pbr summary",
133+
"access_lists": "show access-list",
134134
},
135135
"interfaces": {
136136
"interface_status": "show interface brief",

testing/live/conftest.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,46 @@
44
Sets up sys.path only. Tests in testing/live/ connect to real lab devices
55
via the actual transport layer (SSH). Set NO_LAB=0 to enable.
66
"""
7+
import subprocess
78
import sys
89
from pathlib import Path
910

11+
import pytest
12+
1013
_ROOT = Path(__file__).resolve().parent.parent.parent
1114
if str(_ROOT) not in sys.path:
1215
sys.path.insert(0, str(_ROOT))
16+
17+
18+
@pytest.fixture(autouse=True, scope="session")
19+
def refresh_host_keys():
20+
"""Remove stale and scan fresh SSH host keys for all lab devices.
21+
22+
Containerlab regenerates device host keys on every restart. This fixture
23+
runs once before any live test to keep ~/.ssh/known_hosts in sync, so
24+
SSH_STRICT_HOST_KEY=True remains effective without manual intervention.
25+
"""
26+
from core.inventory import devices
27+
28+
known_hosts = Path.home() / ".ssh" / "known_hosts"
29+
known_hosts.parent.mkdir(mode=0o700, exist_ok=True)
30+
known_hosts.touch(exist_ok=True)
31+
32+
hosts = [d["host"] for d in devices.values()]
33+
34+
# Remove all stale entries for lab IPs
35+
for host in hosts:
36+
subprocess.run(
37+
["ssh-keygen", "-R", host],
38+
capture_output=True,
39+
)
40+
41+
# Scan and append fresh keys for all devices in one pass
42+
result = subprocess.run(
43+
["ssh-keyscan", "-T", "5"] + hosts,
44+
capture_output=True,
45+
text=True,
46+
)
47+
if result.stdout:
48+
with known_hosts.open("a") as f:
49+
f.write(result.stdout)

testing/live/test_platform_coverage.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,28 @@ def classify_result(result) -> tuple[str, str]:
9595

9696
raw = result.get("raw", "")
9797

98-
if isinstance(raw, str) and "% Invalid input" in raw:
99-
return "FAIL", raw.strip()[:200]
98+
if isinstance(raw, str):
99+
stripped = raw.strip()
100100

101-
# IOS/EOS "% <msg>" = feature not configured → EMPTY (not a command error)
102-
if isinstance(raw, str) and raw.strip().startswith("% "):
103-
return "EMPTY", ""
101+
# Empty output — device returned nothing meaningful
102+
if not stripped:
103+
return "EMPTY", ""
104+
105+
# IOS/EOS: "% Invalid input ..."
106+
if "% Invalid input" in stripped:
107+
return "FAIL", stripped[:200]
108+
109+
# AOS-CX: "Invalid input: <keyword>" (no % prefix)
110+
if stripped.startswith("Invalid input"):
111+
return "FAIL", stripped[:200]
112+
113+
# JunOS: "error: syntax error, ..." or "error: unknown command"
114+
if stripped.startswith("error:"):
115+
return "FAIL", stripped[:200]
116+
117+
# IOS/EOS "% <msg>" = feature not configured → EMPTY (not a command error)
118+
if stripped.startswith("% "):
119+
return "EMPTY", ""
104120

105121
return "PASS", ""
106122

testing/run_tests.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ set -euo pipefail
88
# Parse flags
99
for arg in "$@"; do
1010
case "$arg" in
11-
--live) NO_LAB=0 ;;
11+
--live) export NO_LAB=0 ;;
1212
esac
1313
done
1414

@@ -43,7 +43,7 @@ run_suite() {
4343
printf " %-8s %-55s " "$suite_id" "$suite_name"
4444

4545
local output
46-
if output=$(cd "$PROJECT_ROOT" && $PYTEST "$suite_file" -v --tb=short -q 2>&1); then
46+
if output=$(cd "$PROJECT_ROOT" && $PYTEST "$suite_file" -v --tb=short -q --timeout=60 2>&1); then
4747
printf "${GREEN}PASS${RESET}\n"
4848
PASS_COUNT=$((PASS_COUNT + 1))
4949
else
@@ -91,7 +91,7 @@ run_suite "IT-003" "testing/mock/integration/test_cli_orchestration.py" "CLI or
9191
echo ""
9292
if [[ "${NO_LAB:-1}" == "0" ]]; then
9393
echo " ── Live Device Tests ──────────────────────────────────────────"
94-
run_suite "LT-001" "testing/live/test_platform_coverage.py" "Platform coverage (6 vendors, real SSH)"
94+
run_suite "LT-001" "testing/live/test_platform_coverage.py" "Platform coverage (5 vendors, real SSH)"
9595
else
9696
printf " ${YELLOW}Live tests skipped${RESET} — rerun with --live to include\n"
9797
SKIP_COUNT=$((SKIP_COUNT + 1))

transport/ssh.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def _build_cli(device: dict, timeout_ops: int | None = None) -> Cli:
8686
elif SSH_STRICT_HOST_KEY:
8787
transport = BinOptions(enable_strict_key=True, known_hosts_path=_known_hosts)
8888
else:
89-
transport = None
89+
transport = BinOptions(enable_strict_key=False)
9090

9191
return Cli(
9292
host=device["host"],

0 commit comments

Comments
 (0)