Trilio Share Protectionfor OpenStack File Shares
Guide

Installation

Package install and the DevStack plugin for dev/test


Overview

This page explains how to install Trilio Share Protection for OpenStack (Abacá) in two deployment contexts: the RHOSO 18 production path (OpenStack Services on OpenShift) and the Kolla-Ansible reference environment used for development and testing. Both paths are scripted and idempotent — you can re-run them safely. After installation, your control plane (abaca-api and abaca-conductor) is registered in the Keystone service catalog and ready to accept backup target enrollment, policy creation, and backup/restore requests. A local developer workstation stack (Docker Compose, no-auth mode) is also described for contributors who do not have a full OpenStack cluster available.


Prerequisites

Before you begin, confirm that the following are in place.

Cluster requirements (all deployment paths)

  • Red Hat OpenStack Services on OpenShift (RHOSO) ≥ 18 or a Kolla-Ansible–deployed OpenStack cluster (development/reference only)
  • OpenShift ≥ 4.14 (RHOSO path only)
  • OpenStack services: Keystone ≥ 2023.1 (with trusts enabled and domain-scoped tokens supported), Manila, Nova, Neutron, Glance, Barbican
  • Dedicated MariaDB or MySQL (Galera) instance for Abacá — do not share with the OpenStack control plane
  • Dedicated RabbitMQ instance for Abacá — do not share with the platform broker
  • S3-compatible object storage (Wasabi, MinIO, AWS S3, etc.) with versioning and Object Lock enabled at bucket creation time

Workstation requirements

  • oc CLI authenticated to the target OpenShift cluster (RHOSO path); kubeconfig is the only credential needed — the admin password and CA bundle are read from the cluster
  • Admin OpenStack openrc sourced in the shell (Kolla path)
  • Python ≥ 3.11
  • kustomize (for reviewing or customising manifests)
  • libguestfs-tools / qemu-utils if you will build the worker VM image locally
  • Docker or Podman with Compose (local developer stack only)
  • tox ≥ 4.0 (running tests; optional)

Worker VM image

  • A RHEL ≥ 9 base cloud image accessible on the build host
  • Kopia binary, FIPS-built, version-pinned, ≥ 0.17.0
  • Glance must be available to store the finished worker image

Horizon plugin (optional)

  • An existing Horizon pod into which abaca-dashboard and python-abacaclient will be layered

Verify that Manila is enabled on the cluster before enrolling any backup targets. The RHOSO preflight script (00-preflight.sh) checks this automatically and reports the result without aborting.


Installation

RHOSO 18 (production)

The RHOSO install script is the primary supported path. It requires only a valid kubeconfig — it derives the admin password from osp-secret and the CA bundle from the cluster.

Step 1 — Clone the repository and review the manifests

git clone https://github.com/triliovault/abaca.git
cd abaca
export KUBECONFIG=~/.kube/config-rhoso18

Step 2 — Run the one-shot installer

bash deploy/rhoso/install.sh

The installer runs eight steps in order. Use --skip-<step> to bypass any step you have already completed:

FlagStepWhat it does
--skip-preflight00-preflight.shVerifies CRDs, Galera, RabbitMQ, cert-manager, and admin auth; reports Manila status
--skip-identity01-identity.shCreates generated passwords as Secrets; provisions the KeystoneService CR (service user + catalog entry) plus the worker project and role grants
--skip-catalog02-catalog.shCreates KeystoneEndpoint CRs for the public (Route) and internal (Service) endpoints
--skip-db03-database.shCreates MariaDBDatabase and MariaDBAccount CRs on Abacá's own Galera instance
--skip-mq04-messaging.shCreates a TransportURL CR on Abacá's own RabbitMQ cluster
--skip-build05-build.shRuns an in-cluster BuildConfigImageStream build; no local container runtime required
--skip-config06-config.shRenders abaca.conf and my.cnf into secret/abaca-config-data
--skip-deploy07-deploy.shApplies kustomize manifests, runs the db_sync migration Job, and waits for /healthcheck on both pods

All objects Abacá creates carry the label app.kubernetes.io/part-of=abaca.

Example: re-run only the config and deploy steps after editing abaca.conf:

bash deploy/rhoso/install.sh \
    --skip-preflight --skip-identity --skip-catalog \
    --skip-db --skip-mq --skip-build

Step 3 — Run the database migration

The migration runs automatically inside 07-deploy.sh as a Kubernetes Job (db_sync). Verify it completed successfully:

oc -n abaca get jobs
oc -n abaca logs job/abaca-db-sync

The Job has backoffLimit: 0. A failure surfaces immediately rather than being buried under retries. If it fails, fix the root cause (usually a connectivity or credentials issue) and re-run:

bash deploy/rhoso/install.sh --skip-preflight --skip-identity \
    --skip-catalog --skip-db --skip-mq --skip-build --skip-config

Step 4 — Register the bootstrap Domain

01-identity.sh resolves ABACA_BOOTSTRAP_DOMAIN to a project ID and records it in configmap/abaca-domain-map. 07-deploy.sh inserts this row into the database once db_sync has created the schema. Confirm the entry was recorded:

oc -n abaca get cm abaca-domain-map -o jsonpath='{.data}'

For every additional Keystone Domain that will use Abacá, register its service project manually:

POD=$(oc -n abaca get pods -l app.kubernetes.io/component=conductor \
    --field-selector=status.phase=Running -o name | head -1 | cut -d/ -f2)

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_register \
    --domain-id <domain-id> \
    --service-project-id <project-id>

# Confirm
oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_list

This operation is idempotent. Re-run it to correct a value. Do not pass --key-custody-project-id or --clear-key-custody unless you intend to change key custody — doing so silently orphans all existing repository passwords stored under the previous custody project.

Step 5 — Build and upload the worker VM image

Worker VMs must not fetch packages from the internet at boot. Build the image once (or after any code change) using virt-customize and guestfish, then upload it to Glance:

bash deploy/rhoso/worker/20-worker-image.sh

This script installs Kopia, NFS utilities, and abaca-worker-agent into a base RHEL cloud image and uploads the result to Glance with the property abaca_worker_image=1. Pass --distro to target a non-default RHEL variant. Pass --force to rebuild an image that is already present in Glance.

Step 6 (optional) — Install the Horizon plugin

The dashboard plugin is a separate, gated step because it modifies the shared Horizon image:

bash deploy/rhoso/08-dashboard.sh --build-only   # build only, change nothing
bash deploy/rhoso/08-dashboard.sh                # build and repoint Horizon
bash deploy/rhoso/08-dashboard.sh --revert       # restore the previous image

Important: 10-public-cert.sh is a prerequisite for 08-dashboard.sh. Horizon resolves the share-protection endpoint at publicURL, and the Horizon pods must trust the public route's certificate. Run 10-public-cert.sh before the dashboard step:

bash deploy/rhoso/10-public-cert.sh --dry-run   # review first
bash deploy/rhoso/10-public-cert.sh

Step 7 — Enable Manila (if needed, development clusters only)

In production, Manila is already present with a vendor driver. On a development cluster without Manila shares, enable the generic driver:

bash deploy/rhoso/manila/10-service-image.sh     # uploads a Glance image (safe)
bash deploy/rhoso/manila/11-enable.sh --dry-run  # review the patch
bash deploy/rhoso/manila/11-enable.sh            # patches OpenStackControlPlane
bash deploy/rhoso/manila/12-verify.sh            # creates a share end-to-end

Use --assume-yes to suppress interactive prompts in automation.


Kolla-Ansible (development/reference only)

The Kolla path targets an existing Kolla-Ansible–deployed OpenStack cluster. The admin openrc must be sourced before you begin.

Step 1 — Source your admin credentials

source /path/to/admin-openrc.sh

Step 2 — Run the one-shot installer

bash deploy/kolla/install.sh

Or target a specific controller:

bash deploy/kolla/install.sh <controller-vip> <cluster-name>

The installer runs eight steps in order:

