Files
felhom.eu/documentation/audits/release-2026-10-26/tools/t1probe.py
T

101 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""t1probe.py — controller 0.343.0 on Tester 1 (release brief Part 5): one local unit restore and one kept-data cycle with
a THROWAWAY Tandoor, through the endpoints the pages use. Each restore: the seeded item back, then the undo copy
released after the stable window — read from the controller's own line „ran stably for … — the restore copy is
released" (the positive observable) and the restore-undo folder gone. Removed through the product at the end.
usage: SC=<dir with .ctlpw> SWEEP_EV=<evidence dir> TARGET=tester-1 python3 t1probe.py"""
import json, os, re, sys, time
TOOLS = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../catalog-sweep-2026-10-24/tools")
sys.path.insert(0, TOOLS)
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../restore-truth-2026-10-25/tools"))
import sweep as sw # noqa: E402
import unitprobe2 as up # noqa: E402
w, fxm = sw.w, sw.fxm
APP = "tandoor"
def wait_release(since, say, label, cap=600):
for _ in range(cap // 15):
time.sleep(15)
line = sw.g(f"docker logs --since {int(since)} felhom-controller 2>&1 | grep -E '{APP}: ran stably for .* restore copy is released' | tail -1").strip()
if line:
undo = sw.g(f"ls -d /mnt/*/felhom-data/backups/restore-undo/{APP} /mnt/felhom-drives/*/felhom-data/backups/restore-undo/{APP} 2>/dev/null").strip()
say(f" [{label}] released: {line[:200]} | undo folder now: {undo or 'gone'}")
return {"line": line, "undo_left": undo}
say(f" [{label}] NO release line within {cap}s")
return {"line": "", "undo_left": "?"}
def main():
d = os.path.join(sw.EV, "_t1probe")
os.makedirs(d, exist_ok=True)
log = open(os.path.join(d, "walk.log"), "a")
def say(*a):
line = time.strftime("%H:%M:%SZ", time.gmtime()) + " " + " ".join(str(x) for x in a)
print(line, flush=True)
log.write(sw.scrub(line) + "\n")
log.flush()
w.say = say
fx = fxm.FIXTURES[APP]
sub = getattr(fx, "sub", APP)
rec = {"app": APP}
w.login()
up.w.SC = w.SC
t_start = int(time.time())
try:
rec["install"] = w.deploy(APP, sub) and w.wait_app(sub, "/", tries=90, delay=5)
tok = fx.seed(w, sub, say)
rec["seed"] = bool(tok)
sw.backup_now(rec, say)
# 1. a plain local unit restore
t1 = time.time()
sw.restore_unit(APP, rec, say)
rec["unit_restore"] = rec.pop("restore")
say(" [1] unit restore: " + rec["unit_restore"].get("status", "")[:300])
time.sleep(20)
rec["unit_restore_item"] = bool(tok) and bool(fx.verify(w, sub, tok, say))
say(f" [1] item after the unit restore: {rec['unit_restore_item']}")
rec["unit_restore_release"] = wait_release(t1, say, "1")
# 2. the kept-data cycle: remove keeping the backups, reinstall, restore the earlier install's set-aside unit
sw.remove(APP, keep=True, say=say)
t_re = time.time()
rec["reinstall"] = w.deploy(APP, sub, dict(w.GENERATED.get(APP) or {}))
aside = ""
for _ in range(60):
time.sleep(10)
aside = sw.g(f"docker logs --since {int(t_re)} felhom-controller 2>&1 | grep -E '{APP}: the recovery unit of an earlier install' | cut -c1-300")
if aside.strip():
break
rec["set_aside_line"] = aside.strip()
say(" [2] set aside: " + (aside.strip()[:260] or "NONE within 10 min"))
html = up.page("/backups/restore")
m = re.search(r'action="/backup/kept/restore".*?name="id" value="([0-9a-f]{16})"', html, re.S)
rec["restore_page_offers"] = bool(m)
if not m:
say(" [2] the restore page offers NO restore of the set-aside unit")
return
t2 = time.time()
head, loc = up.post_form("/backup/kept/restore", {"id": m.group(1), "confirm": "1"})
say(f" [2] POST /backup/kept/restore -> {head} {loc[:1]}")
up.wait_restore("kept_restore", rec)
say(" [2] kept restore: " + rec["kept_restore"]["status"][:300])
time.sleep(20)
rec["kept_restore_item"] = bool(tok) and bool(fx.verify(w, sub, tok, say))
say(f" [2] item after the kept-data restore: {rec['kept_restore_item']}")
rec["kept_restore_release"] = wait_release(t2, say, "2")
finally:
rec["controller_lines"] = sw.scrub(sw.g(
f"docker logs --since {t_start} felhom-controller 2>&1 | grep -E '{APP}' | grep -iE 'set aside|restor|stabl|install id|D47|D48|R-1270|refused|released|KEPT' | cut -c1-300"))
sw.remove(APP, keep=False, say=say)
rec["after_remove"] = sw.leftovers(APP, sub)
rec["set_aside_after_remove"] = sw.g(f"ls -1 /mnt/*/felhom-data/backups/set-aside/{APP} 2>&1 | head -3").strip()
json.dump(rec, open(os.path.join(d, "record.json"), "w"), ensure_ascii=False, indent=1)
if __name__ == "__main__":
main()