Trilio Site Recovery for OpenStackDisaster Recovery Solution for OpenStack Virtual Machines
CLI reference

CLI Reference

Trilio Site Recovery implements a new OpenStack CLI plugin called dr. Users can use the DR plugin to manage the Trilio Site Recovery solution, including adding/removing sites and creating protection groups.


Description

The openstack dr CLI plugin is the primary interface for managing Trilio Site Recovery operations. It extends the standard OpenStack client with a dr command group that covers the full DR lifecycle: registering sites, creating and populating Protection Groups, executing failover and failback, running non-disruptive DR tests, monitoring replication health, and administering tenant mappings. Install the plugin separately from the server package — it is distributed as the triliositerecovery Python package located in the client/ subdirectory of the repository.


Synopsis
openstack dr <resource> <action> [<positional-args>] [--flags]

All commands follow the pattern openstack dr <resource> <action>. Multi-word resources use spaces, not dashes — for example openstack dr protection group create, not openstack dr protection-group create.

Full command shapes:

openstack dr site list
openstack dr site show <site>
openstack dr site create <name> --site-type <PRIMARY|SECONDARY> --auth-url <url> [flags]
openstack dr site set <site> [flags]
openstack dr site delete [<site...>]
openstack dr site failover <site> [flags]
openstack dr failover to this site [flags]
openstack dr site operation list [flags]
openstack dr site operation show <operation>

openstack dr protection group list [--all-projects]
openstack dr protection group show <protection_group> [--all-projects]
openstack dr protection group create <name> --primary-site <site> --secondary-site <site> [flags]
openstack dr protection group set <protection_group> [flags]
openstack dr protection group delete [<protection_group...>] [--force]
openstack dr protection group member add <protection_group> --instance <id> [--boot-order <n>]
openstack dr protection group member set <protection_group> --member <id> --boot-order <n>
openstack dr protection group member remove <protection_group> --member <id>
openstack dr protection group member list <protection_group>
openstack dr protection group member validate <protection_group> --instance <id>
openstack dr protection group sync <protection_group> [flags]
openstack dr protection group mapping list <protection_group> [flags]
openstack dr protection group mapping create <protection_group> --resource-type <type> --primary-resource-id <id> --secondary-resource-id <id>
openstack dr protection group mapping delete <protection_group> <mapping>
openstack dr protection group reconcile <protection_group>

openstack dr replication health <protection_group>
openstack dr recovery point list <protection_group> [--limit <n>]
openstack dr recovery point create <protection_group> [--suffix <string>]

openstack dr failover <protection_group> [flags]
openstack dr failback <protection_group> [flags]
openstack dr test failover <protection_group> [flags]
openstack dr test failover cleanup <protection_group> [flags]

openstack dr operation list [flags]
openstack dr operation show <operation>
openstack dr operation cancel <operation>
openstack dr metadata sync <protection_group> [--force]

openstack dr tenant mapping list [flags]
openstack dr tenant mapping show <mapping>
openstack dr tenant mapping create --local-tenant <id> --remote-site <id> --remote-tenant <id> [flags]
openstack dr tenant mapping delete [<mapping...>] [--no-sync-remote]
openstack dr tenant mapping validate <mapping>

openstack dr usage summary
openstack dr usage report [flags]

Flags

Flags are grouped by command below. Standard OpenStack client output flags (-f, -c, --sort-column, --fit-width, etc.) apply to all commands and are not repeated here.


dr site create <name>

