Trilio Share Protectionfor OpenStack File Shares
Guide

OpenStack Integration

How it uses Keystone, the message bus, and other services


Overview

This page explains how Trilio Share Protection for OpenStack (Abacá) integrates with Keystone, the message bus, and other OpenStack services. Understanding these integration points matters because Abacá is a first-class OpenStack service: it registers in the Keystone service catalog, uses oslo.messaging for API-to-conductor communication, delegates secret management to Barbican, and coordinates with Nova, Neutron, Manila, and Glance to run backup and restore jobs. Operators deploying Abacá or developers building against its API need to know how authentication flows from a tenant request through to a worker VM, how secrets are isolated from the control plane, and why certain architectural choices — such as a dedicated RabbitMQ broker and outbound-only worker transport — are non-negotiable for security and deployability.


Prerequisites

Before reading this page, you should have:

  • RHOSO ≥ 18 with OpenShift ≥ 4.14 hosting the control plane
  • OpenStack Keystone ≥ 2023.1 with domain-scoped tokens and trusts enabled
  • OpenStack Manila (Shared File System service)
  • OpenStack Barbican (Key Manager service)
  • OpenStack Nova and Neutron for worker VM lifecycle
  • OpenStack Glance for worker image storage
  • A dedicated MySQL or MariaDB (Galera) instance for Abacá's bookkeeping database
  • A dedicated RabbitMQ instance for Abacá's oslo.messaging bus — never the platform's shared broker
  • S3-compatible object storage with versioning and Object Lock enabled at bucket creation
  • Familiarity with Keystone concepts: service catalog, roles, trusts, domain-scoped tokens
  • The Abacá deployment scripts from deploy/rhoso/ checked out and your clouds.yaml configured

Installation

Abacá's OpenStack integration is established by three deployment scripts that must run in order. Each script is idempotent — re-running it is safe and will not rotate passwords unless you set ABACA_ROTATE=1.

Step 1 — Register the Keystone identity

Run deploy/rhoso/01-identity.sh. This script:

  • Creates the abaca OpenShift namespace and Abacá's own certificate authority
  • Generates the service account password and stores it in an OpenShift Secret (abaca-secret)
  • Applies a KeystoneService CR in the control-plane namespace; the keystone-operator creates both the abaca Keystone service user and the catalog service record automatically
  • Creates the abaca worker project, which is the OpenStack project where worker VMs run
  • Grants the service user the service and admin roles on the service project, and admin on the worker project
  • Creates the abaca_domain_owner role and grants it to the bootstrap admin user so that a Domain owner can immediately create backup target templates
  • Records the bootstrap domain-to-service-project mapping in a ConfigMap (abaca-domain-map) for the database migration step
cd deploy/rhoso
bash 01-identity.sh

Wait for the script to report OK. and confirm the KeystoneService reached Ready status:

oc -n openstack get keystoneservice abaca -o jsonpath='{.status.conditions}'

Step 2 — Register catalog endpoints

Run deploy/rhoso/02-catalog.sh. This applies a KeystoneEndpoint CR that registers the public and internal interfaces for the share-protection service type. There is no admin interface — modern RHOSO deployments register only public and internal.

bash 02-catalog.sh

Verify the endpoints are visible in the catalog:

openstack endpoint list --service abaca -c Interface -c URL -c Enabled -f table

You should see two rows: one public URL (an OpenShift Route) and one internal URL (an in-cluster Service).

Step 3 — Provision the message bus

Run deploy/rhoso/04-messaging.sh. This creates Abacá's own dedicated RabbitMQ instance in the abaca namespace and provisions a TransportURL CR to generate the connection string. The script deliberately avoids the platform's shared broker — see Configuration for why this is a hard requirement.

bash 04-messaging.sh

The script waits up to 15 minutes for the RabbitMq CR and the TransportURL CR to reach Ready. When it prints OK., the transport URL secret is available in the abaca namespace for the deployment step to consume.

