Newer
Older
openstack-caracal-dc-dc / tests / dc-selector / run-tests.sh
#!/usr/bin/env bash
# tests/dc-selector/run-tests.sh -- unit tests for the $DC selector
# convention (2026-07-09, tooling gap register #1): lib_net_select_dc() in
# scripts/lib-net.sh and lib_hosts_select_dc() in scripts/lib-hosts.sh.
#
# Asserts:
#  - sourcing either file with no further action is unaffected (backward
#    compatible by construction -- the flat vars populate exactly as before).
#  - lib_net_select_dc: vr0-dc0/vr1-dc0 no-op (D-101 inheritance); vr1-dc1
#    OVERRIDES to the D-124-amendment literals (2026-07-21: contiguous /22s in
#    10.12.64.0/19) and UNSETS the not-yet-ruled OpenStack-layer values;
#    unknown/retired tokens fail loud; one selection per shell (the guard
#    added with the diverging dc1 arm).
#  - lib_hosts_select_dc: vr0-dc0 no-op, VR1 arms POPULATED 2026-07-23 (was: fail loud -- no real
#    per-DC host enrollment exists yet for either -- this is the documented
#    asymmetry vs. lib-net.sh).
#  - Neither function ever silently invents/reuses a value across DCs.
set -uo pipefail
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NET="$SD/../../scripts/lib-net.sh"
HOSTSLIB="$SD/../../scripts/lib-hosts.sh"
P=0; F=0
ok(){ echo "PASS: $1"; P=$((P+1)); }
no(){ echo "FAIL: $1"; F=$((F+1)); }
chk(){ [ "$2" = "$3" ] && ok "$1" || no "$1 (got '$2' want '$3')"; }

# guard: both libs still refuse direct execution (unchanged by this addition)
( bash "$NET" >/dev/null 2>&1 );      chk "lib-net direct-exec-refused"   "$?" 2
( bash "$HOSTSLIB" >/dev/null 2>&1 ); chk "lib-hosts direct-exec-refused" "$?" 2

# shellcheck source=/dev/null
. "$NET"
# shellcheck source=/dev/null
. "$HOSTSLIB"

# --- backward compatibility: sourcing alone still populates the flat vars ---
chk "PLANE_CIDRS unaffected"       "${PLANE_CIDRS[0]}"       "10.12.4.0/22"
chk "KEYSTONE_VIP_DEFAULT unaffected" "$KEYSTONE_VIP_DEFAULT" "10.12.4.50"
chk "HOSTS array unaffected"       "${HOSTS[0]}"             "openstack0"
chk "HOST_OCTET unaffected"        "${HOST_OCTET[openstack0]}" "40"

# --- lib_net_select_dc (D-119 region-qualified): vr0-dc0/vr1-dc0 no-op,
#     vr1-dc1 OVERRIDES (D-124 amendment 2026-07-21), RETIRED bare dcN fails
#     loud, unknown fails loud ---
( lib_net_select_dc vr0-dc0 ); chk "net vr0-dc0 no-op rc"    "$?" 0
( lib_net_select_dc vr1-dc0 ); chk "net vr1-dc0 no-op rc"    "$?" 0
( lib_net_select_dc vr1-dc1 ); chk "net vr1-dc1 selects rc"  "$?" 0
( lib_net_select_dc bogus 2>/dev/null ); chk "net unknown-token fails-loud rc" "$?" 1
( lib_net_select_dc 2>/dev/null ); chk "net missing-arg fails-loud rc" "$?" 1

