Trilio Site Recovery for Kubernetes
CLI reference

Kubectl Commands

Common kubectl commands for managing site recovery resources, checking status, and inspecting events


Description

This reference covers the kubectl (and oc) commands you use every day to manage Trilio Site Recovery resources: creating protection zones, monitoring replication health, triggering failovers, and collecting diagnostics. All DR operations are driven by Custom Resources in the siterecovery.trilio.io/v1alpha1 API group; kubectl and oc are the only supported CLIs. Use oc wherever OpenShift-specific paths are required (must-gather, Routes).


Synopsis
kubectl [--context=<ctx>] [--kubeconfig=<path>] <verb> <resource>[/<name>] [flags]
oc     [--context=<ctx>] [--kubeconfig=<path>] <verb> <resource>[/<name>] [flags]

Every command that targets the quorum cluster requires a kubeconfig or context pointing at that cluster. Commands that target a workload cluster (primary or DR) require the corresponding kubeconfig. Both are shown explicitly in every example below.


Flags

The flags below are standard kubectl/oc flags used throughout this reference. Site Recovery adds no custom flags to the CLI.

FlagShortDefaultDescription
--kubeconfig=<path>~/.kube/configPath to the kubeconfig file. Use separate files for the quorum, primary, and DR clusters.
--context=<name>active contextKubeconfig context to use.
--namespace=<ns>-nactive namespaceKubernetes namespace. Quorum CRs live in dr-<name>; workload components live in trilio-site-recovery-system.
--all-namespaces-AfalseList resources across all namespaces.
--output=<format>-o(table)Output format: yaml, json, wide, jsonpath=<expr>.
--watch-wfalseWatch for changes and print updates as they arrive.
--field-selector=<expr>Filter on metadata.name or metadata.namespace only. Field selectors on spec.* or status.* are not supported on Site Recovery CRDs.
--selector=<labels>-lFilter by label selector. Preferred over field selectors for CRD filtering.
--type=<strategy>strategicPatch strategy for kubectl patch: strategic, merge, or json.
--since=<duration>Used with kubectl logs to limit log history, e.g. --since=1h.
--previous-pfalseUsed with kubectl logs to print the previous container's logs (pre-restart).

Subcommands

The table below shows the kubectl verbs most commonly used with Site Recovery resources.

VerbDescription
getList or describe one or more resources. Supports -o yaml for full spec/status.
applyCreate or update a resource from a manifest file. The primary way to create ProtectionZones, FailoverRequests, TestFailovers, and ProtectionRequests.
deleteDelete a resource. For ProtectionRequests this begins the unprotection flow; do not force-delete unless you understand the data implications.
describePrint a human-readable summary including events and conditions.
logsStream or print container logs. Used to inspect controller and node-agent output.
execExecute a command inside a container. Used for DRBD kernel state inspection on node-agent pods.
patchUpdate specific fields of a resource in place. Use --type=merge or --type=json.
editOpen a resource in your editor. Admission webhooks will reject disallowed changes.
waitBlock until a resource reaches a condition. Useful in scripts and CI pipelines.

Environment variables

Site Recovery itself does not read environment variables, but the following shell conventions are used throughout this reference to keep examples readable.

VariableDefaultDescription
KUBECONFIG_QUORUM(none)Path to the quorum cluster kubeconfig. Set this before running any quorum-side command.
KUBECONFIG_PRIMARY(none)Path to the primary (cluster1) workload cluster kubeconfig.
KUBECONFIG_DR(none)Path to the DR (cluster2) workload cluster kubeconfig.
ZONE_NS(none)The zone namespace, always dr-<name>. Set once and reuse across commands.

Note: kubectl does not read KUBE_CONTEXT. Pass --context explicitly when switching contexts within a single kubeconfig file.


Exit codes

Standard kubectl/oc exit codes apply.

CodeMeaning
0Success.
1Generic error (API error, resource not found, admission webhook rejection, connection refused). The error message is printed to stderr.

For oc adm must-gather (the tsr-gather diagnostics image), see the examples section for the tool's own exit codes.


Examples

All examples use shell variables defined as:

