Research-Stack/4-Infrastructure/infra/embedded_surface/kvm/kvm_array_harness.py

89 lines
2.6 KiB
Python
Executable file

#!/usr/bin/env python3
"""Run a small array of minimal KVM boot surfaces."""
from __future__ import annotations
import argparse
import subprocess
import sys
import time
from dataclasses import dataclass
from pathlib import Path
ROOT = Path(__file__).resolve().parent
BIN = ROOT / "build" / "gcl_kvm_boot"
OUT_DIR = ROOT / "build" / "array"
REQUIRED_LINES = (
"GCLKVM v0.1",
"surface=node kind=kvm.boot op=recover",
"kvm memslot=0 guest_phys=0x00000000 entry=0x00001000 io=0x00E9",
"ot1 op=0x0D source=0x002A route=0x0001 seq=0x0001",
"gcl admission=admitted invariant=recovery_subset",
"BOOT_OK",
)
@dataclass
class RunResult:
index: int
status: str
elapsed: float
log_path: Path
error: str | None = None
def validate(text: str) -> str | None:
missing = [line for line in REQUIRED_LINES if line not in text]
if missing:
return "missing line(s): " + ", ".join(missing)
if text.count("BOOT_OK") != 1:
return f"expected one BOOT_OK marker, saw {text.count('BOOT_OK')}"
return None
def run_one(index: int, timeout: float) -> RunResult:
log_path = OUT_DIR / f"node-{index:02d}.log"
start = time.monotonic()
proc = subprocess.run(
[str(BIN)],
cwd=ROOT,
capture_output=True,
text=True,
timeout=timeout,
check=False,
)
elapsed = time.monotonic() - start
log_path.write_text(proc.stdout, encoding="ascii")
if proc.returncode != 0:
return RunResult(index, "FAIL", elapsed, log_path, proc.stderr.strip()[-400:])
error = validate(proc.stdout)
return RunResult(index, "FAIL" if error else "OK", elapsed, log_path, error)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--count", type=int, default=10)
parser.add_argument("--timeout", type=float, default=2.0)
args = parser.parse_args()
subprocess.run(["make", "build/gcl_kvm_boot"], cwd=ROOT, check=True)
OUT_DIR.mkdir(parents=True, exist_ok=True)
failures = 0
for result in [run_one(i, args.timeout) for i in range(args.count)]:
print(f"node-{result.index:02d} {result.status} {result.elapsed:.4f}s {result.log_path.relative_to(ROOT)}")
if result.error:
failures += 1
print(f" {result.error}")
if failures:
print(f"kvm array result: {failures}/{args.count} failed", file=sys.stderr)
return 1
print(f"kvm array result: {args.count}/{args.count} minimal KVM surfaces emitted recovery pulses")
return 0
if __name__ == "__main__":
raise SystemExit(main())