FlagStepWhat it does
--skip-identity01-identity.shKeystone abaca service user (roles: service, admin) + worker project
--skip-catalog02-catalog.shKeystone service share-protection + three endpoints (public / internal / admin)
--skip-db03-database.shMariaDB abaca database and user with a fresh random password
--skip-mq04-messaging.shRabbitMQ /abaca vhost and user with a fresh random password
--skip-build05-build.shrsync source to the controller; docker build the api and conductor images
--skip-config06-config.shRender /etc/kolla/abaca-api/abaca.conf and /etc/kolla/abaca-conductor/abaca.conf
--skip-deploy07-deploy.shRun db_sync, (re)start containers, wait for /healthcheck
--skip-dashboard08-dashboard.shInstall abaca-dashboard into the horizon container

Generated passwords for the database and message queue users are written to cluster1-abaca-secrets.env at the repository root (mode 0600, gitignored).

Example: rapid dev loop — rebuild and restart containers only:

bash deploy/kolla/install.sh \
    --skip-identity --skip-catalog --skip-db --skip-mq \
    --skip-config --skip-dashboard

Step 3 — Build and upload the worker VM image

Use the maintained builder (the Kolla-specific builder was removed on 2026-08-19):

bash deploy/rhoso/worker/20-worker-image.sh

The script runs libguestfs locally on the workstation (not on the controller). The first run downloads approximately 700 MB of base image and Kopia tarball.

Step 4 — Verify a worker registers

Boot at least one worker VM before a tenant attempts to enroll a backup target:

# Read the transport URL from the controller config:
export ABACA_TRANSPORT_URL=$(ssh <controller> \
    "sudo grep '^transport_url' /etc/kolla/abaca-conductor/abaca.conf" \
    | cut -d' ' -f3)

# Boot a worker:
OS_CLOUD=abaca-service abaca-dev worker-boot \
    --network <tenant-network> --key-name abaca-worker

# Confirm it registered:
openstack share protection worker list

You should see one entry with state=active and a last_heartbeat_at timestamp less than one minute old.


Local developer workstation (Docker Compose, no-auth)

For contributors who do not have a full OpenStack cluster, the Docker Compose stack runs abaca-api and abaca-conductor in --noauth mode with SQLite and in-memory fakes:

docker compose -f deploy/docker-compose.dev.yml up

This mode is not suitable for production. Authentication is disabled ([api] noauth = true), and the database is SQLite rather than MariaDB. Use this path only for unit-level integration work on a developer workstation.


Teardown

RHOSO:

bash deploy/rhoso/uninstall.sh

Kolla:

bash deploy/kolla/99-teardown.sh

Both scripts are idempotent and safe to run on a partially-installed cluster. Note that operator-backed CRs (RHOSO) reverse the Keystone user, catalog, and database account, but the abaca database schema and the durable RabbitMQ queues created by oslo.messaging at runtime are removed explicitly by the uninstall script, not by CR deletion.


Configuration

Abacá uses oslo.config. All options are declared in code and read from an INI-style .conf file. On RHOSO the rendered file lives in secret/abaca-config-data and is mounted read-only into the pod at /etc/abaca/abaca.conf. On Kolla it lives at /etc/kolla/abaca-api/abaca.conf and /etc/kolla/abaca-conductor/abaca.conf.

Options are grouped by section. The sections most commonly tuned during installation and day-2 operations are described below.


[DEFAULT]

Global service discovery and endpoint resolution.

OptionTypeDefaultPurpose
catalog_typestringshare-protectionThe Keystone service type Abacá registers under. Change only if your catalog uses a site-specific type name.
endpoint_typestringpublicURLWhich catalog endpoint the client SDK uses. Valid values: publicURL, adminURL, internalURL. On RHOSO, publicURL is the correct value.

[abaca]

Identity of the Abacá service account and the bootstrap worker project.

OptionTypeDefaultPurpose
service_user_idstringKeystone UUID of the Abacá service user. Populated automatically by 01-identity.sh.
service_user_namestringDisplay name of the service user (typically abaca).
worker_project_idstringID of the project where worker VMs for the bootstrap Domain are booted. Used as a fallback when no domain_service_projects row exists for a Domain.
worker_project_namestringHuman-readable name of the same project.

For multi-Domain deployments, worker_project_id is a single-Domain bootstrap allowance only. Register each Domain with abaca-manage domain_register instead of relying on this fallback.


[api]

