Disks and pools

Storage is set up once: data disks plus one SSD for the dedupe table become the system zpool. Pools are deduplicated datasets on it, and each library pairs with exactly one pool. The order the UI enforces is the order the API expects: system storage, then pool, then library.

GET /api/devices

read-only

Block devices with eligibility, plus the system storage status.

curl -sk https://appliance:8443/api/devices -H "Authorization: Bearer $OVTL_KEY"
{
  "devices": [
    {"path": "/dev/sda", "by_id": "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi0", "size_bytes": 53687091200, "model": "QEMU HARDDISK", "transport": "scsi", "rotational": true, "eligible": false, "reason": "operating system disk", "role": "os"},
    {"path": "/dev/sdb", "by_id": "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi1", "size_bytes": 3298534883328, "model": "QEMU HARDDISK", "transport": "scsi", "rotational": true, "eligible": true},
    {"path": "/dev/sdc", "by_id": "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive-scsi2", "size_bytes": 107374182400, "model": "QEMU HARDDISK", "transport": "scsi", "rotational": false, "eligible": true}
  ],
  "system": {"ready": false, "zpool": "ovz", "data_devs": [], "dedup_dev": "", "dedup_fixed": false, "dedup_ratio": 0, "size_bytes": 0, "alloc_bytes": 0, "data_size_bytes": 0, "data_alloc_bytes": 0, "dedup_size_bytes": 0, "dedup_alloc_bytes": 0}
}

Choose disks by size and type. path can re-letter across boots; by_id is stable. role is os, cache-device, or pool:<name> when a disk is already in use.

POST /api/storage/rescan

admin

Re-probes every SCSI host so a disk hot-added by the hypervisor appears without a reboot, then returns the fresh device list.

{"scsi_hosts": 3, "devices": ["…"], "system": {"…": "…"}}

POST /api/storage/setup

admin Destructive confirm: "create"

Builds the system zpool from the selected data disks and one dedupe SSD. Every selected disk is erased. The dedupe device choice is permanent for the life of the system storage. There is no force path past a disk the daemon considers ineligible.

Body field Type Notes
data_devs string[] Paths of the data disks.
dedup_dev string Path of the SSD for the dedupe table.
confirm string Must be "create".
curl -sk -X POST https://appliance:8443/api/storage/setup \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"data_devs":["/dev/sdb"],"dedup_dev":"/dev/sdc","confirm":"create"}'

Returns the system status object with ready: true. Validation failures (wrong confirmation, ineligible disk, storage already set up) are 400.

POST /api/storage/grow

admin

Expands the zpool onto a data disk the hypervisor enlarged underneath it. Online, non-destructive, idempotent. No body.

{"before_bytes": 3298534883328, "after_bytes": 4398046511104, "grew": true, "system": {"…": "…"}}

POST /api/storage/teardown

admin Destructive confirm: "teardown"

Destroys the system zpool and frees its data disks. Valid only once every pool has been removed. The dedupe SSD stays reserved as the permanent metadata device.

curl -sk -X POST https://appliance:8443/api/storage/teardown \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"confirm":"teardown"}'

Returns the system status object.

GET /api/pools

read-only

[
  {"id": 1, "name": "prod", "vg": "", "data_lv": "", "mountpoint": "/var/lib/openvtl/pools/prod", "data_dev": "", "cache_slice_bytes": 0, "virtual_size_bytes": 0, "state": "active", "created_at": "2026-08-31T00:03:40Z"}
]

state is creating, active, or removing. Only an active pool can home a library. The vg, data_lv, data_dev, and size fields are kept for shape stability from earlier storage designs and are empty on ZFS appliances. Live capacity and dedupe figures for a pool are in GET /api/status under pools[].

POST /api/pools

admin

Creates a deduplicated dataset on the system storage. Dedupe granularity (recordsize) is chosen from installed RAM at this moment and is fixed for the pool.

Body field Rules
name Lowercase letters, digits, _.
curl -sk -X POST https://appliance:8443/api/pools \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"name":"prod"}'
{"pool_id": 1}

201 on success. 409 while a graceful restart is draining.

DELETE /api/pools/{id}

admin Destructive confirm: pool name

Destroys the pool’s dataset and everything on it. Refused while a library is paired with the pool: delete the library first. The system storage and its disks are untouched.

curl -sk -X DELETE https://appliance:8443/api/pools/1 \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"confirm":"prod"}'
{"ok": true, "pool_id": 1}

404 for an unknown pool; 400 if the name does not match or a library still uses it.

GET /api/pool/history

read-only

Persisted capacity samples for the dashboard trend.

Query Default Notes
pool first pool Pool name.
hours 24 1 to 2160 (90 days).
[
  {"ts": "2026-09-01T06:00:00Z", "pool": "prod", "fs_used_bytes": 622770257920, "fs_total_bytes": 3408486400000, "vdo_used_bytes": 622770257920, "vdo_phys_bytes": 0, "vdo_saving_pct": 0, "logical_bytes": 7696581394432, "cache_used_pct": 0}
]

The vdo_* field names are historical; vdo_used_bytes is the dataset’s compressed size and logical_bytes is what the host wrote.