feat(poc): rewind MCP, skill, and recover-ladder docs
Initial PoC: find_protection, tagged checkpoints, FLR, mutating catalog. Lab 10.9 status enums (0=Initializing, 1=MeetingSLA). Credentials stay in gitignored config.json.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
"""FLR session helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from zerto_rewind_mcp.client import ZertoClient, ZertoError
|
||||
from zerto_rewind_mcp.util import pick
|
||||
|
||||
|
||||
def session_id_from(payload: Any) -> str:
|
||||
if payload is None:
|
||||
raise ZertoError("FLR start returned empty body")
|
||||
if isinstance(payload, str):
|
||||
return payload.strip().strip('"')
|
||||
if isinstance(payload, dict):
|
||||
value = pick(
|
||||
payload,
|
||||
"sessionId",
|
||||
"SessionId",
|
||||
"flrSessionIdentifier",
|
||||
"identifier",
|
||||
"Identifier",
|
||||
"id",
|
||||
"Id",
|
||||
)
|
||||
if value:
|
||||
return str(value)
|
||||
raise ZertoError(f"Could not read FLR session id from {payload!r}")
|
||||
|
||||
|
||||
def download_token_from(payload: Any) -> str:
|
||||
if isinstance(payload, str):
|
||||
return payload.strip().strip('"')
|
||||
if isinstance(payload, dict):
|
||||
value = pick(payload, "token", "Token", "downloadToken", "DownloadToken")
|
||||
if value:
|
||||
return str(value)
|
||||
raise ZertoError(f"Could not read FLR download token from {payload!r}")
|
||||
|
||||
|
||||
async def wait_flr_ready(
|
||||
client: ZertoClient,
|
||||
session_id: str,
|
||||
*,
|
||||
timeout_s: float = 180.0,
|
||||
interval_s: float = 3.0,
|
||||
) -> dict[str, Any]:
|
||||
deadline = asyncio.get_event_loop().time() + timeout_s
|
||||
last: Any = None
|
||||
while asyncio.get_event_loop().time() < deadline:
|
||||
last = await client.get_flr(session_id)
|
||||
status = ""
|
||||
if isinstance(last, dict):
|
||||
status = str(pick(last, "Status", "status", "state", "State") or "")
|
||||
if status.lower() in {"ready", "mounted", "available"}:
|
||||
return last if isinstance(last, dict) else {"status": status}
|
||||
if "mountinprogress" in status.lower() or "inprogress" in status.lower():
|
||||
await asyncio.sleep(interval_s)
|
||||
continue
|
||||
if status.lower() in {"failed", "error"}:
|
||||
raise ZertoError(f"FLR session {session_id} failed: {last}")
|
||||
# some appliances omit status once mounted
|
||||
if isinstance(last, dict) and not status:
|
||||
return last
|
||||
await asyncio.sleep(interval_s)
|
||||
raise ZertoError(
|
||||
f"FLR session {session_id} not ready within {timeout_s:.0f}s (last={last!r}). "
|
||||
"Retry browse after a minute, or check EJC is not running and no clone/test is active."
|
||||
)
|
||||
Reference in New Issue
Block a user