Files
proxmox-api-simulator/app/simulation/seed.py
T
2026-07-12 23:56:06 +03:00

102 lines
3.1 KiB
Python

"""Deterministic idempotent simulation seed profiles."""
from __future__ import annotations
import json
import uuid
from dataclasses import dataclass
import asyncpg # type: ignore[import-untyped]
from asyncpg import Connection
NAMESPACE = uuid.UUID("c9040a72-b391-4a7e-9864-3ae46291a531")
@dataclass(frozen=True, slots=True)
class SeedNode:
id: uuid.UUID
name: str
status: str
@dataclass(frozen=True, slots=True)
class SeedResource:
id: uuid.UUID
node_id: uuid.UUID
kind: str
external_id: str
state: dict[str, object]
@dataclass(frozen=True, slots=True)
class SeedProfile:
name: str
nodes: tuple[SeedNode, ...]
resources: tuple[SeedResource, ...]
def logical_state(self) -> dict[str, object]:
nodes = [{"name": node.name, "status": node.status} for node in self.nodes]
resources = [
{
"kind": resource.kind,
"external_id": resource.external_id,
"node": next(node.name for node in self.nodes if node.id == resource.node_id),
"state": resource.state,
}
for resource in self.resources
]
return {"profile": self.name, "nodes": nodes, "resources": resources}
def stable_id(name: str) -> uuid.UUID:
return uuid.uuid5(NAMESPACE, name)
def small_profile() -> SeedProfile:
first = SeedNode(stable_id("node:pve1"), "pve1", "online")
second = SeedNode(stable_id("node:pve2"), "pve2", "online")
resources = (
SeedResource(
stable_id("qemu:100"), first.id, "qemu", "100", {"name": "demo", "status": "stopped"}
),
SeedResource(
stable_id("storage:local"), first.id, "storage", "local", {"content": ["iso", "backup"]}
),
)
return SeedProfile("small", (first, second), resources)
async def apply_seed(connection: Connection, profile: SeedProfile) -> None:
async with connection.transaction():
await connection.executemany(
"""INSERT INTO nodes(id, name, status) VALUES($1, $2, $3)
ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, status=EXCLUDED.status""",
[(node.id, node.name, node.status) for node in profile.nodes],
)
await connection.executemany(
"""INSERT INTO resources(id, node_id, kind, external_id, state)
VALUES($1, $2, $3, $4, $5::jsonb)
ON CONFLICT (id) DO UPDATE SET node_id=EXCLUDED.node_id,
kind=EXCLUDED.kind, external_id=EXCLUDED.external_id, state=EXCLUDED.state""",
[
(
resource.id,
resource.node_id,
resource.kind,
resource.external_id,
json.dumps(resource.state, sort_keys=True),
)
for resource in profile.resources
],
)
async def seed_url(database_url: str) -> dict[str, object]:
connection = await asyncpg.connect(database_url)
try:
profile = small_profile()
await apply_seed(connection, profile)
return profile.logical_state()
finally:
await connection.close()