Skip to main content

Storage

Backend API​

Storage routes are implemented in llm_d_lens/storage/router.py, backed by llm_d_lens/storage/service.py and StorageVolumeService.

List storage volumes​

List Lens storage volumes and optionally filter by cluster, kind, readiness, purpose, or a name substring. The response also includes drift and usage metadata derived from the storage service.

GET /api/v1/storage/volumes

Request: no request body.

  • cluster_id (string, query, optional): filter to one cluster.
  • kind (string, query, optional): one of local-disk, nfs, or dynamic-pvc.
  • status (string, query, optional): one of pending, ready, failed, or deleting.
  • purpose (string, query, optional): currently model-cache.
  • query (string, query, optional): case-insensitive substring matched against name.

Response: returns an items array of storage-volume payloads augmented with inUseCount, nodesAdded, and modelCacheCount. usedBytes is reserved for future usage tracking and is currently always null.

{
"items": [
{
"id": "storage-3e9a0f52",
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"status": "ready",
"capacity": "500Gi",
"usedBytes": null,
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
},
"pvcName": "prism-storage-storage-3e9a0f52",
"pvName": "prism-storage-storage-3e9a0f52",
"knownNodes": [
"worker-1",
"worker-2"
],
"createdAt": "2026-09-23T08:30:00Z",
"updatedAt": "2026-09-23T08:31:12Z",
"inUseCount": 1,
"nodesAdded": [],
"modelCacheCount": 2
}
]
}

Get a storage volume​

Get one Lens storage volume by id, including its current node-drift and usage metadata. Access is cluster-scoped through the volume's owning cluster.

GET /api/v1/storage/volumes/{volume_id}

Request: no request body.

  • volume_id (string, path): storage volume id.

Response: one storage-volume payload with the same augmented fields used by the list endpoint.

{
"id": "storage-3e9a0f52",
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"status": "ready",
"capacity": "500Gi",
"usedBytes": null,
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
},
"pvcName": "prism-storage-storage-3e9a0f52",
"pvName": "prism-storage-storage-3e9a0f52",
"knownNodes": [
"worker-1",
"worker-2"
],
"createdAt": "2026-09-23T08:30:00Z",
"updatedAt": "2026-09-23T08:31:12Z",
"inUseCount": 1,
"nodesAdded": [],
"modelCacheCount": 2
}

Get storage resource status​

Get live PVC and PV status for selected storage volumes. Unknown ids are ignored and duplicate query ids are deduplicated before the Kubernetes read.

GET /api/v1/storage/volume-resource-status

Request: no request body.

  • volume_id (string[], query, repeated): one or more storage volume ids.

Response: items keyed by storage volume id.

{
"items": {
"storage-3e9a0f52": {
"persistentVolumeClaims": [
{
"name": "prism-storage-storage-3e9a0f52",
"namespace": "llm-d-bench-storage",
"phase": "Bound",
"capacity": "500Gi",
"accessModes": [
"ReadWriteMany"
],
"storageClassName": null,
"volumeMode": "Filesystem",
"createdAt": "2026-09-23T08:31:12Z",
"uid": "6ac7d2ef-1b9f-48df-9006-e4dff0b6cc02",
"volumeName": "prism-storage-storage-3e9a0f52",
"persistentVolume": {
"name": "prism-storage-storage-3e9a0f52",
"namespace": null,
"phase": "Bound",
"capacity": "500Gi",
"accessModes": [
"ReadWriteMany"
],
"storageClassName": null,
"volumeMode": "Filesystem",
"createdAt": "2026-09-23T08:31:12Z",
"uid": "3b84cf34-4a04-44b5-baf0-a57f7efc7ff1",
"reclaimPolicy": "Retain",
"source": {
"type": "hostPath",
"detail": "/srv/model-cache"
}
}
}
]
}
}
}

Create a storage volume​

Create and provision a new Lens storage volume. The route registers the pending record immediately and provisions the Kubernetes objects in the background.

POST /api/v1/storage/volumes

Request body (StorageVolumeCreateRequest):

  • clusterId (string): owning cluster id.
  • name (string): volume name; blank allows the route to auto-generate one. Names must be unique within a cluster and are compared case-insensitively, so two clusters may each hold an a100-model-cache while Cache and cache collide on one cluster. A collision returns a 409 storage_volume_name_conflict problem for an explicit name, or disambiguates an auto-generated name with a numeric suffix. The name is a display label only: the volume's id is its identity, and its PVC/PV are named prism-storage-{id}.
  • kind ("local-disk" | "nfs" | "dynamic-pvc")
  • capacity (string): Kubernetes quantity like 500Gi.
  • readOnly (boolean, default true)
  • purposes (StorageVolumePurpose[]): for example model-cache. A model-cache purpose cannot be combined with kind: "dynamic-pvc" (validation error); use local-disk or nfs instead.
  • localDisk (LocalDiskSpec, optional for local-disk)
  • nfs (NfsSpec, optional for nfs)
  • dynamicPvc (DynamicPvcSpec, optional for dynamic-pvc)

