---
title: Getting Started
product: trilio-share-protection
doc_type: tutorial
version: master
source: git2docs (code-derived, validation-filtered)
canonical: https://git2docs.com/murali-balcha/docs/trilio-share-protection/trilio-share-protection-/getting-started
---

# Getting Started

_Prerequisites and steps to make your first API request_

## Overview

This page walks you through everything you need to make your first successful API request against Trilio Share Protection for OpenStack (codename Abacá). You will install the client library, bring up a local development stack with Docker Compose, register a backup target, attach a protection policy to a Manila share, and trigger an on-demand backup — all without requiring a real OpenStack environment. By the end, you will have end-to-end confirmation that the API is reachable and that the backup job state machine progresses from `queued` to `available`.

## Prerequisites

Before you begin, make sure you have the following:

**Runtime and tools**
- Python 3.11 or 3.12 (3.12 is the primary target; both are supported)
- Docker or Podman (required for the local dev stack only)
- `curl` or any HTTP client for ad-hoc requests
- `pip` and `venv` available on your `PATH`

**OpenStack services** *(required only when connecting to a real cluster — not needed for the local dev stack)*
- OpenStack ≥ 2023.1 (Antelope) or RHOSO 18
- Keystone (Identity)
- Manila (Shared File System service)
- Barbican (Key Manager service)
- Nova and Neutron (required for DHSS=true share network attachment)

**Object storage** *(required only for real backups)*
- An S3-compatible bucket on AWS S3, MinIO, Ceph RGW, or ODF Wasabi
- Object lock enabled on the bucket at creation time (cannot be added later)

**OpenShift** *(required only for production control-plane deployment)*
- OpenShift ≥ 4.12

**Database and messaging** *(required only for production deployment)*
- MariaDB 10.x or later
- RabbitMQ

**Optional but recommended**
- `python-abacaclient` installed (gives you the `openstack share protection …` CLI)
- The `abaca-dashboard` Horizon plugin (gives you a graphical view in the OpenStack web UI)

## Quick start

The fastest path to a working API request is the local Docker Compose dev stack. It runs the API in `--noauth` mode on `http://localhost:9797` with an SQLite database and in-memory OpenStack fakes — no real OpenStack, no S3 bucket, and no Keystone token required.

1. **Clone the repository and enter it**

```bash
git clone <repo-url> abaca
cd abaca
```

2. **Start the dev stack**

```bash
cd deploy
docker compose -f docker-compose.dev.yml up --build
```

Wait until you see the API ready message. The service listens on `http://localhost:9797`.

3. **Confirm the API is reachable**

```bash
curl -s http://localhost:9797/healthcheck
```

A `200 OK` response confirms the control plane is up.

4. **Register a backup target**

```bash
curl -s -XPOST http://localhost:9797/v1/targets \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "primary",
    "endpoint": "https://s3.example.com",
    "bucket": "tenant-abaca",
    "barbican_secret_refs": ["https://barbican/secrets/pw"],
    "trust_id": "trust-1"
  }'
```

Note the `id` field in the response — you need it in the next steps.

5. **Create a protection policy**

```bash
curl -s -XPOST http://localhost:9797/v1/policies \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "nightly",
    "share_id": "share-uuid-0001",
    "target_id": "<TARGET_ID>",
    "schedule": "0 2 * * *",
    "retention": {"daily": 7}
  }'
```

6. **Trigger an on-demand backup**

```bash
curl -s -XPOST http://localhost:9797/v1/backups \
  -H 'Content-Type: application/json' \
  -d '{
    "share_id": "share-uuid-0001",
    "target_id": "<TARGET_ID>"
  }'
```

The response includes a `job_id`. Poll it to watch the state machine advance to `available`.

7. **Poll the job until it completes**

```bash
curl -s http://localhost:9797/v1/jobs/<JOB_ID>
```

Repeat until `status` is `available` (or `error` — see Troubleshooting).

## Steps

These steps expand the quickstart with full context, expected output, and next-action guidance.

**Step 1 — Install the Python client library (optional but recommended)**

Installing `python-abacaclient` gives you both the `openstack share protection …` CLI and a Python SDK you can use in your own code. It has no dependency on the server package.

```bash
pip install python-abacaclient
```

Verify the plugin loaded:

```bash
openstack share protection --help
```