Step 4 — Grant the domain-owner role to real users

After the initial bootstrap, grant the abaca_domain_owner role to the users who will register S3 buckets for each Keystone Domain:

openstack role add \
  --user <username> \
  --user-domain <domain-name> \
  --domain <domain-name> \
  abaca_domain_owner

Without this role, no user in that Domain can create a BackupTargetTemplate or register an S3 bucket — every such request returns HTTP 403.


Configuration

Abacá is configured with an oslo.config INI file. The options below govern OpenStack integration specifically; see the deployment guide for the full annotated sample configuration.

[DEFAULT] — Service catalog discovery

Abacá discovers other OpenStack services via the Keystone catalog. These options control which service type name it looks for and which endpoint interface it calls.

OptionTypeDefaultPurpose
catalog_typestringshare-protectionThe Keystone service type under which Abacá's own endpoints are registered. Change this only if you registered a non-standard type.
endpoint_typestringpublicURLThe catalog interface Abacá uses when calling other OpenStack services. Valid values: publicURL, adminURL, internalURL. Use internalURL in production to keep inter-service traffic on the internal network.

[abaca] — Service identity and worker project

These options identify Abacá's own service account and the OpenStack project where worker VMs run. The conductor uses these to scope Nova, Neutron, and Glance API calls when booting and managing the worker fleet.

OptionTypeDefaultPurpose
service_user_idstringKeystone UUID of the Abacá service user. Set this or service_user_name; the conductor uses it to scope its own token.
service_user_namestringName of the Abacá service user, as an alternative to service_user_id.
worker_project_idstringKeystone UUID of the project where worker VMs are booted. Used as a single-Domain fallback when no domain_service_projects row exists.
worker_project_namestringName of the worker project, as an alternative to worker_project_id.

[api] — API service binding and behaviour

OptionTypeDefaultPurpose
bind_hoststring0.0.0.0Address the gunicorn workers bind to inside the pod.
bind_portinteger9797Port the API listens on.
noauthbooleanfalseDisables Keystone authentication entirely. Never set true in production — this is a dev-only mode for the docker-compose stack.
max_limitinteger1000Hard cap on items returned per list request.
default_limitinteger100Default page size when the caller does not supply a limit parameter.

[database] — Database connection

OptionTypeDefaultPurpose
connectionstringSQLAlchemy connection URL for Abacá's dedicated MySQL/MariaDB (Galera) database, e.g. mysql+pymysql://abaca:PASSWORD@HOST/abaca. Abacá stores only bookkeeping metadata here — no secret values, no backup data.

[conductor] — Orchestration and worker fleet

These options govern how the conductor interacts with Nova and Neutron to manage the worker VM fleet, and how frequently it runs its reconciliation sweeps.

