---
title: Custom Resource Reference
product: trilio-site-recovery-for-kubernetes-openshift-virtualization
doc_type: guide
version: feature-tsr-24
source: git2docs (code-derived, validation-filtered)
canonical: https://git2docs.com/murali-balcha/docs/trilio-site-recovery-for-kubernetes-openshift-virtualization/site-recovery/crd-reference
---

# Custom Resource Reference

_Complete API reference for all site recovery custom resources_

## Overview

This page is the complete API reference for all Custom Resource Definitions (CRDs) shipped with Trilio Site Recovery for OpenShift Virtualization. Each CRD entry describes its purpose, the cluster where it lives, its spec fields, its status fields, and the lifecycle phases or conditions you will encounter when operating DR workflows. Understanding these resources is essential because all DR orchestration—protecting VMs, monitoring replication health, triggering failovers, and auditing RPO compliance—is expressed entirely through these Kubernetes-native objects that you create, inspect, and delete with `kubectl`.

## Prerequisites

Before working with these custom resources, ensure the following are in place:

- **Kubernetes** ≥ 1.28 or **OpenShift** ≥ 4.14 on all three clusters (quorum, primary, DR)
- **KubeVirt** ≥ 1.0 installed on the primary and DR clusters
- **Helm** ≥ 3.0 (used to deploy operators that install these CRDs)
- **DRBD kernel module** ≥ 9.0 on every worker node in the primary and DR clusters
- **site-recovery-protectionzone-controller** deployed on the quorum cluster before the quorum control plane chart — this operator installs the CRDs themselves
- **site-recovery-quorum-control-plane** deployed on the quorum cluster — hosts the reconcilers that watch and act on these CRDs
- **site-recovery-workload-control-plane** deployed on both the primary and DR clusters
- Kubeconfig credentials for the primary and DR clusters stored as Secrets on the quorum cluster
- TCP port 6443 open from the quorum cluster to both workload cluster API servers
- TCP ports 7000–7999 open between primary and DR worker nodes for DRBD replication
- For **DRBD Operator** deployments: the DRBD Operator installed on both the primary and DR clusters

> **Note:** Test failover (`TestFailover`) is supported only in the DRBD Operator deployment model.

## Installation

CRDs are installed automatically when you deploy the `site-recovery-protectionzone-controller` Helm chart on the quorum cluster. You do not apply CRD manifests manually.

**Step 1 — Add the Trilio Helm repository**

```bash
helm repo add trilio https://charts.trilio.io
helm repo update
```

**Step 2 — Deploy the ProtectionZone controller (installs CRDs)**

This chart must be deployed first. It registers all site recovery CRDs with the quorum cluster's API server.

```bash
helm install site-recovery-protectionzone-controller trilio/site-recovery-protectionzone-controller \
  --namespace trilio-system \
  --create-namespace
```

Verify the CRDs are registered:

```bash
kubectl get crds | grep siterecovery.trilio.io
```

Expected output includes entries such as:

```
protectiongroups.siterecovery.trilio.io
protectionrequests.siterecovery.trilio.io
failoverrequests.siterecovery.trilio.io
testfailovers.siterecovery.trilio.io
drbdreplicationpolicies.siterecovery.trilio.io
drbdresources.siterecovery.trilio.io
rpoevents.siterecovery.trilio.io
replicationgroupstatuses.siterecovery.trilio.io
```

**Step 3 — Deploy the quorum control plane**

The quorum control plane hosts the reconcilers that act on these CRDs. Each DR deployment runs in its own namespace named `dr-<deployment-name>`.

```bash
helm install site-recovery-quorum-control-plane trilio/site-recovery-quorum-control-plane \
  --namespace dr-prod \
  --create-namespace \
  --set deploymentName=prod
```

**Step 4 — Deploy the workload control plane on each workload cluster**

Run this on both the primary and DR clusters:

