demo: commit the recording harness #8

Merged
claude merged 3 commits from demo/recording-harness into main 2026-09-22 19:42:00 -04:00
2 changed files with 30 additions and 6 deletions
Showing only changes of commit 3e7b777e53 - Show all commits

No files matched your search

+17 -4
View File
@@ -16,6 +16,17 @@ CYAN, GREEN, RED, YEL, MAG = "\033[36m", "\033[32m", "\033[31m", "\033[33m", "\0
SPEED = float(os.environ.get("DEMO_SPEED", "1.0")) SPEED = float(os.environ.get("DEMO_SPEED", "1.0"))
def recovered_bytes(rec: dict) -> bytes:
"""zerto_recover_file returns the content now, not a path on the MCP host.
The server may be on another machine, so a path there is of no use here.
"""
import base64
if rec.get("encoding") == "base64":
return base64.b64decode(rec["content"])
return rec["content"].encode()
def w(s=""): def w(s=""):
sys.stdout.write(s + "\n"); sys.stdout.flush() sys.stdout.write(s + "\n"); sys.stdout.flush()
@@ -212,8 +223,8 @@ async def main():
if not rec.get("ok"): if not rec.get("ok"):
w(f"{RED} FLR failed{RST}"); return 1 w(f"{RED} FLR failed{RST}"); return 1
beat(1.5) beat(1.5)
w(f"{YEL} $ cat {rec['path']}{RST}") w(f"{YEL} recovered {rec['bytes']} bytes, sha256 {rec['sha256'][:16]}...{RST}")
body = Path(rec["path"]).read_text() body = recovered_bytes(rec).decode()
for line in body.splitlines(): for line in body.splitlines():
w(f"{GREEN}{BOLD} | {line}{RST}") w(f"{GREEN}{BOLD} | {line}{RST}")
beat(2.5) beat(2.5)
@@ -222,11 +233,13 @@ async def main():
step("Copy it back to the guest") step("Copy it back to the guest")
note("This part is scp, not Zerto. Zerto got the bytes back.") note("This part is scp, not Zerto. Zerto got the bytes back.")
target = f"{CFG['user']}@{CFG['host']}" target = f"{CFG['user']}@{CFG['host']}"
local_copy = SP / rec["name"]
local_copy.write_bytes(recovered_bytes(rec))
scp = (["sshpass", "-p", CFG["password"]] if CFG.get("password") else []) + \ scp = (["sshpass", "-p", CFG["password"]] if CFG.get("password") else []) + \
["scp", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", ["scp", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
"-o", "LogLevel=ERROR"] + (["-i", CFG["key"]] if CFG.get("key") else []) + \ "-o", "LogLevel=ERROR"] + (["-i", CFG["key"]] if CFG.get("key") else []) + \
[rec["path"], f"{target}:{CFG['guest_path']}"] [str(local_copy), f"{target}:{CFG['guest_path']}"]
w(f"{YEL} $ scp {Path(rec['path']).name} {target}:{CFG['guest_path']}{RST}") w(f"{YEL} $ scp {rec['name']} {target}:{CFG['guest_path']}{RST}")
subprocess.run(scp, capture_output=True, text=True, timeout=60) subprocess.run(scp, capture_output=True, text=True, timeout=60)
guest(f"cat {CFG['guest_path']}") guest(f"cat {CFG['guest_path']}")
beat(1.5) beat(1.5)
+13 -2
View File
@@ -22,6 +22,17 @@ T0 = time.time()
STEP = [0] STEP = [0]
def recovered_bytes(rec: dict) -> bytes:
"""zerto_recover_file returns the content now, not a path on the MCP host.
The server may be on another machine, so a path there is of no use here.
"""
import base64
if rec.get("encoding") == "base64":
return base64.b64decode(rec["content"])
return rec["content"].encode()
def w(s=""): def w(s=""):
sys.stdout.write(s + "\n"); sys.stdout.flush() sys.stdout.write(s + "\n"); sys.stdout.flush()
@@ -155,7 +166,7 @@ async def main():
show(rec, keep=["ok", "bytes", "flr_path", "unmount"]) show(rec, keep=["ok", "bytes", "flr_path", "unmount"])
if not rec.get("ok"): if not rec.get("ok"):
w(f"{RED} FLR failed{RST}"); return 1 w(f"{RED} FLR failed{RST}"); return 1
body = Path(rec["path"]).read_text() body = recovered_bytes(rec).decode()
w(f"{YEL} recovered file:{RST}") w(f"{YEL} recovered file:{RST}")
for line in body.splitlines(): for line in body.splitlines():
w(f"{GREEN}{BOLD} | {line}{RST}") w(f"{GREEN}{BOLD} | {line}{RST}")
@@ -166,7 +177,7 @@ async def main():
# endings and appends one, which makes the restored file differ # endings and appends one, which makes the restored file differ
# from what Zerto handed back. WriteAllBytes does not. # from what Zerto handed back. WriteAllBytes does not.
import base64 import base64
blob = Path(rec["path"]).read_bytes() blob = recovered_bytes(rec)
b64 = base64.b64encode(blob).decode() b64 = base64.b64encode(blob).decode()
guest_ps( guest_ps(
f"[IO.File]::WriteAllBytes('{CFG['guest_path']}'," f"[IO.File]::WriteAllBytes('{CFG['guest_path']}',"