# vr1-dc1 arm: the D-124-amendment literals land (apex-verified 2026-07-21).
DC1_P0="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${PLANE_CIDRS[0]}")"
chk "net vr1-dc1 provider-public is 10.12.64.0/22" "$DC1_P0" "10.12.64.0/22"
DC1_P5="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${PLANE_CIDRS[5]}")"
chk "net vr1-dc1 replication is 10.12.84.0/22" "$DC1_P5" "10.12.84.0/22"
DC1_MA="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${PLANE_NAME[10.12.68.0/22]}")"
chk "net vr1-dc1 10.12.68.0/22 is metal-admin" "$DC1_MA" "metal-admin"
DC1_GW="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${PLANE_GW[10.12.64.0/22]}")"
chk "net vr1-dc1 provider-public gw is 10.12.64.1 (ruled edge LAN gw)" "$DC1_GW" "10.12.64.1"
DC1_MI="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "$METAL_INTERNAL_CIDR")"
chk "net vr1-dc1 metal-internal CIDR is 10.12.72.0/22" "$DC1_MI" "10.12.72.0/22"
# --- R9 group: REPLACED 2026-07-29, not deleted -------------------------------------
# These two cases asserted that vr1-dc1 UNSETS KEYSTONE_VIP_DEFAULT and VIP_PREFIX_*.
# That state was correct only while the values were unruled. R9 (the D-119 AMENDMENT,
# RULED 2026-07-27) ruled them and named overlays/vr1-dc1-vips.yaml the single AUTHORED
# source, and the unset was actively costing: `lib_net_select_dc vr1-dc1` left
# KEYSTONE_VIP_DEFAULT unbound, so every selector-CALLER touching it died under `set -u`
# while the non-callers silently used dc0's address.
# The assertions are REPOINTED at the ruled values -- a strictly stronger check than the
# absence they replace -- and paired with the DRIFT cases below, which re-derive the same
# values from the overlay so the arm and the ratified artifact cannot diverge silently.
DC1_KV="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${KEYSTONE_VIP_DEFAULT:-}")"
chk "net vr1-dc1 sets KEYSTONE_VIP_DEFAULT (R9)" "$DC1_KV" "10.12.64.50"
DC1_VP="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_PREFIX_PROVIDER:-}")"
chk "net vr1-dc1 sets VIP_PREFIX_PROVIDER (R9)" "$DC1_VP" "10.12.64"
DC1_VA="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_PREFIX_ADMIN:-}")"
chk "net vr1-dc1 sets VIP_PREFIX_ADMIN (R9)" "$DC1_VA" "10.12.68"
DC1_VI="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_PREFIX_INTERNAL:-}")"
chk "net vr1-dc1 sets VIP_PREFIX_INTERNAL (R9)" "$DC1_VI" "10.12.72"
DC1_VC="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_COUNT_EXPECT:-}")"
chk "net vr1-dc1 sets VIP_COUNT_EXPECT 13 (R11)" "$DC1_VC" "13"

# --- R9's DRIFT half: the arm is checked against the artifact it was copied from ------
# R9's ruled shape is DERIVE + drift-check. Only the drift half exists today (the arm is
# hand-authored, see lib-net.sh's own note), and this is it: re-read the RATIFIED
# overlay and require the arm to agree, in both directions. Re-rendering the overlay
# without updating the arm, or editing the arm without the overlay, turns this red.
OVL="$SD/../../overlays/vr1-dc1-vips.yaml"
if [ ! -r "$OVL" ]; then
  no "R9 drift: overlays/vr1-dc1-vips.yaml is missing -- the arm's authored source is gone"
else
  # First v4 leg of each vip line = provider column; 2nd = admin; 3rd = internal.
  OVL_PP="$(awk -F'"' '/^[[:space:]]+vip:/{split($2,a," "); split(a[1],p,"."); print p[1]"."p[2]"."p[3]; exit}' "$OVL")"
  OVL_PA="$(awk -F'"' '/^[[:space:]]+vip:/{split($2,a," "); split(a[2],p,"."); print p[1]"."p[2]"."p[3]; exit}' "$OVL")"
  OVL_PI="$(awk -F'"' '/^[[:space:]]+vip:/{split($2,a," "); split(a[3],p,"."); print p[1]"."p[2]"."p[3]; exit}' "$OVL")"
  OVL_CNT="$(grep -cE '^[[:space:]]+vip:' "$OVL")"
  OVL_KS="$(awk -F'"' '/^  keystone:/{k=1} k&&/^[[:space:]]+vip:/{split($2,a," "); print a[1]; exit}' "$OVL")"
  chk "R9 drift: arm provider prefix == overlay" "$DC1_VP" "$OVL_PP"
  chk "R9 drift: arm admin prefix == overlay"    "$DC1_VA" "$OVL_PA"
  chk "R9 drift: arm internal prefix == overlay" "$DC1_VI" "$OVL_PI"
  chk "R9 drift: arm VIP_COUNT_EXPECT == overlay vip: line count" "$DC1_VC" "$OVL_CNT"
  chk "R9 drift: arm KEYSTONE_VIP_DEFAULT == overlay keystone provider leg" "$DC1_KV" "$OVL_KS"