OptionTypeDefaultPurpose
scheduler_intervalinteger60Seconds between policy scheduling sweeps. Lower values reduce backup start latency but increase database load.
reconciliation_intervalinteger300Seconds between orphan-sweep and stuck-job recovery passes.
worker_heartbeat_deadline_secondsinteger60A worker that has not sent a heartbeat within this window is considered dead and its jobs are recovered.
worker_heartbeat_interval_secondsinteger10How often a healthy worker sends a heartbeat to the conductor.
sweep_command_wait_secondsinteger120Seconds the reconciliation loop waits for a command to be claimed before marking it stale.
worker_token_ttl_secondsinteger1800Lifetime of the short-lived Keystone token issued to a worker VM at boot.
maintenance_interval_secondsinteger604800Seconds between Kopia repository maintenance runs (default: one week).
catalogue_sync_interval_secondsinteger3600How often the conductor reconciles its internal share catalogue against the Manila API.
usage_sample_interval_secondsinteger21600Frequency of usage/metering samples for chargeback (default: six hours).
min_workersinteger1Minimum number of healthy worker VMs the conductor maintains in the fleet.
worker_boot_cooldown_secondsinteger180Minimum time between successive worker boot attempts.
worker_boot_max_failuresinteger3If this many boot attempts fail within worker_boot_failure_window_seconds, the conductor stops trying and raises operator_action_required.
worker_boot_failure_window_secondsinteger7200Rolling window for counting boot failures.
worker_boot_grace_secondsinteger1200Time allowed for a newly booted worker to register before it is considered failed.
worker_boot_os_cloudstringabaca-serviceThe clouds.yaml cloud name the conductor uses to call Nova/Neutron when booting worker VMs.
worker_boot_imagestringabaca-worker-0.23.1Glance image name (must carry the abaca_worker_image=1 property) used when booting new workers.
worker_boot_flavorstringm1.smallNova flavor for worker VMs.
worker_boot_networkstringNeutron network ID or name to attach worker VMs to at boot.
worker_boot_key_namestringNova key pair name injected into worker VMs for emergency SSH access.
worker_api_urlstringBase URL the conductor passes to new worker VMs so they know where to claim leased commands.
worker_api_ca_filestringPath to the CA bundle the conductor presents to workers for TLS verification of the API endpoint.
worker_api_host_aliaseslist""Hostname aliases injected into worker cloud-init when the API endpoint resolves to an IP the worker network cannot route.
queued_job_deadline_secondsinteger300A job that remains queued longer than this is considered stuck and recovered by the reconciliation loop.

[kopia] — Backup engine

OptionTypeDefaultPurpose
binarystringkopiaPath to the Kopia binary inside the worker VM. Should not need changing if the image is built with deploy/rhoso/worker/20-worker-image.sh.
require_fips_profilebooleantrueReject repository initialization if the FIPS cryptographic profile (AES-256-GCM-HMAC-SHA256, PBKDF2) cannot be applied. Set false only in development environments that lack a FIPS-built Kopia binary.
subprocess_timeoutinteger3600Maximum seconds a single Kopia subprocess may run before the worker agent kills it.
executorstringHow Kopia is invoked. http is the production path (worker VM fleet). ephemeral_container is docker-compose dev-only and is not paired with the worker fleet. Valid values: http, ephemeral_container.

[worker] — Worker agent configuration

These options are set in the configuration of the abaca-worker-agent process running inside each worker VM, not in the control-plane configuration.

OptionTypeDefaultPurpose
capacity_slotsintegerMaximum number of concurrent Kopia jobs this worker will accept. The conductor's fleet picker only assigns a job to a worker whose running job count is strictly below this limit.
mount_basestring/var/lib/abaca/mntDirectory under which the agent creates per-job NFS mount points.
command_lease_secondsinteger120How long a claimed command lease is valid. The agent renews it via progress updates before expiry.
max_command_duration_secondsinteger21600Hard limit (six hours) on any single command. The agent abandons a command and reports failure if this is exceeded.
long_poll_secondsinteger20How long the agent holds the GET /v1/workers/{id}/commands?wait=… request open when there are no pending commands.
progress_interval_secondsinteger30How often the agent posts progress updates (which also renew the command lease).
api_urlstringBase URL of the Abacá API, passed to the worker at boot via cloud-init.
tokenstringShort-lived Keystone token the conductor issues at boot (worker_token_ttl_seconds).
api_ca_filestringPath to the CA bundle the agent uses to verify the API endpoint's TLS certificate.

[enrollment] — Target enrollment dispatch

OptionTypeDefaultPurpose
dispatchstringControls how S3 bucket enrollment jobs are dispatched. rpc sends them through the conductor to the worker fleet (production). inline runs enrollment in the conductor process — for testing only. Valid values: rpc, inline.

Usage

How authentication works

Every request to the Abacá API must carry a Keystone token in the X-Auth-Token header, exactly as with any other OpenStack service. The API's keystonemiddleware validates the token against Keystone and populates the request context with the caller's project, domain, and roles. Access control is then enforced by oslo.policy rules.

