System, health and settings

GET /healthz

public

Liveness plus the running version. Also served on the plaintext listener. The update flow polls this until the target version answers.

curl -sk https://appliance:8443/healthz
{"ok": true, "ts": "2026-09-01T07:12:04.118Z", "version": "v1.0.0"}

GET /api/status

read-only

The whole live picture in one document: every library with its drives, slots, and cartridges; every pool’s statistics; FC target state; and an aggregate fabric summary. The UI polls this and applies the event stream on top. It is the first thing to read when a caption in the UI is vague.

curl -sk https://appliance:8443/api/status -H "Authorization: Bearer $OVTL_KEY"
{
  "updated_at": "2026-09-01T07:12:03Z",
  "libraries": [
    {
      "library": {"id": 10, "product": "3573-TL", "serial": "OVTL280164", "home_dir": "/var/lib/openvtl/pools/prod", "num_slots": 100, "num_ie": 4, "num_drives": 2, "changer_sg": "/dev/sg3", "live": true, "name": "PROD-VTL"},
      "drives": [
        {"index": 0, "library": 10, "queue_id": 11, "serial": "OVTD280164A", "product": "ULT3580-TD5", "sg": "/dev/sg4", "st": "/dev/st0", "loaded": "OVB003L5", "source_slot": 3, "activity": "writing", "blocks_written": 1845210, "blocks_read": 0, "last_active": "2026-09-01T07:12:02Z"}
      ],
      "slots": [
        {"kind": "storage", "num": 1, "label": "OVB001L5"},
        {"kind": "storage", "num": 2, "label": "OVB002L5"},
        {"kind": "storage", "num": 3, "label": ""},
        {"kind": "ie", "num": 1, "label": ""}
      ],
      "cartridges": [
        {"label": "OVB001L5", "library": 10, "size_bytes": 267421859840, "phys_bytes": 171798691840, "modified": "2026-08-31T00:14:20Z", "location": "slot:1"}
      ]
    }
  ],
  "pools": [
    {"name": "prod", "mountpoint": "/var/lib/openvtl/pools/prod", "fs_total_bytes": 3408486400000, "fs_used_bytes": 622770257920, "logical_bytes": 7696581394432, "dedup_ratio": 9.31, "compress_ratio": 1.46, "record_bytes": 16384, "phys_est_bytes": 66892600000, "zpool_size_bytes": 3408486400000, "zpool_alloc_bytes": 622770257920, "collected_at": "2026-09-01T07:12:00Z"}
  ],
  "fc": {"target_wwn": "", "verified": true, "no_hba": false, "detail": "fc: 2 port(s), 3 LUNs identity-verified, ACLs reconciled", "verified_at": "2026-08-31T00:05:41Z"},
  "cartridges": [
    {"label": "OVB001L5", "library": 10, "size_bytes": 267421859840, "phys_bytes": 171798691840, "modified": "2026-08-31T00:14:20Z", "location": "slot:1", "local_state": "resident", "last_export_gen": "20260831T001507Z"}
  ],
  "fabrics": {"fc": {"present": true, "verified": true, "detail": "fc: 2 port(s), 3 LUNs identity-verified, ACLs reconciled", "sessions": 2}}
}
Field Meaning
libraries[].library.live The changer device is present and being polled. A library declared but not yet activated shows live: false with empty drives and slots.
libraries[].library.product The emulated model’s wire identifier: 3573-TL is TS3100/TS3200 (3573); 03584L32 is TS3500 (3584).
drives[].activity idle, reading, writing, or mounting. Counters are since boot.
cartridges[].location slot:N, ie:N, drive:N, or missing.
cartridges[].local_state resident or evicted (a labelled stub with no data on disk).
pools[] Per-pool ZFS figures. logical_bytes is what the host wrote; phys_est_bytes is the pool’s estimated share of real disk after global dedup; zpool_* figures are the shared system pool and repeat on every entry. Older vdo_* fields remain for shape stability.
fc.target_wwn Always empty: every serving port presents the same LUN table. Per-port WWNs are in GET /api/targets.
fabrics.fc.sessions Live initiator logins across all ports.

GET /api/library

read-only

An older, smaller shape: {"libraries": [...]} with the same per-library objects as /api/status but without display names or the cartridge state join. Prefer /api/status.

GET /api/system

read-only

{
  "version": "v1.0.0",
  "started_at": "2026-08-31T00:05:12Z",
  "uptime_sec": 112012,
  "active_jobs": 1,
  "plain_listener": ":8080",
  "draining": false,
  "apikeys_enabled": true,
  "system_name": "demo01",
  "system_uuid": "5f2c1c8e-0d0a-4d1e-9b7a-2f9f5f0c1a11"
}

plain_listener is empty when the plaintext port is disabled. system_name is the first segment of this appliance’s keys in an S3 bucket; system_uuid is its stable backstop.

POST /api/system/restart

admin confirm: "restart"

Restarts the control plane only. The tape emulator, the FC target, and host I/O do not notice.

Body field Values
confirm "restart", required.
mode graceful (default) waits for active jobs to finish, up to 30 minutes, and refuses new job-creating calls with 409 meanwhile. immediate restarts now; interrupted jobs become retryable and exports resume from their chunk ledger.
curl -sk -X POST https://appliance:8443/api/system/restart \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"confirm":"restart","mode":"graceful"}'

With no jobs active, either mode restarts within a second and returns 200 {"ok": true, "detail": "restarting now"}. With jobs active, graceful returns 202 {"ok": true, "draining": true, "active_jobs": 1, "detail": "…"} and the restart fires once the queue is empty. A second graceful request while one is draining is a 400.