HTTP listener configuration for abaca-api.

OptionTypeDefaultPurpose
bind_hoststring0.0.0.0Address the gunicorn process binds to. Restrict to a specific IP in environments where binding to all interfaces is undesirable.
bind_portinteger9797TCP port for the API. Must match the Keystone catalog endpoints and any load-balancer rules.
noauthbooleanfalseDisable Keystone token validation. For developer workstation use only. Never set to true in production.
max_limitinteger1000Maximum page size for list endpoints.
default_limitinteger100Default page size when the caller does not specify a limit parameter.

[conductor]

Job scheduling, fleet management, and reconciliation tuning.

OptionTypeDefaultPurpose
scheduler_intervalinteger60Seconds between policy-driven backup scheduling sweeps. Reduce on clusters with many policies and tight schedule windows; increase on idle development clusters.
reconciliation_intervalinteger300Seconds between reconciliation sweeps. The sweep detects orphaned jobs, dead workers, and overdue maintenance.
worker_heartbeat_deadline_secondsinteger60Seconds without a heartbeat before the conductor considers a worker dead and reassigns its jobs.
worker_heartbeat_interval_secondsinteger10How often (in seconds) a healthy worker sends a heartbeat. Must be well below worker_heartbeat_deadline_seconds.
sweep_command_wait_secondsinteger120How long the reconciliation sweep waits for an in-flight command to complete before flagging the job as stuck.
worker_token_ttl_secondsinteger1800Lifetime of the short-lived token issued to a worker VM at job start.
maintenance_interval_secondsinteger604800Seconds between scheduled repository maintenance runs (default: 7 days).
catalogue_sync_interval_secondsinteger3600How often the conductor synchronises the backup catalogue from the Kopia repository into the database.
usage_sample_interval_secondsinteger21600How often the conductor samples usage data for metering (default: 6 hours).
min_workersinteger1Minimum number of healthy worker VMs the conductor will maintain. If the fleet drops below this, the conductor boots replacement workers.
worker_boot_cooldown_secondsinteger180Minimum seconds between consecutive worker boot attempts, preventing a rapid boot loop on repeated failures.
worker_boot_max_failuresinteger3Number of consecutive boot failures within worker_boot_failure_window_seconds that cause the conductor to stop automatic boots and raise an alert.
worker_boot_failure_window_secondsinteger7200Rolling window (seconds) for counting consecutive boot failures.
worker_boot_grace_secondsinteger1200Seconds the conductor waits for a freshly booted VM to register before treating the boot as failed.
worker_boot_os_cloudstringabaca-serviceNamed cloud entry from clouds.yaml used to boot worker VMs. Must correspond to a project with the right quota and network access.
worker_boot_imagestringabaca-worker-0.23.1Glance image name for worker VMs. Update this after uploading a new worker image with 20-worker-image.sh.
worker_boot_flavorstringm1.smallNova flavor for worker VMs.
worker_boot_networkstringNeutron network name or ID where worker VMs are attached. Must be routable to the Manila share export locations.
worker_boot_key_namestringName of the Nova key pair injected into worker VMs for emergency SSH access.
worker_api_urlstringURL at which the conductor reaches the worker API listener. Set when workers are on a separate network segment.
worker_api_ca_filestringPath to the CA bundle used to verify the worker API TLS certificate.
worker_api_host_aliaseslist``Additional hostnames the conductor accepts in worker API TLS certificates.
queued_job_deadline_secondsinteger300Seconds a job may sit in queued state before the reconciliation sweep flags it as stuck.

[database]

OptionTypeDefaultPurpose
connectionstringSQLAlchemy database URL, e.g. mysql+pymysql://abaca:<password>@<host>/abaca?read_default_file=/etc/my.cnf. On RHOSO, rendered automatically by 06-config.sh with TLS options via my.cnf.

[enrollment]

OptionTypeDefaultPurpose
dispatchstringWhether target enrollment jobs are dispatched via rpc (to a worker VM) or inline (in the conductor process). Use rpc in production.

[kopia]

Controls how the Kopia backup engine is invoked inside worker VMs.

