#!/usr/bin/env bash
# scripts/dc-dc-rbd-mirror.sh -- rbd-mirror pool peer bootstrap for D-108's
# Glance replication mechanism (tooling gap register item #5). Extracts
# runbooks/dc-dc-phase5-dr-failover-drill.md Step 5 (stage ONE-WAY: rx-only
# peer bootstrap) and Step 8's rbd-mirror half (flip to rx-tx -- TWO-WAY)
# into a callable, parameterized script.
#
# STANDARD, well-documented upstream Ceph administration (trust these): every
# `rbd mirror pool` subcommand/flag below (enable image, peer bootstrap
# create/import, --direction rx-only|rx-tx) is copied verbatim from the
# runbook, which is itself copied from upstream rbd-mirror docs -- nothing
# here is invented.
#
# GENUINELY REPO-SPECIFIC (needs review before trusting; none of it exists
# yet): the Glance POOL name (runbook Step 5 explicitly warns "do not assume
# glance -- confirm against the live ceph-mon / `juju config glance
# rbd-pool`"), the site names, and the juju model/unit this targets. All
# REQUIRED arguments below -- no invented defaults.
#
# SECRET-ADJACENT TOKEN HANDLING: the bootstrap token is credential material
# (whoever holds it can join the mirror relationship). This script NEVER
# reads the token's contents into its own context -- bootstrap-primary
# redirects the remote command's stdout straight to a file ON THE REMOTE
# UNIT (via the ssh'd shell's own `>`), and bootstrap-secondary expects the
# token file to already exist on ITS target unit (transferred out of band by
# the operator, per the runbook's own "transfer the token to DC2 out of band
# ... never printed into this session's context" instruction) -- this script
# does not perform that transfer itself.
#
# NO LIVE RBD-MIRROR DAEMON EXISTS THIS SESSION (2026-07-10, prep-only) --
# never run for real, cannot be tonight. Default mode is a DRY-RUN plan
# print; --apply executes, one command at a time, aborting on first failure.
# Its matching harness (tests/dc-dc-rbd-mirror/run-tests.sh) validates ONLY
# this script's own logic (argument parsing, guard clauses, dry-run output,
# the required --direction choice) -- it does NOT and CANNOT validate real
# rbd-mirror daemon behavior.
#
# $DC gate: sources scripts/lib-net.sh and calls `lib_net_select_dc "$DC"`
# once (DOCFIX-151 convention), immediately after argument parsing --
# deliberately makes `--dc dc2` FAIL LOUD today (D-101/gap #3: DC2 has no
# assigned network literals, so no real DC2 Ceph cluster exists to target).
# Also prints the replication-plane CIDR key (PLANE_NAME in lib-net.sh) as a
# REMINDER that rbd-mirror daemon peering must ride that plane, per D-108's
# explicit correction of the DR-seed's original v4 carrier language -- never
# substituted into a command, the real ULA peer address is always separately
# measured.
#
# Usage:
#   dc-dc-rbd-mirror.sh bootstrap-primary --dc dc1 --model M --unit U \
#     --pool NAME --site-name NAME [--token-out /remote/path] [--apply]
#   dc-dc-rbd-mirror.sh bootstrap-secondary --dc dc2 --model M --unit U \
#     --pool NAME --site-name NAME --direction rx-only|rx-tx \
#     --token-in /remote/path [--apply]
#   dc-dc-rbd-mirror.sh --help
#
# Exit: 0 ok | 1 bad/missing args | 2 juju not on PATH (apply only) |
#       3 $DC gate refused | 5 apply-mode command failed.  ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