Abacá registers in the Keystone service catalog under the share-protection service type. Clients discover the API endpoint the same way they discover any other OpenStack service — from the catalog or via openstack catalog show abaca.

How secrets are kept off the bus

Abacá never stores S3 credentials or Kopia repository passwords in its own database or on the message bus. When a Domain owner registers an S3 bucket, Abacá stores the credentials in Barbican and records only the Barbican secret href in its database. When the conductor dispatches a job, the message carries that href and a Keystone trust ID — never the secret value itself. The worker VM fetches the secret from Barbican at job time, holds it only in memory for the duration of the job, and discards it afterward.

How workers authenticate

Worker VMs do not use the shared message bus. Instead, the conductor issues each worker a short-lived Keystone token (valid for worker_token_ttl_seconds, default 30 minutes) at boot time, delivered via cloud-init. The worker uses this token to call GET /v1/workers/{id}/commands on the Abacá API, which holds the request open (long-poll) until a command is available or the wait timeout (long_poll_seconds) expires. This outbound-only model means workers require no inbound network access and introduce no shared credential whose blast radius exceeds their own jobs.

How Keystone trusts are used

Abacá uses Keystone trusts to act on behalf of a tenant without holding the tenant's password. When a tenant creates a policy or registers a target, Abacá requests a trust delegation scoped to the specific operations it needs — reading Barbican secrets and managing Manila access rules. The tenant can revoke the trust at any time, which immediately prevents Abacá from acting on that tenant's behalf. Trust IDs are carried on the message bus; the actual trust token is materialized at job time.

Interacting with the API

Abacá's API is versioned at /v1. You can interact with it three ways:

OpenStack CLI (the python-abacaclient plugin provides openstack share protection … subcommands):

# List protection policies for the current project
openstack share protection policy list

# Request an on-demand backup
openstack share protection backup create --share <share-id> --policy <policy-id>

# Check job status
openstack share protection job show <job-id>

REST API directly:

# Obtain a token
OS_TOKEN=$(openstack token issue -f value -c id)
ABACA_URL=$(openstack catalog show abaca -f value -c endpoints \
  | grep public | awk '{print $2}')

# List targets visible to the current domain
curl -s -H "X-Auth-Token: ${OS_TOKEN}" \
  "${ABACA_URL}/v1/targets" | python3 -m json.tool

Python SDK (the python-abacaclient library):

from abacaclient import Client
import openstack

conn = openstack.connect(cloud='mycloud')
client = Client(session=conn.session)

for policy in client.policies.list():
    print(policy.id, policy.share_id, policy.schedule)

Registering a Domain and its service project

After the initial install, each Keystone Domain that needs backup coverage must be registered with abaca-manage:

# Register a domain and map it to its dedicated service project
abaca-manage domain_register \
  --domain-id <keystone-domain-id> \
  --service-project-id <openstack-project-id>

Without this mapping, the API cannot determine which project should own worker VMs and S3 credentials for that Domain, and users in that Domain cannot register backup targets.

Using the Horizon plugin

If abaca-dashboard is installed (see deploy/rhoso/08-dashboard.sh), tenant users see a Share Protection panel in the Horizon project view covering targets, policies, backups, restores, and usage. Operators see an additional Admin panel showing fleet health and coverage status across all projects.


Examples

Example 1 — Verify catalog registration

After running 02-catalog.sh, confirm that both interfaces are registered and point to the correct URLs.

openstack endpoint list --service abaca -c Interface -c URL -c Enabled -f table

Expected output:

+-----------+----------------------------------------------+---------+
| Interface | URL                                          | Enabled |
+-----------+----------------------------------------------+---------+
| public    | https://abaca.apps.cluster.example.com/v1    | True    |
| internal  | http://abaca-api.abaca.svc.cluster.local/v1  | True    |
+-----------+----------------------------------------------+---------+

Example 2 — Confirm the service user has the correct role grants

openstack role assignment list \
  --user abaca \
  --names \
  -c Role -c Project -c Domain -f table

