Newer
Older
openstack-caracal-dc-dc / docs / audit / node-v6-carve-scope-20260727.md

Node IPv6 carve -- SCOPE (2026-07-27). Not executed.

Operator-directed scoping ("scope the node v6 carve"). This is a plan, not a status surface: stage/gate status lives only in docs/CURRENT-STATE.md (GA-R1).

Why this exists, and the correction behind it

When the node-v6-acquisition question was ruled ("MAAS static assignment is correct"), this session told the operator that static assignment "needs only the subnet, which now exists". That was wrong, and the operator's own question exposed it ("Are we mirroring the assigned IPv6 octet with the last IPv4 octet like we did before?").

Measured: MAAS mode=static means an EXPLICITLY CONFIGURED address. The 90 v4 node links were set that way by the Stage-4 carve, not auto-allocated. So a v6 address only appears on a node if it is assigned, exactly as the v4 statics were.

superb-piglet   enp1s0  10.12.8.121   mode=static     <- explicitly set, Stage-4 carve
                enp3s0  10.12.12.121  mode=static
                enp4s0  10.12.16.121  mode=static
   v4 links = 6      v6 links = 0
ACROSS ALL 18 Ready nodes:  IPv6 links = 0

The v4 side already demonstrates the octet mirror this carve must reproduce in v6: one stable octet per node across every plane (.121 above), per D-134.

Governing decisions

  • D-101 family matrix + its 2026-07-27 GOVERNING RATIONALE: IPv6 unless IPv4 is necessary. This carve is that principle applied at the node layer -- the layer where the IPv4 sizing constraint actually bites.
  • D-134 (2026-07-23 amendment): node band .100-.200, one stable octet per node across all six planes; control .100-.119 / compute .120-.149 / storage .150-.200.
  • v6 host-part convention RULED 2026-07-27: mirror the v4 octet TEXTUALLY. Node .121 becomes ::121, NOT the hex conversion. (The same trap already caught the band work: printf '%x' 50 yields 32.)
  • R2 / D-101: dual-stack where IPv4 is required, IPv6-only where sound, BOTH DCs.

Size and shape

108 link assignments: 18 nodes x 6 planes. Per node, per plane, one v6 static whose host part equals that node's existing v4 octet, inside that plane's /64.

plane dc0 v6 /64 dc1 v6 /64
provider-public (GUA) 2602:f3e2:f02:10::/64 2602:f3e2:f03:10::/64
metal-admin (ULA) fd50:840e:74e2:220::/64 fd50:840e:74e2:320::/64
metal-internal (ULA) :221::/64 :321::/64
data-tenant (ULA) :230::/64 :330::/64
storage (ULA) :240::/64 :340::/64
replication (ULA) :250::/64 :350::/64

NOTE the provider leg uses the node /64 (f0X:10::), NOT the VIP /64 (f0X:11::) which holds the hacluster-managed API VIPs already written to the apex.

All 12 subnets EXIST in MAAS (carved earlier this session) and each sits on the SAME vlan as its v4 twin, so this is addressing on established L2, not new fabric.

Mechanism -- prior art exists and is v4-only

scripts/carve-host-interfaces.sh performed the v4 carve and already makes the exact call needed:

maas <profile> interface link-subnet <system_id> <iface_id> \
     mode=STATIC subnet=<subnet_id> ip_address=<addr>

Measured: grep -ciE 'ipv6|::|inet6' over that script returns 0 -- it is v4-only, and its addresses are built as hardcoded v4 bases plus the octet (e.g. :263 ip_address="10.12.8.$OCTET"). So this carve needs either a v6 arm in that script or a companion tool. RECOMMEND a companion on the dc-plane-ipam.sh pattern already proven this session: site-keyed, dry by default, idempotent, and READ BACK per write.

Precedent that the operation is safe on Ready nodes: the Stage-4 carve did 90 of these on Ready nodes with a read-back each, and all 18 stayed Ready.

Preconditions

  1. Nodes stay in Ready (powered off). link-subnet does not require a boot, and MAAS renders these into node netplan at deploy -- which is why doing it BEFORE Stage 5 makes them land at first boot rather than needing a re-deploy.
  2. Octet source is the node's EXISTING v4 assignment, read live -- never a table. The mirror must be derived from what MAAS actually holds, so a node whose octet ever drifted cannot silently get a mismatched v6 address.
  3. The v6 subnets' gateway_ip and dns_servers are UNSET (measured). That is consistent with the no-external-v6-routing posture, but see the open item below -- it is a separate decision from this carve and does NOT block it.

Risks, and the one the operator named

  • Charm/module IPv6 support is the real risk. The operator's own rationale records that v4-first was chosen partly to avoid "troubleshooting IPv6 configurations around modules that may not have supported it as default". A node carrying v6 on all six planes is exactly what surfaces those modules. That argues for carving BEFORE the deploy, not after -- discovering a charm that mishandles a v6 leg during the bundle deploy is far more expensive than discovering it on a static-assignment read-back.
  • Blast radius is per-link and reversible: interface unlink-subnet removes one. No node is powered on; no tfstate, no running service.
  • Do NOT re-commission to apply these. The 2026-07-20 incident regenerated all 9 node NIC MACs on an in-place apply and stranded MAAS's records; a carve must touch links only.

Verification owed

  1. Per-write read-back, as every mutation this session has done.
  2. A gate assertion: extend dc-plane-ipam.sh check so 18/18 nodes carrying their mirrored v6 octet on all six planes is a NAMED executable check rather than a count in prose -- the same reason the bands got a gate.
  3. At Stage-5 first boot (G17): that the node actually brings the address up. MAAS holding a link is not the same as an interface carrying it -- this session has already been bitten by asserting existence instead of content.

Explicitly OUT of scope

  • v6 gateway_ip on any plane, and v6 dns_servers -- open, and deliberate under the no-external-v6-routing posture.
  • Rack-bridge v6 legs -- NOT needed under the static-assignment ruling.
  • NAT64/DNS64 -- REJECTED 2026-07-27 (Roosevelt has native v6 edge transport, so it has no Roosevelt analog). Recorded on D-101.
  • Tenant addressing -- Neutron-managed, never in the apex (D-016/D-074).