OptionTypeDefaultPurpose
binarystringkopiaPath to the Kopia executable on the worker VM. Change if Kopia is installed outside the default PATH.
require_fips_profilebooleantrueRefuse to connect to repositories that were not created with the FIPS-approved algorithm profile (AES-256-GCM-HMAC-SHA256, HMAC-SHA256-128, PBKDF2). Do not disable in production.
subprocess_timeoutinteger3600Maximum seconds a single Kopia subprocess may run before the worker agent kills it.
executorstringHow Kopia is invoked. Valid values: http (leased-command transport, production), ephemeral_container (Docker/Podman container per job, local dev only).
imagestringContainer image for the ephemeral_container executor. Not used in production.
container_enginestringdocker or podman. Used only with ephemeral_container.
container_networkstringDocker/Podman network for ephemeral containers. Used only with ephemeral_container.

[worker]

Configuration read by abaca-worker-agent inside the worker VM.

OptionTypeDefaultPurpose
capacity_slotsintegerNumber of concurrent jobs this worker VM may run simultaneously. The fleet picker will not assign a new job to a worker at or above this limit.
mount_basestring/var/lib/abaca/mntDirectory under which the agent mounts Manila shares.
command_timeout_secondsinteger300Seconds before an idle command times out.
command_lease_secondsinteger120Duration of the lease the agent holds on a claimed command. Must be renewed before expiry.
max_command_duration_secondsinteger21600Hard upper bound (seconds) on any single command's total runtime.
claim_poll_interval_secondsfloat2How often (seconds) the agent polls for new commands when in short-poll mode.
long_poll_secondsinteger20Duration of each long-poll request to the conductor.
progress_interval_secondsinteger30How often (seconds) the agent reports transfer progress back to the conductor.
listener_portinteger9798TCP port the worker agent's HTTP listener binds to.
command_poll_interval_secondsfloat2How often (seconds) the listener checks for new commands in its queue.
idstringUUID assigned to this worker VM at boot. Populated automatically; do not set manually.
api_urlstringURL of the conductor's worker-facing API endpoint, injected at boot time.
tokenstringShort-lived token the agent uses to authenticate to the conductor, injected at boot time.
api_ca_filestringCA bundle for verifying the conductor's TLS certificate.

Annotated sample abaca.conf

[DEFAULT]
catalog_type = share-protection
endpoint_type = publicURL

[abaca]
service_user_name = abaca
worker_project_name = abaca-service

[api]
bind_host = 0.0.0.0
bind_port = 9797
noauth = false
max_limit = 1000
default_limit = 100

[conductor]
scheduler_interval = 60
reconciliation_interval = 300
worker_heartbeat_deadline_seconds = 60
worker_heartbeat_interval_seconds = 10
min_workers = 1
worker_boot_os_cloud = abaca-service
worker_boot_image = abaca-worker-0.23.1
worker_boot_flavor = m1.small
worker_boot_network = abaca-worker-net
worker_boot_key_name = abaca-worker
worker_boot_cooldown_seconds = 180
worker_boot_grace_seconds = 1200
worker_boot_max_failures = 3
worker_boot_failure_window_seconds = 7200
maintenance_interval_seconds = 604800

[database]
connection = mysql+pymysql://abaca:<password>@<db-host>/abaca?read_default_file=/etc/my.cnf

[enrollment]
dispatch = rpc

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

[worker]
capacity_slots = 4
mount_base = /var/lib/abaca/mnt
command_timeout_seconds = 300
command_lease_seconds = 120
max_command_duration_seconds = 21600
long_poll_seconds = 20
progress_interval_seconds = 30
listener_port = 9798

Usage

Once the control plane is running, you interact with Abacá through three surfaces:

  • OpenStack CLI — the openstack share protection subcommands provided by python-abacaclient
  • REST API — versioned at /v1, authenticated with Keystone tokens
  • Horizon dashboard — the abaca-dashboard panels in the OpenStack web UI

Verifying the installation

Confirm that abaca-api and abaca-conductor are healthy:

# RHOSO: check pod status
oc -n abaca get pods

# Confirm the service appears in the Keystone catalog
openstack catalog show share-protection

Registering a Domain (operators)