export KUBECONFIG_QUORUM=~/.kube/config-quorum
export KUBECONFIG_PRIMARY=~/.kube/config-primary
export KUBECONFIG_DR=~/.kube/config-dr
export ZONE_NS=dr-edge          # replace with your zone name

Short names quick-reference

Site Recovery CRDs register the following short names:

KindShort names
ProtectionZonepz
ProtectionGrouppg
ProtectionRequestpr, protect
DRBDReplicationPolicydrp, drbdpolicy
FailoverRequestfr
TestFailovertfo
ReplicationGroupStatusrgs
RPOEventrpoe
DRBDResourcedres

Cluster placement reminder

Before running any command, confirm which cluster the resource lives on:

Resource kindLives on
ProtectionZone, ProtectionGroup, ProtectionRequest, DRBDReplicationPolicy, FailoverRequest, TestFailover, ReplicationGroupStatus, RPOEventQuorum cluster, in namespace dr-<name>
DRBDResourcePrimary and DR workload clusters, in the protected VM's namespace
Node-agent DaemonSet podsPrimary and DR workload clusters, in trilio-site-recovery-system

Running kubectl get drbdresource against the quorum cluster returns server doesn't have a resource type — that is expected.


1. Inspect the ProtectionZone

The ProtectionZone CR is the root of every DR deployment. Check it first when diagnosing zone-wide problems.

# List all zones (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pz -A

Expected output:

NAMESPACE   NAME   DISPLAY NAME   BACKEND   CLUSTERS          STATUS      AGE
dr-edge     edge   Edge Zone      DRBD      primary,dr-site   Available   12d
# Full spec and status for one zone
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pz edge -n "$ZONE_NS" -o yaml

Key fields to check:

  • status.phase: should be Available
  • status.clusters[*].status: both entries should be Reachable
# Describe for human-readable conditions and events
kubectl --kubeconfig "$KUBECONFIG_QUORUM" describe pz edge -n "$ZONE_NS"

2. Create a ProtectionZone

Create the dr-<name> namespace and the kubeconfig Secrets first, then apply the zone CR. The ProtectionZone controller deploys the quorum and workload control planes automatically once the zone reaches Available.

# Create the zone namespace
oc --kubeconfig "$KUBECONFIG_QUORUM" new-project "$ZONE_NS"

# Store workload cluster kubeconfigs as Secrets in the zone namespace
oc --kubeconfig "$KUBECONFIG_QUORUM" create secret generic primary-kubeconfig \
  --from-file=kubeconfig="$KUBECONFIG_PRIMARY" \
  -n "$ZONE_NS"

oc --kubeconfig "$KUBECONFIG_QUORUM" create secret generic dr-kubeconfig \
  --from-file=kubeconfig="$KUBECONFIG_DR" \
  -n "$ZONE_NS"
# Apply the ProtectionZone CR
oc --kubeconfig "$KUBECONFIG_QUORUM" apply -f - <<'EOF'
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionZone
metadata:
  name: edge
  namespace: dr-edge
spec:
  displayName: "Edge Zone"
  storageBackendMode: DRBD
  clusters:
    - name: primary
      clusterID: cluster1
      distributionName: RHEL-CoreOS-9
      kubeconfigSecretRef:
        name: primary-kubeconfig
        key: kubeconfig
    - name: dr-site
      clusterID: cluster2
      distributionName: RHEL-CoreOS-9
      kubeconfigSecretRef:
        name: dr-kubeconfig
        key: kubeconfig
  logging:
    level: info
EOF
# Watch the zone reach Available
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pz edge -n "$ZONE_NS" -w

Expected progression: Provisioning → Available.


3. List and inspect ProtectionGroups

ProtectionGroups define the VMs that fail over together.

# List all protection groups in a zone (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pg -n "$ZONE_NS"

Expected output:

NAME     VMS                    STATE      ACTIVE     AGE
web-pg   vm-web-1,vm-web-2      Protected  cluster1   5d
# Full status
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pg web-pg -n "$ZONE_NS" -o yaml

Key fields:

  • status.state: Protected | Syncing | Degraded | Failed
  • status.activeClusterID: which cluster currently runs the VMs
  • status.protectedVMs[*].replicationStatus: per-VM replication state

