Interactive Backup Integrations

This page describes the OpenNebula interactive backup workflow for backup integrations. It is intended for integration developers and for administrators following a specific integration guide, such as the OpenNebula-Veeam® Backup Integration.

The interactive backup datastore driver is a coordination driver. It is not a general-purpose backup backend where users store and manage backup payloads directly. For regular OpenNebula backup storage, use the Restic or Rsync backup datastore guides. For Veeam deployments, follow the Veeam guide, which explains the datastore attributes required by that integration.

Interactive backups use the OpenNebula Backup Exporter (OneBEX). OneBEX is started on demand on the hypervisor that is running the VM backup operation. OpenNebula prepares the disk export, OneBEX exposes the export through an HTTP API, and the external backup system reads the backup data from the hypervisor. The external backup product stores the backup payload in its own repository, while OpenNebula keeps the backup image metadata needed to track and restore the backup.

How It Works

When a VM backup is created through an interactive backup integration, OpenNebula performs the following actions:

  1. The VM backup workflow prepares the selected disks for export. Full backups and CBT incremental backups are supported.
  2. OpenNebula writes the export metadata to interactive_exports.json in the VM backup directory on the hypervisor.
  3. OneBEX is started on the hypervisor if it is not already running.
  4. The external backup system requests the export from OneBEX, discovers the available disk transfers, and reads disk data ranges and block extents.
  5. The external backup system finalizes each transfer and then finishes the VM backup session.
  6. OpenNebula records the backup metadata as a backup image in the integration datastore.

OneBEX stops automatically when the backup session is finished or when it remains idle for longer than the configured timeout.

Compatibility

The current interactive backup implementation supports the following configuration:

ComponentSupport
HypervisorKVM
VM disk storageFile-based qcow2 disks and disks on LVM datastores
Backup typesFull and incremental
Incremental modeCBT only (INCREMENT_MODE="CBT")
VM stateRunning and powered off VMs
OneBEX exporterNBD, LVM

Network Requirements

The external backup system must be able to connect to OneBEX on every hypervisor that can run VMs backed up by the integration.

Make sure that:

  • The OneBEX listen address and port are reachable from the external backup system.
  • Firewalls allow the configured OneBEX port on the hypervisors.
  • OpenNebula remotes are synchronized after changing the OneBEX configuration.
  • The standard OpenNebula Front-end to Host connectivity is working.

Configuring OneBEX

OneBEX is configured from the OpenNebula remotes directory on the Front-end:

/var/lib/one/remotes/etc/onebex/onebex-server.conf

After changing this file, synchronize the remotes to the Hosts:

$ onehost sync -f

The configuration file defines the OneBEX listen address, shutdown behavior, logging settings, and Puma web server concurrency limits.

Server Configuration

ParameterDefault valueDescription
:host:0.0.0.0Address where OneBEX listens for HTTP requests. By default, it listens on all available interfaces.
:port:13014TCP port where OneBEX listens for HTTP requests.
:shutdown_delay:2Delay, in seconds, after the final /vms/:VM_ID/finish request before stopping OneBEX.
:idle_timeout:300Maximum time, in seconds, without receiving any HTTP request before OneBEX stops automatically.
:onebex_timeout:1800Maximum time, in seconds, that OpenNebula waits for an interactive export to finish after it has started.

Log Configuration

ParameterDefault valueDescription
:log: :level:2Log verbosity level. Supported values are 0 for ERROR, 1 for WARNING, 2 for INFO, and 3 for DEBUG.
:log: :system:fileLogging backend used by OneBEX. Supported values are file and syslog.

Puma Configuration

ParameterDefault valueDescription
:puma: :min_threads:1Minimum number of Puma threads used to handle concurrent OneBEX HTTP requests.
:puma: :max_threads:4Maximum number of Puma threads used to handle concurrent OneBEX HTTP requests.

Integration Datastore

An interactive backup integration needs a BACKUP_DS datastore using DS_MAD="interactive". This datastore records OpenNebula backup metadata and lets the integration identify which backups belong to it. The backup payload itself is stored by the external backup system.

Do not create this datastore as a standalone backup target. Create it only when required by an integration guide. Integrations can require additional marker attributes. For example, the Veeam integration requires VEEAM_DS="YES" so the oVirtAPI server can select the datastore used by Veeam.

The minimal datastore shape is:

NAME   = "Integration Backups"
TYPE   = "BACKUP_DS"

DS_MAD = "interactive"
TM_MAD = "-"

DATASTORE_CAPACITY_CHECK="NO"