Before tenants can enroll backup targets, each Keystone Domain must be mapped to a service project. The bootstrap Domain is registered automatically during installation. For additional Domains:

POD=$(oc -n abaca get pods -l app.kubernetes.io/component=conductor \
    --field-selector=status.phase=Running -o name | head -1 | cut -d/ -f2)

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_register \
    --domain-id <keystone-domain-id> \
    --service-project-id <project-id>

Enrolling a backup target (Domain owners)

A Domain owner (a user holding the abaca_domain_owner Keystone role) registers an S3 bucket as a backup target. Abacá runs preflight conformance checks, generates an encryption key stored in Barbican, and initialises a Kopia repository — all as a job on a worker VM:

openstack share protection target create \
    --name my-backup-target \
    --template-id <template-id>

Monitor enrollment progress:

openstack share protection job show <job-id>

Creating a protection policy

A policy binds a Manila share to a backup target template, sets a cron schedule, and defines retention:

openstack share protection policy create \
    --share-id <manila-share-id> \
    --template-id <template-id> \
    --schedule "0 2 * * *" \
    --retention-days 30

Requesting an on-demand backup

openstack share protection backup create \
    --share-id <manila-share-id> \
    --target-id <target-id>

Restoring from a backup

Restore to a new share (or sub-path, or single file) from a completed backup:

openstack share protection restore create \
    --backup-id <backup-id> \
    --destination-share-id <share-id>

Checking worker fleet health (operators)

openstack share protection worker list

Workers in state=active with a recent last_heartbeat_at are healthy and accepting jobs. Workers missing heartbeats for more than worker_heartbeat_deadline_seconds seconds are considered dead and will be replaced by the conductor.

Rebuilding the catalog from a repository (disaster recovery)

If the Abacá database is lost but S3 data is intact, reconstruct the catalog:

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    rebuild-from-repository \
    --target-id <target-id>

Examples

Example 1: Verify the service catalog entry

After installation, confirm that Abacá registered correctly with Keystone.

openstack catalog show share-protection

Expected output (RHOSO — public and internal endpoints only):

+-----------+---------------------------------------------------------------+
| Field     | Value                                                         |
+-----------+---------------------------------------------------------------+
| endpoints | RegionOne                                                     |
|           |   publicURL: https://abaca-api.apps.example.com/v1           |
|           |   internalURL: http://abaca-api.abaca.svc.cluster.local:9797/v1 |
| id        | 8f2c...                                                       |
| name      | abaca                                                         |
| type      | share-protection                                              |
+-----------+---------------------------------------------------------------+

Example 2: Run the preflight check standalone

Before committing to a full install, run the preflight step on its own to surface any cluster issues.

bash deploy/rhoso/install.sh \
    --skip-identity --skip-catalog --skip-db --skip-mq \
    --skip-build --skip-config --skip-deploy

Expected output (healthy cluster):

--- 00 preflight ---
[preflight] Galera: OK
[preflight] RabbitMQ: OK
[preflight] cert-manager: OK
[preflight] Keystone: OK
[preflight] Manila: present
[preflight] All checks passed.

Example 3: Check pod readiness after deployment

oc -n abaca get pods

Expected output:

NAME                               READY   STATUS      RESTARTS   AGE
abaca-api-7d9f4b8c6-xkqpz         1/1     Running     0          4m
abaca-conductor-6b5c9d7f4-mnjvr   1/1     Running     0          4m
abaca-db-sync-x4tgp               0/1     Completed   0          5m

The abaca-db-sync Job must show Completed before the other pods are considered healthy.


Example 4: Register an additional Domain's service project

POD=$(oc -n abaca get pods -l app.kubernetes.io/component=conductor \
    --field-selector=status.phase=Running -o name | head -1 | cut -d/ -f2)

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_register \
    --domain-id d1e2f3a4-0000-0000-0000-000000000001 \
    --service-project-id a9b8c7d6-0000-0000-0000-000000000002

Expected output:

Domain d1e2f3a4-0000-0000-0000-000000000001 registered with service project a9b8c7d6-0000-0000-0000-000000000002

List all registered Domains to confirm:

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_list

Expected output:

+--------------------------------------+--------------------------------------+
| domain_id                            | service_project_id                   |
+--------------------------------------+--------------------------------------+
| d1e2f3a4-0000-0000-0000-000000000001 | a9b8c7d6-0000-0000-0000-000000000002 |
+--------------------------------------+--------------------------------------+

Example 5: Fast dev loop on Kolla — rebuild and restart only

After a code change, skip all setup steps and rebuild/restart the containers:

bash deploy/kolla/install.sh \
    --skip-identity --skip-catalog --skip-db --skip-mq \
    --skip-config --skip-dashboard

This runs only 05-build.sh (rsync + docker build) and 07-deploy.sh (db_sync + container restart).


Example 6: Confirm a worker VM registered

After booting a worker with abaca-dev worker-boot:

openstack share protection worker list

Expected output (one healthy worker):

+--------------------------------------+--------+---------------------+
| id                                   | state  | last_heartbeat_at   |
+--------------------------------------+--------+---------------------+
| 7c3a1e8f-0000-0000-0000-000000000001 | active | 2026-09-01T10:23:45 |
+--------------------------------------+--------+---------------------+

If no workers appear, or state is not active, see the troubleshooting section.


Troubleshooting

Use a consistent format for each issue: Symptom describes what you observe; Likely cause explains why; Fix tells you what to do.


Symptom: abaca-db-sync Job fails with exit code 1; logs show MySQL error 1050: Table already exists.

Likely cause: The API pod started (triggered by an OpenShift image.openshift.io/triggers annotation on the Deployment) and called create_all() before the migration Job ran, creating tables without stamping alembic_version. The migration then conflicts with the pre-created tables.

Fix: Do not drop the database if it contains real data. Identify which table the migration tried to create, add any missing columns by hand to match the migration output, then set the version stamp:

UPDATE alembic_version SET version_num='<revision>';

Verify with SELECT * FROM information_schema.columns WHERE table_schema='abaca'; before and after. Then re-run the deploy step:

bash deploy/rhoso/install.sh \
    --skip-preflight --skip-identity --skip-catalog \
    --skip-db --skip-mq --skip-build --skip-config

Symptom: Pods are Running but kubectl logs shows connection refused or SSL handshake failed for database connections.

Likely cause: The [database] connection URL was rendered without the ?read_default_file=/etc/my.cnf suffix, or my.cnf is missing the ssl-ca directive pointing to the CA bundle.

Fix: Re-run the config step, which regenerates secret/abaca-config-data and restarts pods:

bash deploy/rhoso/install.sh \
    --skip-preflight --skip-identity --skip-catalog \
    --skip-db --skip-mq --skip-build

Verify the rendered connection string:

oc -n abaca get secret abaca-config-data -o jsonpath='{.data.abaca\.conf}' \
    | base64 -d | grep connection

Symptom: Horizon Share Protection panels render empty (no error, just no data).

Likely cause: The public route for abaca-api is not serving a certificate that the Horizon pods trust. Horizon resolves the share-protection endpoint at publicURL, and a certificate verification failure silently returns empty results.

Fix: Run 10-public-cert.sh before or after 08-dashboard.sh:

bash deploy/rhoso/10-public-cert.sh --dry-run  # review
bash deploy/rhoso/10-public-cert.sh
bash deploy/rhoso/08-dashboard.sh

Symptom: After 08-dashboard.sh, Share Protection panels are gone after a Horizon pod restart or kolla-ansible reconfigure horizon (Kolla path).

Likely cause: The dashboard plugin is installed into the running container's venv; a new container image wipes it.

Fix (Kolla): Re-run 08-dashboard.sh after any Horizon reconfigure:

bash deploy/kolla/install.sh --skip-identity --skip-catalog --skip-db \
    --skip-mq --skip-build --skip-config --skip-deploy

Fix (RHOSO): Use --revert to restore the previous image, then rebuild:

bash deploy/rhoso/08-dashboard.sh --revert
bash deploy/rhoso/08-dashboard.sh

Symptom: Worker VM boots (visible in Nova) but never appears in openstack share protection worker list; state never becomes active.

