Files
proxmox-api-simulator/app/tasks/backup.py
T
Sergey Antropoff 777926487b Add a stateful Proxmox API console and broad handler coverage beyond the
initial QEMU slice, backed by imported contracts for majors 6–9.
- Implement durable handlers for access/auth, cluster, LXC, storage, HA,
  firewall, Ceph, SDN, ACME, notifications, pools, mapping, and node ops
- Serve an interactive Web UI with catalog browsing, demo seed controls,
  and OpenAPI/help surfaces
- Bundle PVE 6.4-15, 7.4-16, and 8.4.5 contract revisions alongside 9.2.3
- Support in-memory runtime contract Apply (POST /ui/api/contract/apply)
  so /version and /api2 routes follow the selected major until restart
- Expand seed profiles (including demo-cluster), migrations 007–008, TLS
  gateway config, Compose/Makefile tooling, and compatibility evidence
- Tighten .gitignore for macOS, hidden directories (.*/), and local secrets
2026-07-16 01:08:01 +03:00

87 lines
4.0 KiB
Python

"""Worker semantics for backup/vzdump tasks."""
from __future__ import annotations
import json
from typing import Any
from app.simulation.clock import Clock
from app.simulation.seed import stable_id
from app.tasks.repository import Task, TaskRepository
from app.tasks.worker import TaskHandler
def backup_handler(repository: TaskRepository, clock: Clock) -> TaskHandler:
async def execute(task: Task) -> dict[str, Any]:
if task.task_type == "aptupdate":
node = str(task.payload.get("node", "unknown"))
await repository.append_log(task.id, f"starting apt update on {node}")
await clock.sleep(1.0)
async with repository.pool.acquire() as connection:
metadata = await connection.fetchval(
"SELECT metadata FROM nodes WHERE name=$1",
node,
)
if metadata is not None:
payload = json.loads(metadata) if isinstance(metadata, str) else dict(metadata)
ops = payload.setdefault("ops", {})
apt = ops.setdefault("apt", {})
packages = list(apt.get("packages") or [])
for package in packages:
if isinstance(package, dict) and package.get("Status") == "upgradable":
package["Status"] = "installed"
if package.get("Version"):
package["OldVersion"] = package["Version"]
apt["packages"] = packages
apt["update"] = {"status": "stopped", "exitstatus": "OK"}
payload["ops"] = ops
await connection.execute(
"UPDATE nodes SET metadata=$2::jsonb, updated_at=now() WHERE name=$1",
node,
json.dumps(payload, sort_keys=True),
)
await repository.append_log(task.id, "apt update finished")
return {"status": "OK"}
node = str(task.payload["node"])
vmids = [str(item) for item in task.payload.get("vmids", [])]
storage_id = str(task.payload.get("storage") or "nfs-backup")
await repository.append_log(task.id, f"starting vzdump on {node} for {len(vmids)} guests")
async with repository.pool.acquire() as connection:
storage_resource_id = await connection.fetchval(
"SELECT resource_id FROM storages WHERE storage_id=$1",
storage_id,
)
if storage_resource_id is None:
raise ValueError(f"storage {storage_id} does not exist")
created = 0
for index, vmid in enumerate(vmids):
resource_id = await connection.fetchval(
"""SELECT r.id FROM resources r JOIN nodes n ON n.id=r.node_id
WHERE n.name=$1 AND r.kind='qemu' AND r.external_id=$2""",
node,
vmid,
)
volume_id = f"backup/vzdump-qemu-{vmid}-{task.id.hex[:8]}-{index:04d}.vma.zst"
await connection.execute(
"""INSERT INTO backups(
id, resource_id, storage_resource_id, volume_id, size_bytes, metadata
) VALUES($1, $2, $3, $4, $5, $6::jsonb)
ON CONFLICT (storage_resource_id, volume_id) DO NOTHING""",
stable_id(f"backup-task:{task.id}:{vmid}"),
resource_id,
storage_resource_id,
volume_id,
(8 + index) * 1024**3,
json.dumps(
{"mode": task.payload.get("mode", "snapshot"), "type": "vzdump"},
sort_keys=True,
),
)
created += 1
await repository.append_log(task.id, f"backup archive created: {volume_id}")
await repository.append_log(task.id, f"vzdump finished ({created} archives)")
return {"created": created}
return execute