fix(flr): partition-rooted FLR paths + agent/intent in checkpoint names (#2)
This commit was merged in pull request #2.
This commit is contained in:
@@ -13,7 +13,12 @@ from zerto_rewind_mcp.checkpoints import checkpoint_id, checkpoint_tag, make_tag
|
||||
from zerto_rewind_mcp.client import ZertoClient, ZertoError
|
||||
from zerto_rewind_mcp.config import load_catalog, load_config
|
||||
from zerto_rewind_mcp.protection import find_from_rows
|
||||
from zerto_rewind_mcp.recover import download_token_from, session_id_from, wait_flr_ready
|
||||
from zerto_rewind_mcp.recover import (
|
||||
download_token_from,
|
||||
resolve_flr_path,
|
||||
session_id_from,
|
||||
wait_flr_ready,
|
||||
)
|
||||
from zerto_rewind_mcp.util import pick
|
||||
|
||||
mcp = FastMCP("zerto_rewind_mcp")
|
||||
@@ -110,12 +115,20 @@ async def zerto_create_tagged_checkpoint(
|
||||
query: str,
|
||||
change_id: str,
|
||||
agent: str = "agent",
|
||||
action: str = "",
|
||||
tag: str | None = None,
|
||||
) -> str:
|
||||
"""Insert the same tagged checkpoint on every protecting VPG, then wait.
|
||||
|
||||
Call this before every cataloged guest-mutating tool. If it fails, refuse the change.
|
||||
Tag format defaults to ai:<agent>:<change_id>:<utc>.
|
||||
|
||||
action: say plainly what you are about to do to this VM, e.g.
|
||||
"edit /etc/nginx/nginx.conf" or "apt upgrade". It is written into the
|
||||
checkpoint name so an operator reading the journal in the Zerto UI can see
|
||||
which agent made the checkpoint and why. CheckpointName is the only field
|
||||
the Zerto API accepts, so this is the only place that context can live.
|
||||
|
||||
Name format: ai:<agent> | <action> | vm=<vm> | change=<change_id> | <utc>
|
||||
Docs: tagged checkpoints are not supported when the protected site is Azure or AWS;
|
||||
run this against the vSphere protected ZVM.
|
||||
"""
|
||||
@@ -125,7 +138,12 @@ async def zerto_create_tagged_checkpoint(
|
||||
payload = result.as_dict()
|
||||
payload["ok"] = False
|
||||
return _dump(payload)
|
||||
use_tag = tag or make_tag(agent, change_id)
|
||||
use_tag = tag or make_tag(
|
||||
agent,
|
||||
change_id,
|
||||
action=action,
|
||||
vm_name=result.vm.vm_name if result.vm else None,
|
||||
)
|
||||
out = await tag_vpgs(get_client(), result, use_tag)
|
||||
return _dump(out)
|
||||
except ZertoError as exc:
|
||||
@@ -182,13 +200,19 @@ async def zerto_guard_before_mutate(
|
||||
query: str,
|
||||
change_id: str,
|
||||
agent: str = "agent",
|
||||
action: str = "",
|
||||
) -> str:
|
||||
"""Find protection and tag. If ok is not true, do not mutate.
|
||||
|
||||
The skill and any cataloged tool wrapper call this first. Capture-before-execute:
|
||||
if this does not return ok=true, do not run the guest-mutating call.
|
||||
|
||||
Pass action describing the change you are about to make; it is recorded in
|
||||
the checkpoint name so the journal says which agent did what.
|
||||
"""
|
||||
return await zerto_create_tagged_checkpoint(query=query, change_id=change_id, agent=agent)
|
||||
return await zerto_create_tagged_checkpoint(
|
||||
query=query, change_id=change_id, agent=agent, action=action
|
||||
)
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
@@ -261,8 +285,7 @@ async def zerto_recover_file(
|
||||
"ok": False,
|
||||
"needs_confirm": True,
|
||||
"message": (
|
||||
"Set confirmed=true after a human yes. "
|
||||
"FLR mounts a disk on the recovery site."
|
||||
"Set confirmed=true after a human yes. FLR mounts a disk on the recovery site."
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -279,8 +302,8 @@ async def zerto_recover_file(
|
||||
)
|
||||
session_id = session_id_from(started)
|
||||
await wait_flr_ready(client, session_id)
|
||||
await client.browse_flr(session_id, path="", recursive=False)
|
||||
token_payload = await client.download_flr(session_id, [guest_path])
|
||||
flr_path = await resolve_flr_path(client, session_id, guest_path)
|
||||
token_payload = await client.download_flr(session_id, [flr_path])
|
||||
token = download_token_from(token_payload)
|
||||
blob = await client.fetch_download(token)
|
||||
name = Path(guest_path.replace("\\", "/")).name or "recovered.bin"
|
||||
@@ -292,6 +315,7 @@ async def zerto_recover_file(
|
||||
"path": str(out_path.resolve()),
|
||||
"bytes": len(blob),
|
||||
"session_id": session_id,
|
||||
"flr_path": flr_path,
|
||||
"message": f"Wrote {len(blob)} bytes to {out_path}",
|
||||
}
|
||||
)
|
||||
@@ -394,8 +418,7 @@ async def zerto_offsite_clone(
|
||||
"ok": False,
|
||||
"needs_confirm": True,
|
||||
"message": (
|
||||
"Set confirmed=true after a human yes. "
|
||||
"Clone consumes recovery datastore."
|
||||
"Set confirmed=true after a human yes. Clone consumes recovery datastore."
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user