fi
# REPLACED 2026-07-27, not deleted. This asserted that vr1-dc1 UNSETS FIP_POOL_START.
# The pool was RULED that day (GA-R5: "Rule dc1 FIP pool 10.12.65.0-10.12.67.254";
# recorded on the D-134 R4 amendment), so the old assertion encoded a state the ruling
# retired. Re-pointed at the ruled VALUE, which is a STRONGER check than the absence
# it replaces -- and paired with the guard below, so narrowing the unset list by one
# pair cannot silently narrow it further.
DC1_FP="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${FIP_POOL_START:-}")"
chk "net vr1-dc1 sets the RULED FIP_POOL_START" "$DC1_FP" "10.12.65.0"
DC1_FPE="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${FIP_POOL_END:-}")"
chk "net vr1-dc1 sets the RULED FIP_POOL_END" "$DC1_FPE" "10.12.67.254"
# GUARD, NON-NEGOTIABLE. The unset list shrank twice (the FIP pool in 2026-07-27's
# ruling, the R9 VIP group in 2026-07-29's delivery) and both times the same pair had to
# survive: METAL_INTERNAL_VID / METAL_INTERNAL_IFACE are not "unruled", they are
# ABOLISHED for VR1 by D-133 (flat per-NIC carve; metal-internal untagged on enp3s0).
# Anything that repopulates them silently reintroduces a retired VLAN stack, which is
# exactly what R9's own GUARD paragraph forbids. Both DCs, both variables -- the
# vr1-dc0 arm inherited them from VR0 until 2026-07-29 and now unsets them too.
DC1_MV="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${METAL_INTERNAL_VID+still-set}")"
chk "net vr1-dc1 STILL unsets METAL_INTERNAL_VID (D-133 retired that stack)" "$DC1_MV" ""
DC1_MIF="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${METAL_INTERNAL_IFACE+still-set}")"
chk "net vr1-dc1 STILL unsets METAL_INTERNAL_IFACE (D-133 retired br-internal)" "$DC1_MIF" ""
DC0_MV="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${METAL_INTERNAL_VID+still-set}")"
chk "net vr1-dc0 unsets METAL_INTERNAL_VID (D-133 'affected surfaces', executed 2026-07-29)" "$DC0_MV" ""
DC0_MIF="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${METAL_INTERNAL_IFACE+still-set}")"
chk "net vr1-dc0 unsets METAL_INTERNAL_IFACE (D-133)" "$DC0_MIF" ""
# VR0's own as-built is UNTOUCHED: D-133 supersedes Pattern A "FOR VR1 DC NODES ONLY".
VR0_MV="$(lib_net_select_dc vr0-dc0 >/dev/null 2>&1; echo "${METAL_INTERNAL_VID:-}")"
chk "net vr0-dc0 KEEPS METAL_INTERNAL_VID 103 (VR0 as-built untouched)" "$VR0_MV" "103"
VR0_MIF="$(lib_net_select_dc vr0-dc0 >/dev/null 2>&1; echo "${METAL_INTERNAL_IFACE:-}")"
chk "net vr0-dc0 KEEPS METAL_INTERNAL_IFACE br-internal" "$VR0_MIF" "br-internal"