Expected output (exact project and domain names depend on your deployment):

+-------+------------------+--------+
| Role  | Project          | Domain |
+-------+------------------+--------+
| admin | abaca            |        |
| admin | service          |        |
| service | service        |        |
+-------+------------------+--------+

Example 3 — Grant the domain-owner role to a real user

After bootstrap, grant the role to the user who will manage backup targets for a Domain:

openstack role add \
  --user alice \
  --user-domain acme \
  --domain acme \
  abaca_domain_owner

Verify the grant:

openstack role assignment list \
  --user alice \
  --domain acme \
  --names \
  -c Role -c Domain -f table

Expected output:

+--------------------+--------+
| Role               | Domain |
+--------------------+--------+
| abaca_domain_owner | acme   |
+--------------------+--------+

Example 4 — Register a Domain with its service project using abaca-manage

Resolve the IDs first, then register:

DOMAIN_ID=$(openstack domain show acme -f value -c id)
PROJECT_ID=$(openstack project show abaca-acme -f value -c id)

abaca-manage domain_register \
  --domain-id "${DOMAIN_ID}" \
  --service-project-id "${PROJECT_ID}"

Example 5 — Annotated conductor configuration for OpenStack integration

The following snippet shows the options most relevant to OpenStack integration. Place this in /etc/abaca/abaca.conf (or the path mounted into the abaca-conductor pod):

[DEFAULT]
# The catalog service type Abacá registers under
catalog_type = share-protection
# Use the internal endpoint for inter-service calls
endpoint_type = internalURL

[abaca]
# The Keystone user Abacá uses for all service-level API calls
service_user_name = abaca
# The project where worker VMs are booted (single-Domain fallback)
worker_project_name = abaca

[api]
bind_host = 0.0.0.0
bind_port = 9797
# Never set noauth = true in production
noauth = false
default_limit = 100
max_limit = 1000

[conductor]
# Minimum workers the fleet must maintain
min_workers = 2
# Glance image for new workers (must have abaca_worker_image=1 property)
worker_boot_image = abaca-worker-0.23.1
worker_boot_flavor = m1.medium
# clouds.yaml entry the conductor uses to call Nova/Neutron
worker_boot_os_cloud = abaca-service
# URL workers use to claim leased commands
worker_api_url = https://abaca.apps.cluster.example.com/v1
# CA bundle for workers to verify the API endpoint
worker_api_ca_file = /etc/abaca/ca-bundle.pem
# Seconds a job may remain queued before the reconciliation loop recovers it
queued_job_deadline_seconds = 300

[database]
connection = mysql+pymysql://abaca:REDACTED@mariadb.abaca.svc.cluster.local/abaca

[kopia]
binary = kopia
require_fips_profile = true
executor = http

Example 6 — Trigger an on-demand backup via the CLI

openstack share protection backup create \
  --share a1b2c3d4-0000-0000-0000-111122223333 \
  --policy e5f6a7b8-0000-0000-0000-444455556666

Then poll the resulting job until it reaches available:

watch -n 5 openstack share protection job show <job-id> -f table

Troubleshooting

Issue: KeystoneService CR never reaches Ready

Symptom: 01-identity.sh times out waiting for the KeystoneService to become Ready; oc -n openstack get keystoneservice abaca -o jsonpath='{.status.conditions}' shows a non-True status.

Likely cause: The abaca-secret Secret does not exist in the control-plane namespace (openstack), or the AbacaPassword key is missing. The keystone-operator requires the secret to be present in its own namespace.

Fix: Confirm the secret exists in both namespaces:

oc -n abaca get secret abaca-secret -o jsonpath='{.data.AbacaPassword}' | base64 -d
oc -n openstack get secret abaca-secret -o jsonpath='{.data.AbacaPassword}' | base64 -d

If the openstack-namespace copy is missing, the script copies it automatically — re-run 01-identity.sh.


Issue: 403 Forbidden when creating a BackupTargetTemplate

