demo: read the recovered content instead of a path
zerto_recover_file no longer writes the file to this host and hands back a path, because a path here means nothing to a caller elsewhere and letting the caller choose it was an arbitrary write. Both drivers still read rec["path"], so they broke. They now use the returned content. A small recovered_bytes() helper in each driver decodes the text or base64 form, so the Windows copy-back keeps shipping exact bytes rather than letting PowerShell rewrite line endings, which is the bug that put a stray CR in an earlier take. The Linux driver writes the bytes to a local file before scp, since scp needs something on disk to send. Verified against a live recovery: the helper returns 164 bytes whose sha256 matches the one the server reported, and the keys the on-screen show() filter uses are all still present. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_016yVfC5nvZowoLFnEGWhLGn
This commit is contained in:
+17
-4
@@ -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"))
|
||||
|
||||
|
||||
|
||||
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=""):
|
||||
sys.stdout.write(s + "\n"); sys.stdout.flush()
|
||||
|
||||
@@ -212,8 +223,8 @@ async def main():
|
||||
if not rec.get("ok"):
|
||||
w(f"{RED} FLR failed{RST}"); return 1
|
||||
beat(1.5)
|
||||
w(f"{YEL} $ cat {rec['path']}{RST}")
|
||||
body = Path(rec["path"]).read_text()
|
||||
w(f"{YEL} recovered {rec['bytes']} bytes, sha256 {rec['sha256'][:16]}...{RST}")
|
||||
body = recovered_bytes(rec).decode()
|
||||
for line in body.splitlines():
|
||||
w(f"{GREEN}{BOLD} | {line}{RST}")
|
||||
beat(2.5)
|
||||
@@ -222,11 +233,13 @@ async def main():
|
||||
step("Copy it back to the guest")
|
||||
note("This part is scp, not Zerto. Zerto got the bytes back.")
|
||||
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", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null",
|
||||
"-o", "LogLevel=ERROR"] + (["-i", CFG["key"]] if CFG.get("key") else []) + \
|
||||
[rec["path"], f"{target}:{CFG['guest_path']}"]
|
||||
w(f"{YEL} $ scp {Path(rec['path']).name} {target}:{CFG['guest_path']}{RST}")
|
||||
[str(local_copy), f"{target}:{CFG['guest_path']}"]
|
||||
w(f"{YEL} $ scp {rec['name']} {target}:{CFG['guest_path']}{RST}")
|
||||
subprocess.run(scp, capture_output=True, text=True, timeout=60)
|
||||
guest(f"cat {CFG['guest_path']}")
|
||||
beat(1.5)
|
||||
|
||||
+13
-2
@@ -22,6 +22,17 @@ T0 = time.time()
|
||||
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=""):
|
||||
sys.stdout.write(s + "\n"); sys.stdout.flush()
|
||||
|
||||
@@ -155,7 +166,7 @@ async def main():
|
||||
show(rec, keep=["ok", "bytes", "flr_path", "unmount"])
|
||||
if not rec.get("ok"):
|
||||
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}")
|
||||
for line in body.splitlines():
|
||||
w(f"{GREEN}{BOLD} | {line}{RST}")
|
||||
@@ -166,7 +177,7 @@ async def main():
|
||||
# endings and appends one, which makes the restored file differ
|
||||
# from what Zerto handed back. WriteAllBytes does not.
|
||||
import base64
|
||||
blob = Path(rec["path"]).read_bytes()
|
||||
blob = recovered_bytes(rec)
|
||||
b64 = base64.b64encode(blob).decode()
|
||||
guest_ps(
|
||||
f"[IO.File]::WriteAllBytes('{CFG['guest_path']}',"
|
||||
|
||||
Reference in New Issue
Block a user