"""Boot volume snapshot after first Idle (once).""" from __future__ import annotations import time from collections.abc import Callable from typing import Any from gpu_rent.config import Config from gpu_rent.errors import CloudError from gpu_rent.os_client import find_snapshot_by_name Log = Callable[[str], None] def ensure_boot_snapshot( conn, *, boot_volume_id: str | None, cfg: Config, log: Log, ) -> Any | None: """Create named boot snapshot if missing. Safe to call every up.""" name = cfg.boot_snapshot_name existing = find_snapshot_by_name(conn, name) if existing: log(f"boot snapshot уже есть: {name}") return existing if not boot_volume_id: log("boot snapshot пропущен: нет boot_volume_id") return None log(f"создаю snapshot {name} с boot volume {boot_volume_id} (force)…") try: snap = conn.block_storage.create_snapshot( volume_id=boot_volume_id, name=name, description="gpu-rent first Idle; reuse for next boot", force=True, ) except TypeError: try: snap = conn.block_storage.create_snapshot( volume_id=boot_volume_id, name=name, force=True, ) except Exception as exc: raise CloudError(f"snapshot boot: {exc}") from exc except Exception as exc: raise CloudError(f"snapshot boot: {exc}") from exc snap_id = getattr(snap, "id", None) deadline = time.time() + 1800 while time.time() < deadline: cur = conn.block_storage.get_snapshot(snap_id) status = (getattr(cur, "status", None) or "").lower() if status == "available": log(f"snapshot {name} ready ({snap_id})") return cur if status in {"error", "error_deleting"}: raise CloudError(f"snapshot {name} в статусе {status}") time.sleep(8) raise CloudError(f"snapshot {name} не стал available за 30 мин")