You should see a list of subcommands (`target`, `policy`, `backup`, `restore`, `job`, `usage`, `worker`, `coverage`). If the command is not found, ensure the virtualenv where you installed the package is active.

---

**Step 2 — Start the local dev stack**

The dev stack uses Docker Compose to start the `abaca-api` and `abaca-conductor` services together with a minimal set of in-process fakes. No external OpenStack services, no real S3 bucket, and no Keystone token are required in this mode.

```bash
cd deploy
docker compose -f docker-compose.dev.yml up --build
```

The API comes up in `--noauth` dev mode, meaning Keystone authentication is bypassed. **This mode must never be used in production.** The API listens on `http://localhost:9797`.

Success indicator: the terminal shows both the `abaca-api` and `abaca-conductor` containers running without error. You can also verify:

```bash
curl -s http://localhost:9797/healthcheck
# Expected: HTTP 200
```

---

**Step 3 — Register a backup target**

A backup target represents an S3-compatible bucket together with the Barbican secret references for its credentials. This is the destination for all encrypted backups belonging to one tenant.

In the dev stack, you supply a fake endpoint and a fake Barbican secret href — the in-memory fakes accept any well-formed values.

```bash
curl -s -XPOST http://localhost:9797/v1/targets \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "primary",
    "endpoint": "https://s3.example.com",
    "bucket": "tenant-abaca",
    "barbican_secret_refs": ["https://barbican/secrets/pw"],
    "trust_id": "trust-1"
  }' | python3 -m json.tool
```

The response body includes an `id` field — copy it. Every subsequent call that references this target uses that ID as `<TARGET_ID>`.

Why `trust_id`? A Keystone trust is a delegated authorization token that lets Abacá act on your behalf — fetching secrets from Barbican and performing Manila operations — without you being logged in for every operation. In a real deployment this trust is created for you automatically during enrollment; in the dev stack any non-empty string is accepted.

---

**Step 4 — Create a protection policy**

A protection policy binds a Manila share to a backup target and defines when automated backups run (a cron schedule) and how many backups are kept (retention settings). Once a policy exists, the `abaca-conductor` schedules backups automatically; you do not need to trigger them manually.

```bash
curl -s -XPOST http://localhost:9797/v1/policies \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "nightly",
    "share_id": "share-uuid-0001",
    "target_id": "<TARGET_ID>",
    "schedule": "0 2 * * *",
    "retention": {"daily": 7}
  }' | python3 -m json.tool
```

The `schedule` field is a standard five-field cron expression (minute hour day-of-month month day-of-week). The `retention.daily: 7` setting keeps the seven most recent daily backups; the conductor's maintenance job prunes older ones from the Kopia repository automatically.

Success indicator: the response includes a policy `id` and `status: active`.

---

**Step 5 — Trigger an on-demand backup**

You can request a backup at any time, independent of the policy schedule. This is useful for one-off snapshots before a risky change.

```bash
curl -s -XPOST http://localhost:9797/v1/backups \
  -H 'Content-Type: application/json' \
  -d '{
    "share_id": "share-uuid-0001",
    "target_id": "<TARGET_ID>"
  }' | python3 -m json.tool
```

The response includes both a `backup_id` and a `job_id`. The backup record is created immediately, but the actual data transfer is asynchronous — the job drives it through the state machine.

---

**Step 6 — Poll the job to completion**

Every asynchronous operation in Abacá is tracked as a job. The job moves through a fixed sequence of states:

`queued` → `provisioning_network` → `provisioning_source` → `connecting_repository` → `transferring` → `finalizing` → `releasing` → `available` (or `error`)

Each state name describes what the service is currently doing. Poll the job endpoint until you reach a terminal state:

```bash
curl -s http://localhost:9797/v1/jobs/<JOB_ID> | python3 -m json.tool
```

In the dev stack with in-memory fakes, the job completes almost instantly. Against a real cluster with a worker VM, allow time for network provisioning and data transfer.

Success indicator: `"status": "available"` in the response. If you see `"status": "error"`, check `error_category` — `tenant_action_required` means you need to fix a configuration (for example, a bad bucket or missing credential), while `operator_action_required` means the infrastructure needs attention.

---

**Step 7 — Verify using the CLI (optional)**

If you installed `python-abacaclient`, you can also inspect resources through the OpenStack CLI. In a real cluster, source your openrc first; against the dev stack, the `--noauth` mode means no credentials are needed.