The datastore must be added to every cluster that contains VMs managed by the integration.

$ onecluster adddatastore <cluster_name> <datastore_name>

Restoring Interactive Backups

During interactive restores, OpenNebula passes the Image Datastore downloader a OneBEX URL in the following form:

onebex://<IMAGE_DS_ID>:<PORT_ID>

IMAGE_DS_ID is the destination Image Datastore ID where the restored disk image will be created. PORT_ID is the restore transfer port allocated for the interactive restore session.

If a restore fails, the restored Image remains in LOCKED state and should be removed manually:

OneBEX API Reference

The OneBEX API is consumed by backup integrations. The current API is:

API Endpoints

EndpointMethodPurposeHTTP Status Code
/GETReturns basic server information and the available API routes.200
/statusGETReturns the current export status for a VM. Requires VM_ID.200, 400
/exportersGETLists the exporter backends available in OneBEX.200
/exportPOSTStarts one or more disk exports for a VM. Requires VM_ID and DS_ID. DISKS is optional.200, 400, 404, 500
/transfers/:TRANSFER_ID/infoGETReturns size and format information for a transfer.200, 404, 500
/images/:TRANSFER_IDOPTIONSReturns supported image transfer features and concurrency limits.200
/images/:TRANSFER_ID/extentsGETReturns block extent information for a transfer.200, 404, 500
/images/:TRANSFER_IDGETReads a byte range from a transfer. Requires an HTTP Range header.206, 400, 404, 416, 500
/images/:TRANSFER_IDPUTImage write operation. Currently not implemented.501
/images/:TRANSFER_IDPATCHAccepts a flush operation when the request body uses op=flush.200, 400, 404
/transfer/:TRANSFER_ID/finalizePOSTFinalizes a transfer and releases its exporter resources.200, 400, 404
/vms/:VM_ID/cancelPOSTCancels all active transfers for a VM.200, 400
/vms/:VM_ID/finishPOSTFinishes the VM backup session after all transfers have been finalized.200, 409

HTTP Status Codes

CodeDescription
200 OKRequest completed successfully.
206 Partial ContentRequested byte range returned successfully.
400 Bad RequestInvalid request, missing parameters, malformed JSON, invalid range format, or unsupported operation.
404 Not FoundTransfer, disk, export metadata, or endpoint not found.
409 ConflictVM backup cannot finish while transfers are still pending.
416 Range Not SatisfiableRequired byte range is missing or invalid.
500 Internal Server ErrorUnexpected server-side error, invalid export metadata, or exporter/backend failure.
501 Not ImplementedOperation exists but is not implemented.

Responses

GET /

200 OK

{
  "NAME": "OpenNebula OneBEX Server",
  "VERSION": "0.1",
  "ROUTES": {
    "STATUS": "GET /status",
    "EXPORTERS": "GET /exporters",
    "EXPORT": "POST /export",
    "EXPORT_FINISH": "POST /vms/:VM_ID/finish",
    "EXPORT_CANCEL": "POST /vms/:VM_ID/cancel",
    "TRANSFER_INFO": "GET /transfers/:TRANSFER_ID/info",
    "IMAGE_OPTIONS": "OPTIONS /images/:TRANSFER_ID",
    "IMAGE_EXTENTS": "GET /images/:TRANSFER_ID/extents",
    "IMAGE_READ": "GET /images/:TRANSFER_ID",
    "IMAGE_WRITE": "PUT /images/:TRANSFER_ID",
    "IMAGE_FLUSH": "PATCH /images/:TRANSFER_ID",
    "IMAGE_FINALIZE": "POST /transfer/:TRANSFER_ID/finalize"
  }
}

GET /status

200 OK

{
  "VM_ID": 123,
  "STATUS": "executing",
  "SUCCESS": true,
  "TRANSFERS": [
    {
      "TRANSFER_ID": "one-123-0-ab12cd34",
      "DISK_ID": 0,
      "EXPORTER": "nbd",
      "STATUS": "ready",
      "RC": true
    }
  ]
}

400 Bad Request

{
  "error": "Missing VM_ID"
}

GET /exporters

200 OK

{
  "EXPORTERS": [
    "nbd",
    "lvm"
  ]
}

POST /export

200 OK

{
  "VM_ID": 123,
  "DS_ID": 100,
  "TRANSFERS": [
    {
      "TRANSFER_ID": "one-123-0-ab12cd34",
      "DISK_ID": 0,
      "EXPORTER": "nbd",
      "STATUS": "ready",
      "RC": true
    }
  ]
}