usage() {
  cat <<'EOF'
usage:
  dc-dc-rbd-mirror.sh bootstrap-primary --dc vr1-dc0|vr1-dc1 --model M --unit U \
    --pool NAME --site-name NAME [--token-out /remote/path] [--apply]
  dc-dc-rbd-mirror.sh bootstrap-secondary --dc vr1-dc0|vr1-dc1 --model M --unit U \
    --pool NAME --site-name NAME --direction rx-only|rx-tx \
    --token-in /remote/path [--apply]
  dc-dc-rbd-mirror.sh --help

Default mode is dry-run (prints the plan, executes nothing). --apply executes,
one command at a time, aborting on the first failure. --direction has no
default -- rx-only (Step 5, one-way staging) and rx-tx (Step 8, two-way) are
a deliberate, conscious choice each time, never assumed.
EOF
}

SUBCMD="${1:-}"
case "$SUBCMD" in
  -h|--help) usage; exit 0 ;;
  bootstrap-primary|bootstrap-secondary) shift ;;
  "") echo "FAIL: a subcommand is required" >&2; usage >&2; exit 1 ;;
  *) echo "FAIL: unknown subcommand '$SUBCMD'" >&2; usage >&2; exit 1 ;;
esac

DC=""; MODEL="${MODEL:-openstack}"; UNIT=""; APPLY=0
POOL=""; SITE_NAME=""; DIRECTION=""
TOKEN_OUT="/tmp/rbd-mirror-bootstrap-token.$(od -An -N4 -tx1 /dev/urandom 2>/dev/null | tr -d ' \n' || echo "$$")"
TOKEN_IN=""

while [ $# -gt 0 ]; do
  case "$1" in
    -h|--help) usage; exit 0 ;;
    --dc) DC="${2:-}"; shift 2 ;;
    --model) MODEL="${2:-}"; shift 2 ;;
    --unit) UNIT="${2:-}"; shift 2 ;;
    --pool) POOL="${2:-}"; shift 2 ;;
    --site-name) SITE_NAME="${2:-}"; shift 2 ;;
    --direction) DIRECTION="${2:-}"; shift 2 ;;
    --token-out) TOKEN_OUT="${2:-}"; shift 2 ;;
    --token-in) TOKEN_IN="${2:-}"; shift 2 ;;
    --apply) APPLY=1; shift ;;
    *) echo "FAIL: unknown argument '$1'" >&2; usage >&2; exit 1 ;;
  esac
done

# --- guard clauses: required args, no invented defaults ---
[ -n "$DC" ] || { echo "FAIL: --dc is required (vr1-dc0|vr1-dc1)" >&2; exit 1; }
case "$DC" in vr1-dc0|vr1-dc1) ;; *) echo "FAIL: --dc must be vr1-dc0 or vr1-dc1 (got '$DC'). D-119: bare dcN is RETIRED -- it was ambiguous across regions." >&2; exit 1 ;; esac
[ -n "$UNIT" ] || { echo "FAIL: --unit is required (e.g. ceph-rbd-mirror/0 or ceph-mon/0 -- measured, never assumed)" >&2; exit 1; }
[ -n "$POOL" ] || { echo "FAIL: --pool is required (confirm against 'ceph osd pool ls' + 'juju config glance rbd-pool' -- do not assume 'glance', per the runbook)" >&2; exit 1; }
[ -n "$SITE_NAME" ] || { echo "FAIL: --site-name is required" >&2; exit 1; }

case "$SUBCMD" in
  bootstrap-secondary)
    [ -n "$DIRECTION" ] || { echo "FAIL: --direction is required for bootstrap-secondary (rx-only|rx-tx -- a conscious choice, never defaulted)" >&2; exit 1; }
    case "$DIRECTION" in rx-only|rx-tx) ;; *) echo "FAIL: --direction must be rx-only or rx-tx (got '$DIRECTION')" >&2; exit 1 ;; esac
    [ -n "$TOKEN_IN" ] || { echo "FAIL: --token-in is required for bootstrap-secondary (the path, ON THIS UNIT, where the out-of-band-transferred bootstrap token file lives -- this script does not transfer it)" >&2; exit 1; }
    ;;
esac