POST /api/system/dataplane-restart

admin Disruptive confirm: "restart"

The wedged-daemon recovery sequence: restarts the tape emulator and rebuilds the FC target fabrics. Every host session drops. Vary the host’s tape devices off and on afterwards. Refused with 400 while any job is active or queued, and with 409 while a graceful restart is draining. The steps stream on the event bus as maint_step events, and any library that comes up live is marked active.

curl -sk -X POST https://appliance:8443/api/system/dataplane-restart \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"confirm":"restart"}'
{
  "steps": ["stopped mhvtl.target", "flushed IPC queues", "started mhvtl.target", "discovery settled: 3 sg devices", "fc: 2 port(s), 3 LUNs identity-verified, ACLs reconciled"],
  "fc": {"OK": true, "Detail": "fc: 2 port(s), 3 LUNs identity-verified, ACLs reconciled"}
}

The steps strings are human-readable and change between releases; do not parse them. A failed sequence is a 500 carrying the steps that completed.

POST /api/system/reboot

admin Disruptive confirm: "reboot"

Reboots the appliance. Host sessions drop; boot orchestration restores pools, daemons, and targets; active jobs are interrupted and become retryable. The response is sent before the reboot fires.

curl -sk -X POST https://appliance:8443/api/system/reboot \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"confirm":"reboot"}'
{"ok": true, "detail": "rebooting — boot orchestration restores pools, daemons and targets; interrupted jobs become retryable"}

GET /api/system/support-bundle

admin

Streams a redacted diagnostics archive (application/gzip, Content-Disposition: attachment) for support: logs, status, storage state, FC state, non-secret settings, recent events and audit entries. It never contains the database file, S3 credentials, password or key hashes, or session tokens. Admin-only even though it is a GET; downloading it is audited as support.bundle.

curl -sk -OJ https://appliance:8443/api/system/support-bundle -H "Authorization: Bearer $OVTL_KEY"

GET /api/license

read-only

The support key: a fingerprint derived from the machine’s durable identity, shown in Settings and given to support to link the appliance to an account. Nothing is uploaded.

{"fingerprint": "OVTL-XXXXX-XXXXX-XXXXX-XXXXX", "changed": false, "previous": ""}

changed becomes true when the key differs from the last acknowledged one (an OS reinstall or a hardware transfer re-keys the box).

POST /api/license/ack

admin

Records the current key as acknowledged, clearing the change notice. Returns {"ok": true, "fingerprint": "…"}.

GET /api/settings

read-only

The closed set of operator-editable settings. Values are strings.

{
  "apikeys.enabled": "1",
  "evict.threshold_pct": "",
  "export.default_remote": "1",
  "export.ie_watcher": "on",
  "fc.disabled_ports": "",
  "minting.enabled": "",
  "nag.no_offsite_dismissed": "",
  "system.name": "demo01"
}
Key Meaning
apikeys.enabled "1" honours API keys; anything else ignores them.
export.ie_watcher "on" auto-exports a cartridge the host ejects to the import/export slot and returns it to its slot once the upload verifies. Default off.
export.default_remote Remote id the watcher and eviction policy use.
evict.threshold_pct Pool fill percentage above which exported cartridges are evicted automatically. Empty disables.
fc.disabled_ports Comma-separated port WWNs excluded from serving. Normally managed by PUT /api/targets/ports.
system.name This appliance’s name in S3 keys: 1 to 32 characters, lowercase letters, digits, -, _, starting with a letter or digit.
minting.enabled Reserved; no effect in v1.0.0.
nag.no_offsite_dismissed "1" hides the dashboard’s no-remote warning. Cleared when the last remote is deleted.

PUT /api/settings

admin

Sets one or more keys. Unknown keys are refused with 400 and nothing is written. Returns the full settings map.

curl -sk -X PUT https://appliance:8443/api/settings \
  -H "Authorization: Bearer $OVTL_KEY" -H 'Content-Type: application/json' \
  -d '{"export.ie_watcher":"on","export.default_remote":"1"}'

GET /api/models

read-only

The emulation catalog behind the create-library form: library models with their variants, and drive models with the barcode suffix and native capacity a minted cartridge takes. Only entries with creatable: true can be created; ibmi_compatible marks what has been validated against IBM i. The catalog also lists frames the emulator recognises but cannot create.

{
  "libraries": [
    {"vendor": "IBM", "display": "TS3100/TS3200 (3573)", "variants": [{"product": "3573-TL", "family": "LTO", "display": "3573-TL", "creatable": true}], "ibmi_compatible": true, "creatable": true, "max_drives": 4},
    {"vendor": "IBM", "display": "TS3500 (3584)", "variants": [{"product": "03584L32", "family": "LTO", "display": "3584-L32 (LTO base frame)", "creatable": true}, {"product": "03584L52", "family": "LTO", "display": "3584-L52 (LTO base frame)", "creatable": false}], "ibmi_compatible": true, "creatable": true, "max_drives": 12}
  ],
  "drives": [
    {"product": "ULT3580-TD5", "vendor": "IBM", "display": "IBM LTO-5 (ULT3580-TD5)", "family": "LTO", "density": "LTO5", "suffix": "L5", "capacity_mb": 1500000, "ibmi_compatible": true},
    {"product": "ULT3580-TD9", "vendor": "IBM", "display": "IBM LTO-9 (ULT3580-TD9)", "family": "LTO", "density": "LTO8", "suffix": "L8", "capacity_mb": 18000000, "ibmi_compatible": true}
  ]
}

Abridged: the real response lists LTO-3 through LTO-9 and the other frames the emulator knows.