Add OpenStack request-body schemas and nested console PARAM sync.
This commit is contained in:
+814
-184
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ from app.openstack.contract_loader import (
|
||||
major_for_series,
|
||||
series_for_major,
|
||||
)
|
||||
from app.openstack.request_examples import body_fields_from_example, schema_example
|
||||
|
||||
_PATH_PARAM = re.compile(r"\{([^{}]+)\}")
|
||||
|
||||
@@ -87,25 +88,24 @@ def openstack_method_payload(
|
||||
for name in path_params
|
||||
]
|
||||
body_fields: list[dict[str, Any]] = []
|
||||
if op.method in {"POST", "PUT", "PATCH"} and op.kind in {
|
||||
"collection",
|
||||
"item",
|
||||
"action",
|
||||
"custom",
|
||||
}:
|
||||
key = op.item_key or op.collection_key or "resource"
|
||||
body_fields.append(
|
||||
{
|
||||
"name": key,
|
||||
"type": "object",
|
||||
"description": "Request body envelope",
|
||||
"optional": op.kind == "action",
|
||||
"enum": [],
|
||||
"example": {key: {"name": "example"}}
|
||||
if op.kind != "action"
|
||||
else {op.action_name or "os-start": None},
|
||||
}
|
||||
)
|
||||
body_example: dict[str, Any] = {}
|
||||
if op.method in {"POST", "PUT", "PATCH"}:
|
||||
if op.request_schema:
|
||||
example = schema_example(op.request_schema)
|
||||
body_example = example if isinstance(example, dict) else {}
|
||||
# PARAM drawer: nested leaves from body_example (oVirt-style).
|
||||
body_fields = body_fields_from_example(body_example)
|
||||
else:
|
||||
# Schemas are required for write ops; keep a minimal
|
||||
# envelope only as a last-resort safety net.
|
||||
key = op.item_key or op.collection_key or "resource"
|
||||
if op.kind == "action":
|
||||
action = op.action_name or "os-start"
|
||||
body_example = {action: None}
|
||||
body_fields = body_fields_from_example(body_example)
|
||||
else:
|
||||
body_example = {key: {"name": "example"}}
|
||||
body_fields = body_fields_from_example(body_example)
|
||||
return {
|
||||
"major": major,
|
||||
"series": series,
|
||||
@@ -138,6 +138,7 @@ def openstack_method_payload(
|
||||
if op.method == "GET" and op.kind in {"collection", "detail"}
|
||||
else [],
|
||||
"body_fields": body_fields,
|
||||
"body_example": body_example,
|
||||
"returns": {"type": "object"},
|
||||
"permissions": [],
|
||||
"service": pack.name,
|
||||
@@ -160,6 +161,7 @@ def openstack_series_majors(runtime_version: str | None = None) -> dict[str, obj
|
||||
"artifact_url": f"contracts/openstack/{item['series']}",
|
||||
"bundled": True,
|
||||
"operation_count": item["operation_count"],
|
||||
"microversions": item.get("microversions") or [],
|
||||
}
|
||||
for item in sorted(series, key=lambda row: row["major"])
|
||||
],
|
||||
|
||||
+26
-4
@@ -291,20 +291,42 @@ async def ui_openstack_microversions(request: Request) -> JSONResponse:
|
||||
|
||||
@router.post("/ui/api/demo/load", include_in_schema=False)
|
||||
async def ui_demo_load(request: Request) -> JSONResponse:
|
||||
"""Load synthetic OpenStack cloud (~1000 servers + full topology)."""
|
||||
"""Load synthetic OpenStack cloud (size: small | large | big)."""
|
||||
|
||||
from app.openstack.demo_cloud import DEMO_SIZE_DEFAULT, resolve_demo_size
|
||||
|
||||
size = DEMO_SIZE_DEFAULT
|
||||
try:
|
||||
payload = await request.json()
|
||||
except Exception:
|
||||
payload = {}
|
||||
if isinstance(payload, dict) and payload.get("size"):
|
||||
size = str(payload["size"])
|
||||
elif request.query_params.get("size"):
|
||||
size = str(request.query_params.get("size"))
|
||||
try:
|
||||
resolve_demo_size(size)
|
||||
except ValueError as error:
|
||||
raise HTTPException(status_code=400, detail=str(error)) from error
|
||||
|
||||
pool = _database_pool(request)
|
||||
try:
|
||||
async with pool.acquire() as connection:
|
||||
async with connection.transaction():
|
||||
result = await seed_openstack_demo(connection)
|
||||
result = await seed_openstack_demo(connection, size=size)
|
||||
summary = await openstack_demo_summary(connection)
|
||||
except Exception as error:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"failed to load OpenStack demo cloud: {error}"
|
||||
) from error
|
||||
return JSONResponse(
|
||||
{"ok": True, "profile": result["profile"], "summary": summary, "seed": result}
|
||||
{
|
||||
"ok": True,
|
||||
"profile": result["profile"],
|
||||
"size": result.get("size"),
|
||||
"summary": summary,
|
||||
"seed": result,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -323,7 +345,7 @@ async def ui_demo_unload(request: Request) -> JSONResponse:
|
||||
summary = await openstack_demo_summary(connection)
|
||||
except Exception as error:
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"failed to remove demo data: {error}"
|
||||
status_code=500, detail=f"failed to reset to minimal cluster: {error}"
|
||||
) from error
|
||||
return JSONResponse(
|
||||
{"ok": True, "profile": result.get("profile", "minimal"), "summary": summary}
|
||||
|
||||
Reference in New Issue
Block a user