Likely cause (most common): The worker VM cannot reach the conductor's RabbitMQ port or the conductor's worker API URL from inside the tenant network. A secondary cause is a mismatched transport URL between the worker agent's config and the conductor.

Fix:

  1. SSH into the worker VM and check the agent logs:
    journalctl -u abaca-worker-agent
    
  2. Confirm the worker can reach the conductor's RabbitMQ port (5672) and the worker_api_url from inside the VM.
  3. On Kolla, verify the transport URL matches between the conductor config and the VM:
    ssh <controller> "sudo grep '^transport_url' /etc/kolla/abaca-conductor/abaca.conf"
    
  4. If the image predates the worker agent being baked in (the abaca_worker_image=1 property is absent from Glance), rebuild:
    bash deploy/rhoso/worker/20-worker-image.sh --force
    

Symptom: abaca-manage domain_register succeeds, but GET /v1/domain still reports is_service_project: false for all projects, and the Horizon "Register Bucket" button is hidden everywhere.

Likely cause: No domain_service_projects row exists in the database, so the API is falling back to the [abaca] worker_project_id single-Domain bootstrap value, which belongs to one Domain only.

Fix: Confirm the row was written:

oc -n abaca exec "$POD" -- abaca-manage \
    --config-file /etc/abaca/abaca.conf \
    domain_list

If the Domain is missing, re-run domain_register with the correct IDs. If the Domain appears but the API still returns is_service_project: false, verify that the abaca_domain_owner Keystone role is assigned to the user in the correct Domain scope.


Symptom: worker_boot attempts fail repeatedly; the conductor stops booting new workers and emits an alert.

Likely cause: The conductor hit worker_boot_max_failures (default: 3) consecutive failures within worker_boot_failure_window_seconds (default: 7200 seconds). Common causes include a wrong worker_boot_image name (the image may not exist in Glance), an incorrect worker_boot_network, or Nova quota exhaustion.

Fix:

  1. Check conductor logs for the boot failure reason:
    oc -n abaca logs deployment/abaca-conductor | grep -i "worker boot"
    
  2. Verify the image exists in Glance with the expected name:
    openstack image show "$(grep worker_boot_image /etc/abaca/abaca.conf | awk '{print $3}')"
    
  3. Correct the [conductor] worker_boot_image or worker_boot_network in secret/abaca-config-data and re-run the config step:
    bash deploy/rhoso/install.sh --skip-preflight --skip-identity \
        --skip-catalog --skip-db --skip-mq --skip-build
    

Symptom: Kolla deployment: container is Running but GET /healthcheck returns 502 or 500.

Likely cause: One of three issues — the db_sync migration did not complete, the RabbitMQ vhost is missing, or abaca.conf has a syntax error.

Fix:

# Check container logs on the controller:
docker logs abaca_api
docker logs abaca_conductor

# Re-run just the deploy step (which re-runs db_sync first):
bash deploy/kolla/install.sh --skip-identity --skip-catalog --skip-db \
    --skip-mq --skip-build --skip-config --skip-dashboard

If the config file was edited in place, restart the containers to pick up the change:

docker restart abaca_api abaca_conductor

Symptom: RabbitMQ TLS listener never becomes ready; the conductor pod logs Peer discovery: no nodes available for auto-clustering.

Likely cause: RabbitMq.spec.tls.caSecretName was pointed at a CA bundle Secret instead of the cert-manager certificate Secret. The TLS listener fails to configure, which manifests as a clustering error rather than a TLS error.

Fix: Both caSecretName and secretName in RabbitMq.spec.tls must reference the same cert-manager certificate Secret (which carries ca.crt), not the abaca-ca-bundle Secret (which is a concatenated PEM bundle). Update the RabbitMq CR and allow the operator to reconcile.


Symptom: oc apply on the CA bundle Secret fails or silently truncates the bundle; pods cannot verify Keystone or tenant S3 endpoints.

Likely cause: oc apply stores a full copy of the object in the last-applied-configuration annotation. The platform CA bundle (~245 KB, ~160 certificates) exceeds the 256 KB annotation limit.

Fix: Always use oc create or oc replace (never oc apply) for secret/abaca-ca-bundle:

oc -n abaca replace -f abaca-ca-bundle.yaml