```bash
# List targets
openstack share protection target list

# List backups
openstack share protection backup list

# Show job detail
openstack share protection job show <JOB_ID>
```

## Examples

**Example 1 — Register a backup target and capture its ID**

This pattern registers a target and captures its ID in a shell variable for use in subsequent commands.

```bash
TARGET_ID=$(curl -s -XPOST http://localhost:9797/v1/targets \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "primary",
    "endpoint": "https://s3.example.com",
    "bucket": "tenant-abaca",
    "barbican_secret_refs": ["https://barbican/secrets/pw"],
    "trust_id": "trust-1"
  }' | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")

echo "Target ID: $TARGET_ID"
```

Expected output:

```
Target ID: 3f2a1b9c-0001-0002-0003-000000000001
```

---

**Example 2 — Create a policy, run a backup, and poll to completion in one script**

This end-to-end script mirrors exactly what the functional smoke test (`abaca/tests/functional/test_smoke.py`) exercises programmatically.

```bash
#!/usr/bin/env bash
set -euo pipefail
BASE=http://localhost:9797/v1

# 1. Register target
TARGET_ID=$(curl -s -XPOST "$BASE/targets" \
  -H 'Content-Type: application/json' \
  -d '{"name":"primary","endpoint":"https://s3.example.com",
       "bucket":"tenant-abaca",
       "barbican_secret_refs":["https://barbican/secrets/pw"],
       "trust_id":"trust-1"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "[1] Target: $TARGET_ID"

# 2. Attach a protection policy
curl -s -XPOST "$BASE/policies" \
  -H 'Content-Type: application/json' \
  -d "{\"name\":\"nightly\",\"share_id\":\"share-uuid-0001\",
       \"target_id\":\"$TARGET_ID\",\"schedule\":\"0 2 * * *\",
       \"retention\":{\"daily\":7}}" > /dev/null
echo "[2] Policy created"

# 3. Trigger backup
JOB_ID=$(curl -s -XPOST "$BASE/backups" \
  -H 'Content-Type: application/json' \
  -d "{\"share_id\":\"share-uuid-0001\",\"target_id\":\"$TARGET_ID\"}" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['job_id'])")
echo "[3] Job: $JOB_ID"

# 4. Poll until terminal state
for i in $(seq 1 30); do
  STATUS=$(curl -s "$BASE/jobs/$JOB_ID" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "    status: $STATUS"
  [[ "$STATUS" == "available" || "$STATUS" == "error" ]] && break
  sleep 2
done

echo "[4] Final status: $STATUS"
```

Expected output (dev stack with fakes — completes almost instantly):

```
[1] Target: 3f2a1b9c-0001-0002-0003-000000000001
[2] Policy created
[3] Job: a1b2c3d4-0001-0002-0003-000000000099
    status: queued
    status: transferring
    status: available
[4] Final status: available
```

---

**Example 3 — Using the Python SDK**

The `python-abacaclient` package includes a thin SDK client you can use directly in Python code.

```python
from abacaclient.client import Client

# In --noauth dev mode, any non-empty string is accepted as a token.
# Against a real cluster, supply a valid Keystone token.
client = Client("http://localhost:9797", token="dev-noauth")

# List existing targets
targets = client.target_list()
for t in targets:
    print(t["id"], t["name"])

# Create a new target
target = client.target_create(
    name="primary",
    endpoint="https://s3.example.com",
    bucket="tenant-abaca",
    barbican_secret_refs=["https://barbican/secrets/pw"],
    trust_id="trust-1",
)
print("Created target:", target["id"])
```

Non-2xx responses raise `abacaclient.exceptions.AbacaClientError`, which carries the HTTP `status` and the error envelope fields (`code`, `title`, `detail`, `category`).

---

**Example 4 — Run the built-in functional smoke test**

The smoke test drives the complete target-create → policy-create → backup flow through the full state machine using in-memory fakes and `FakeKopia`, with no running server required.

```bash
# From the repo root, with the dev virtualenv active:
python -m venv .venv && . .venv/bin/activate
pip install -e ".[test]" -e ./python-abacaclient -e ./abaca-dashboard
pytest abaca/tests/functional/test_smoke.py -q
```

Expected output:

```
.                                                                      [100%]
1 passed in 0.42s
```