```bash
helm install site-recovery-workload-control-plane trilio/site-recovery-workload-control-plane \
  --namespace trilio-system \
  --create-namespace
```

The workload control plane deploys a Deployment and a DaemonSet (`drbd-node-agent`) on every worker node.

## Configuration

Each CRD has its own spec fields. The table below summarizes the key configurable fields per resource and their effects.

---

### ProtectionGroup

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.virtualMachines` | `[]VMRef` | required | List of `{name, namespace}` pairs identifying the VMs to protect as a group. |
| `spec.sourceCluster` | `ClusterID` | `""` | Designates which workload cluster (`cluster1` or `cluster2`) holds the authoritative Primary DRBD copy. Used as a tie-breaker when neither copy reports Primary role. |
| `spec.sla.rtoTargetSeconds` | `int32` | required if `sla` is set | Target recovery time in seconds. Minimum value: 1. |
| `spec.sla.rpoTargetSeconds` | `int32` | `0` | Target recovery point in seconds. `0` means RPO=0 (synchronous). The replication monitor propagates this value to the associated `ReplicationGroupStatus`. |

---

### ProtectionRequest (DRBD Operator model)

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.vmName` | `string` | required | Name of the VirtualMachine resource to protect. |
| `spec.vmNamespace` | `string` | required | Namespace containing the VM. |
| `spec.replicationPolicyRef` | `string` | required | Name of the `DRBDReplicationPolicy` to use for this VM. The policy must already exist. |

---

### FailoverRequest

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.protectionGroupRef` | `string` | required | Name of the `ProtectionGroup` to fail over. |
| `spec.type` | `string` | required | `Planned` (graceful, zero data loss) or `Unplanned` (disaster, immediate promotion). |
| `spec.targetCluster` | `ClusterID` | required | The cluster (`cluster1` or `cluster2`) to which VMs will be started after failover. |

---

### TestFailover (DRBD Operator model only)

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.protectionGroupRef` | `string` | required | Name of the `ProtectionGroup` to test. |
| `spec.targetNamespace` | `string` | required | Isolated namespace on the DR cluster where snapshot-based VM copies are created. |

---

### DRBDReplicationPolicy (DRBD Operator model)

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.protocol` | `string` | required | Replication protocol: `A` (asynchronous, RPO=seconds) or `C` (synchronous, RPO=0). Protocol C requires network round-trip latency below ~50ms between clusters. |
| `spec.storageClassMappings` | `map[string]string` | required | Maps primary cluster storage class names to DR cluster storage class names. |
| `spec.primaryEndpoints` | `[]string` | required | DRBD endpoint addresses (IP:port) on the primary cluster worker nodes. |
| `spec.drEndpoints` | `[]string` | required | DRBD endpoint addresses (IP:port) on the DR cluster worker nodes. |

---

### ReplicationGroupStatus

| Field | Type | Default | Description |
|---|---|---|---|
| `spec.protectionGroupRef` | `PGRef` | required | `{name, namespace}` of the owning `ProtectionGroup`. |
| `spec.replicationProtocol` | `string` | `C` | Replication protocol (`A` or `C`). Propagated from the `DRBDReplicationPolicy`. |
| `spec.rpoObjectiveSeconds` | `int32` | `0` | RPO objective in seconds, propagated from the PG's SLA. The replication monitor updates this when the PG's SLA changes; if the PG's SLA is removed, a manually tuned value is preserved. |
| `spec.pollingIntervalSeconds` | `int32` | `15` | How often (in seconds) the replication monitor scrapes DRBD metrics and patches this resource's status. Overrides the deployment-wide `DEFAULT_SCRAPE_INTERVAL`. |

## Usage

All site recovery operations are performed by creating or deleting custom resource manifests and then observing status fields with `kubectl`. The workflows below cover the most common operator tasks.

---

### Protecting a VM (DRBD Operator model)

Before protecting any VM, a `DRBDReplicationPolicy` must exist in the deployment namespace.

```bash
# Apply the replication policy first
kubectl apply -f drbd-replication-policy.yaml --namespace dr-prod

