diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index ee842bc..06c35e5 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -2576,6 +2576,35 @@ bundle and grading all 13 apps against the first app's families. **`vr1-dc1` is UNTOUCHED** and still gates correctly through the same per-leg path. `tests/render-dc-overlays` 18 -> **23/23**, `tests/provider-bundle-check` **55/55**, gauntlet **ALL GREEN (96)**. + **>>> D-139 STEPS 2 AND 3 ARE DONE FOR vr1-dc0, 2026-08-01. THE NODES ARE ON GUA. <<<** + Capture `docs/audit/d139-steps2-3-dc0-20260801.txt`. **STEP 2:** `dc-plane-ipam.sh carve-v6 + vr1-dc0 --commit` -> `applied=5 errors=2`, each GUA `/64` created on the SAME vlan as its v4 + twin and read back; independent read shows **11 v6 subnets, 6 GUA + 5 ULA, every GUA paired + on one vlan**. The 2 errors are CORRECT REFUSALS -- `lbaas-mgmt` and `oob` have no v4 plane to + pair with, so the tool refused to guess a fabric. **STEP 3:** `dc-node-v6-carve.py replace + vr1-dc0 --v6-family gua --commit` -> `applied=45 skipped=9 errors=0`, `READ-BACK: 45/45 + verified live`. **Independent query: PRE 54 v6 links (GUA 9 / ULA 45) -> POST 54 (GUA 54 / + ULA 0), and ZERO NICs carry more than one global v6, so G19's sole-global predicate holds.** + The 9 skipped are `br-ex` on provider-public, GUA already. **v4 WAS NOT TOUCHED** -- that is + the ordering step 3 exists to enforce, because `dc-node-v6-carve.py` pivots on IPv4 existing. + **GATES AFTER:** `dc-node-v6-carve check --v6-family gua` **PASS (54 correct, 0 missing, 0 + errors)**; `dc-plane-ipam check` 29 pass / **2 fail, and both are expected absences, neither + new** -- `lbaas-mgmt :80::/64` (D-139 step 5 creates that plane) and `oob :f0::/64` (oob has + no MAAS plane at all). It was 7 fail before step 2; the 5 closed are the 5 planes carved. + **REVERSIBLE:** `replace vr1-dc0 --v6-family ula --commit` swaps the node statics back; both + families remain in MAAS until step 6 retires the ULA rows, and nothing was deleted. + **THE TOOL NEEDED TWO FIXES AND A NEW ACTION FIRST, all in `22af620`.** `v6_by_vlan[vid] = s` + was **last-writer-wins**, so once step 2 landed, the FAMILY of all 54 node statics was decided + by MAAS's JSON array order -- **the FOURTH copy of the 2026-07-29 chain-audit defect**, and it + happened to pick GUA, which is luck rather than a contract. Now keyed by `(vlan, family)` with + `--v6-family`, refusing on ambiguity. A `replace` action was added because `apply` correctly + refuses to add a second address (two globals per NIC breaks G19) and the repo had no + unlink-then-link path. **A THIRD GAP SURFACED ONLY BY WRITING THE TEST: `want in have` + short-circuited to `skipped++`, so a NIC holding the wanted address AND a stray second global + counted as "already correct"** -- silently masking exactly the half-done migration G19 forbids. + Now an error; the assertion was REPLACED with that invariant and a further case added for the + refusal path the original was written for. Harness 14 -> **23**, three mutations each killing + named cases, script restored sha256-identical. **STILL OWED BEFORE THE DEPLOY, in ruled order:** ~~D-139 step 1~~ DONE (apex CREATE-only push, `netbox/d139-gua-carve.py --dc vr1-dc0 --commit`; tool built, independently reviewed, dry-run byte-identical, `--commit` never yet passed), step 2 (MAAS GUA `/64`s alongside the diff --git a/docs/audit/d139-steps2-3-dc0-20260801.txt b/docs/audit/d139-steps2-3-dc0-20260801.txt new file mode 100644 index 0000000..53662fb --- /dev/null +++ b/docs/audit/d139-steps2-3-dc0-20260801.txt @@ -0,0 +1,45 @@ +d139-steps2-3-dc0-20260801.txt +=============================== +D-139 execution STEPS 2 and 3 for vr1-dc0. EXECUTED 2026-08-01, operator-gated. +Run from voffice1 (D-128 Plane-2) via the restored :5241 forward, profile +vr1-dc0-region asserted by rack identity before any call. + +STEP 2 -- MAAS GUA /64s alongside the existing ULA + scripts/dc-plane-ipam.sh carve-v6 vr1-dc0 --commit + planned=5 applied=5 skipped=7 errors=2 + Each GUA /64 created on the SAME vlan as its v4 twin, each read back: + 2602:f3e2:f02:20::/64 vlan 5001 (metal-admin) + fd50:840e:74e2:220::/64 + 2602:f3e2:f02:21::/64 vlan 5004 (metal-internal) + fd50:840e:74e2:221::/64 + 2602:f3e2:f02:30::/64 vlan 5005 (data-tenant) + fd50:840e:74e2:230::/64 + 2602:f3e2:f02:40::/64 vlan 5006 (storage) + fd50:840e:74e2:240::/64 + 2602:f3e2:f02:50::/64 vlan 5007 (replication) + fd50:840e:74e2:250::/64 + 2602:f3e2:f02:10::/64 vlan 5002 (provider-public) -- already GUA, untouched + The 2 errors are CORRECT REFUSALS, not failures: lbaas-mgmt and oob have no v4 + plane to pair with ("refusing to guess a fabric"). lb-mgmt is D-139 step 5; oob's + 10.12.40.0/22 is ruled-but-not-built. + Independent read-back: 11 v6 subnets, 6 GUA + 5 ULA, every GUA paired on one vlan. + +STEP 3 -- node v6 statics migrated ULA -> GUA, WHILE v4 IS STILL PRESENT + scripts/dc-node-v6-carve.py replace vr1-dc0 --v6-family gua --commit + applied=45 skipped=9 errors=0; READ-BACK: 45/45 link(s) verified live + PRE : 54 v6 links on tagged nodes -- GUA 9, ULA 45 + POST: 54 v6 links on tagged nodes -- GUA 54, ULA 0 + NICs carrying more than one global v6: 0 (G19's sole-global predicate holds) + The 9 skipped are br-ex on provider-public, already GUA before this step. + v4 was NOT touched -- that is the ordering step 3 exists to enforce, because + dc-node-v6-carve.py pivots on IPv4 existing. + +GATES AFTER + dc-node-v6-carve check vr1-dc0 --v6-family gua : PASS, 54 correct, 0 missing, 0 errors + dc-plane-ipam check vr1-dc0 : 29 pass / 2 fail + The 2 are EXACTLY the two expected absences and neither is new: + v6 lbaas-mgmt 2602:f3e2:f02:80::/64 ABSENT (D-139 step 5 creates that plane) + v6 oob 2602:f3e2:f02:f0::/64 ABSENT (oob has no plane in MAAS at all) + It was 7 fail before step 2; the 5 closed are the 5 planes carved. + +REVERSIBLE: `replace vr1-dc0 --v6-family ula --commit` swaps the node statics back -- +both /64s remain in MAAS until step 6 retires the ULA rows. Nothing has been deleted. + +NOT DONE BY THESE STEPS: no v4 was removed (step 7, still gated on the unmeasured +`network-get` question); no ULA prefix was retired (step 6); lb-mgmt's plane does not +exist (step 5); vr1-dc1 is untouched throughout. diff --git a/docs/changelog-20260801-stage5-deploy-prep.md b/docs/changelog-20260801-stage5-deploy-prep.md index 9496208..5eb1d97 100644 --- a/docs/changelog-20260801-stage5-deploy-prep.md +++ b/docs/changelog-20260801-stage5-deploy-prep.md @@ -234,3 +234,19 @@ **Revert.** `git revert `; the overlay's prior bytes are recoverable from the values file by restoring the three prefix lines and re-rendering. + +--- + +## Item 7 -- D-139 steps 2 and 3 EXECUTED for vr1-dc0 (operator-gated) + +Capture: `docs/audit/d139-steps2-3-dc0-20260801.txt`. Step 2 created the 5 GUA `/64`s on the +same vlan as their v4 twins (`applied=5`, 2 correct refusals for `lbaas-mgmt`/`oob`, which have +no v4 plane). Step 3 migrated the node statics **ULA -> GUA while v4 stayed in place**: +`applied=45 errors=0`, `READ-BACK 45/45`, and an independent query confirms **GUA 54 / ULA 0 +with ZERO multi-global NICs**, so G19's predicate holds. + +`dc-plane-ipam check` went 7 fail -> 2 fail, and both remaining are expected absences that +existed before this work (`lbaas-mgmt` is step 5; `oob` has no MAAS plane). + +**Revert.** `dc-node-v6-carve.py replace vr1-dc0 --v6-family ula --commit` restores the node +statics; both families remain in MAAS until step 6. Nothing was deleted at any point.