# --- $DC gate (DOCFIX-151 convention) ---
# Informational in dry-run (a plan preview is not a mutation); HARD BLOCKING
# before any --apply execution below.
# shellcheck source=scripts/lib-net.sh
. "$HERE/lib-net.sh"
DC_GATE_MSG="$(lib_net_select_dc "$DC" 2>&1 1>/dev/null)"; DC_GATE_RC=$?
if [ "$DC_GATE_RC" -eq 0 ]; then
  DC_GATE_LINE="\$DC gate: OK ($DC)"
  # The gate check ran in a subshell; select again HERE so the plane vars the
  # reminder reads below belong to $DC, not the file's flat DC0 defaults
  # (cross-DC value surfacing). rc 0 is already proven, safe under set -e.
  lib_net_select_dc "$DC"
else
  DC_GATE_LINE="\$DC gate: FAILED for '$DC' -- $DC_GATE_MSG"
fi

REPL_CIDR=""
for c in "${PLANE_CIDRS[@]}"; do
  [ "${PLANE_NAME[$c]:-}" = "replication" ] && REPL_CIDR="$c"
done

hdr()  { echo; echo "=== $* ==="; }
note() { echo "  $*"; }

hdr "dc-dc-rbd-mirror  subcmd=$SUBCMD  dc=$DC  mode=$([ "$APPLY" -eq 1 ] && echo apply || echo dry-run)"
note "juju model=$MODEL unit=$UNIT pool=$POOL site-name=$SITE_NAME"
note "$DC_GATE_LINE"
note "reminder: rbd-mirror daemon peering must ride the REPLICATION plane (${REPL_CIDR:-unassigned}, per D-101/D-108/lib-net.sh) -- not substituted into any command, confirm the real ULA peer address separately"

# --- build the remote rbd command sequence (verbatim upstream ops) ---
SSH_CMDS=()
case "$SUBCMD" in
  bootstrap-primary)
    SSH_CMDS+=("rbd mirror pool enable $POOL image")
    SSH_CMDS+=("rbd mirror pool peer bootstrap create --site-name $SITE_NAME $POOL > $TOKEN_OUT && chmod 600 $TOKEN_OUT")
    ;;
  bootstrap-secondary)
    SSH_CMDS+=("rbd mirror pool enable $POOL image")
    SSH_CMDS+=("rbd mirror pool peer bootstrap import --site-name $SITE_NAME --direction $DIRECTION $POOL $TOKEN_IN")
    ;;
esac

hdr "PLAN"
n=0
for cmd in "${SSH_CMDS[@]}"; do
  n=$((n+1))
  note "STEP $n: juju ssh -m $MODEL $UNIT -- \"$cmd\""
done
if [ "$SUBCMD" = "bootstrap-primary" ]; then
  note "NOTE: the token now lives at $TOKEN_OUT ON THE REMOTE UNIT, chmod 600'd immediately after creation (random-suffixed path, not a fixed guessable name) -- transfer it to the DC2 unit OUT OF BAND (never printed/piped through this session), then run bootstrap-secondary there, then remove it from this unit"
fi

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "re-run with --apply to execute (one command at a time, aborts on first failure)."
  echo "OK (dry-run)"
  exit 0
fi

[ "$DC_GATE_RC" -eq 0 ] || { echo "FAIL: \$DC gate refused for '$DC' -- STOP, do not mutate an unassigned DC ($DC_GATE_MSG)" >&2; exit 3; }
command -v juju >/dev/null 2>&1 || { echo "FAIL: juju required on PATH for --apply" >&2; exit 2; }

hdr "EXECUTE (apply mode -- one command at a time, abort on first failure)"
n=0
for cmd in "${SSH_CMDS[@]}"; do
  n=$((n+1))
  echo "  DO STEP $n: juju ssh -m $MODEL $UNIT -- \"$cmd\""
  juju ssh -m "$MODEL" "$UNIT" -- "$cmd" </dev/null || { echo "FAIL: step $n failed -- STOP (no further commands run)" >&2; exit 5; }
done

echo "OK (apply) -- verify with: rbd mirror pool info/status --verbose (per runbook Step 5/8 VERIFY)"