# Then create a ProtectionRequest for each VM
kubectl apply -f protection-request-vm1.yaml --namespace dr-prod
```

Watch the protection lifecycle progress through its phases:

```bash
kubectl get protectionrequest vm1-protect --namespace dr-prod -w
```

Phases progress from `Pending` → validation → DRBD volume creation → data synchronization → `Protected`. When the `ProtectionRequest` reaches the `Protected` phase, the VM's disk is backed by a DRBD-replicated (Frontend) PVC and replication to the DR cluster is active.

---

### Checking replication health

The replication monitor automatically creates a `ReplicationGroupStatus` for every `ProtectionGroup`. Query it to see aggregate health:

```bash
kubectl get replicationgroupstatus pg-web-tier-replication --namespace dr-prod -o yaml
```

Key status fields to inspect:
- `status.rpoStatus` — `Met`, `Degraded`, `Violated`, or `Unknown`
- `status.aggregated.connectionState` — `Connected` or `Degraded`
- `status.aggregated.totalVolumeCount` / `syncedVolumeCount`
- `status.volumeReplicationStatuses[]` — per-VM breakdown with `outOfSyncBytes`, `connectionState`, `replicationState`, and `estimatedRPOSeconds`
- `status.lastFullySyncedAt` — timestamp of the last full synchronization

---

### Auditing RPO violations

The replication monitor creates an immutable `RPOEvent` whenever the `rpoStatus` transitions (for example, from `Met` to `Violated`). List all events for a protection group:

```bash
kubectl get rpoevents --namespace dr-prod \
  -l siterecovery.trilio.io/protection-group=pg-web-tier
```

Each `RPOEvent` records the previous and new status, the violation reason, the out-of-sync byte count at the time of the event, and a timestamp.

---

### Triggering a planned failover

Create a `FailoverRequest` with `spec.type: Planned`:

```bash
kubectl apply -f failover-request-planned.yaml --namespace dr-prod
```

The failover controller orchestrates VM shutdown on the primary cluster, DRBD volume promotion on the DR cluster, and VM startup on the DR cluster. Monitor progress:

```bash
kubectl get failoverrequest pg-web-tier-failover --namespace dr-prod -w
```

---

### Running a non-disruptive DR test (DRBD Operator model only)

Create a `TestFailover` resource. The controller creates snapshot-based copies of protected VMs in an isolated namespace on the DR cluster, runs verification checks, and cleans up — without affecting production VMs:

```bash
kubectl apply -f test-failover.yaml --namespace dr-prod
```

Monitor the test:

```bash
kubectl get testfailover pg-web-tier-test --namespace dr-prod -w
```

When the test completes, all resources in the isolated namespace are cleaned up automatically.

## Examples

### Example 1 — DRBDReplicationPolicy with synchronous replication (Protocol C)

Use this for clusters with sub-50ms round-trip latency to achieve RPO=0.

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: DRBDReplicationPolicy
metadata:
  name: policy-sync
  namespace: dr-prod
spec:
  protocol: C
  storageClassMappings:
    fast-ssd: fast-ssd-dr
  primaryEndpoints:
    - 192.168.10.11:7000
    - 192.168.10.12:7000
  drEndpoints:
    - 192.168.20.11:7000
    - 192.168.20.12:7000
```

---

### Example 2 — ProtectionGroup with SLA

This groups two VMs into a single failover unit with a 30-second RPO target and 10-minute RTO target.

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionGroup
metadata:
  name: pg-web-tier
  namespace: dr-prod
spec:
  virtualMachines:
    - name: web-vm-1
      namespace: production
    - name: web-vm-2
      namespace: production
  sla:
    rpoTargetSeconds: 30
    rtoTargetSeconds: 600