# --- metal-admin carries NO gateway in either VR1 DC (retired VR0 inheritance) -------
# MEASURED 2026-07-27 (docs/audit/stage5-committee-raw-20260727.md:309, reconciled at
# docs/CURRENT-STATE.md:938-950): MAAS returns gateway `none` for 10.12.8.0/22 and
# 10.12.68.0/22, and both .1 addresses are held by NOTHING (100% loss, ARP never
# resolves) while both provider-public gateways answer and carry the rack default route.
# D-134's carve rules `.1` gateways for the PROVIDER subnets only. The pin was the
# defect, not MAAS -- it made scripts/pre-flight-checks.sh hard-fail on a correct cloud.
VR0_GWN="$(lib_net_select_dc vr0-dc0 >/dev/null 2>&1; echo "${#PLANE_GW[@]}")"
chk "net vr0-dc0 keeps BOTH gateways (VR0 real as-built)" "$VR0_GWN" "2"
DC0_GWN="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${#PLANE_GW[@]}")"
chk "net vr1-dc0 has exactly ONE routed plane" "$DC0_GWN" "1"
DC0_GWK="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${!PLANE_GW[*]}")"
chk "net vr1-dc0 routed plane is provider-public" "$DC0_GWK" "10.12.4.0/22"
DC0_GWA="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${PLANE_GW[10.12.8.0/22]:-unset}")"
chk "net vr1-dc0 metal-admin has NO gateway (measured: 10.12.8.1 answers nothing)" "$DC0_GWA" "unset"
DC1_GWN="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${#PLANE_GW[@]}")"
chk "net vr1-dc1 has exactly ONE routed plane" "$DC1_GWN" "1"
DC1_GWA="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${PLANE_GW[10.12.68.0/22]:-unset}")"
chk "net vr1-dc1 metal-admin has NO gateway (measured: 10.12.68.1 answers nothing)" "$DC1_GWA" "unset"

# ONE SELECTION PER SHELL (guard added with the diverging dc1 arm): changing
# selection after dc1 is REFUSED; re-selecting the SAME DC stays a no-op.
( lib_net_select_dc vr1-dc1 >/dev/null 2>&1; lib_net_select_dc vr0-dc0 2>/dev/null ); \
  chk "net dc1->vr0-dc0 re-select REFUSED (stale cross-DC values)" "$?" 1
SWITCH_ERR="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; lib_net_select_dc vr0-dc0 2>&1 1>/dev/null || true)"
grep -q "already selected" <<<"$SWITCH_ERR" && ok "net re-select error says already selected" || no "net re-select error says already selected"
( lib_net_select_dc vr1-dc0 >/dev/null 2>&1; lib_net_select_dc vr1-dc0 ); \
  chk "net same-DC re-select stays a no-op rc" "$?" 0

# D-119 REGRESSION GUARD: the bare dcN tokens are RETIRED and must be REJECTED.
# Accepting them "for compatibility" would preserve the exact cross-region
# ambiguity D-119 deletes -- 'dc0' meant VR0's LIVE cloud in lib-net.sh but VR1's
# FIRST DC in the NetBox importer. Silence here is how the off-by-one comes back.
for retired in dc0 dc1 dc2; do
  ( lib_net_select_dc "$retired" 2>/dev/null ); chk "net RETIRED '$retired' rejected (D-119)" "$?" 1
  R_ERR="$(lib_net_select_dc "$retired" 2>&1 1>/dev/null || true)"
  grep -q "RETIRED" <<<"$R_ERR" && ok "net '$retired' error says RETIRED" || no "net '$retired' error says RETIRED"
done

# no-op really means no-op: values identical after selecting vr0-dc0 or vr1-dc0.
# They match by INHERITANCE (D-101: VR1's first DC inherits VR0 DC0's v4 layout),
# not by coincidence -- and they are separate case arms so they can diverge later.
VR0_VAL="$(lib_net_select_dc vr0-dc0 >/dev/null 2>&1; echo "${PLANE_CIDRS[0]}")"
VR1_VAL="$(lib_net_select_dc vr1-dc0 >/dev/null 2>&1; echo "${PLANE_CIDRS[0]}")"
chk "net vr0-dc0/vr1-dc0 identical values (D-101 inheritance)" "$VR0_VAL" "$VR1_VAL"

