6033967e6a
Ship Keystone auth, multi-service handlers (Yoga→Dalmatian), Compose/Helm packaging, API contract packs, and pytest/Pulumi coverage labs.
132 lines
5.3 KiB
Python
132 lines
5.3 KiB
Python
"""Seed ``os_api_objects`` rows for every contract pack resource_type.
|
|
|
|
Ensures list GETs across all OpenStack series have persistent DB rows
|
|
(so list/show handlers serve PostgreSQL rows only).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from asyncpg import Connection
|
|
|
|
from app.openstack.contract_loader import list_series, load_series_pack
|
|
from app.openstack.ids import oid
|
|
|
|
|
|
def _default_payload(service: str, resource_type: str, name: str, index: int) -> dict[str, Any]:
|
|
"""Reasonable lab JSON per resource family."""
|
|
|
|
base: dict[str, Any] = {
|
|
"name": name,
|
|
"status": "ACTIVE",
|
|
"enabled": True,
|
|
"description": f"lab {service} {resource_type} {index}",
|
|
"index": index,
|
|
}
|
|
# Light resource-specific hints for common clients.
|
|
if resource_type in {"share", "volume", "backup", "share_snapshot"}:
|
|
base.update({"size": 10, "status": "available"})
|
|
elif resource_type in {"zone", "tld"}:
|
|
base.update({"email": "hostmaster@lab.example", "ttl": 3600, "type": "PRIMARY"})
|
|
elif resource_type in {"alarm"}:
|
|
base.update({"type": "threshold", "state": "ok"})
|
|
elif resource_type in {"queue"}:
|
|
base.update({"_default_message_ttl": 3600})
|
|
elif resource_type in {"cluster"}:
|
|
base.update({"coe": "kubernetes", "status": "CREATE_COMPLETE", "node_count": 2})
|
|
elif resource_type in {"container"} and service == "zun":
|
|
base.update({"image": "cirros", "status": "Running"})
|
|
elif resource_type in {"container"} and service == "barbican":
|
|
base.update({"type": "generic", "status": "ACTIVE"})
|
|
elif resource_type in {"secret"}:
|
|
base.update({"secret_type": "passphrase", "payload_content_type": "text/plain"})
|
|
elif resource_type in {"datastore"}:
|
|
base.update({"type": "mysql", "version": "8.0"})
|
|
elif resource_type in {"instance"} and service == "trove":
|
|
base.update({"datastore": {"type": "mysql", "version": "8.0"}, "status": "ACTIVE"})
|
|
elif resource_type in {"workflow", "workbook"}:
|
|
base.update({"definition": "version: '2.0'\ndemo:\n tasks: {}"})
|
|
elif resource_type in {"dataframes"}:
|
|
base.update({"period": "3600"})
|
|
elif resource_type in {"quota", "quota_set"}:
|
|
base.update({"limit": 100, "in_use": index})
|
|
elif resource_type in {"status", "service_status", "health"}:
|
|
base.update({"status": "UP", "state": "up", "service": service})
|
|
elif resource_type == "ping":
|
|
base.update({"ping": "pong", "ok": True})
|
|
elif resource_type == "driver" and service == "ironic":
|
|
base.update({"hosts": ["simulator"], "type": "classic"})
|
|
elif resource_type == "agent" and service == "neutron":
|
|
base.update(
|
|
{
|
|
"agent_type": "L3 agent",
|
|
"host": f"network-{index}",
|
|
"alive": True,
|
|
"admin_state_up": True,
|
|
}
|
|
)
|
|
elif resource_type == "console_output":
|
|
base.update({"output": "Booting...\nSimulator console\n"})
|
|
elif resource_type == "console":
|
|
base.update(
|
|
{"type": "novnc", "url": "https://127.0.0.1:6080/vnc_auto.html?token=simulator"}
|
|
)
|
|
return base
|
|
|
|
|
|
def iter_pack_resource_types(*, series: str | None = None) -> set[tuple[str, str]]:
|
|
"""Return ``{(service, resource_type)}`` declared by GET collection/detail/custom ops."""
|
|
|
|
series_names = [series] if series else [str(item["series"]) for item in list_series()]
|
|
found: set[tuple[str, str]] = set()
|
|
for name in series_names:
|
|
packs = load_series_pack(name)
|
|
for pack in packs.values():
|
|
for op in pack.operations:
|
|
if op.method != "GET":
|
|
continue
|
|
if op.kind not in {"collection", "detail", "custom"}:
|
|
continue
|
|
if not op.resource_type or op.resource_type in {"version", "ping"}:
|
|
continue
|
|
found.add((pack.name, op.resource_type))
|
|
return found
|
|
|
|
|
|
async def seed_pack_surface_samples(
|
|
conn: Connection,
|
|
*,
|
|
series: str | None = None,
|
|
per_type: int = 3,
|
|
project_id: Any | None = None,
|
|
) -> dict[str, int]:
|
|
"""Insert lab rows for every pack resource_type (idempotent via stable oid)."""
|
|
|
|
types = iter_pack_resource_types(series=series)
|
|
inserted = 0
|
|
for service, resource_type in sorted(types):
|
|
for index in range(per_type):
|
|
name = f"{resource_type}-{index}"
|
|
item_id = oid(f"packseed:{service}:{resource_type}:{name}")
|
|
payload = _default_payload(service, resource_type, name, index)
|
|
payload["id"] = str(item_id)
|
|
status = str(payload.get("status") or "ACTIVE")
|
|
result = await conn.execute(
|
|
"""INSERT INTO os_api_objects(id, service, resource_type, project_id, name, status, data)
|
|
VALUES($1,$2,$3,$4,$5,$6,$7::jsonb)
|
|
ON CONFLICT (id) DO NOTHING""",
|
|
item_id,
|
|
service,
|
|
resource_type,
|
|
project_id,
|
|
name,
|
|
status,
|
|
json.dumps(payload),
|
|
)
|
|
# asyncpg: "INSERT 0 1" on insert, "INSERT 0 0" on conflict skip
|
|
if result.split()[-1] == "1":
|
|
inserted += 1
|
|
return {"resource_types": len(types), "rows_inserted": inserted, "per_type": per_type}
|