REST API
Resources, methods, request/response bodies, and status codes
This page documents the Abacá REST API — the primary integration surface for tenants protecting Manila shares and for operators managing the Abacá service. The API is versioned at /v1, authenticated via Keystone tokens, and governed by oslo.policy rules. Every list endpoint supports pagination; every error response carries a structured envelope that tells you whether the tenant or the operator must act to resolve the problem. If you need the machine-readable contract, the authoritative OpenAPI 3 specification lives at docs/api-ref/openapi.yaml and is generated directly from the service's route and schema definitions.
Before calling the API you need:
- A running Abacá control plane —
abaca-apiandabaca-conductordeployed as OpenShift pods on RHOSO 18 or later (see the deployment guide). - Keystone credentials with an appropriate role:
- Tenant operations: a standard project-scoped token.
- Domain-owner operations (target templates, bucket registration): a domain-scoped token for a user holding the
abaca_domain_ownerrole. - Admin operations (
/v1/admin/…): an operator-scoped token with the admin policy role.
- The Abacá service endpoint — discoverable via the Keystone service catalog under the
share-protectioncatalog type (configurable; see[DEFAULT] catalog_type). python-openstackclientwith theabacadistribution installed if you plan to use theopenstack share protection …CLI instead of raw HTTP calls. Install it by following the deployment instructions for your environment (RHOSO operators layer it into the Horizon pod venv viadeploy/rhoso/08-dashboard.sh; developers usetoxor local pip from the repo).- S3-compatible object storage with versioning enabled; Object Lock must be enabled at bucket creation time if you want immutable backups.
- OpenStack Manila with at least one share to protect.
The abaca-api service is not installed as a standalone package — it is deployed as part of the Abacá control plane using the kustomize manifests and bash install scripts in deploy/rhoso/. The following steps show how to reach the API endpoint after a successful deployment.
Step 1 — Confirm the service is in the Keystone catalog
openstack catalog show share-protection
Expected output lists public, internal, and admin endpoints. If the entry is absent, re-run the catalog registration script:
bash deploy/rhoso/02-keystone.sh
Step 2 — Set your environment variables
Export the standard OpenStack auth variables (or use a clouds.yaml entry) so that the CLI and your scripts can discover the endpoint automatically:
export OS_AUTH_URL=https://keystone.example.com/v3
export OS_USERNAME=myuser
export OS_PASSWORD=secret
export OS_PROJECT_NAME=myproject
export OS_USER_DOMAIN_NAME=Default
export OS_PROJECT_DOMAIN_NAME=Default
Step 3 — Verify connectivity
Fetch the API version document:
curl -s -H "X-Auth-Token: $(openstack token issue -f value -c id)" \
https://<abaca-api-endpoint>/v1/
Step 4 — Install the CLI plugin (tenant / operator workstations)
The abaca distribution provides the openstack share protection … subcommands. Follow the deployment model for your environment:
- RHOSO operators: the plugin is layered into the Horizon pod venv by
deploy/rhoso/08-dashboard.sh. No separate install step is needed on the control plane. - Developer workstations: use
toxfrom the repository root, or install the package directly from the repo source into a virtual environment.
Note: Do not install from PyPI or assume a public registry — install from the repository as directed by
deploy/rhoso/08-dashboard.shor the development setup instructions.
Abacá's API behaviour is controlled by oslo.config options declared in an INI-style .conf file. The options most relevant to the API surface are in the [api] and [DEFAULT] sections.
[api] section
| Option | Type | Default | Effect |
|---|---|---|---|
bind_host | string | 0.0.0.0 | IP address the API process listens on. |
bind_port | integer | 9797 | TCP port for the API. The OpenShift Service and Route manifests expose this port. |
noauth | boolean | false | Disable Keystone authentication. Never set true in production — intended only for the local Docker Compose dev stack. |
max_limit | integer | 1000 | Maximum value a caller may specify for the ?limit query parameter on list endpoints. |
default_limit | integer | 100 | Page size returned when the caller does not specify ?limit. |
[DEFAULT] section (service discovery)
| Option | Type | Default | Effect |
|---|---|---|---|
catalog_type | string | share-protection | The Keystone service catalog type under which Abacá's endpoints are registered. Change this only if your site uses a non-standard catalog layout. |
endpoint_type | string | publicURL | Which endpoint interface (publicURL, internalURL, or adminURL) the service uses when looking up sibling OpenStack services. |
[database] section
| Option | Type | Default | Effect |
|---|---|---|---|
connection | string | (required) | SQLAlchemy connection string for Abacá's dedicated MySQL/MariaDB (Galera) database, e.g. mysql+pymysql://abaca:pass@db.example.com/abaca. |
[conductor] section (affects API-observable behaviour)
The conductor drives the job state machine; its settings influence how quickly the API reflects job progress.
| Option | Type | Default | Effect |
|---|---|---|---|
scheduler_interval | integer | 60 | Seconds between policy schedule evaluations. |
reconciliation_interval | integer | 300 | Seconds between reconciliation sweeps that recover stuck jobs. |
worker_heartbeat_deadline_seconds | integer | 60 | Seconds after which a worker with no heartbeat is considered dead. |
queued_job_deadline_seconds | integer | 300 | Seconds a job may sit in queued state before the reconciler declares it stuck. |
[enrollment] section
| Option | Type | Valid values | Effect |
|---|---|---|---|
dispatch | string | rpc, inline | Whether target enrollment jobs are dispatched to a worker VM via RPC or run inline in the conductor process (development only). |
Annotated sample abaca.conf (API-relevant excerpt)
[DEFAULT]
catalog_type = share-protection
endpoint_type = publicURL
[api]
bind_host = 0.0.0.0
bind_port = 9797
noauth = false
max_limit = 1000
default_limit = 100
[database]
connection = mysql+pymysql://abaca:changeme@galera.example.com/abaca
[conductor]
scheduler_interval = 60
reconciliation_interval = 300
worker_heartbeat_deadline_seconds = 60
queued_job_deadline_seconds = 300
[enrollment]
dispatch = rpc
All requests must carry a Keystone token in the X-Auth-Token header. The base path for every tenant and admin resource is /v1.
Authentication
TOKEN=$(openstack token issue -f value -c id)
ABACA=https://abaca.example.com/v1
Pagination
Every list endpoint accepts ?limit (1–1000, default 100) and ?marker (the next_marker value from the previous page). A null next_marker means you are on the last page.
GET /v1/backups?limit=25
GET /v1/backups?limit=25&marker=<next_marker>
Error envelope
Every non-2xx response returns a JSON object with four fields:
{
"code": "target_not_found",
"title": "Target not found",
"detail": "No backup target with id 'abc123' exists in this project.",
"category": "tenant_action_required"
}
category is always one of tenant_action_required (the caller must fix something — wrong credentials, missing resource) or operator_action_required (the operator must act — no worker fleet available, infrastructure fault).
Tenant-facing resources
Target templates (/v1/target-templates)
A Domain owner creates a target template to describe the S3 endpoint, region, provider, and bucket_scheme (shared or per_project). The bucket_scheme is immutable after creation — changing it would orphan existing backups.
| Method | Path | Purpose |
|---|---|---|
GET | /v1/target-templates | List templates visible to the caller's Domain. |
POST | /v1/target-templates | Create a template (Domain-owner token required). |
GET | /v1/target-templates/{id} | Fetch a single template. |
PATCH | /v1/target-templates/{id} | Update mutable fields (name, retention defaults). |
DELETE | /v1/target-templates/{id} | Delete a template (only if no targets reference it). |
Backup targets (/v1/targets)
Registering a target triggers the preflight conformance checks (reachability, read/write, versioning, Object Lock) and initialises a Kopia repository in the bucket. This runs as an enrollment job on a worker VM.
| Method | Path | Purpose |
|---|---|---|
GET | /v1/targets | List targets. |
POST | /v1/targets | Register an S3 bucket as a backup target. |
GET | /v1/targets/{id} | Fetch target detail including status and usage_stats. |
PATCH | /v1/targets/{id} | Rotate S3 credentials or update metadata. |
DELETE | /v1/targets/{id} | Delete a target record (does not destroy S3 data). |
Protection policies (/v1/policies)
A policy binds a Manila share to a target template and defines the backup schedule (cron expression) and retention rules.
| Method | Path | Purpose |
|---|---|---|
GET | /v1/policies | List policies. |
POST | /v1/policies | Create a policy. |
GET | /v1/policies/{id} | Fetch policy detail. |
PATCH | /v1/policies/{id} | Update schedule, retention, or enabled flag. |
DELETE | /v1/policies/{id} | Delete a policy (does not delete existing backups). |
Policy fields:
| Field | Type | Notes |
|---|---|---|
share_id | string | Manila share UUID. |
target_template_id | string | Template to use for backup storage. |
schedule | string | Cron expression (server-side validation). |
retention | object | Retention rules (e.g. keep_last, keep_daily). |
enabled | boolean | false suspends scheduled backups without deleting the policy. |
max_retries | integer | 0–10; 0 means one attempt. Only pre-transfer, operator-actionable failures are retried. |
Backups (/v1/backups)
Backups are created on-demand via POST /v1/backups or automatically by the conductor according to the policy schedule. The list is sorted newest-first and hides status=expired entries by default.
| Method | Path | Purpose |
|---|---|---|
GET | /v1/backups | List backups (newest-first). |
POST | /v1/backups | Request an on-demand backup. |
GET | /v1/backups/{id} | Fetch backup detail. |
DELETE | /v1/backups/{id} | Delete a backup (subject to retention and Object Lock). |
Useful query parameters for GET /v1/backups:
?share_id=<uuid>— filter to one share.?include_expired=true— include tombstoned backups.?status=<value>— filter by explicit status value.
Backup fields:
| Field | Type | Notes |
|---|---|---|
id | string | Backup UUID. |
share_id | string | The protected Manila share. |
target_bucket_id | string | The target bucket that holds this backup. |
status | string | Current status string. |
crash_consistent | boolean | true if the backup reflects a snapshot; false if taken from the live share. |
kopia_snapshot_id | string / null | Kopia content-address; null while in progress. |
Restores (/v1/restores)
A restore recovers a share (or a sub-path within it) from a backup. Two modes are supported: new_share creates a fresh Manila share from the backup data; in_place overwrites the existing share (requires force=true).
| Method | Path | Purpose |
|---|---|---|
GET | /v1/restores | List restore operations. |
POST | /v1/restores | Start a restore. |
GET | /v1/restores/{id} | Fetch restore detail and status. |
Restore fields:
| Field | Type | Notes |
|---|---|---|
backup_id | string | The backup to restore from. |
mode | enum | new_share or in_place. |
sub_path | string / null | Optional path within the share to restore. |
force | boolean | Required true for in_place mode. |
status | string | Current restore status. |
Jobs (/v1/jobs)
Every backup, restore, enrollment, and maintenance operation creates a Job. Poll the job to track progress.
| Method | Path | Purpose |
|---|---|---|
GET | /v1/jobs | List jobs for the current project. |
GET | /v1/jobs/{id} | Fetch job state and progress. |
POST | /v1/jobs/{id}/cancel | Request cancellation of a running job. |
Job state machine: queued → provisioning_network → provisioning_source → connecting_repository → transferring → finalizing → releasing → available (success) or error (failure).
Job fields:
| Field | Type | Notes |
|---|---|---|
id | string | Job UUID. |
job_type | string | E.g. backup, restore, enrollment. |
state | enum | Current state in the state machine. |
progress | integer | Percentage complete (0–100), heartbeat-fed from the worker. |
Usage (/v1/usage)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/usage | Tenant consumption data: protected shares, bytes stored, activity. |
Admin resources (/v1/admin/…)
Admin endpoints are policy-gated and audited. They provide a cross-tenant view for operators.
Worker fleet (/v1/admin/workers)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/workers | List all worker VMs and their state. |
POST | /v1/admin/workers/{id}/drain | Stop assigning new jobs to a worker; let running jobs finish. |
POST | /v1/admin/workers/{id}/retire | Mark a worker for deletion after draining. |
Admin jobs (/v1/admin/jobs)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/jobs | List all jobs across all tenants (paginated). |
POST | /v1/admin/jobs/{id}/cancel | Cancel any job. |
POST | /v1/admin/jobs/{id}/retry | Retry a failed job. |
Coverage (/v1/admin/coverage)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/coverage | Report which shares are protected versus overdue. |
Admin target templates (/v1/admin/target-templates)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/target-templates | Cross-tenant listing of all templates. |
Scheduling (/v1/admin/scheduling)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/scheduling | Current scheduler status. |
POST | /v1/admin/scheduling/pause | Pause all policy-driven backup scheduling. |
POST | /v1/admin/scheduling/resume | Resume scheduling. |
Reconciliation (/v1/admin/reconciliation)
| Method | Path | Purpose |
|---|---|---|
GET | /v1/admin/reconciliation | Reconciliation loop status (last run, next run, any detected drift). |
POST | /v1/admin/reconciliation/trigger | Force an immediate reconciliation sweep. |
CLI equivalents
The abaca distribution provides the openstack share protection … CLI (via python-openstackclient). Each subcommand maps to an API call:
# List policies
openstack share protection policy list
# Create an on-demand backup
openstack share protection backup create --share <share-id> --policy <policy-id>
# Show a job
openstack share protection job show <job-id>
# Restore a backup to a new share
openstack share protection restore create \
--backup <backup-id> \
--mode new_share
1 — Register an S3 bucket as a backup target
This assumes a Domain-owner token is in $TOKEN and the target template already exists.
curl -s -X POST \
-H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bucket_name": "acme-backups-prod",
"template_id": "tmpl-11111111",
"s3_access_key_ref": "<barbican-href>",
"s3_secret_key_ref": "<barbican-href>"
}' \
"$ABACA/targets"
Expected response (202 Accepted):
{
"id": "tgt-22222222",
"bucket_name": "acme-backups-prod",
"template_id": "tmpl-11111111",
"status": "enrolling",
"project_id": "proj-33333333",
"registered_by": "user-44444444",
"kopia_key_ref": null,
"usage_stats": null
}
The status will transition to available once the enrollment job (which runs preflight checks and initialises the Kopia repository) completes. Poll the job or the target to track progress.
2 — Request an on-demand backup
curl -s -X POST \
-H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"share_id": "share-55555555",
"policy_id": "pol-66666666"
}' \
"$ABACA/backups"
Expected response (202 Accepted):
{
"id": "bkp-77777777",
"share_id": "share-55555555",
"target_bucket_id": "tgt-22222222",
"status": "running",
"crash_consistent": true,
"kopia_snapshot_id": null
}
The kopia_snapshot_id is populated once the transfer completes.
3 — Poll a job until it reaches a terminal state
JOB_ID=job-88888888
while true; do
STATE=$(curl -s -H "X-Auth-Token: $TOKEN" \
"$ABACA/jobs/$JOB_ID" | jq -r '.state')
echo "$(date -u +%H:%M:%S) state=$STATE"
[[ "$STATE" == "available" || "$STATE" == "error" ]] && break
sleep 10
done
Sample output:
14:02:10 state=provisioning_network
14:02:20 state=provisioning_source
14:02:30 state=connecting_repository
14:02:40 state=transferring
14:04:55 state=finalizing
14:05:05 state=available
4 — Restore a backup to a new share (sub-path)
curl -s -X POST \
-H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"backup_id": "bkp-77777777",
"mode": "new_share",
"sub_path": "reports/2025-Q1"
}' \
"$ABACA/restores"
Expected response (202 Accepted):
{
"id": "rst-99999999",
"backup_id": "bkp-77777777",
"mode": "new_share",
"sub_path": "reports/2025-Q1",
"force": false,
"status": "running"
}
5 — Admin: pause scheduling before a maintenance window
# Pause
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
"$ABACA/admin/scheduling/pause"
# Response: 202 Accepted
# Confirm
curl -s -H "X-Auth-Token: $ADMIN_TOKEN" \
"$ABACA/admin/scheduling" | jq .
Expected status response:
{
"paused": true,
"paused_at": "2025-07-14T14:00:00Z",
"next_run": null
}
Resume after maintenance:
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
"$ABACA/admin/scheduling/resume"
6 — Admin: drain a worker before rolling a new image
WORKER_ID=wkr-aaaaaaaa
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
"$ABACA/admin/workers/$WORKER_ID/drain"
# Response: 202 Accepted
# Watch until the worker has no running jobs, then retire it
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
"$ABACA/admin/workers/$WORKER_ID/retire"
7 — List backups for a specific share (newest-first, including expired)
curl -s \
-H "X-Auth-Token: $TOKEN" \
"$ABACA/backups?share_id=share-55555555&include_expired=true&limit=10" \
| jq '.items[] | {id, status, crash_consistent, kopia_snapshot_id}'
8 — Using the CLI plugin
# Create a protection policy
openstack share protection policy create \
--share share-55555555 \
--target-template tmpl-11111111 \
--schedule "0 2 * * *" \
--retention '{"keep_last": 7}' \
my-daily-policy
# List backups for a share
openstack share protection backup list --share share-55555555
# Show admin coverage report
openstack share protection admin coverage show
Job stays in queued indefinitely
Symptom: GET /v1/jobs/{id} returns state=queued for longer than the queued_job_deadline_seconds value (default 300 s), and the reconciler has not moved it forward.
Likely causes:
- No worker VMs are available or all workers are at capacity (
capacity_slotsexhausted). - The conductor is not running or is not connected to RabbitMQ.
Fix:
- Check the worker fleet:
GET /v1/admin/workers. If it is empty, the conductor needs to boot workers — verify[conductor] min_workers,worker_boot_image,worker_boot_flavor, andworker_boot_networkinabaca.conf. - Check conductor logs in OpenShift:
oc logs -n abaca deployment/abaca-conductor. - Force a reconciliation sweep to unstick the job:
POST /v1/admin/reconciliation/trigger.
403 Forbidden on admin endpoints
Symptom: POST /v1/admin/scheduling/pause (or any /v1/admin/… call) returns 403 with category=tenant_action_required.
Likely cause: The token used does not carry the required admin policy role.
Fix: Obtain a token for a user with the operator role and retry. Check the effective policy with openstack policy show or review policy.yaml in the abaca configuration directory.
Target enrollment fails (job reaches error in connecting_repository)
Symptom: The enrollment job transitions to error at the connecting_repository stage. The error detail says the bucket is unreachable or credentials are invalid.
Likely causes:
- S3 endpoint URL is wrong, or the bucket does not exist.
- The Barbican hrefs passed as
s3_access_key_ref/s3_secret_key_refare for a different project or the trust has been revoked. - Object Lock or versioning is not enabled on the bucket (required for WORM immutability).
Fix:
- Verify the S3 endpoint and credentials independently (e.g. using the
awsCLI ormc). - Confirm that the Keystone trust is still valid: check the Barbican href is accessible from the Abacá service user's context.
- Verify the bucket was created with versioning and Object Lock enabled — these cannot be enabled retroactively.
- Retry the enrollment:
POST /v1/admin/jobs/{job_id}/retry.
409 Conflict when cancelling or retrying a job
Symptom: POST /v1/admin/jobs/{id}/cancel or /retry returns 409.
Likely cause: The job is already in a terminal state (available or error) or has already been cancelled. You cannot cancel a completed job or retry one that is still running.
Fix: Fetch the current job state first with GET /v1/jobs/{id} and act based on the actual state value.
Backups list appears empty even though backups exist
Symptom: GET /v1/backups returns an empty list, but you know backups were taken.
Likely cause: Expired backups are hidden by default. If all backups for the share have been marked expired by the retention policy, the default view shows nothing.
Fix: Add ?include_expired=true to surface expired backups, or add ?status=<value> to filter by a specific status. Also check ?share_id=<uuid> to scope the query to the right share.
Worker drain returns 409
Symptom: POST /v1/admin/workers/{id}/drain returns 409.
Likely cause: The worker is already in a draining or retiring state.
Fix: Check the worker's current state with GET /v1/admin/workers. If it is already draining, wait for running jobs to complete. If it is already retired, no further action is needed.
category: operator_action_required in a backup error
Symptom: A backup job reaches error and the error envelope contains category=operator_action_required.
Meaning: The failure is infrastructure-side — the tenant cannot fix it themselves. Common causes include no available worker VMs, a RabbitMQ connectivity problem, or a Barbican service outage.
Fix: Check the conductor logs (oc logs -n abaca deployment/abaca-conductor) and the worker fleet status (GET /v1/admin/workers). Resolve the infrastructure issue, then retry the job via POST /v1/admin/jobs/{id}/retry.