## Troubleshooting

**Issue: `docker compose` command not found**

*Symptom:* Running `docker compose -f docker-compose.dev.yml up --build` prints `docker: 'compose' is not a docker command`.

*Cause:* Older Docker installations ship the legacy `docker-compose` (v1) binary instead of the built-in Compose v2 plugin.

*Fix:* Install Docker Compose v2 (`docker compose` as a subcommand) from the [Docker documentation](https://docs.docker.com/compose/install/), or replace `docker compose` with `docker-compose` if you are pinned to v1.

---

**Issue: `curl` to `localhost:9797` returns `Connection refused`**

*Symptom:* `curl: (7) Failed to connect to localhost port 9797: Connection refused`.

*Cause:* The `abaca-api` container has not finished starting, or it exited due to a startup error.

*Fix:* Run `docker compose -f docker-compose.dev.yml logs abaca_api` to inspect startup output. Common causes are a port conflict (another process already bound to 9797) or a missing build artifact. If the container exited, look for a Python traceback in the logs. Rebuild with `--build` and try again.

---

**Issue: API returns `500` or `502` on `/healthcheck`**

*Symptom:* The containers are running but `curl http://localhost:9797/healthcheck` returns a `500` or `502` response.

*Cause:* A database migration failure or misconfiguration prevented the API from initialising cleanly.

*Fix:* Check `docker compose -f docker-compose.dev.yml logs abaca_api` and `abaca_conductor` for the root cause. If you see a migration error, ensure `abaca-manage` has run `db_sync`. Restart both containers after fixing the underlying issue.

---

**Issue: Backup job stalls in `queued` state**

*Symptom:* Polling `GET /v1/jobs/<JOB_ID>` repeatedly returns `"status": "queued"` and never advances.

*Cause:* The `abaca-conductor` is either not running, not connected to the message bus, or — on a real cluster — there are no registered workers with available capacity slots.

*Fix:* Verify the conductor container is healthy: `docker compose -f docker-compose.dev.yml ps`. In the dev stack the conductor should pick up jobs automatically. On a real cluster, check that at least one worker VM is registered and in `state=active` by running `openstack share protection worker list` or `GET /v1/admin/workers`.

---

**Issue: Backup job reaches `error` state with `tenant_action_required`**

*Symptom:* `GET /v1/jobs/<JOB_ID>` returns `"status": "error"` and `"error_category": "tenant_action_required"`.

*Cause:* Something in your configuration needs to be corrected — common causes include an unreachable or misconfigured S3 bucket, a missing or expired Barbican secret reference, or an invalid Keystone trust.

*Fix:* Read the `detail` field in the error response for the specific message. Correct the underlying configuration (for example, verify your bucket endpoint, credentials, and object-lock settings), then re-register the target or re-trigger the backup.

---

**Issue: Backup job reaches `error` state with `operator_action_required`**

*Symptom:* `GET /v1/jobs/<JOB_ID>` returns `"status": "error"` and `"error_category": "operator_action_required"`.

*Cause:* A problem in the infrastructure or the Abacá service itself needs attention — for example, the worker VM could not be booted, the RabbitMQ connection dropped, or the conductor hit an unexpected internal error.

*Fix:* Report the `detail` and `code` fields from the error response to your platform operator. On a self-managed cluster, check `docker compose -f docker-compose.dev.yml logs abaca_conductor` for a stack trace.

---

**Issue: `openstack share protection` commands are not recognised**

*Symptom:* Running `openstack share protection target list` prints `openstack: 'share' is not an openstack command`.

*Cause:* `python-abacaclient` is not installed in the active Python environment, or the OSC plugin entry point has not been registered.

*Fix:* Install the package in the same environment where `openstack` is installed: `pip install python-abacaclient`. If you installed it with `pip install -e ./python-abacaclient` in a virtualenv, make sure that virtualenv is activated before invoking `openstack`.

---

**Issue: `pip install` fails on Python < 3.11**

*Symptom:* The install fails with a syntax or compatibility error referencing a Python version.

*Cause:* Abacá requires Python 3.11 or 3.12. Older versions are not supported.

*Fix:* Switch to a Python 3.11 or 3.12 interpreter. Use `python3 --version` to confirm, and create a fresh virtualenv with the correct version: `python3.12 -m venv .venv`.