Symptom: A Domain user receives HTTP 403 when attempting to create a backup target template, even when logged in with an admin-equivalent account.

Likely cause: The abaca_domain_owner role has not been granted on the Domain (not the project). The role must be a domain-scoped assignment; a project-scoped admin role does not satisfy the policy rule, and a domain-scoped token cannot be issued without a domain role assignment.

Fix:

openstack role add \
  --user <username> \
  --user-domain <domain-name> \
  --domain <domain-name> \
  abaca_domain_owner

Issue: 401 Unauthorized on every API request

Symptom: All API calls return 401 even with a valid token.

Likely cause 1: The catalog_type in abaca.conf does not match the service type registered in step 2 (share-protection). The keystonemiddleware cannot find the service in the catalog to validate against.

Likely cause 2: noauth = true has been set in the [api] section of a production deployment (makes no difference to 401s but disables all auth; check that it is false).

Fix: Confirm the registered service type:

openstack service show abaca -c type -f value

Ensure [DEFAULT] catalog_type in abaca.conf matches exactly. Restart the abaca-api pods after any config change:

oc -n abaca rollout restart deployment abaca-api

Issue: Workers register but jobs remain queued indefinitely

Symptom: Worker VMs appear healthy (heartbeats arriving), but backup jobs stay in the queued state past queued_job_deadline_seconds.

Likely cause: The conductor cannot issue leased commands because worker_api_url is not set or resolves to an address the worker cannot reach. Workers cannot claim commands without a reachable API URL.

Fix: Confirm the value in [conductor] worker_api_url points to the public Abacá API endpoint and that the route is accessible from the worker VM's network. Check the conductor logs:

oc -n abaca logs deployment/abaca-conductor --tail=100 | grep -i worker

Issue: TransportURL CR never reaches Ready

Symptom: 04-messaging.sh times out; oc -n abaca get transporturl abaca-transport -o jsonpath='{.status}' shows no secretName.

Likely cause: The RabbitMQ CR is not yet Ready, or the cert-manager certificate for the broker has not been issued. The rabbitmq-operator will not provision the vhost or user until the broker is healthy.

Fix: Check the broker first:

oc -n abaca get rabbitmqs.rabbitmq.openstack.org -o wide
oc -n abaca describe rabbitmqs.rabbitmq.openstack.org abaca-rabbitmq

If the broker is Running but not Ready, inspect the certificate:

oc -n abaca describe certificate cert-abaca-rabbitmq

Issue: Domain users see no "Register Bucket" button in Horizon

Symptom: The Share Protection panel is visible but no option to register a bucket appears, even in the service project.

Likely cause: The domain_service_projects database row for this Domain is missing. Without it, GET /v1/domain reports is_service_project: false for every project, and the dashboard hides the registration UI.

Fix: Register the Domain-to-service-project mapping:

DOMAIN_ID=$(openstack domain show <domain-name> -f value -c id)
PROJECT_ID=$(openstack project show <service-project-name> -f value -c id)

abaca-manage domain_register \
  --domain-id "${DOMAIN_ID}" \
  --service-project-id "${PROJECT_ID}"

Issue: Worker VM boots but does not register with the conductor

Symptom: A worker VM reaches ACTIVE in Nova but never appears in the Abacá worker fleet; openstack share protection commands report insufficient fleet capacity.

Likely cause 1: The Glance image used to boot the worker does not have the abaca_worker_image=1 property. An unprepared image will not start the abaca-worker-agent systemd unit.

Likely cause 2: worker_api_url was not injected into the worker via cloud-init, so the agent has no endpoint to call.

Fix: Verify the Glance image property:

openstack image show <image-name> -c properties -f json | python3 -m json.tool

Confirm abaca_worker_image is "1". If the property is missing, rebuild the image using deploy/rhoso/worker/20-worker-image.sh and re-upload to Glance. Check the [conductor] worker_boot_image option matches the correct image name.