Initial commit: VMware vSphere API simulator scaffold.
Add the FastAPI app, PostgreSQL migrations, Docker/Helm packaging, API contracts, docs, client examples, and the unit/integration/compatibility test suite for local client and tooling labs without a real vCenter.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
"""Legacy `/rest/...` JSON wrappers mirroring Automation API `/api` paths."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request, Response
|
||||
|
||||
from app.db.pool import Database
|
||||
from app.dependencies import get_database
|
||||
from app.vsphere.domain import vm_ops
|
||||
from app.vsphere.errors import invalid_argument
|
||||
from app.vsphere.rest import router as core
|
||||
from app.vsphere.rest import tagging_rest
|
||||
from app.vsphere.security.authz import require_privilege, require_read
|
||||
from app.vsphere.security.session import SessionInfo
|
||||
|
||||
router = APIRouter(tags=["vSphere Legacy REST"])
|
||||
|
||||
|
||||
def _value(payload: Any) -> dict[str, Any]:
|
||||
return {"value": payload}
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/vm")
|
||||
async def rest_list_vms(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
names: list[str] | None = Query(default=None),
|
||||
power_states: list[str] | None = Query(default=None),
|
||||
hosts: list[str] | None = Query(default=None),
|
||||
folders: list[str] | None = Query(default=None),
|
||||
limit: int | None = Query(default=None, ge=1, le=5000),
|
||||
cursor: int = Query(default=0, ge=0),
|
||||
) -> dict[str, Any]:
|
||||
payload = await core.list_vms(
|
||||
database=database,
|
||||
_=session,
|
||||
names=names,
|
||||
power_states=power_states,
|
||||
hosts=hosts,
|
||||
folders=folders,
|
||||
datacenters=None,
|
||||
clusters=None,
|
||||
resource_pools=None,
|
||||
limit=limit,
|
||||
cursor=cursor,
|
||||
)
|
||||
return _value(payload)
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/vm/{vm}")
|
||||
async def rest_get_vm(
|
||||
vm: str,
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.get_vm(vm=vm, database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/host")
|
||||
async def rest_list_hosts(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.list_hosts(database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/datastore")
|
||||
async def rest_list_datastores(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.list_datastores(database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/network")
|
||||
async def rest_list_networks(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.list_networks(database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/datacenter")
|
||||
async def rest_list_datacenters(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.list_datacenters(database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/vcenter/cluster")
|
||||
async def rest_list_clusters(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.list_clusters(database=database, _=session))
|
||||
|
||||
|
||||
@router.get("/rest/appliance/system/version")
|
||||
async def rest_appliance_version(
|
||||
request: Request,
|
||||
database: Database = Depends(get_database),
|
||||
_: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await core.appliance_version(request=request, database=database))
|
||||
|
||||
|
||||
@router.post("/rest/vcenter/vm/{vm}/power")
|
||||
async def rest_power_vm(
|
||||
vm: str,
|
||||
action: str = Query(...),
|
||||
database: Database = Depends(get_database),
|
||||
_: SessionInfo = Depends(require_privilege("VirtualMachine.Interact.PowerOn")),
|
||||
) -> dict[str, Any]:
|
||||
task_id = await vm_ops.set_power(database, vm, action.lower())
|
||||
return _value({"task": task_id})
|
||||
|
||||
|
||||
# --- CIS tagging (govmomi / terraform-provider-vsphere / pulumi-vsphere) ---
|
||||
|
||||
|
||||
@router.get("/rest/com/vmware/cis/tagging/category")
|
||||
async def rest_list_categories(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await tagging_rest.list_categories(database=database, _=session))
|
||||
|
||||
|
||||
@router.post("/rest/com/vmware/cis/tagging/category")
|
||||
async def rest_category_action(
|
||||
body: dict[str, Any] | None = None,
|
||||
action: str | None = Query(default=None, alias="~action"),
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateCategory")),
|
||||
) -> Any:
|
||||
payload = body or {}
|
||||
act = (action or "create").lower()
|
||||
if act == "create":
|
||||
return _value(
|
||||
await tagging_rest.create_category(body=payload, database=database, _=session)
|
||||
)
|
||||
raise invalid_argument(f"unsupported ~action {act}")
|
||||
|
||||
|
||||
@router.get("/rest/com/vmware/cis/tagging/category/{category_id}")
|
||||
async def rest_get_category(
|
||||
category_id: str,
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(
|
||||
await tagging_rest.get_category(category_id=category_id, database=database, _=session)
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/rest/com/vmware/cis/tagging/category/{category_id}")
|
||||
async def rest_delete_category(
|
||||
category_id: str,
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateCategory")),
|
||||
) -> Response:
|
||||
return await tagging_rest.delete_category(category_id=category_id, database=database, _=session)
|
||||
|
||||
|
||||
@router.get("/rest/com/vmware/cis/tagging/tag")
|
||||
async def rest_list_tags(
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await tagging_rest.list_tags(database=database, _=session))
|
||||
|
||||
|
||||
@router.post("/rest/com/vmware/cis/tagging/tag")
|
||||
async def rest_tag_action(
|
||||
body: dict[str, Any] | None = None,
|
||||
action: str | None = Query(default=None, alias="~action"),
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateTag")),
|
||||
) -> Any:
|
||||
payload = body or {}
|
||||
act = (action or "create").lower()
|
||||
if act == "create":
|
||||
return _value(await tagging_rest.create_tag(body=payload, database=database, _=session))
|
||||
raise invalid_argument(f"unsupported ~action {act}")
|
||||
|
||||
|
||||
@router.get("/rest/com/vmware/cis/tagging/tag/{tag_id}")
|
||||
async def rest_get_tag(
|
||||
tag_id: str,
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_read),
|
||||
) -> dict[str, Any]:
|
||||
return _value(await tagging_rest.get_tag(tag_id=tag_id, database=database, _=session))
|
||||
|
||||
|
||||
@router.delete("/rest/com/vmware/cis/tagging/tag/{tag_id}")
|
||||
async def rest_delete_tag(
|
||||
tag_id: str,
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_privilege("InventoryService.Tagging.CreateTag")),
|
||||
) -> Response:
|
||||
return await tagging_rest.delete_tag(tag_id=tag_id, database=database, _=session)
|
||||
|
||||
|
||||
@router.post("/rest/com/vmware/cis/tagging/tag-association")
|
||||
async def rest_tag_association(
|
||||
body: dict[str, Any] | None = None,
|
||||
action: str | None = Query(default=None, alias="~action"),
|
||||
database: Database = Depends(get_database),
|
||||
session: SessionInfo = Depends(require_privilege("InventoryService.Tagging.AttachTag")),
|
||||
) -> Any:
|
||||
"""govmomi/terraform use ``?~action=`` instead of JSON ``action``."""
|
||||
|
||||
payload = dict(body or {})
|
||||
if action and "action" not in payload:
|
||||
payload["action"] = action
|
||||
result = await tagging_rest.tag_association(body=payload, database=database, _=session)
|
||||
if isinstance(result, Response):
|
||||
return result
|
||||
return _value(result)
|
||||
Reference in New Issue
Block a user