Files
felhom.eu/documentation/audits/night-2026-09-25/tools/a4_backup.py
T

36 lines
1.7 KiB
Python

"""A4: ONE whole-box backup of demo-hp 9201 by the product's own trigger (POST /api/guest-backup/trigger),
polled through /api/guest-backup/status, with the host root disk sampled. Evidence, not product."""
import json, time, subprocess, os
os.environ.setdefault("GUEST", "9201")
import walk as w
OUT = w.EV + "/A/A4-hp-backup-run.txt"
def host(cmd):
return subprocess.run(["ssh", "hp", "export LC_ALL=C; " + cmd], capture_output=True, text=True, timeout=120).stdout.strip()
lines = []
def say(s):
s = time.strftime("%H:%M:%S ") + s
print(s, flush=True); lines.append(s); open(OUT, "w").write("\n".join(lines) + "\n")
w.login()
say("root before: " + host("df -B1 / | tail -1"))
c, d = w.ctl("POST", "/api/guest-backup/trigger")
say(f"POST /api/guest-backup/trigger -> {c} {json.dumps(d, ensure_ascii=False)[:300]}")
peak = 0
t0 = time.time()
while time.time() - t0 < 3600:
time.sleep(20)
c, d = w.ctl("GET", "/api/guest-backup/status")
used = int(host("df -B1 / | tail -1 | awk '{print $3}'") or 0)
peak = max(peak, used)
st = json.dumps(d, ensure_ascii=False)[:400]
say(f"status {c}: {st} | root used {used}")
dd = (d or {}).get("data") or {}
if isinstance(dd, dict) and not dd.get("running", dd.get("active", True)) and time.time() - t0 > 60:
break
say(f"root peak used bytes: {peak}")
say("root after: " + host("df -B1 / | tail -1"))
say("local archives: " + host("pvesm list local | grep 9201"))
say("host journal: " + host("journalctl -u felhom-agent --since '-70min' --no-pager | grep -iE 'vzdump|backup (done|failed|started)|prune|No space' | tail -15"))
w.login()
c, d = w.ctl("GET", "/api/stacks")
say("apps after: " + str([(s['name'], s.get('state')) for s in (d or {}).get('data') or [] if s.get('deployed')]))