215 lines
6.9 KiB
Python
215 lines
6.9 KiB
Python
"""Inventory CRUD, host maintenance, datastore files, DVS."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends, Query, Response
|
|
|
|
from app.db.pool import Database
|
|
from app.dependencies import get_database
|
|
from app.vsphere import inventory
|
|
from app.vsphere.domain import content, inventory_ops
|
|
from app.vsphere.errors import invalid_argument
|
|
from app.vsphere.security.authz import require_privilege, require_read
|
|
from app.vsphere.security.session import SessionInfo
|
|
|
|
router = APIRouter(tags=["vSphere Inventory Ext"])
|
|
|
|
|
|
def _require_name(body: dict[str, Any]) -> str:
|
|
name = body.get("name")
|
|
if not name:
|
|
raise invalid_argument("name is required")
|
|
return str(name)
|
|
|
|
|
|
@router.post("/api/vcenter/datacenter")
|
|
async def create_dc(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Datacenter.Create")),
|
|
) -> str:
|
|
return await inventory_ops.create_datacenter(
|
|
database, name=_require_name(body), folder=str(body.get("folder") or "group-d1")
|
|
)
|
|
|
|
|
|
@router.delete("/api/vcenter/datacenter/{datacenter}")
|
|
async def delete_dc(
|
|
datacenter: str,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Datacenter.Delete")),
|
|
) -> Response:
|
|
await inventory_ops.delete_managed(database, datacenter)
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.post("/api/vcenter/cluster")
|
|
async def create_cluster(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Cluster.Create")),
|
|
) -> str:
|
|
return await inventory_ops.create_cluster(
|
|
database,
|
|
name=_require_name(body),
|
|
folder=str(body.get("folder") or "group-h23"),
|
|
drs_enabled=bool((body.get("drs") or {}).get("enabled", True)),
|
|
ha_enabled=bool((body.get("ha") or {}).get("enabled", True)),
|
|
)
|
|
|
|
|
|
@router.delete("/api/vcenter/cluster/{cluster}")
|
|
async def delete_cluster(
|
|
cluster: str,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Cluster.Delete")),
|
|
) -> Response:
|
|
await inventory_ops.delete_managed(database, cluster)
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.post("/api/vcenter/folder")
|
|
async def create_folder(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Folder.Create")),
|
|
) -> str:
|
|
spec = body.get("create_spec") if isinstance(body.get("create_spec"), dict) else body
|
|
return await inventory_ops.create_folder(
|
|
database,
|
|
name=_require_name(spec),
|
|
parent=str(spec.get("parent") or spec.get("folder") or "group-v23"),
|
|
folder_type=str(spec.get("type") or "VIRTUAL_MACHINE"),
|
|
)
|
|
|
|
|
|
@router.post("/api/vcenter/folder/{folder}")
|
|
async def folder_action(
|
|
folder: str,
|
|
action: str = Query(...),
|
|
body: dict[str, Any] | None = None,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Folder.Rename")),
|
|
) -> Response:
|
|
body = body or {}
|
|
if action == "rename":
|
|
await inventory_ops.rename_object(database, folder, _require_name(body))
|
|
elif action == "move":
|
|
parent = body.get("parent")
|
|
if not parent:
|
|
raise invalid_argument("parent is required")
|
|
await inventory_ops.move_object(database, folder, str(parent))
|
|
else:
|
|
raise invalid_argument(f"unsupported action {action}")
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.delete("/api/vcenter/folder/{folder}")
|
|
async def delete_folder(
|
|
folder: str,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Folder.Delete")),
|
|
) -> Response:
|
|
await inventory_ops.delete_managed(database, folder)
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.post("/api/vcenter/resource-pool")
|
|
async def create_rp(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Resource.CreatePool")),
|
|
) -> str:
|
|
return await inventory_ops.create_resource_pool(
|
|
database, name=_require_name(body), parent=str(body.get("parent") or "resgroup-22")
|
|
)
|
|
|
|
|
|
@router.delete("/api/vcenter/resource-pool/{resource_pool}")
|
|
async def delete_rp(
|
|
resource_pool: str,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Resource.DeletePool")),
|
|
) -> Response:
|
|
await inventory_ops.delete_managed(database, resource_pool)
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.post("/api/vcenter/host/{host}/maintenance")
|
|
async def host_maintenance(
|
|
host: str,
|
|
action: str = Query(...),
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Host.Config.Maintenance")),
|
|
) -> dict[str, Any]:
|
|
if action not in {"enter", "exit"}:
|
|
raise invalid_argument("action must be enter|exit")
|
|
return await inventory_ops.set_host_maintenance(database, host, enabled=(action == "enter"))
|
|
|
|
|
|
@router.get("/api/vcenter/datastore/{datastore}/files")
|
|
async def ds_files(
|
|
datastore: str,
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_read),
|
|
) -> list[dict[str, Any]]:
|
|
return await content.list_datastore_files(database, datastore)
|
|
|
|
|
|
@router.post("/api/vcenter/datastore/{datastore}/files")
|
|
async def ds_put_file(
|
|
datastore: str,
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Datastore.FileManagement")),
|
|
) -> Response:
|
|
path = body.get("path")
|
|
if not path:
|
|
raise invalid_argument("path is required")
|
|
await content.put_datastore_file(
|
|
database,
|
|
datastore,
|
|
str(path),
|
|
size=int(body.get("size") or 0),
|
|
file_type=str(body.get("type") or "FILE"),
|
|
)
|
|
return Response(status_code=204)
|
|
|
|
|
|
@router.get("/api/vcenter/network/dvs")
|
|
async def list_dvs(
|
|
database: Database = Depends(get_database), _: SessionInfo = Depends(require_read)
|
|
) -> list[dict[str, Any]]:
|
|
objects = await inventory.list_objects(database, type_name="VmwareDistributedVirtualSwitch")
|
|
return [{"dvs": o.moid, "name": o.name} for o in objects]
|
|
|
|
|
|
@router.post("/api/vcenter/network/dvs")
|
|
async def create_dvs(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Network.Assign")),
|
|
) -> str:
|
|
return await inventory_ops.create_dvs(
|
|
database, name=_require_name(body), folder=str(body.get("folder") or "group-n23")
|
|
)
|
|
|
|
|
|
@router.post("/api/vcenter/network/dvpg")
|
|
async def create_dvpg(
|
|
body: dict[str, Any],
|
|
database: Database = Depends(get_database),
|
|
_: SessionInfo = Depends(require_privilege("Network.Assign")),
|
|
) -> str:
|
|
dvs = body.get("dvs")
|
|
if not dvs:
|
|
raise invalid_argument("dvs is required")
|
|
return await inventory_ops.create_dvpg(
|
|
database,
|
|
name=_require_name(body),
|
|
dvs=str(dvs),
|
|
vlan_id=int(body.get("vlan_id") or 0),
|
|
)
|