4. Protect a virtual machine (ProtectionRequest)

A ProtectionRequest instructs the protection controller to set up DRBD replication for one VM's disks.

oc --kubeconfig "$KUBECONFIG_QUORUM" apply -f - <<'EOF'
apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionRequest
metadata:
  name: protect-vm-web-1
  namespace: dr-edge
spec:
  sourceCluster: cluster1
  virtualMachine:
    name: vm-web-1
    namespace: production
EOF
# Watch protection progress
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pr protect-vm-web-1 -n "$ZONE_NS" -w

Expected phase progression: Pending → Validating → CreatingDRBD → Syncing → ReadyToActivate → Activating → Protected

# Check sync progress (percentage field)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pr protect-vm-web-1 -n "$ZONE_NS" \
  -o jsonpath='{.status.syncProgress}'

5. Check replication health (ReplicationGroupStatus)

The replication monitor auto-creates one ReplicationGroupStatus per ProtectionGroup, named <protection-group-name>-replication.

# List all replication statuses in a zone (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rgs -n "$ZONE_NS"

Expected output:

NAME                  PROTECTION-GROUP   RPO-STATUS   OUT-OF-SYNC   SYNCED   CONNECTION   LAST-SYNC
web-pg-replication    web-pg             Met          0             4        Connected    2m ago
# Full detail for one group
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rgs web-pg-replication -n "$ZONE_NS" -o yaml

Key fields:

  • status.rpoStatus: Met | Degraded | Violated | Unknown
  • status.aggregated.connectionState: Connected | Degraded | Disconnected
  • status.aggregated.outOfSyncBytes: should be 0 for a healthy synchronous replication
  • status.lastFullySyncedAt: timestamp of the last full sync
  • status.volumeReplicationStatuses: per-volume breakdown
# Filter by protection group using the label (field selectors on spec.* are not supported)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rgs -n "$ZONE_NS" \
  -l siterecovery.trilio.io/protection-group=web-pg

6. Inspect DRBDResource on workload clusters

DRBDResource CRs live on the workload clusters (primary and DR), not on the quorum. One CR exists per VM per cluster side.

# List DRBDResources on the primary cluster
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get dres -A

Expected output:

NAMESPACE    NAME                              SIDE     ROLE      NODE       STATE       PHASE
production   drbd-production-vm-web-1-a1b2c3d4 Source   Primary   worker-1   Connected   Consistent
# Same on the DR cluster
kubectl --kubeconfig "$KUBECONFIG_DR" get dres -A
# Full status for one resource on primary (shows per-volume sync progress)
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get dres drbd-production-vm-web-1-a1b2c3d4 \
  -n production -o yaml

Key status fields:

  • status.phase: Consistent is the healthy state; Syncing means initial sync in progress; Degraded or Failed requires attention
  • status.connectionState: Connected | Connecting | Disconnected | StandAlone
  • status.role: Primary on the active cluster, Secondary on the standby
  • status.syncProgress.percentage: 0–100 during initial sync
  • status.volumes[*].replicationState: per-volume DRBD state
# Watch sync progress across all resources
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get dres -A -w

7. Identify protected VMs

The label siterecovery.trilio.io/protected-vm=true is stamped on VirtualMachine objects on the workload clusters.

# List all protected VMs on the primary cluster
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get vm -A \
  -l siterecovery.trilio.io/protected-vm=true

Important: This label is on the VirtualMachine object only. Querying it on dres or pr returns nothing.


8. Inspect node-agent pods and DRBD state

The DRBD node-agent runs as a DaemonSet on every worker node of the primary and DR clusters, in the trilio-site-recovery-system namespace.

# List all node-agent pods on the primary cluster
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get pods -n trilio-site-recovery-system \
  -l app.kubernetes.io/component=drbd-node-agent

Expected output:

NAME                                                          READY   STATUS    NODE
edge-site-recovery-workload-control-plane-agent-abcd1         1/1     Running   worker-1
edge-site-recovery-workload-control-plane-agent-efgh2         1/1     Running   worker-2
# Stream logs from a node-agent pod (two-step: get pod name for a node, then stream logs)
NODE=worker-1
POD=$(kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get pods -n trilio-site-recovery-system \
  --field-selector spec.nodeName="$NODE" \
  -l app.kubernetes.io/component=drbd-node-agent \
  -o jsonpath='{.items[0].metadata.name}')

kubectl --kubeconfig "$KUBECONFIG_PRIMARY" logs "$POD" \
  -n trilio-site-recovery-system -c agent --since=1h
# View DRBD module-loader init container log (init logs are not time-bounded)
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" logs "$POD" \
  -n trilio-site-recovery-system -c drbd-module-loader
# Run a DRBD kernel-state command inside the agent container
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" exec "$POD" \
  -n trilio-site-recovery-system -c agent -- drbdsetup status --verbose

9. Inspect quorum control-plane logs

The quorum control plane runs as a single Deployment per zone namespace on the quorum cluster. The pod runs one container named manager and hosts all reconcilers (failover, protection, pg-sync, replication-monitor, test-failover).

# Get the quorum control-plane pod
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pods -n "$ZONE_NS" \
  -l app.kubernetes.io/component=quorum-control-plane

Expected output (1/1 — one container named manager):

NAME                                          READY   STATUS    AGE
site-recovery-quorum-control-plane-7d9f-abc   1/1     Running   12d
# Stream logs from the quorum manager
QUORUM_POD=$(kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pods -n "$ZONE_NS" \
  -l app.kubernetes.io/component=quorum-control-plane \
  -o jsonpath='{.items[0].metadata.name}')

kubectl --kubeconfig "$KUBECONFIG_QUORUM" logs "$QUORUM_POD" \
  -n "$ZONE_NS" -c manager --since=1h
# Previous container logs (if manager restarted)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" logs "$QUORUM_POD" \
  -n "$ZONE_NS" -c manager --previous

10. Inspect the ProtectionZone controller logs

The OLM-installed ProtectionZone controller runs in trilio-site-recovery-system on the quorum cluster.

PZ_POD=$(kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pods -n trilio-site-recovery-system \
  -l control-plane=protection-zone-controller \
  -o jsonpath='{.items[0].metadata.name}')

kubectl --kubeconfig "$KUBECONFIG_QUORUM" logs "$PZ_POD" \
  -n trilio-site-recovery-system -c manager --since=1h

11. Adjust runtime log level

Log levels are controlled per namespace via the site-recovery-logging ConfigMap. Valid levels are error, info, debug, and trace. There is no warn level.

# Set the quorum control-plane to debug for the zone namespace
kubectl --kubeconfig "$KUBECONFIG_QUORUM" patch configmap site-recovery-logging \
  -n "$ZONE_NS" --type=merge \
  -p '{"data":{"level":"debug"}}'

# Set only the node-agent component to debug on a workload cluster
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" patch configmap site-recovery-logging \
  -n trilio-site-recovery-system --type=merge \
  -p '{"data":{"level.drbd-node-agent":"debug"}}'

# Reset to info
kubectl --kubeconfig "$KUBECONFIG_QUORUM" patch configmap site-recovery-logging \
  -n "$ZONE_NS" --type=merge \
  -p '{"data":{"level":"info"}}'

12. Run a test failover

A TestFailover validates your DR capability non-disruptively using volume snapshots. The test namespace is generated by the controller; do not try to predict or specify it.

oc --kubeconfig "$KUBECONFIG_QUORUM" apply -f - <<'EOF'
apiVersion: siterecovery.trilio.io/v1alpha1
kind: TestFailover
metadata:
  name: web-pg-test
  namespace: dr-edge
spec:
  protectionGroupRef:
    name: web-pg
  cleanupPolicy: Automatic
  retentionTime: 2h
EOF
# Watch phases
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get tfo web-pg-test -n "$ZONE_NS" -w

Expected phase progression: Queued → PreparingEnvironment → CreatingSnapshots → CreatingVolumes → CreatingVMs → StartingVMs → Verifying → Succeeded → CleaningUp → Cleaned

# Check the test namespace that was created (controller-generated, do not guess)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get tfo web-pg-test -n "$ZONE_NS" \
  -o jsonpath='{.status.testNamespace}'