```

---

### Example 3 — ProtectionRequest for a single VM

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionRequest
metadata:
  name: web-vm-1-protect
  namespace: dr-prod
spec:
  vmName: web-vm-1
  vmNamespace: production
  replicationPolicyRef: policy-sync
```

After applying, watch the phase progress:

```bash
kubectl get protectionrequest web-vm-1-protect --namespace dr-prod -w
```

Expected progression:
```
NAME                  PHASE
web-vm-1-protect      Pending
web-vm-1-protect      Validating
web-vm-1-protect      Syncing
web-vm-1-protect      Protected
```

---

### Example 4 — Inspecting ReplicationGroupStatus

```bash
kubectl get replicationgroupstatus pg-web-tier-replication \
  --namespace dr-prod -o yaml
```

Expected status when all volumes are synchronized:

```yaml
status:
  rpoStatus: Met
  aggregated:
    connectionState: Connected
    totalVolumeCount: 2
    syncedVolumeCount: 2
  volumeReplicationStatuses:
    - drbdResourceName: drbd-production-web-vm-1
      vmName: web-vm-1
      pvcName: web-vm-1-boot-disk
      outOfSyncBytes: 0
      connectionState: Connected
      replicationState: Established
      estimatedRPOSeconds: 0
      lastSyncedAt: "2026-06-10T12:00:00Z"
  lastFullySyncedAt: "2026-06-10T12:00:00Z"
  lastPolledAt: "2026-06-10T12:00:15Z"
```

---

### Example 5 — Planned failover

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
  name: pg-web-tier-failover
  namespace: dr-prod
spec:
  protectionGroupRef: pg-web-tier
  type: Planned
  targetCluster: cluster2
```

---

### Example 6 — Non-disruptive TestFailover (DRBD Operator model only)

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: TestFailover
metadata:
  name: pg-web-tier-test
  namespace: dr-prod
spec:
  protectionGroupRef: pg-web-tier
  targetNamespace: dr-test-isolation
```

---

### Example 7 — Querying RPOEvents for a protection group

```bash
kubectl get rpoevents --namespace dr-prod \
  -l siterecovery.trilio.io/protection-group=pg-web-tier \
  -o yaml
```

An RPOEvent created when replication fell out of sync looks like:

```yaml
apiVersion: siterecovery.trilio.io/v1alpha1
kind: RPOEvent
metadata:
  name: pg-web-tier-1749556800
  namespace: dr-prod
  labels:
    siterecovery.trilio.io/protection-group: pg-web-tier
    siterecovery.trilio.io/rpo-event-type: RPOViolated
spec:
  protectionGroupRef:
    name: pg-web-tier
    namespace: dr-prod
  eventType: RPOViolated
  previousRPOStatus: Met
  newRPOStatus: Violated
  violationReason: ProtocolCOutOfSync
  outOfSyncBytesAtEvent: 4096
  message: >-
    RPO status changed from Met to Violated: Synchronous replication has
    pending data. Check for network or I/O issues.
```

## Troubleshooting

Use the following patterns to diagnose common CRD-related failures. For deeper diagnostics, collect a support bundle with `oc adm must-gather --image=<tsr-gather-image>`, which collects logs, CRD manifests, and status from the quorum cluster and all connected workload clusters.

---

### ProtectionRequest stuck in `Pending`

**Symptom:** `kubectl get protectionrequest <name> --namespace dr-prod` shows `PHASE: Pending` for more than a few minutes.

**Likely cause:** The referenced `DRBDReplicationPolicy` does not exist in the same namespace, or the `drbd-node-agent` DaemonSet pods on the workload clusters are not running.

**Fix:**
1. Confirm the policy exists: `kubectl get drbdreplicationpolicy --namespace dr-prod`
2. Check agent pods on both workload clusters:
   ```bash
   kubectl get pods -n trilio-system \
     -l app.kubernetes.io/component=drbd-node-agent
   ```
3. Inspect events on the ProtectionRequest:
   ```bash
   kubectl describe protectionrequest <name> --namespace dr-prod
   ```

