Files
proxmox-api-simulator/app/tasks/backup.py
T
Sergey Antropoff 0773f721ea Align sized cluster seeds and GET dumps with PVE wire shapes; restyle DATA panel.
- Scale small/large/big seeds (3×50 / 10×1000 / 20×2000) with proportional
  backups, snapshots, HA, replication, Ceph capacity, and OSD totals
  (10 / 100 / 500) plus matching node disks and crush/pg metadata
- Enrich handler responses for apt, certificates, qemu/lxc status, storage,
  SDN, metrics export, and related cluster/node dumps
- Flatten nested body_example fields into PARAMS and sync the request body
  via dotted paths (oVirt-style)
- Restyle DATA controls as size cards with full-width Reset to minimal /
  Refresh stats; unload reloads the minimal cluster
2026-07-18 08:46:11 +03:00

92 lines
4.3 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/cluster ops whose durable side effects were applied by the API handler.
if task.task_type != "vzdump":
await repository.append_log(task.id, f"{task.task_type} completed")
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