feat: add basic QEMU and task vertical slice

This commit is contained in:
Sergey Antropoff
2026-07-13 00:41:13 +03:00
parent d141994540
commit 4b923e63f2
15 changed files with 452 additions and 14 deletions
+43
View File
@@ -0,0 +1,43 @@
"""Worker semantics for asynchronous QEMU transitions."""
from __future__ import annotations
import json
import uuid
from app.simulation.clock import Clock
from app.simulation.transitions import VmState, plan_transition
from app.tasks.repository import Task, TaskRepository
from app.tasks.worker import TaskHandler
def qemu_handler(repository: TaskRepository, clock: Clock) -> TaskHandler:
async def execute(task: Task) -> dict[str, str]:
operation = task.task_type.removeprefix("qemu-")
resource_id = uuid.UUID(str(task.payload["resource_id"]))
async with repository.pool.acquire() as connection:
row = await connection.fetchrow("SELECT state FROM resources WHERE id=$1", resource_id)
if row is None:
raise ValueError("resource disappeared")
raw = row["state"]
state = json.loads(raw) if isinstance(raw, str) else dict(raw)
transition = plan_transition(VmState(str(state["status"])), operation)
state["status"] = transition.intermediate
await connection.execute(
"UPDATE resources SET state=$2::jsonb WHERE id=$1",
resource_id,
json.dumps(state),
)
await repository.append_log(task.id, f"VM {operation} started")
await clock.sleep(1.0)
async with repository.pool.acquire() as connection:
state["status"] = transition.after
await connection.execute(
"UPDATE resources SET state=$2::jsonb WHERE id=$1",
resource_id,
json.dumps(state),
)
await repository.append_log(task.id, f"VM {operation} completed")
return {"status": str(transition.after)}
return execute
+12
View File
@@ -178,6 +178,18 @@ class TaskRepository:
row = await self.pool.fetchrow("SELECT * FROM tasks WHERE id=$1", task_id)
return _task(row) if row is not None else None
async def get_by_upid(self, upid: str) -> Task | None:
row = await self.pool.fetchrow("SELECT * FROM tasks WHERE upid=$1", upid)
return _task(row) if row is not None else None
async def list_for_node(self, node: str) -> tuple[Task, ...]:
rows = await self.pool.fetch(
"""SELECT * FROM tasks WHERE payload->>'node'=$1
ORDER BY created_at DESC LIMIT 1000""",
node,
)
return tuple(_task(row) for row in rows)
async def logs(self, task_id: uuid.UUID) -> tuple[str, ...]:
rows = await self.pool.fetch(
"SELECT message FROM task_logs WHERE task_id=$1 ORDER BY sequence", task_id