# Check verification results
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get tfo web-pg-test -n "$ZONE_NS" \
  -o jsonpath='{.status.verificationResults}'
# Delete a manual-cleanup test failover (removes all test resources)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" delete tfo web-pg-test -n "$ZONE_NS"

13. Execute a planned failover

A planned failover gracefully stops VMs on the active cluster and starts them on the target cluster.

oc --kubeconfig "$KUBECONFIG_QUORUM" apply -f - <<'EOF'
apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
  name: web-pg-planned-fo
  namespace: dr-edge
spec:
  protectionGroupRef:
    name: web-pg
  targetCluster: cluster2
  failoverType: planned
EOF
# Watch state and phase
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get fr web-pg-planned-fo -n "$ZONE_NS" -w

Expected output columns: PROTECTION GROUP, TARGET, STATE, PHASE, AGE

Expected phase progression: Initializing → Validating → StoppingSource → WaitingForLocks → RemovingTaints → StartingTarget → Monitoring → Completed

# Check per-VM status (no .status.failedVMs field — detail is in vmStatuses)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get fr web-pg-planned-fo -n "$ZONE_NS" \
  -o jsonpath='{.status.vmStatuses}'
# Full status
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get fr web-pg-planned-fo -n "$ZONE_NS" -o yaml

14. Execute an unplanned failover

Use failoverType: unplanned when the primary cluster is unavailable. DRBD volumes are force-promoted on the DR cluster without waiting for the primary to shut down gracefully.

oc --kubeconfig "$KUBECONFIG_QUORUM" apply -f - <<'EOF'
apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
  name: web-pg-emergency-fo
  namespace: dr-edge
spec:
  protectionGroupRef:
    name: web-pg
  targetCluster: cluster2
  failoverType: unplanned
EOF
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get fr web-pg-emergency-fo -n "$ZONE_NS" -w

Data loss note: If replication was Consistent at the moment of failure, zero data loss occurs. If replication was Degraded, minimal data loss may occur depending on out-of-sync bytes at failure time. Check ReplicationGroupStatus history and RPOEvent records after restoring quorum connectivity.


15. View RPO events

RPOEvents record replication lag violations with severity, lag duration, and affected volumes.

# List recent RPO events for a zone (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rpoe -n "$ZONE_NS"

Expected output columns: PROTECTION-GROUP, EVENT-TYPE, RPO-SECONDS, OUT-OF-SYNC, REASON, TIMESTAMP, AGE

# Filter by protection group using the label
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rpoe -n "$ZONE_NS" \
  -l siterecovery.trilio.io/protection-group=web-pg
# Get full detail of one event
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get rpoe <event-name> -n "$ZONE_NS" -o yaml

Key spec fields:

  • spec.eventType: e.g. ConnectionLost, RPOViolated, ResyncStarted
  • spec.rpoAtEvent: RPO in seconds at the time of the event
  • spec.outOfSyncBytesAtEvent: bytes out of sync when the event was recorded
  • spec.violationReason: why the RPO was violated

16. Check replication policies

The DRBDReplicationPolicy for your zone is created automatically by the ProtectionZone controller; you do not create or modify it. The policy is named <zoneName>-default and has isDefault: true.

# View the auto-created replication policy (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get drp -n "$ZONE_NS"

Expected output:

NAME           CLUSTERS   MODE          PROTOCOL   DEFAULT   PHASE
edge-default   ...        Synchronous   C          true      Ready
# Full spec
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get drp edge-default -n "$ZONE_NS" -o yaml

Note: Protocol C synchronous replication is the only supported mode. The admission webhook rejects Protocol A. RPO is always 0 and is not tunable.


17. Wait for a resource condition (scripting)

# Wait for a ProtectionRequest to reach Protected phase
kubectl --kubeconfig "$KUBECONFIG_QUORUM" wait pr/protect-vm-web-1 \
  -n "$ZONE_NS" \
  --for=jsonpath='{.status.phase}'=Protected \
  --timeout=30m

