Files
openstack-api-simulator/app/handlers/pools.py
T
inecs 6033967e6a Initial commit: stateful OpenStack API laboratory simulator.
Ship Keystone auth, multi-service handlers (Yoga→Dalmatian), Compose/Helm
packaging, API contract packs, and pytest/Pulumi coverage labs.
2026-07-18 04:26:48 +03:00

135 lines
5.3 KiB
Python

"""Resource pool semantic handlers."""
from __future__ import annotations
import json
import uuid
from typing import Any
from fastapi import Request
from app.api.errors import ApiError
from app.api.registry import HandlerRegistry
from app.handlers.common import database, state, values
from app.simulation.seed import CLUSTER_ID, stable_id
async def _pool_members(request: Request, pool_id: uuid.UUID) -> list[str]:
rows = await database(request).pool.fetch(
"""SELECT r.external_id FROM pool_members pm
JOIN resources r ON r.id = pm.resource_id
WHERE pm.pool_id=$1 ORDER BY r.external_id::integer""",
pool_id,
)
return [str(row["external_id"]) for row in rows]
def register_pool_handlers(registry: HandlerRegistry) -> None:
async def pool_list(request: Request, inputs: dict[str, Any]) -> list[dict[str, Any]]:
payload = values(inputs)
filter_poolid = payload.get("poolid")
rows = await database(request).pool.fetch(
"""SELECT id, pool_id, comment, metadata FROM pools
WHERE ($1::text IS NULL OR pool_id=$1)
ORDER BY pool_id""",
str(filter_poolid) if filter_poolid is not None else None,
)
result: list[dict[str, Any]] = []
for row in rows:
metadata = state(row["metadata"])
members = await _pool_members(request, row["id"])
if not members and isinstance(metadata.get("members"), list):
members = [str(item) for item in metadata["members"]]
item: dict[str, Any] = {
"poolid": str(row["pool_id"]),
"members": members,
}
if row["comment"] is not None:
item["comment"] = str(row["comment"])
elif metadata.get("comment"):
item["comment"] = str(metadata["comment"])
result.append(item)
return result
async def pool_get(request: Request, inputs: dict[str, Any]) -> dict[str, Any]:
items = await pool_list(request, inputs)
if not items:
raise ApiError(404, "pool does not exist")
return items[0]
async def pool_create(request: Request, inputs: dict[str, Any]) -> None:
payload = values(inputs)
poolid = str(payload["poolid"])
exists = await database(request).pool.fetchval(
"SELECT EXISTS(SELECT 1 FROM pools WHERE pool_id=$1)",
poolid,
)
if exists:
raise ApiError(409, "pool already exists")
await database(request).pool.execute(
"""INSERT INTO pools(id, cluster_id, pool_id, comment, metadata)
VALUES($1, $2, $3, $4, $5::jsonb)""",
stable_id(f"pool:{poolid}"),
CLUSTER_ID,
poolid,
payload.get("comment"),
json.dumps({"members": []}, sort_keys=True),
)
async def pool_update(request: Request, inputs: dict[str, Any]) -> None:
payload = values(inputs)
poolid = str(payload["poolid"])
pool_row = await database(request).pool.fetchrow(
"SELECT id FROM pools WHERE pool_id=$1",
poolid,
)
if pool_row is None:
raise ApiError(404, "pool does not exist")
if payload.get("comment") is not None:
await database(request).pool.execute(
"UPDATE pools SET comment=$2 WHERE pool_id=$1",
poolid,
payload.get("comment"),
)
if "vms" in payload:
vmids = [item.strip() for item in str(payload["vms"]).split(",") if item.strip()]
for vmid in vmids:
resource = await database(request).pool.fetchrow(
"""SELECT id FROM resources
WHERE kind IN ('qemu', 'lxc') AND external_id=$1""",
vmid,
)
if resource is None:
continue
await database(request).pool.execute(
"""INSERT INTO pool_members(pool_id, resource_id)
VALUES($1, $2) ON CONFLICT DO NOTHING""",
pool_row["id"],
resource["id"],
)
if "delete" in payload:
vmids = [item.strip() for item in str(payload["delete"]).split(",") if item.strip()]
await database(request).pool.execute(
"""DELETE FROM pool_members pm USING resources r
WHERE pm.pool_id=$1 AND pm.resource_id=r.id AND r.external_id = ANY($2::text[])""",
pool_row["id"],
vmids,
)
async def pool_delete(request: Request, inputs: dict[str, Any]) -> None:
poolid = str(values(inputs)["poolid"])
status = await database(request).pool.execute(
"DELETE FROM pools WHERE pool_id=$1",
poolid,
)
if status != "DELETE 1":
raise ApiError(404, "pool does not exist")
registry.register("/pools", "GET", pool_list)
registry.register("/pools", "POST", pool_create)
registry.register("/pools", "PUT", pool_update)
registry.register("/pools", "DELETE", pool_delete)
registry.register("/pools/{poolid}", "GET", pool_get)
registry.register("/pools/{poolid}", "PUT", pool_update)
registry.register("/pools/{poolid}", "DELETE", pool_delete)