41 lines
2.2 KiB
Python
41 lines
2.2 KiB
Python
#!/usr/bin/env python3
|
|
"""offrestore.py APP — the restore wizard's „Teljes visszaállítás" for one app, endpoint-level (the form POSTs the
|
|
wizard sends): 1) /backup/offbox/restore mode=full (size check), 2) the same with confirm=1 (download to the scratch),
|
|
3) /backup/offbox/reconstitute confirm=1 (files + database into the live app). Waits on /api/backup/restore-status
|
|
after 2 and 3. Prints redirects (flash keys) and the status lines only."""
|
|
import json, os, subprocess, sys, time
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import ctl
|
|
app = sys.argv[1]
|
|
def say(*a): print(time.strftime("%H:%M:%SZ", time.gmtime()), *a, flush=True)
|
|
def post(path, fields):
|
|
if ctl._SESS is None:
|
|
ctl.login()
|
|
args = ["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", ctl.HOST, "-H", "Cookie: " + ctl._SESS, "-X", "POST",
|
|
"--data-urlencode", "_csrf=" + ctl._CSRF]
|
|
for k, v in fields.items():
|
|
args += ["--data-urlencode", f"{k}={v}"]
|
|
out = subprocess.run(args + [ctl.BASE + path], capture_output=True, text=True, timeout=300).stdout
|
|
loc = [l for l in out.splitlines() if l.lower().startswith("location")]
|
|
say("POST", path, fields, "->", out.splitlines()[0] if out else "?", loc[:1])
|
|
def wait():
|
|
t0, last = time.time(), None
|
|
while time.time() - t0 < 3600:
|
|
c, d = ctl.call("GET", "/api/backup/restore-status", timeout=90)
|
|
dd = (d.get("data") or {}) if isinstance(d, dict) else {}
|
|
s = json.dumps(dd, ensure_ascii=False)[:600]
|
|
if s != last:
|
|
say("status:", s); last = s
|
|
if not dd.get("running") and time.time() - t0 > 10:
|
|
return
|
|
time.sleep(10)
|
|
ack = "--ack-placement" in sys.argv # the wizard's second confirm: „yes, into a different place than the backup recorded"
|
|
if "--reconstitute-only" not in sys.argv:
|
|
post("/backup/offbox/restore", {"app": app, "mode": "full"})
|
|
post("/backup/offbox/restore", {"app": app, "mode": "full", "confirm": "1"})
|
|
wait()
|
|
post("/backup/offbox/reconstitute", dict({"app": app, "confirm": "1"}, **({"ack_placement": "1"} if ack else {})))
|
|
wait()
|
|
c, d = ctl.call("GET", f"/api/stacks/{app}")
|
|
say("app state:", (d.get("data") or {}).get("state"))
|