# --- lib_hosts_select_dc: vr0-dc0 no-op; VR1 arms POPULATED 2026-07-23 ---
# (Stage 4 carve session: both fleets enrolled+Ready, identities measured --
# the historical fail-loud behavior and its asymmetry test are history.)
( lib_hosts_select_dc vr0-dc0 ); chk "hosts vr0-dc0 no-op rc" "$?" 0
( lib_hosts_select_dc vr1-dc0 ); chk "hosts vr1-dc0 selects rc" "$?" 0
( lib_hosts_select_dc vr1-dc1 ); chk "hosts vr1-dc1 selects rc" "$?" 0
( lib_hosts_select_dc bogus 2>/dev/null ); chk "hosts unknown-token fails-loud rc" "$?" 1
( lib_hosts_select_dc 2>/dev/null ); chk "hosts missing-arg fails-loud rc" "$?" 1
for retired in dc0 dc1 dc2; do
  ( lib_hosts_select_dc "$retired" 2>/dev/null ); chk "hosts RETIRED '$retired' rejected (D-119)" "$?" 1
done

# --- VR1 arms carry the MEASURED identity + D-134-amended octets ---
H0_OCT="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; echo "${HOST_OCTET[vr1-dc0-control-01]}")"
chk "hosts vr1-dc0 control-01 octet .100 (D-134 amendment)" "$H0_OCT" "100"
H0_ST="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; echo "${HOST_OCTET[vr1-dc0-storage-04]}")"
chk "hosts vr1-dc0 storage-04 octet .153" "$H0_ST" "153"
H0_MAC="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; echo "${HOST_BOOT_MAC[vr1-dc0-compute-02]}")"
chk "hosts vr1-dc0 compute-02 pinned boot MAC" "$H0_MAC" "52:54:00:18:ab:b4"
H1_MAC="$(lib_hosts_select_dc vr1-dc1 >/dev/null 2>&1; echo "${HOST_BOOT_MAC[vr1-dc1-storage-04]}")"
chk "hosts vr1-dc1 storage-04 pinned boot MAC (index-encoded)" "$H1_MAC" "52:54:01:d1:09:01"
H0_TAG="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; echo "$HOST_TAG")"
chk "hosts vr1-dc0 tag openstack-vr1-dc0 (ruled 2026-07-23)" "$H0_TAG" "openstack-vr1-dc0"
H1_PWR="$(lib_hosts_select_dc vr1-dc1 >/dev/null 2>&1; echo "$VIRSH_POWER_ADDRESS")"
chk "hosts vr1-dc1 power addr rack transit .6" "$H1_PWR" "qemu+ssh://jessea123@172.31.0.6/system"
H0_N="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; echo "${#HOSTS[@]}")"
chk "hosts vr1-dc0 fleet count 10 (D-121 Option C 9 role + D-104 juju-01)" "$H0_N" "10"

# --- ONE SELECTION PER SHELL (stale cross-DC guard, lib-net precedent) ---
( lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; lib_hosts_select_dc vr1-dc1 2>/dev/null ); \
  chk "hosts dc0->dc1 re-select REFUSED (stale cross-DC values)" "$?" 1
HSW_ERR="$(lib_hosts_select_dc vr1-dc0 >/dev/null 2>&1; lib_hosts_select_dc vr1-dc1 2>&1 1>/dev/null || true)"
grep -q "already selected" <<<"$HSW_ERR" && ok "hosts re-select error says already selected" || no "hosts re-select error says already selected"
( lib_hosts_select_dc vr1-dc1 >/dev/null 2>&1; lib_hosts_select_dc vr1-dc1 ); \
  chk "hosts same-DC re-select stays a no-op rc" "$?" 0

# --- the VR1 resolver exists: system_id by pinned boot MAC, never hostname ---
type host_sysid_by_bootmac >/dev/null 2>&1 && ok "host_sysid_by_bootmac defined (VR1 resolver)" || no "host_sysid_by_bootmac defined (VR1 resolver)"

echo; [ "$F" = 0 ] && { echo "ALL PASS ($P checks)"; exit 0; } || { echo "FAILURES: $F"; exit 1; }