400 Bad Request

{
  "error": "Missing VM_ID or DS_ID"
}

or:

{
  "error": "Unsupported exporter: <exporter>"
}

404 Not Found

{
  "error": "Disk 0 not found"
}

or:

{
  "error": "Export file not found: <path>"
}

500 Internal Server Error

{
  "error": "Invalid interactive_exports.json: <error>"
}

GET /transfers/:TRANSFER_ID/info

200 OK

Example for an NBD transfer:

{
  "TRANSFER_ID": "one-123-0-ab12cd34",
  "SIZE": 10240,
  "FORMAT": "qcow2"
}

SIZE is returned in MiB.

404 Not Found

{
  "error": "Transfer not found"
}

OPTIONS /images/:TRANSFER_ID

200 OK

{
  "features": [
    "checksum",
    "extents",
    "flush",
    "zero"
  ],
  "max_readers": 1,
  "max_writers": 1
}

GET /images/:TRANSFER_ID/extents

200 OK

Returns block extent information as JSON. For example:

[
  {
    "start": 0,
    "length": 1048576,
    "dirty": true,
    "zero": false,
    "hole": false
  }
]

404 Not Found

{
  "error": "Transfer not found"
}

GET /images/:TRANSFER_ID

Requires a range in the following format:

Range: bytes=start-end

206 Partial Content

Returns the requested byte range as binary application/octet-stream data.

400 Bad Request

{
  "error": "Invalid Range format. Expected: bytes=start-end"
}

404 Not Found

{
  "error": "Transfer not found"
}

416 Range Not Satisfiable

When the Range header is missing:

{
  "error": "Missing Range header"
}

When the end byte is lower than the start byte:

{
  "error": "Invalid Range header"
}

PUT /images/:TRANSFER_ID

501 Not Implemented

{
  "error": "Write operation not implemented"
}

PATCH /images/:TRANSFER_ID

Request:

{
  "op": "flush"
}

200 OK

The flush request is accepted. The response has no JSON body.

400 Bad Request

{
  "error": "Unsupported operation"
}

404 Not Found

{
  "error": "Transfer not found"
}

POST /transfer/:TRANSFER_ID/finalize

Optional request body:

{
  "SUCCESS": true,
  "MESSAGE": "Transfer completed"
}

SUCCESS defaults to true.

200 OK

{
  "VM_ID": 123,
  "TRANSFER_ID": "one-123-0-ab12cd34",
  "STATUS": "finished",
  "SUCCESS": true,
  "PENDING_TRANSFERS": []
}

400 Bad Request

Returned when the request body contains invalid JSON.

404 Not Found

{
  "error": "Transfer not found"
}

POST /vms/:VM_ID/cancel

Optional request body:

{
  "MESSAGE": "Backup cancelled by an Administrator"
}

MESSAGE defaults to Backup cancelled.

200 OK

{
  "VM_ID": 123,
  "STATUS": "cancelled",
  "SUCCESS": false,
  "PENDING_TRANSFERS": []
}

400 Bad Request

Returned when the request body contains invalid JSON.

POST /vms/:VM_ID/finish

200 OK

{
  "VM_ID": 123,
  "STATUS": "finished",
  "SUCCESS": true,
  "PENDING_TRANSFERS": []
}

If transfers are still pending:

409 Conflict

{
  "VM_ID": 123,
  "STATUS": "executing",
  "SUCCESS": true,
  "PENDING_TRANSFERS": [
    "one-123-0-ab12cd34"
  ]
}

Common Error Responses

Malformed JSON request bodies return 400 Bad Request:

{
  "error": "Invalid JSON body: <error>"
}

When a transfer does not exist or is no longer available, endpoints that look up an existing transfer return 404 Not Found:

{
  "error": "Transfer not found"
}

Unsupported endpoints return 404 Not Found:

{
  "error": "Unsupported endpoint"
}

Unexpected server errors and exporter/backend failures return 500 Internal Server Error:

{
  "error": "<error message>"
}

Exporters

OneBEX uses exporters to expose VM disk data to external backup systems.

ExporterVM disk storageTransportDescription
nbdFile-based qcow2 disksNetwork Block DeviceExposes the backup disk through NBD. OneBEX starts a read-only qemu-nbd process and serves the disk export through a Unix socket.
lvmDisks on LVM datastoresDirect block-device readsExposes the prepared LVM block device directly. Full backups return the full device extent. Incremental backups use thin_delta to return changed extents from LVM thin metadata.