---

### ReplicationGroupStatus shows `rpoStatus: Unknown`

**Symptom:** `status.rpoStatus` is `Unknown` and `status.aggregated.connectionState` is `Degraded`.

**Likely cause:** The replication monitor cannot observe DRBD statistics for any volume in the group. In DRBD Operator mode this typically means no `DRBDResource` CRs exist yet on the workload clusters (protection has not completed), or the quorum control plane cannot reach the workload cluster API servers.

**Fix:**
1. Confirm that `ProtectionRequest` resources for all VMs in the group have reached the `Protected` phase.
2. Verify that `DRBDResource` CRs exist on both workload clusters in the VM's own namespace:
   ```bash
   kubectl get drbdresource --namespace <vm-namespace>
   ```
3. Check that TCP port 6443 is open from the quorum cluster to both workload cluster API servers.
4. Inspect the replication monitor logs:
   ```bash
   kubectl logs -n dr-prod \
     -l app.kubernetes.io/name=site-recovery-quorum-control-plane \
     --container replication-monitor
   ```

---

### ReplicationGroupStatus shows `rpoStatus: Violated` with `violationReason: ProtocolCOutOfSync`

**Symptom:** The protection group is using Protocol C (synchronous) but `outOfSyncBytes` is non-zero.

**Likely cause:** Network latency between primary and DR clusters has exceeded ~50ms round-trip, causing DRBD to fall behind. A transient I/O spike on the primary can also cause this.

**Fix:**
1. Measure current round-trip latency between primary and DR worker nodes.
2. Check for packet loss or network congestion on the replication path (TCP ports 7000–7999).
3. If latency is consistently above 50ms, consider switching to Protocol A by updating the `DRBDReplicationPolicy`.
4. Monitor `RPOEvent` resources to understand the history and frequency of violations:
   ```bash
   kubectl get rpoevents --namespace dr-prod \
     -l siterecovery.trilio.io/protection-group=<pg-name>
   ```

---

### FailoverRequest does not progress

**Symptom:** A `FailoverRequest` is created but VMs do not start on the target cluster.

**Likely cause:** The failover controller (part of `site-recovery-quorum-control-plane`) is not running, or the `ProtectionGroup` referenced by the request has `rpoStatus: Violated` and a planned failover was attempted (which requires zero data loss).

**Fix:**
1. Confirm the quorum control plane pods are running:
   ```bash
   kubectl get pods --namespace dr-prod \
     -l app.kubernetes.io/name=site-recovery-quorum-control-plane
   ```
2. Check events on the `FailoverRequest`:
   ```bash
   kubectl describe failoverrequest <name> --namespace dr-prod
   ```
3. For unplanned failover of an out-of-sync group, ensure `spec.type` is `Unplanned`.

---

### TestFailover not supported error

**Symptom:** Creating a `TestFailover` resource produces a validation error or the controller ignores it.

**Likely cause:** Your deployment uses the LINSTOR model, not the DRBD Operator model. `TestFailover` is only supported in DRBD Operator deployments.

**Fix:** Confirm your deployment model. Test failover requires the DRBD Operator to be installed on both workload clusters.

---

### RPOEvents accumulating rapidly

**Symptom:** Many `RPOEvent` resources are created in a short time for the same protection group.

**Likely cause:** The replication state is oscillating between `Met` and `Violated` rapidly, typically due to intermittent network connectivity between clusters.

**Fix:**
1. Inspect the `violationReason` field across recent events to identify a pattern.
2. Check network stability between the primary and DR worker nodes on ports 7000–7999.
3. Review `DRBDResource` status on both clusters for the affected VMs:
   ```bash
   kubectl get drbdresource --namespace <vm-namespace> -o yaml
   ```
4. If the issue persists, collect a support bundle:
   ```bash
   oc adm must-gather --image=<tsr-gather-image> \
     --dest-dir=./tsr-bundle
   ```