Example for a local-disk volume:

{
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"capacity": "500Gi",
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
}
}

Response: the newly registered volume in pending state.

{
"id": "storage-3e9a0f52",
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"status": "pending",
"capacity": "500Gi",
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
},
"knownNodes": [],
"createdAt": "2026-09-23T08:30:00Z",
"updatedAt": "2026-09-23T08:30:00Z",
"inUseCount": 0,
"nodesAdded": [],
"modelCacheCount": 0
}

Acknowledge new local-disk nodes​

Acknowledge newly added nodes for a local-disk storage volume after you have replicated the underlying contents. This resets the stored node baseline and clears drift warnings.

POST /api/v1/storage/volumes/{volume_id}/acknowledge-nodes

Request: no request body.

  • volume_id (string, path): storage volume id.

Response: the updated storage-volume payload with nodesAdded cleared.

{
"id": "storage-3e9a0f52",
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"status": "ready",
"capacity": "500Gi",
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
},
"knownNodes": [
"worker-1",
"worker-2",
"worker-3"
],
"createdAt": "2026-09-23T08:30:00Z",
"updatedAt": "2026-09-23T09:05:00Z",
"inUseCount": 1,
"nodesAdded": [],
"modelCacheCount": 2
}

Scan a volume for existing models​

Re-scan a ready Lens storage volume for model files that already exist on disk, then auto-register Model Cache entries for any discovered models. The import work runs in the background.

POST /api/v1/storage/volumes/{volume_id}/scan-models

Request: no request body.

  • volume_id (string, path): ready storage volume id.

Response: immediate acknowledgement that the scan has started. Returns a 409 storage_volume_not_ready problem if the volume's status is not ready.

{
"status": "scanning"
}

Delete a storage volume​

Delete a Lens storage volume registration and, where relevant, its Kubernetes resources. The route marks the volume as deleting immediately, then finishes cleanup in the background.

DELETE /api/v1/storage/volumes/{volume_id}

Request: no request body.

  • volume_id (string, path): storage volume id.
  • keep_model_files (boolean, query, default false): keep model files on disk while deleting only their Lens records.

Deletion is blocked outright (409 storage_volume_in_use) while the volume is referenced by one or more deployment executions; Model Cache entries on the volume no longer block deletion -- they are cascade-deleted (optionally keeping their files) as part of the background cleanup instead.

Response: the volume record as it enters deleting state.

{
"id": "storage-3e9a0f52",
"clusterId": "cluster-a",
"name": "shared-model-cache",
"kind": "local-disk",
"status": "deleting",
"capacity": "500Gi",
"readOnly": false,
"purposes": [
"model-cache"
],
"localDisk": {
"hostPath": "/srv/model-cache"
},
"pvcName": "prism-storage-storage-3e9a0f52",
"pvName": "prism-storage-storage-3e9a0f52",
"knownNodes": [
"worker-1",
"worker-2"
],
"createdAt": "2026-09-23T08:30:00Z",
"updatedAt": "2026-09-23T09:20:00Z",
"inUseCount": 0,
"nodesAdded": [],
"modelCacheCount": 2
}

List storage classes​

List the Kubernetes StorageClass objects visible to one cluster. Use this before creating dynamic-pvc storage volumes.

GET /api/v1/storage/storage-classes

Request: no request body.

  • cluster_id (string, query): target cluster id.

Response: items array of StorageClassSummary payloads.

{
"items": [
{
"name": "rook-cephfs",
"provisioner": "rook-ceph.cephfs.csi.ceph.com",
"isDefault": true
},
{
"name": "local-path",
"provisioner": "rancher.io/local-path",
"isDefault": false
}
]
}

List storage nodes​

List the cluster nodes as seen by the Storage module. Each item reports whether the node is ready and whether untolerated storage jobs can be scheduled there.

GET /api/v1/storage/nodes

Request: no request body.

  • cluster_id (string, query): target cluster id.

Response: items array of StorageNodeSummary payloads.

{
"items": [
{
"name": "worker-1",
"ready": true,
"schedulable": true
},
{
"name": "control-plane-1",
"ready": true,
"schedulable": false
}
]
}

See the full request/response schemas in the FastAPI OpenAPI schema (regenerate with npm run docs:api).