diff --git a/docs/design-decisions.md b/docs/design-decisions.md index c3a1e95..c1cc447 100644 --- a/docs/design-decisions.md +++ b/docs/design-decisions.md @@ -5345,6 +5345,23 @@ 2. One gated pass for the **full v4 scheme**: utility `.4-.49` and VIP `.50-.99` on all 12 DC plane subnets, plus the **FIP pool** `10.12.5.0-10.12.7.254`, whose absence `scripts/phase-04-network-verify.sh:100` already hard-fails on. + + **dc1's FIP POOL RULED 2026-07-27 (GA-R5) -- this clause named only dc0's.** Question as + presented: the band pass executed for both DCs but `reserve vr1-dc1` REFUSED the FIP pool + and exited 1, because `scripts/lib-net.sh:158` UNSETS `FIP_POOL_START`/`END` for the dc1 + arm BY DESIGN ("OpenStack-layer values ... are NOT yet ruled/measured for vr1-dc1 ... UNSET + so any use fails loud"). Mirroring dc0's shape unasked would be an inferred value under + hard rule 2, so R4 could not close for dc1 without a ruling. Operator answer, exact + utterance: **"Rule dc1 FIP pool 10.12.65.0-10.12.67.254"**. + VALIDATED BEFORE RECORDING, not assumed: the range is inside the dc1 provider-public plane + `10.12.64.0/22` (which spans `10.12.64.0`-`10.12.67.255`); it is **767 addresses, byte-for- + byte the same size as dc0's** `10.12.5.0-10.12.7.254`, so the two DCs are structurally + symmetric; and it does NOT overlap the D-134 utility `.64.4-.49` or VIP `.64.50-.99` bands. + EFFECT: `lib-net.sh`'s dc1 arm now sets `FIP_POOL_START`/`END` instead of unsetting them, + and R4's ruled scope can close for dc1. **NOTE the neighbouring values stay UNSET** -- + `VIP_PREFIX_*`, `VIP_COUNT_EXPECT` and `KEYSTONE_VIP_DEFAULT` remain deliberately unset for + dc1 (R9's D-119 amendment rules them GENERATED from the overlay), so this ruling narrows + that unset list by exactly one pair and does not reopen it. 3. **v6 bands follow as a second pass with the same tool, after the R2 carve lands.** This is FORCED SEQUENCING, not a deferral: a reserved range cannot be created on a subnet that does not yet exist, and the v6 plane subnets are not in MAAS. diff --git a/scripts/lib-net.sh b/scripts/lib-net.sh index 341c560..1a66055 100644 --- a/scripts/lib-net.sh +++ b/scripts/lib-net.sh @@ -149,13 +149,24 @@ PLANE_GW=( ["10.12.64.0/22"]="10.12.64.1" ["10.12.68.0/22"]="10.12.68.1" ) DATA_PLANE_CIDRS=( "10.12.72.0/22" "10.12.76.0/22" "10.12.80.0/22" "10.12.84.0/22" ) METAL_INTERNAL_CIDR="10.12.72.0/22" - # OpenStack-layer values (VIP bands, FIP pool, keystone VIP) and the VR0 - # metal fabric facts (VLAN id, bridge iface) are NOT yet ruled/measured for - # vr1-dc1 (they arrive with its Stage-5 analog). UNSET so any use fails - # loud under `set -u` instead of silently borrowing another DC's addresses. + # FIP pool RULED 2026-07-27 (GA-R5, operator: "Rule dc1 FIP pool + # 10.12.65.0-10.12.67.254"; recorded on the D-134 R4 amendment). It sits inside + # provider-public 10.12.64.0/22, is 767 addresses -- the SAME size as dc0's + # 10.12.5.0-10.12.7.254, so the DCs stay structurally symmetric -- and does not + # overlap the D-134 .64.4-.49 / .64.50-.99 bands. Consumed by + # scripts/dc-plane-ipam.sh reserve and asserted by phase-04-network-verify.sh:100. + FIP_POOL_START="10.12.65.0" + FIP_POOL_END="10.12.67.254" + # The REMAINING OpenStack-layer values (VIP bands, keystone VIP) and the VR0 metal + # fabric facts (VLAN id, bridge iface) are still NOT ruled/measured for vr1-dc1. + # UNSET so any use fails loud under `set -u` instead of silently borrowing another + # DC's addresses. Per R9 (D-119 amendment) the VIP group becomes GENERATED from + # overlays/vr1-dc1-vips.yaml -- so it must NOT be hand-set here; and per D-133 the + # METAL_INTERNAL_VID/IFACE pair must STAY unset, since that VLAN stack is retired + # for VR1 and a generator repopulating it would silently reintroduce it. unset METAL_INTERNAL_VID METAL_INTERNAL_IFACE unset VIP_PREFIX_PROVIDER VIP_PREFIX_ADMIN VIP_PREFIX_INTERNAL VIP_COUNT_EXPECT - unset FIP_POOL_START FIP_POOL_END KEYSTONE_VIP_DEFAULT + unset KEYSTONE_VIP_DEFAULT ;; dc0|dc1|dc2) echo "FAIL: bare '$dc' is RETIRED (D-119). It was AMBIGUOUS ACROSS REGIONS: 'dc0' meant VR0's live DC0 here, but VR1's FIRST DC in the NetBox importer. Use the region-qualified selector: vr0-dc0 | vr1-dc0 | vr1-dc1" >&2 diff --git a/tests/dc-plane-ipam/run-tests.sh b/tests/dc-plane-ipam/run-tests.sh index 878a020..ffea561 100755 --- a/tests/dc-plane-ipam/run-tests.sh +++ b/tests/dc-plane-ipam/run-tests.sh @@ -203,12 +203,19 @@ json.dump({"ipam/prefixes":out},open(sys.argv[1],"w")) PY2 } +# REPLACED 2026-07-27, not deleted. This asserted dc1 REFUSES the FIP pool because it +# was unruled. It was ruled that day ("Rule dc1 FIP pool 10.12.65.0-10.12.67.254"), so +# the refusal path is no longer reachable for dc1. Re-pointed at the ruled value: the +# tool must now PLAN the ruled pool, and must plan dc0's DIFFERENT pool for dc0 -- which +# together prove it reads the per-DC value rather than a shared literal. The generic +# "refuse rather than infer" behaviour is still covered, by the FIP_POOL_START guard in +# the script and by tests/dc-selector's still-unset assertions. D="$TMP/dc1fip"; mkdir -p "$D"; mkapex1 "$D/apex.json"; mkmaas "$D" none none out="$(cd "$REPO" && PATH="$D/fakebin:$PATH" APEX_RECORD="$D/apex.json" \ - bash scripts/dc-plane-ipam.sh reserve vr1-dc1 2>&1)"; rc=$? -if [ "$rc" = 1 ] && grep -q 'Refusing to infer it from dc0' <<<"$out"; then - ok "T20 dc1 REFUSES to invent the unruled FIP pool (hard rule 2), exit 1" -else bad "T20 dc1 FIP refusal wrong (rc=$rc)" "$(printf '%s' "$out" | tail -4)"; fi + bash scripts/dc-plane-ipam.sh reserve vr1-dc1 2>&1)" +if grep -q '10.12.65.0-10.12.67.254' <<<"$out" && ! grep -q '10.12.5.0-10.12.7.254' <<<"$out"; then + ok "T20 dc1 plans its OWN ruled FIP pool (.65-.67), never dc0's (.5-.7)" +else bad "T20 dc1 FIP pool wrong" "$(grep -i fip <<<"$out" | head -3)"; fi echo echo "RESULT: PASS=$PASS FAIL=$FAIL" diff --git a/tests/dc-selector/run-tests.sh b/tests/dc-selector/run-tests.sh index f079d3e..770dd3f 100644 --- a/tests/dc-selector/run-tests.sh +++ b/tests/dc-selector/run-tests.sh @@ -65,8 +65,23 @@ chk "net vr1-dc1 unsets KEYSTONE_VIP_DEFAULT" "$DC1_KV" "" DC1_VP="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_PREFIX_PROVIDER+still-set}")" chk "net vr1-dc1 unsets VIP_PREFIX_PROVIDER" "$DC1_VP" "" -DC1_FP="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${FIP_POOL_START+still-set}")" -chk "net vr1-dc1 unsets FIP_POOL_START" "$DC1_FP" "" +# 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: the ruling narrowed the unset list by exactly one pair. The VIP group must +# STAY unset (R9 rules it GENERATED from the overlay) and so must the D-133-retired +# metal-internal VLAN stack. +DC1_VP="$(lib_net_select_dc vr1-dc1 >/dev/null 2>&1; echo "${VIP_PREFIX_PROVIDER+still-set}")" +chk "net vr1-dc1 STILL unsets VIP_PREFIX_PROVIDER (R9: generated, not hand-set)" "$DC1_VP" "" +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" "" # 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.