API Reference
REST API and Python client bindings
The Abacá REST API is a Keystone-authenticated HTTP service versioned at /v1 that lets tenants and operators manage every aspect of Manila share protection: registering S3 backup targets, defining protection policies, triggering on-demand backups, requesting restores, and inspecting job progress. Operators additionally have access to admin-scoped endpoints that expose cross-tenant job administration, worker fleet lifecycle management, scheduler control, reconciliation triggers, and protection-coverage reporting. All asynchronous operations return a 202 Accepted with a Job resource that you poll to track progress through the job state machine.
Most collection endpoints accept the following common query parameters.
| Parameter | Type | Required | Description |
|---|---|---|---|
limit | integer (1–1000) | No | Maximum number of items to return. Server default is 100; hard maximum is 1000. Controlled by the api.default_limit and api.max_limit config options. |
marker | string (UUID) | No | Pagination cursor. Pass the next_marker value from a previous response to retrieve the next page. When next_marker is null there are no more pages. |
Path parameters vary by resource and are described in each resource section below. All write operations accept a JSON body (Content-Type: application/json).
Every successful response carries one of the following shapes.
Single resource — a JSON object whose fields match the schema for that resource (see Resource schemas).
Paginated collection — a JSON object with two top-level keys:
{
"items": [ … ],
"pagination": {
"count": 42,
"limit": 100,
"next_marker": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
next_marker is null when you have reached the last page.
Accepted action — 202 Accepted with no body (or, for backup creation, with a Backup object). Poll the returned Job resource for completion.
Deleted — 204 No Content with no body.
Error responses always use the Error schema (see Error responses).
Every error response body conforms to the Error schema:
{
"code": "policy_not_found",
"title": "Policy not found",
"detail": "No policy with id 'abc…' exists in this project.",
"category": "tenant_action_required"
}
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error identifier. |
title | string | Short human-readable summary. |
detail | string | Full explanation, including remediation hints where possible. |
category | enum | tenant_action_required — the tenant must act (e.g., invalid credentials, missing resource). operator_action_required — the operator must act (e.g., no worker fleet available). |
Common HTTP status codes and when they occur:
| Status | Meaning |
|---|---|
400 Bad Request | Malformed JSON, missing required field, or a field value that violates a schema constraint. |
401 Unauthorized | No X-Auth-Token header, or the token has expired. Obtain a fresh Keystone token. |
403 Forbidden | The authenticated user lacks the required oslo.policy role for this operation. |
404 Not Found | The addressed resource does not exist or is not visible to this project. |
409 Conflict | The operation is not valid for the resource's current state (e.g., cancelling an already-terminal job, or enrolling a bucket that is already enrolling). |
Authentication
All requests require a Keystone token in the X-Auth-Token header. Obtain one with the OpenStack CLI or directly from Keystone:
# Using the OpenStack CLI (recommended)
export OS_CLOUD=mycloud
TOKEN=$(openstack token issue -f value -c id)
curl -s -H "X-Auth-Token: $TOKEN" \
https://abaca.example.com/v1/policies
The API base URL is registered in the Keystone service catalog under the share-protection catalog type (configurable via DEFAULT.catalog_type). Discover it with:
openstack catalog show share-protection
List protection policies
Retrieve all policies visible to the current project, with pagination:
curl -s -H "X-Auth-Token: $TOKEN" \
"https://abaca.example.com/v1/policies?limit=2"
{
"items": [
{
"id": "1a2b3c4d-0000-0000-0000-000000000001",
"name": "daily-gold",
"share_id": "share-uuid-aaa",
"target_template_id": "tmpl-uuid-bbb",
"schedule": "0 2 * * *",
"retention": { "keep_last": 7 },
"enabled": true,
"max_retries": 0
}
],
"pagination": {
"count": 1,
"limit": 2,
"next_marker": null
}
}
OpenStack CLI equivalent:
openstack share protection policy list
Create an on-demand backup
POST /v1/backups accepts a JSON body identifying the share and (optionally) whether to force a crash-consistent backup:
curl -s -X POST \
-H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"share_id": "share-uuid-aaa", "crash_consistent": false}' \
https://abaca.example.com/v1/backups
{
"id": "bkp-uuid-ccc",
"share_id": "share-uuid-aaa",
"target_bucket_id": "bucket-uuid-ddd",
"kopia_snapshot_id": null,
"crash_consistent": false,
"status": "queued"
}
The status field reflects the Job state machine. Poll GET /v1/jobs/{job_id} (returned in the Location response header) until state is available or error.
OpenStack CLI equivalent:
openstack share protection backup create --share share-uuid-aaa
Poll a job
curl -s -H "X-Auth-Token: $TOKEN" \
https://abaca.example.com/v1/jobs/job-uuid-eee
{
"id": "job-uuid-eee",
"job_type": "backup",
"state": "transferring",
"progress": 42
}
The state field walks through: queued → provisioning_network → provisioning_source → connecting_repository → transferring → finalizing → releasing → available (or error).
Restore a share from backup
POST /v1/restores creates a restore job. Choose mode: new_share to restore into a fresh Manila share, or in_place to overwrite the existing share's contents. Optionally specify sub_path to restore only a directory subtree or single file:
curl -s -X POST \
-H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"backup_id": "bkp-uuid-ccc",
"mode": "new_share",
"sub_path": null,
"force": false
}' \
https://abaca.example.com/v1/restores
{
"id": "rst-uuid-fff",
"backup_id": "bkp-uuid-ccc",
"mode": "new_share",
"sub_path": null,
"force": false,
"status": "queued"
}
OpenStack CLI equivalent:
openstack share protection restore create \
--backup bkp-uuid-ccc \
--mode new_share
Admin: list all workers in the fleet
Admin endpoints require a token scoped to a user with the operator-level policy role:
curl -s -H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/workers
{
"items": [
{ "id": "wkr-uuid-ggg", "hostname": "abaca-worker-ggg", "state": "ready" },
{ "id": "wkr-uuid-hhh", "hostname": "abaca-worker-hhh", "state": "draining" }
],
"pagination": { "count": 2, "limit": 100, "next_marker": null }
}
Admin: drain and retire a worker
Draining marks a worker so it receives no new jobs; retiring removes it from the fleet after it finishes current work:
# Drain first
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/workers/wkr-uuid-hhh/drain
# Then retire once drained
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/workers/wkr-uuid-hhh/retire
Both return 202 Accepted. A 409 Conflict is returned if the transition is invalid for the worker's current state (e.g., retiring a worker that still has running jobs and has not been drained).
Admin: pause and resume the scheduler
Use pause before maintenance windows to prevent new policy-driven backups from being queued:
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/scheduling/pause
# … perform maintenance …
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/scheduling/resume
Admin: trigger reconciliation
Force the conductor's reconciliation sweep immediately rather than waiting for the next conductor.reconciliation_interval:
curl -s -X POST \
-H "X-Auth-Token: $ADMIN_TOKEN" \
https://abaca.example.com/v1/admin/reconciliation/trigger
Python client library example
The python-abacaclient package exposes a thin SDK. Use it in scripts and automation that need programmatic access:
import openstack
from abacaclient import AbacaClient
conn = openstack.connect(cloud="mycloud")
client = AbacaClient(session=conn.session)
# List policies
policies = client.policies.list()
for p in policies:
print(p.id, p.name, p.schedule)
# Trigger an on-demand backup
backup = client.backups.create(share_id="share-uuid-aaa")
print("Backup queued:", backup.id)
# Poll until terminal
import time
while True:
job = client.jobs.get(backup.job_id)
print(job.state, job.progress)
if job.state in ("available", "error"):
break
time.sleep(5)
Base path and versioning
All endpoints documented here are served under the /v1 prefix. The full base URL is {catalog_endpoint}/v1. The service currently does not use microversion negotiation; version 0.1.0 is encoded in the OpenAPI specification (info.version).
Authentication and token scope
Acquire a Keystone token scoped to your project (openstack token issue) and pass it as X-Auth-Token. Requests without a valid token receive 401 Unauthorized. The api.noauth config option (false by default) is a development-only override — it is not available on production deployments.
Tenant vs. admin endpoints
Paths under /v1/admin/… require the operator-level oslo.policy role (see the policy reference). Tenant-facing paths (everything else) require only a project-scoped token with the appropriate tenant role. The precise default policy rules and how to override them with policy.yaml are documented in the Access control reference.
Asynchronous operations and the job state machine
Backup creation (POST /v1/backups), restore creation (POST /v1/restores), and target enrollment all return 202 Accepted immediately and proceed asynchronously. The returned resource's status field (and the linked Job object's state field) reflects the current position in the state machine:
queued → provisioning_network → provisioning_source → connecting_repository → transferring → finalizing → releasing → available | error
When state is error, inspect the Job's error fields — the category field tells you whether action is required from the tenant (tenant_action_required) or the operator (operator_action_required).
Pagination
All collection endpoints are paginated. The default page size is 100 items (controlled by api.default_limit); the maximum is 1000 (controlled by api.max_limit). Always check pagination.next_marker — a non-null value means more pages exist. Pass it as the marker query parameter on the next request.
Immutability of bucket_scheme
The bucket_scheme field on a TargetTemplate (per_project or shared) is immutable after creation. Changing it would re-point existing policies at a different Kopia repository and orphan all prior backups. If you need a different scheme, create a new template and migrate policies to it.
max_retries semantics on Policy
The max_retries field (0–10, default 0) controls how many times the conductor automatically retries a failed backup. A value of 0 means a single attempt; the conductor does not retry. Only failures that provably did not write a Kopia snapshot are retried — partial transfers are not retried automatically, because retrying after a partial transfer would write a second snapshot and could displace a restore point under the keep_* retention rules.
Secrets are never returned in API responses
Fields such as s3_access_key_ref, s3_secret_key_ref, kopia_key_ref, and kopia_key_trust_id on TargetBucket are Barbican secret hrefs — opaque references to secrets held in Barbican — not the secret values themselves. Abacá never stores or returns raw credentials in API responses.
project_id on TargetBucket
For templates with bucket_scheme: shared, the project_id field on a TargetBucket is null because the bucket is shared across all projects in the Domain. For per_project templates, project_id identifies the owning project.
Service catalog discovery
The catalog type registered for Abacá is share-protection (the value of DEFAULT.catalog_type). The endpoint type used for internal lookups defaults to publicURL (DEFAULT.endpoint_type). Discover the endpoint with:
openstack catalog show share-protection