#!/usr/bin/env python3 """twoatonce.py — B6 #9: (a) restore + remove on the same app from two sessions at once; (b) a double-click restore. Each act is the POST the page sends; the two of a pair start within ~50 ms (threads). Prints answers and the status.""" import json, os, subprocess, sys, threading, time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))); import ctl APP = sys.argv[1] def restore_post(): out = subprocess.run(["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", ctl.HOST, "-H", "Cookie: " + ctl._SESS, "-X", "POST", "--data-urlencode", "_csrf=" + ctl._CSRF, "--data-urlencode", "stack_name=" + APP, "--data-urlencode", "snapshot_id=helyi", ctl.BASE + "/backup/restore"], capture_output=True, text=True).stdout return (out.splitlines()[0] if out else "?") + " " + str([l for l in out.splitlines() if l.lower().startswith("location")][:1]) def run_pair(a, b, label): res = {} ta = threading.Thread(target=lambda: res.__setitem__("A", a())); tb = threading.Thread(target=lambda: res.__setitem__("B", b())) t0 = time.time(); ta.start(); time.sleep(0.05); tb.start(); ta.join(); tb.join() print(time.strftime("%H:%M:%SZ", time.gmtime()), label, "A:", res["A"], "| B:", res["B"], flush=True) seen = [] for i in range(40): c, d = ctl.call("GET", "/api/backup/restore-status", timeout=30); dd = d.get("data") or {} s = (dd.get("running"), (dd.get("last") or {}).get("ok"), (dd.get("last") or {}).get("message", "")[:140]) if not seen or seen[-1][1] != s: seen.append((round(time.time() - t0, 1), s)) if not dd.get("running") and time.time() - t0 > 10: break time.sleep(2) for x in seen: print(" ", x, flush=True) c, d = ctl.call("GET", f"/api/stacks/{APP}"); print(" app:", (d.get("data") or {}).get("state"), "deployed:", (d.get("data") or {}).get("deployed"), flush=True) ctl.login() run_pair(restore_post, lambda: str(ctl.call("POST", f"/api/stacks/{APP}/remove", {"remove_hdd_data": False, "remove_backups": False})), "(a) restore + remove:") time.sleep(10) run_pair(restore_post, restore_post, "(b) double-click restore:")