#!/usr/bin/env python3 """putback.py — D22 (the put-back covers every restore) on the sweep box, the 10-21 night's method (`audits/night-2026-10-21/P3-restore/`): install, seed one item through the front door, „Mentés most", then the FAULT — the recovery unit's largest volume archive truncated to half (a damaged backup, the only hand-made change) — then the household's „Visszaállítás" of that unit. Expected: the restore reports failure, the LIVE data and definition are put back, the app runs, and the item reads back. Then remove with data. usage: SC=… SWEEP_EV=… DRIVE_ROOT=… python3 putback.py (evidence: /_putback//) """ import json, os, sys, time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import sweep as sw # noqa: E402 w, fxm = sw.w, sw.fxm def main(app): d = os.path.join(sw.EV, "_putback", app) 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() 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) units = sw.g(f"ls -la --time-style=+%T /mnt/sys_drive/felhom-data/backups/primary/{app}/volume-dumps/ 2>&1") say(" unit before the fault:\n" + units) big = sw.g(f"ls -S /mnt/sys_drive/felhom-data/backups/primary/{app}/volume-dumps/*.tar 2>/dev/null | head -1").strip() size = sw.g(f"stat -c %s {big}").strip() sw.g(f"truncate -s $(( {size} / 2 )) {big}") after = sw.g(f"stat -c %s {big}").strip() say(f" THE FAULT: {big} {size} -> {after}") rec["fault"] = {"file": big, "from": size, "to": after} sw.restore_unit(app, rec, say) rec["state_after"] = w.stack(app).get("state") rec["item_after"] = bool(tok) and bool(fx.verify(w, sub, tok, say)) rec["controller_log"] = sw.g("docker logs --since 10m felhom-controller 2>&1 | grep -iE 'put back|R-893|restore failed|put-back' | tail -5") say(" controller: " + rec["controller_log"].strip()[:600]) finally: sw.remove(app, keep=False, say=say) rec["after_remove"] = sw.leftovers(app, sub) json.dump(rec, open(os.path.join(d, "record.json"), "w"), ensure_ascii=False, indent=1) say(" == putback " + json.dumps({k: rec.get(k) for k in ("seed", "state_after", "item_after")})) if __name__ == "__main__": main(sys.argv[1])