# Wait for a FailoverRequest to complete
kubectl --kubeconfig "$KUBECONFIG_QUORUM" wait fr/web-pg-planned-fo \
  -n "$ZONE_NS" \
  --for=jsonpath='{.status.state}'=Completed \
  --timeout=10m

# Wait for a TestFailover to succeed
kubectl --kubeconfig "$KUBECONFIG_QUORUM" wait tfo/web-pg-test \
  -n "$ZONE_NS" \
  --for=jsonpath='{.status.phase}'=Succeeded \
  --timeout=20m

18. Collect a diagnostic bundle with tsr-gather

tsr-gather is shipped as an oc adm must-gather image. Run it against the quorum cluster; it collects the quorum and then reaches through every ProtectionZone's kubeconfig Secrets to collect each workload cluster automatically.

# Full estate — 48h of logs (default)
oc adm must-gather \
  --image=quay.io/triliovault/site-recovery-must-gather:<tag>
# Narrow the log window and restrict to one zone
oc adm must-gather \
  --image=quay.io/triliovault/site-recovery-must-gather:<tag> \
  -- /usr/bin/gather --since 6h --zone edge
# Collect only the local cluster (no reach-through to workload clusters)
oc adm must-gather \
  --image=quay.io/triliovault/site-recovery-must-gather:<tag> \
  -- /usr/bin/gather --local-only
# Bound the total run time (partial bundle stays coherent on timeout)
oc adm must-gather \
  --timeout=10m \
  --image=quay.io/triliovault/site-recovery-must-gather:<tag> \
  -- /usr/bin/gather --since 2h
# Also collect a kind not in the standard matrix
oc adm must-gather \
  --image=quay.io/triliovault/site-recovery-must-gather:<tag> \
  -- /usr/bin/gather --gvk apps/v1/StatefulSet:app-ns

The bundle lands in ./must-gather.local.<hash>/. Open SUMMARY.md first (what is wrong), then manifest.yaml (what was collected and what was missed).

tsr-gather exit codes:

CodeMeaning
0At least one cluster was collected. Partial failures are recorded in manifest.yaml, not reflected in the exit code.
1Fatal startup error: unwritable output, no API config, bad flags, unknown --zone, or unreachable local cluster.
2The cluster answered but runs no Site Recovery component. A successful "wrong cluster" answer.

What is never collected: Secret values (only names, types, and key names), cloud-init payloads (userData, userDataBase64, networkData, networkDataBase64 — replaced with REDACTED-BY-TSR-GATHER), kubeconfig data, DRBD shared-secret values, and kubectl.kubernetes.io/last-applied-configuration values.


19. Selecting all Site Recovery objects in a namespace

Use the app.kubernetes.io/part-of=site-recovery label to select any Site Recovery-owned object.

# All site-recovery pods in the zone namespace (quorum cluster)
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pods -n "$ZONE_NS" \
  -l app.kubernetes.io/part-of=site-recovery

# All site-recovery pods on a workload cluster
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get pods -n trilio-site-recovery-system \
  -l app.kubernetes.io/part-of=site-recovery

# Quorum control-plane pods only
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pods -n "$ZONE_NS" \
  -l app.kubernetes.io/component=quorum-control-plane

# Workload control-plane pods only (on a workload cluster)
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get pods -n trilio-site-recovery-system \
  -l app.kubernetes.io/component=workload-control-plane

20. Node-agent taint and placement constraints

Protected VMs are pinned to one node per cluster for the lifetime of the protection. The node agent applies a siterecovery.trilio.io/not-ready:NoSchedule taint to its own node until DRBD converges.

# Check node taints on primary worker nodes
kubectl --kubeconfig "$KUBECONFIG_PRIMARY" get nodes -o \
  custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

# Check that a protected VM's nodeName matches the expected anchor node
kubectl --kubeconfig "$KUBECONFIG_QUORUM" get pr protect-vm-web-1 -n "$ZONE_NS" \
  -o jsonpath='{.status.sourceAnchorNode}'

Important: Live migration and cold migration of a protected VM are denied by the admission webhook. Draining a node that hosts a running protected VM will not complete. To move a protected VM, you must unprotect it, drain the node, and then re-protect it — which requires a full initial resync.