FlagShortDefaultDescription
--site-type <PRIMARY|SECONDARY>(required)Designates whether this site is the normal workload location (PRIMARY) or the DR target (SECONDARY).
--auth-url <url>(required)Keystone authentication URL for this site. Must exactly match keystone_authtoken.auth_url in the site's protector.conf; the engine identifies which site it is running on by comparing this value as a string.
--description <string>Human-readable description.
--region <string>OpenStack region name on this site.
--service-username <string>Keystone username of the cross-site service account. Strongly recommended — without it, scheduled and engine-initiated operations that run after the requesting user's token expires will fail.
--service-password <string>Password for the service account. The API validates these credentials against the remote Keystone before saving.
--service-project-name <string>Keystone project for the service account (typically service).
--service-user-domain-name <string>Keystone user domain name (typically Default).
--service-project-domain-name <string>Keystone project domain name (typically Default).
--service-user-id <string>Keystone user ID of the service account on this site. Required for trust creation when the service account does not hold Keystone admin — without it the first tenant mapping fails with "Cannot resolve service user ID". Obtain it with openstack user show protector -f value -c id on the site being registered.
--service-project-id <string>Keystone project ID (avoids domain-name ambiguity).
--service-user-domain-id <string>Keystone user domain ID.
--service-project-domain-id <string>Keystone project domain ID.
--protector-api-url <string>Explicit Protector API URL (e.g. https://protector.example.com:8788) when the API runs on a different host than Keystone.

Note: The --site-type, --auth-url, and the site <name> positional argument are the only values argparse marks required. All service-credential flags are optional to the parser but strongly recommended in practice — see the --service-username description above. Consult openstack dr site create --help for the authoritative list.


dr site set <site>

FlagShortDefaultDescription
--name <string>Rename the site.
--description <string>Update description.
--auth-url <string>Update Keystone URL.
--region <string>Update region name.
--status <ACTIVE|MAINTENANCE|ERROR>Override site status (admin only).
--service-username <string>Update service account username.
--service-password <string>Update service account password; triggers credential validation against the remote Keystone.
--service-project-name <string>Update service project name.
--service-user-domain-name <string>Update user domain name.
--service-project-domain-name <string>Update project domain name.
--service-user-id <string>Update service user ID.
--service-project-id <string>Update service project ID.
--service-user-domain-id <string>Update service user domain ID.
--service-project-domain-id <string>Update service project domain ID.
--protector-api-url <string>Update explicit Protector API URL.

dr site delete [<site...>]

(No DR-specific flags. Accepts one or more site IDs or names.)


dr site failover <site>

Fails over all Protection Groups at a site simultaneously. Requires the dr_site_admin role.

FlagShortDefaultDescription
--failover-type <planned|unplanned>plannedFailover mode. planned gracefully shuts down VMs; unplanned skips shutdown for use when the primary is already down.
--forcefalseForce failover even if some Protection Groups have pre-check issues.
--yes / -y-ySkip confirmation prompt.

dr failover to this site

Simplified site-level failover — fails over all Protection Groups that are not currently on this site, to this site. Requires dr_site_admin role.

FlagShortDefaultDescription
--failover-type <planned|unplanned>plannedFailover mode.
--forcefalseForce failover.
--yes / -y-ySkip confirmation prompt.

dr site operation list

FlagShortDefaultDescription
--site <string>Filter by site ID or name.
--status <pending|running|completed|partial_success|failed>Filter by operation status.
--limit <number>50Maximum number of results to return.

dr protection group list

FlagShortDefaultDescription
--all-projectsfalseShow Protection Groups from all projects (admin only).

dr protection group show <protection_group>

FlagShortDefaultDescription
--all-projectsfalseAccess a Protection Group from any project (admin only).

dr protection group create <name>

FlagShortDefaultDescription
--primary-site <string>(required)ID or name of the primary site.
--secondary-site <string>(required)ID or name of the secondary (DR) site.
--replication-type <async|active_dr>asyncasync uses periodic FlashArray Protection Group snapshots (RPO = replication interval). active_dr uses Pure Storage ActiveDR pod-based continuous replication (RPO ≈ 0, requires Purity//FA 6.0+).
--volume-type <string>Cinder volume type ID or name. Required for async replication; the volume type must have replication_enabled='<is> True' set as an extra-spec.
--description <string>Human-readable description.

dr protection group set <protection_group>

FlagShortDefaultDescription
--name <string>New name for the Protection Group.
--description <string>New description.
--status <active|failed_over|error>Force-set the Protection Group status (admin only). Use only to recover from an inconsistent state.
--reset-to-primaryfalseReset current_primary_site to the original primary. Use after manual failback to sync state.
--reset-to-secondaryfalseSet current_primary_site to the secondary site. Use after manual failover to sync state.

dr protection group delete [<protection_group...>]

FlagShortDefaultDescription
--forcefalseForce delete even if the Protection Group is in a transitional state. Resources that cannot be reached during forced deletion are listed in the output as "leftovers".

dr protection group member add <protection_group>

FlagShortDefaultDescription
--instance <string>(required)Nova instance UUID to add. The instance must boot from a Cinder volume; ephemeral boot disks are rejected with InstanceNotCinderBoot (HTTP 422). All attached volumes must use a Cinder volume type with replication_enabled='<is> True'; non-replicated types are rejected with VolumeTypeNotReplicated (HTTP 422).
--boot-order <number>0Integer boot order for sequencing VM startup and shutdown during DR operations. VMs sharing the same value are processed in parallel; lower values complete before higher values begin.

dr protection group member set <protection_group>

FlagShortDefaultDescription
--member <string>(required)Member ID to update.
--boot-order <number>(required)New boot order value.

dr protection group member remove <protection_group>

FlagShortDefaultDescription
--member <string>(required)Member ID to remove. Returns 202 Accepted with a pollable operation ID.

dr protection group member validate <protection_group>

Runs the same eligibility checks as member add without side effects. Returns a structured errors array. Use this before member add to surface problems early.

FlagShortDefaultDescription
--instance <string>(required)Nova instance UUID to validate.

dr protection group sync <protection_group>

FlagShortDefaultDescription
--dry-runfalseShow what would be synced without making changes.
--volumesfalseSync volume metadata only.
--security-groupsfalseSync security group mappings only.
--networksfalseSync network mappings only.

dr protection group mapping list <protection_group>

FlagShortDefaultDescription
--type <string>Filter to one resource type: network, subnet, security_group, or flavor.
--unmappedfalseShow only resources that were considered by auto-mapping but not mapped.
--differingfalseShow only resources that are mapped but differ from the source.

dr protection group mapping create <protection_group>

Creates an explicit mapping that overrides auto-mapping. Auto-mapping will not overwrite an explicit mapping.

FlagShortDefaultDescription
--resource-type <string>(required)network, subnet, security_group, or flavor.
--primary-resource-id <string>(required)Resource ID on the primary site.
--secondary-resource-id <string>(required)Resource ID on the secondary site.

dr recovery point list <protection_group>

FlagShortDefaultDescription
--limit <number>Maximum number of recovery points to return.

dr recovery point create <protection_group>

FlagShortDefaultDescription
--suffix <string>Optional name suffix appended to the generated recovery point name.

dr failover <protection_group>

Moves a Protection Group's workloads from the primary site to the secondary site. A planned failover gracefully shuts down VMs and takes a final snapshot for zero data loss; an unplanned failover skips shutdown and accepts data loss up to the RPO.

FlagShortDefaultDescription
--failover-type <planned|unplanned>plannedFailover type.
--recovery-point <string>Specific recovery point (snapshot) to restore from. Accepts the full name or a suffix. If omitted, creates a new snapshot of the current state. Use openstack dr recovery point list <pg> to see available points. Useful for recovery from ransomware or accidental data corruption.
--forcefalseSkip validation checks and primary site operations. Use with caution.
--timeout <number>30Timeout in seconds for the primary site connectivity check.
--waitfalseBlock until the failover operation completes, polling operation status.
--network-mapping <src-net>=<dst-net>Map a source network ID to a target network ID on the secondary site. Repeatable. If omitted, networks are resolved by name.
--subnet-mapping <src-subnet>=<dst-subnet>Map a source subnet ID to a target subnet ID. Repeatable.
--flavor-mapping <src-flavor>=<dst-flavor>Map a source flavor ID to a target flavor ID. Repeatable. If omitted, flavors are resolved by name.
--all-projectsfalseAccess the Protection Group from any project (admin only).

dr failback <protection_group>

Returns a Protection Group's workloads to the original primary site after a failover.

FlagShortDefaultDescription
--resyncfalseForce a full resync of data accumulated during the failover period before recreating VMs.
--all-projectsfalseAccess the Protection Group from any project (admin only).
--forcefalseForce failback even if pre-checks fail.
--failback-type <planned|unplanned>Failback type.
--network-mapping <src-net>=<dst-net>Map a network on the secondary site back to a network on the primary. Repeatable.
--subnet-mapping <src-subnet>=<dst-subnet>Map a subnet. Repeatable.
--flavor-mapping <src-flavor>=<dst-flavor>Map a flavor. Repeatable.

dr test failover <protection_group>

Non-disruptive DR validation. Clones replicated volumes from a recovery point on the secondary site and boots isolated test VMs with a test- prefix on a separate network. Production workloads are not affected. Either --auto-network or --network-mapping is required.

FlagShortDefaultDescription
--auto-networkAutomatically select an isolated network on the secondary site for test VMs. Required if --network-mapping is not provided.
--network-mapping <string>Explicit network mapping for test VMs. Required if --auto-network is not provided.
--recovery-point <string>Recovery point to clone from. Defaults to the most recent.
--subnet-mapping <string>Subnet mapping for test VMs.
--flavor-mapping <string>Flavor mapping for test VMs.
--security-group-mapping <string>Security group mapping for test VMs.
--availability-zone <string>Availability zone on the secondary site for test VMs.
--instance-prefix <string>test-Name prefix for test VM instances.
--all-projectsfalseAccess the Protection Group from any project (admin only).

dr test failover cleanup <protection_group>

Removes test VMs and cloned volumes created by a test failover. The required positional argument is a Protection Group ID or name, not an operation ID.

FlagShortDefaultDescription
--operation <string>(most recent test failover for the PG)Targets a specific test failover operation ID to clean up.
--forcefalseForce cleanup even if some resources fail to delete.

dr operation list

FlagShortDefaultDescription
--protection-group <string>Filter operations by Protection Group ID.
--status <string>Filter by status (pending, running, completed, failed, etc.).
--operation-type <string>Filter by operation type (failover, failback, test_failover, test_cleanup, sync_volumes, etc.).

dr metadata sync <protection_group>

FlagShortDefaultDescription
--forcefalseForce metadata sync even if the remote site reports it is already up to date.

dr tenant mapping list

FlagShortDefaultDescription
--all-tenantsfalseList mappings from all tenants (admin only).
--remote-site <string>Filter by remote site ID.
--status <active|pending_validation|validation_failed|disabled>Filter by mapping status.

dr tenant mapping create

Creates a cross-site tenant mapping. The API validates both tenant IDs against their respective Keystone deployments before saving, then propagates the reverse mapping to the remote site. Port 8788 must be reachable bidirectionally for the sync to succeed.

FlagShortDefaultDescription
--local-tenant <string>(required)Project UUID on this site.
--remote-site <string>(required)Remote site ID.
--remote-tenant <string>(required)Project UUID on the remote site.
--description <string>Human-readable description.
--skip-validationfalseSkip Keystone tenant validation (admin only).

dr tenant mapping delete [<mapping...>]

FlagShortDefaultDescription
--no-sync-remotefalseDelete only the local mapping record, leaving the remote site's reverse mapping in place.

dr usage report

FlagShortDefaultDescription
--period <monthly|quarterly|annual>monthlyReport period.
--start-date <YYYY-MM-DD>Explicit start date, overrides --period.
--end-date <YYYY-MM-DD>Explicit end date, overrides --period.
--format <pdf|json>pdfOutput format. json writes to stdout unless --output is given.
--output <file>(auto-named for PDF, stdout for JSON)Output file path.

Subcommands

Commands are grouped by the resource they operate on.

Subcommand groupDescription
dr site …Register, update, delete, and query OpenStack sites. Includes site-level failover (fails over all Protection Groups at once).
dr site operation …Query site-level DR operations spawned by dr site failover.
dr protection group …Create and manage Protection Groups and their members, resource mappings, and metadata sync.
dr protection group member …Add, remove, update, validate, and list VMs inside a Protection Group.
dr protection group mapping …Inspect and override the auto-derived resource mappings (networks, subnets, flavors, security groups) used during failover.
dr replication health …Report live replication link status, RPO compliance, and failover readiness for a Protection Group.
dr recovery point …List and manually create point-in-time snapshots selectable during failover or test failover.
dr failoverExecute a planned or unplanned Protection Group failover to the secondary site.
dr failbackReturn a failed-over Protection Group to its original primary site.
dr test failoverRun a non-disruptive DR test by booting isolated clones on the secondary site.
dr test failover cleanupRemove test VMs and cloned volumes left by a test failover.
dr operation …List, inspect, and cancel individual DR operations (failover, failback, test failover, etc.).
dr metadata syncPush Protection Group metadata from this site to the remote site.
dr tenant mapping …Create and manage cross-site project identity translations required for multi-tenant DR.
dr protection group reconcileRecover a Protection Group from an interrupted or partially-completed operation.
dr failover to this siteSite-level "big red button": fail over all Protection Groups not already on this site, to this site.
dr usage summaryShow current per-tenant usage (admin only).
dr usage reportGenerate a usage/billing report in PDF or JSON format (admin only).

Environment variables

The openstack dr plugin authenticates and locates endpoints through the standard OpenStack client environment variables. No DR-specific variables are required beyond these.

VariableDefaultDescription
OS_AUTH_URLKeystone endpoint for the local site. The value here must match the --auth-url you register for this site, or the engine will not be able to identify which site it is running on.
OS_USERNAMEOpenStack username.
OS_PASSWORDOpenStack password.
OS_PROJECT_NAMEOpenStack project (tenant). DR operations are scoped to this project unless --all-projects is given.
OS_PROJECT_DOMAIN_NAMEDefaultDomain containing OS_PROJECT_NAME.
OS_USER_DOMAIN_NAMEDefaultDomain containing OS_USERNAME.
OS_REGION_NAMERegion name, used when the catalog contains multiple regions.
OS_IDENTITY_API_VERSION3Must be 3; Keystone v2 is not supported.
OS_PROTECTOR_API_VERSION1Selects the protector API major version. The maximum supported microversion is 1.1.

Note: The openstackclient snap is incompatible with the triliositerecovery CLI plugin. Install python-openstackclient via pip, not snap.


Exit codes

The plugin returns standard OpenStack client exit codes.

CodeMeaning
0Command completed successfully.
1Command failed. The error message printed to stderr contains the HTTP status code and the API error body, including the type field (exception class name), code, and message.
2Incorrect command usage — unknown flag, missing required argument, or invalid argument value. The OpenStack client prints usage help.

Examples

Install the CLI plugin

The triliositerecovery CLI package is separate from the server. In a Juju charm deployment, the server package is managed by the charm. Install only the CLI on the machine where you run openstack commands.

# From the repository root — installs only the CLI, not the server
pip install -e ./client

Verify the plugin is registered:

openstack --help | grep '^ *dr'

Expected output (excerpt):

  dr failover
  dr failback
  dr metadata sync
  ...

Register both sites

Run these commands from the primary site with admin credentials. Each site has its own independent Keystone, so service credentials differ between sites.

# Obtain the service user ID from the primary site's Keystone
PRIMARY_SVC_USER_ID=$(openstack user show protector -f value -c id)

# Register the primary site
openstack dr site create primary-site \
  --site-type PRIMARY \
  --auth-url https://keystone.primary.example.com:5000 \
  --region RegionOne \
  --service-username protector \
  --service-password "${PRIMARY_SVC_PASS}" \
  --service-project-name service \
  --service-user-domain-name Default \
  --service-project-domain-name Default \
  --service-user-id "${PRIMARY_SVC_USER_ID}"

Expected output:

+--------------------+--------------------------------------+
| Field              | Value                                |
+--------------------+--------------------------------------+
| id                 | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
| name               | primary-site                         |
| site_type          | PRIMARY                              |
| status             | active                               |
| auth_url           | https://keystone.primary.example.com:5000 |
| region_name        | RegionOne                            |
| created_at         | 2025-01-15T10:00:00Z                 |
+--------------------+--------------------------------------+
# Source secondary-site credentials to look up its service user
source secondary-site-openrc.sh
SECONDARY_SVC_USER_ID=$(openstack user show protector -f value -c id)

# Switch back to primary and register the secondary
source primary-site-openrc.sh

openstack dr site create secondary-site \
  --site-type SECONDARY \
  --auth-url https://keystone.secondary.example.com:5000 \
  --region RegionOne \
  --service-username protector \
  --service-password "${SECONDARY_SVC_PASS}" \
  --service-project-name service \
  --service-user-domain-name Default \
  --service-project-domain-name Default \
  --service-user-id "${SECONDARY_SVC_USER_ID}"

openstack dr site list

Expected output:

+--------------------------------------+----------------+-----------+--------+
| ID                                   | Name           | Type      | Status |
+--------------------------------------+----------------+-----------+--------+
| a1b2c3d4-...                         | primary-site   | PRIMARY   | active |
| b2c3d4e5-...                         | secondary-site | SECONDARY | active |
+--------------------------------------+----------------+-----------+--------+

Create a tenant mapping

Each site has an independent Keystone that assigns different UUIDs to the same tenant. Register the translation.

LOCAL_PROJECT_ID=$(openstack project show myproject -f value -c id)

# Obtain the remote project ID (requires credentials on the secondary)
REMOTE_PROJECT_ID="<uuid-of-myproject-on-secondary-site>"
SECONDARY_SITE_ID=$(openstack dr site show secondary-site -f value -c id)

openstack dr tenant mapping create \
  --local-tenant "${LOCAL_PROJECT_ID}" \
  --remote-site "${SECONDARY_SITE_ID}" \
  --remote-tenant "${REMOTE_PROJECT_ID}" \
  --description "myproject DR mapping"

Expected output:

+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| id                  | c3d4e5f6-...                         |
| local_tenant_name   | myproject                            |
| remote_site_name    | secondary-site                       |
| remote_tenant_name  | myproject                            |
| status              | active                               |
| created_at          | 2025-01-15T10:05:00Z                 |
+---------------------+--------------------------------------+

Create a Protection Group and add VMs

Before adding VMs, verify the volume type has replication enabled:

openstack volume type show replicated-vol-type -f json | \
  python3 -c "import sys,json; \
    specs=json.load(sys.stdin)['extra_specs']; \
    print(specs.get('replication_enabled'))"
# Should print: <is> True

Create the Protection Group:

PG_ID=$(openstack dr protection group create prod-pg \
  --primary-site primary-site \
  --secondary-site secondary-site \
  --replication-type async \
  --volume-type replicated-vol-type \
  --description "Production workloads" \
  -f value -c id)

echo "Protection Group ID: ${PG_ID}"

Validate a VM before adding it:

VM_ID=$(openstack server show my-app-vm -f value -c id)

openstack dr protection group member validate "${PG_ID}" \
  --instance "${VM_ID}"

Expected output (eligible instance):

+------------------------+----------------------------------------+
| Field                  | Value                                  |
+------------------------+----------------------------------------+
| valid                  | True                                   |
| errors                 | []                                     |
| warnings               | []                                     |
| details.volume_count   | 2                                      |
| details.backend_name   | pure                                   |
+------------------------+----------------------------------------+

Add the VM:

openstack dr protection group member add "${PG_ID}" \
  --instance "${VM_ID}" \
  --boot-order 1

# Add a dependent VM that should start after the first
DB_VM_ID=$(openstack server show my-db-vm -f value -c id)
openstack dr protection group member add "${PG_ID}" \
  --instance "${DB_VM_ID}" \
  --boot-order 2

openstack dr protection group member list "${PG_ID}"

Check replication health

openstack dr replication health "${PG_ID}"

Expected output:

+----------------------------------------+-------------------+
| Field                                  | Value             |
+----------------------------------------+-------------------+
| protection_group_id                    | <pg-uuid>         |
| protection_group_name                  | prod-pg           |
| replication_health.health_report.state | healthy           |
| replication_health.health_report.lag_seconds | 45          |
| replication_health.health_report.rpo_compliant | True      |
| replication_health.health_report.recovery_points | 12     |
| replication_health.failover_validation.can_failover | True |
| replication_health.failover_validation.warnings | []     |
| replication_health.failover_validation.blockers | []     |
+----------------------------------------+-------------------+

Use can_failover as the programmatic readiness check before automating failover:

CAN_FAILOVER=$(openstack dr replication health "${PG_ID}" \
  -f json | python3 -c \
  "import sys,json; h=json.load(sys.stdin); \
   print(h['replication_health']['failover_validation']['can_failover'])")

[ "${CAN_FAILOVER}" = "True" ] && echo "Ready" || echo "Not ready"

Run a non-disruptive test failover

# Start the test — use --auto-network or provide --network-mapping
TEST_OP_ID=$(openstack dr test failover "${PG_ID}" \
  --auto-network \
  --recovery-point "$(openstack dr recovery point list "${PG_ID}" \
      --limit 1 -f value -c id)" \
  -f value -c operation_id)

echo "Test failover operation: ${TEST_OP_ID}"

# Poll until complete
while true; do
  STATUS=$(openstack dr operation show "${TEST_OP_ID}" -f value -c status)
  PROGRESS=$(openstack dr operation show "${TEST_OP_ID}" -f value -c progress)
  echo "Status: ${STATUS} (${PROGRESS})"
  [ "${STATUS}" = "completed" ] || [ "${STATUS}" = "failed" ] && break
  sleep 15
done

# Inspect test VMs on the secondary site, then clean up
openstack dr test failover cleanup "${PG_ID}" \
  --operation "${TEST_OP_ID}"

Execute a planned failover

# Verify readiness first
openstack dr replication health "${PG_ID}"

# Trigger planned failover and wait for completion
OP_ID=$(openstack dr failover "${PG_ID}" \
  --failover-type planned \
  --wait \
  -f value -c operation_id)

openstack dr operation show "${OP_ID}"

Expected initial response (status is pending):

+---------------------+--------------------------------------+
| Field               | Value                                |
+---------------------+--------------------------------------+
| id                  | <operation-uuid>                     |
| protection_group_id | <pg-uuid>                            |
| operation_type      | failover                             |
| failover_type       | planned                              |
| status              | pending                              |
| source_site         | primary-site                         |
| target_site         | secondary-site                       |
+---------------------+--------------------------------------+

Unplanned failover to a specific recovery point

Useful for recovering from ransomware or accidental data corruption by restoring to a known-good snapshot.

# List available recovery points on the secondary
openstack dr recovery point list "${PG_ID}"

# Fail over to a specific point
openstack dr failover "${PG_ID}" \
  --failover-type unplanned \
  --recovery-point "protector-pg-prod-pg-2025-01-14T08:00:00" \
  --force

Execute failback

After workloads have been running on the secondary, return them to the primary:

OP_ID=$(openstack dr failback "${PG_ID}" \
  --wait \
  -f value -c operation_id)

openstack dr operation show "${OP_ID}"

Site-level failover (big red button)

Fail over every Protection Group at a site simultaneously. Requires the dr_site_admin role.

# Fail over all PGs at primary-site to secondary
openstack dr site failover primary-site \
  --failover-type unplanned \
  --yes

# Monitor site-level progress
openstack dr site operation list --status running
openstack dr site operation show <site-operation-id>

Automate DR health monitoring

#!/bin/bash
# Polls all Protection Groups and alerts if any cannot fail over

PGs=$(openstack dr protection group list -f value -c id)

for pg in ${PGs}; do
  HEALTH=$(openstack dr replication health "${pg}" -f json 2>/dev/null)
  CAN_FAILOVER=$(echo "${HEALTH}" | python3 -c \
    "import sys,json; h=json.load(sys.stdin); \
     print(h['replication_health']['failover_validation']['can_failover'])")
  PG_NAME=$(openstack dr protection group show "${pg}" -f value -c name)

  if [ "${CAN_FAILOVER}" != "True" ]; then
    BLOCKERS=$(echo "${HEALTH}" | python3 -c \
      "import sys,json; h=json.load(sys.stdin); \
       print(h['replication_health']['failover_validation']['blockers'])")
    echo "ALERT: ${PG_NAME} (${pg}) cannot fail over: ${BLOCKERS}"
  else
    echo "OK: ${PG_NAME} (${pg})"
  fi
done

Output formatting and scripting

All commands support standard OpenStack client output flags.

# Extract a single value for use in scripts
PG_ID=$(openstack dr protection group create prod-pg \
  --primary-site primary-site \
  --secondary-site secondary-site \
  --volume-type replicated-vol-type \
  -f value -c id)

# JSON output for programmatic parsing
openstack dr operation show "${OP_ID}" -f json

# YAML output
openstack dr protection group show "${PG_ID}" -f yaml

# Select specific columns
openstack dr protection group list \
  -c id -c name -c status

# Filter operations by type
openstack dr operation list \
  --operation-type failover \
  --status completed

# Debug mode
openstack --debug dr replication health "${PG_ID}"

Get help for any command

# List all dr commands
openstack help dr

# Command-specific help
openstack dr site create --help
openstack dr failover --help
openstack dr test failover cleanup --help