Newer
Older
openstack-caracal-dc-dc / docs / audit / container-elim-pass / pass0-w3-consumers.md

Pass 0 / W0.3 -- Consumer/dependency inventory for the container-layer-elimination pass

READ-ONLY planning finding. Repo HEAD at time of read: branch dc-dc-stage5-preconditions, 2026-08-09. This is worker W0.3 of Phase 0 (docs/audit/container-elim-pass/SCOPE-AND- EXECUTION-PLAN.md, phase-prompts.md). No command was executed; every row cites path:line.

0. Framing recap (do not re-derive)

Model B (D-122/D-123): vcloud (outer libvirt) -> vvr1-dcN (containment VM = inner libvirt, ALSO the DC rack: MAAS rack/region + Juju/openstack client host) -> node VMs. Eliminating the containment VM collapses this to vcloud (libvirt) -> node VMs directly. vvr1-dcN (the containment VM) is NOT the LXD API-charm containers that run on deployed OpenStack nodes -- those are untouched by this pass.

The load-bearing fact this dimension surfaces: vvr1-dcN is not only a nesting boundary, it is also the NAMED EXECUTION HOST for three separate ruled roles: (1) the D-123 nested-libvirt node host, (2) the D-132-amendment per-DC MAAS region+rack, and (3) the D-138 cloud-facing client host (juju/openstack run FROM vvr1-dcN, reached ssh -J voffice1 jessea123@<dc- rack-transit-ip> -- runbooks/dc-dc-phase3-maas-enlist-deploy.md:424,430, runbooks/dc-dc-phase4-juju-bundle-per-dc.md:165,357,1108). Flattening removes the host that carries all three roles simultaneously -- each must be re-homed, and they need not all land on the same place.

1. Repo-wide grep census -- vvr1-dc

Canonical tree (excludes the stale .claude/worktrees/skill-repackage-20260727/ git-worktree snapshot, which mirrors ~384 additional hits from an unrelated 2026-07-27 skill-repackage task and is not part of the live repo surface):

grep -rn "vvr1-dc" --include="*.sh" --include="*.py" --include="*.md" --include="*.yaml" \
     --include="*.tf" .   (from repo root, worktree copy excluded)

424 occurrences across 78 files.

Heaviest files (occurrence count): | File | Count | Nature | |---|---|---| | docs/design-decisions.md | 66 | D-122/123/124/125/126/128/138 rulings, amendments | | opentofu/main.tf | 34 | outer root: creates the vvr1_dc0/vvr1_dc1 containment VM modules | | scripts/site-headend-install.sh | 18 | --role rack = installs on vvr1-dcN | | docs/CURRENT-STATE.md | 17 | living status references | | opentofu/variables.tf | 16 | vvr1_dc0/vvr1_dc1 module var blocks ("Model B: holds the inner libvirt pool") | | opentofu/vr1-dc0-substrate/main.tf | 13 | inner root: qemu+ssh provider targets vvr1-dc0 | | runbooks/dc-dc-phase2-tofu-dc-substrate.md | 13 | outer/inner apply runbook | | docs/dc0-deploy-readiness.md | 12 | readiness record | | opentofu/vr1-dc1-substrate/main.tf | 9 | inner root, dc1 | | docs/dc-dc-deployment-workflow.md | 9 | workflow doc, plane-split doctrine | | runbooks/dc-dc-teardown-rollback.md | 8 | Paths A/B/C/M name vvr1-dcN as the teardown target | | opentofu/vr1-dc0-substrate/variables.tf | 6 | | | docs/audit/record-inventory.md | 6 | | | netbox/dc-rack-mgmt-import.py | 5 | imports vvr1-dcN as a NetBox rack-mgmt device | | tests/dc-rack-mgmt-import/test_logic.py | 5 | harness for the above | | ... | | remaining 64 files carry 1-4 hits each (changelogs, security-ledger, archive) |

scripts/*.sh + scripts/*.py combined: 26 occurrences across site-headend-install.sh, dc-rack-net.sh, site-baseleg.sh, dc-mirror.sh, dc-egress-check.sh, maas-profile-assert.sh, dc-dc-whole-host-budget.py. opentofu/: 17 files. Most of the remainder is docs/ (design-decisions, CURRENT-STATE, changelogs, security-ledger, archive) and runbooks/ -- i.e. DECISION RECORD, not live tooling; those do not need code changes on flattening, only a superseding note (GA-R1/R2 append-only discipline -- do not edit history).

2. Consumer table

Consumer (file:line) What it keys to the containment layer What breaks / changes on flattening Severity
scripts/lib-hosts.sh:52,157-168,212-214,246-251 VIRSH_POWER_ADDRESS, VIRSH_POWER_ADDRESS_FROM_OFFICE1, VIRSH_POWER_ADDRESS_FROM_DCREGION Both forms are qemu+ssh:// URIs that dial the containment VM's libvirtd (172.31.0.2/10.12.8.2 for dc0, 172.31.0.6/10.12.68.2 for dc1) -- this is how MAAS powers on/off node VMs, because the node VMs live INSIDE vvr1-dcN's libvirt, not vcloud's. If node VMs move to vcloud's own libvirt, the power address becomes vcloud's own libvirt (qemu:///system locally, or a qemu+ssh back to vcloud from wherever MAAS's region runs). The FROM_OFFICE1/FROM_DCREGION split (which exists because "the reachable rack address differs by region", lib-hosts.sh:159-167) may collapse entirely if there is no separate inner libvirt host to be reachable FROM. Every value in both DC arms (vr1-dc0/vr1-dc1 cases, lines 212-214, 246-251) needs re-derivation, not a rename. HIGH -- this is the mechanism MAAS uses to power every node; wrong here = "MAAS unreachable" masquerading as a network fault (a failure mode this repo has already hit once, maas-role-tags.sh:48-51).
scripts/lib-hosts.sh:69-72 NIC_PLANE_ORDER, BREX_PARENT_NIC Describes the NIC-to-plane mapping the INNER tofu root pins per node (main.tf macs[0..5] order) -- a property of how opentofu/vr1-dcN-substrate/main.tf (the inner root, applied via qemu+ssh to vvr1-dcN) attaches MACs to node VM NICs. If nodes are created directly by the (now single) tofu root against vcloud libvirt, the NIC/MAC-to-plane pinning logic must be re-authored in that root, but the ORDER CONVENTION itself (metal-admin first, not PLANE_CIDRS order) is a fact about how MAAS/libvirt XML enumerates interfaces, independent of nesting -- it likely CARRIES FORWARD unchanged. Confirm at Phase 2 (tooling), not a Phase-0 blocker. LOW-MEDIUM -- convention likely survives; the FILE that encodes it (inner substrate root) is what changes.
scripts/lib-hosts.sh:29-34 CARVE_AUX_HOSTS Declares the aux (non-Juju) -tailscale-01/-maas-01 VMs as --host-only carve targets; these ALSO live inside vvr1-dcN's inner libvirt today. No conceptual change on flattening -- these become ordinary flat-libvirt VMs alongside the 9 role nodes + juju controller. Their power address inherits the same VIRSH_POWER_ADDRESS fix above. LOW -- mechanical, rides the same fix as the row above.
scripts/lib-net.sh (whole file) No vvr1-dc string appears here (verified: grep vvr1-dc scripts/lib-net.sh = 0 hits). PLANE_CIDRS/PLANE_GW/VIP prefixes are IP-plan facts, not containment-topology facts. UNCHANGED by container elimination as such. It WILL change under the concurrent D-143 re-IP (10.12->10.13), but that is a SEPARATE, already-ruled axis this pass must keep distinguishable (SCOPE-AND-EXECUTION-PLAN.md Section 7 says exactly this). NONE for this dimension -- flag as a non-consumer so Phase 4's change-set does not double-count it.
scripts/maas-node-power.sh:2-46 (usage, POWER_ADDRESS arg, VIRSH_URI) Takes a qemu+ssh:// power address as an argument (not hardcoded) and virsh-lists domains AT THAT URI to MAC-match them to MAAS machines. Every worked example in the header (qemu+ssh://jessea123@172.31.0.2/system vr1-dc0) targets the containment VM. The script itself is topology-agnostic (it takes the URI as input) -- it needs NO code change. But every CALLER that supplies 172.31.0.2/10.12.8.2-class addresses must supply the new (flat) address instead, which is exactly the lib-hosts.sh fix above. Also note: the header's D-103 rationale for per-machine (not pod) power was ALREADY about avoiding MAAS-virsh-pod storage incompatibilities (domblkinfo on volume-ref disks) -- that reasoning is unrelated to nesting and stays valid post-flatten. MEDIUM -- no code change, but every invocation site needs the new address; a stale hardcoded address in a runbook/changelog would silently power the WRONG (or a nonexistent) host.
scripts/site-headend-install.sh:6-20,82-129,204-343,371-409 --role rack, --host-nodes, node_host_check(), node_host_setup() This is THE script that turns vvr1-dcN into a nested-libvirt node host: installs qemu-kvm/libvirtd, turns on nested KVM, creates the inner pool dir, the SEC-010 FORWARD-drop keyed on $TRANSIT_IF ("mgmt"), and verifies the D-125 WAN bridge/uplink. --role rack (no --host-nodes) is the narrower "just a MAAS rack enrolling to Office1's region" case, ALSO run on vvr1-dcN today. If nodes run directly on vcloud, --host-nodes (node_host_setup/node_host_check, ~140 lines) becomes DEAD CODE for VR1 -- there is no separate "rack that also hosts nested KVM" anymore; vcloud itself already IS the outer libvirt host and does not need this bootstrap. The rack role itself (MAAS enrollment) may still be needed if D-132's "MAAS region controller per DC" is KEPT as a normal flat VM (see D-132 row below) -- but it would no longer carry --host-nodes. The SEC-010 FORWARD-drop (TRANSIT_IF="mgmt") is keyed to a transit LEG that exists BECAUSE of the containment VM's two-leg (transit + inner-plane) design; a flat topology may not have a "transit" interface to drop-FORWARD on at all, or the DC-LOCAL boundary the drop enforces may need to move to a different device (a vcloud-host-level nftables rule, or a per-VM firewall) -- this is a design question for Phase 0's target-topology worker (W0.4), flagged here as a dependency. HIGH -- largest single file by consumer surface (18 vvr1-dc hits, ~140 lines of --host-nodes logic); also the SEC-010 enforcement point (a security control, not just wiring) lives here.
scripts/dc-rack-net.sh:2-14,55-85 (whole script; site table for dc0/dc1) RUNS ON THE DC RACK HOST (i.e. vvr1-dcN) per its own header; persists rack bridge-leg IPs (vr1-dc0-metal-admin=10.12.8.2/22 etc.) and the D-131 node-facing DNS forwarder, both keyed to addresses the CONTAINMENT VM itself holds on its own inner bridges. If there is no containment VM, "the rack's own inner bridges" do not exist in the same shape -- these addresses (.2, .3 on metal-admin) were the containment VM's identity on its OWN nested networks. Under a flat topology this functionality (rack-leg persistence + node DNS forwarder) needs a new HOME: either the per-DC MAAS region VM (if D-132's region-per-DC survives as a flat VM) or is retired if the forwarder's reason (rack-only resolver SERVFAILs, dc-rack-net.sh:27-30) no longer applies once "the rack" is gone. Cannot be resolved without W0.4's target topology. HIGH -- a security/DNS-availability control (D-131), not cosmetic; silently losing it would reintroduce the SERVFAIL bug this script was built to fix.
scripts/site-baseleg.sh:40-48 LEGS["office1"], the commented-out [vr1-dc0] row The office1 row is UNRELATED to the containment VM (it's the vcloud<->voffice1 base leg). The DC rows are explicitly DEFERRED with the comment "the DCs nest inside vvr1-dc0 and are reached by qemu+ssh ... add a row ONLY if/when a DC needs one" -- and D-138 (design-decisions.md:7126-7131) already closed this deferral: "the DC rows stay DEFERRED and their MEASURE-first note is now ANSWERED: ... no host-side leg is wanted." If nodes are flat on vcloud, the "DC rows deferred because reached by qemu+ssh through vvr1-dc0" premise disappears -- vcloud already has an L3 view of its own libvirt guests without an SSH hop. Whether a NEW base-leg row is needed depends entirely on whether the flat node VMs get bridged IPs vcloud itself must route to, which again is W0.4's call. LOW -- currently an explicit no-op; only becomes live work if the target topology needs a new leg.
scripts/maas-role-tags.sh:37,48-51 The comment "Run this where maas lives (the D-128 Plane-2 headend)" and the REFUSE message both assume the operator KNOWS which host that is -- today, resolvable to voffice1 (region) OR vvr1-dcN (rack), a distinction this pass changes. No vvr1-dc LITERAL string appears in this script's logic (it is genuinely site/host-agnostic, driven by lib-hosts.sh/MAAS_PROFILE). No code change required. Purely a DOC/comment currency issue once the execution-host question (Section 3 below) is answered -- the "headend" language should be corrected to match whatever host actually runs maas post-flatten. LOW -- comment-only.
opentofu/main.tf:25-33,187,234,309-422 (outer root, module "vvr1_dc0"/"vvr1_dc1") This IS the resource that creates the containment VM (vm_name = "vvr1-dc0", line 412) -- the single biggest and most literal consumer. Line 405: "Site-down = a single virsh destroy vvr1-dc0 (the D-122 intent)" -- i.e. the outer root's entire per-DC unit of work today IS this one VM. The ENTIRE outer-root module for a DC either (a) is deleted and replaced by direct node-VM resources targeting vcloud libvirt (the flattened design), or (b) is repurposed to create something smaller (e.g. just a MAAS-region VM, if that role is kept). This is squarely Phase-0-W0.1's (substrate/tofu map) territory, cross-referenced here because it is also the top literal-reference file in the census. HIGH (cross-referenced to W0.1, not re-owned here).
opentofu/vr1-dc0-substrate/main.tf, vr1-dc1-substrate/main.tf (inner roots) Provider block targets qemu+ssh INTO vvr1-dc0/vvr1-dc1 (per main.tf:25-33 comment on the outer root, and the inner root's own versions.tf/provider config) -- this is THE apply that creates the 9+1(+2 aux) node VMs, currently nested. If flattened, there is no longer an "inner root run via qemu+ssh into the containment VM" -- either this root's provider target becomes qemu:///system on vcloud directly (merging outer+inner into one tofu apply), or it becomes qemu+ssh FROM wherever the new execution host is, straight to vcloud. Either way this is a provider-config + apply-ordering change, not a pure rename. Cross-referenced to W0.1. HIGH (cross-referenced to W0.1).
netbox/dc-rack-mgmt-import.py, tests/dc-rack-mgmt-import/* Imports vvr1-dcN into NetBox as a rack-management device record (a physical/virtual asset in the DCIM model representing the containment VM as "the rack"). If there is no containment VM, this NetBox device either needs to be retired (with a NetBox-side decommission, not silent deletion -- NetBox is the IPAM/DCIM system of record) or repointed to represent something else (e.g., vcloud itself, or nothing, if "the rack" concept is retired outright). This is a DATA-migration consumer, distinct from a code consumer -- flag for Phase 2/W2.4 (which tools become which modules) since it touches an external system of record, not just repo files. MEDIUM -- external-system consumer, easy to miss because it is not a runbook or script that gets "read" in the normal grep sweep for tooling.
scripts/dc-egress-check.sh:63,72 Comments citing MEASURED ip route output taken directly ON vvr1-dc0/vvr1-dc1 (the default-route-via-provider-public fact). Historical/measurement citations, not live logic keyed to the containment layer. No functional change -- the DEFAULT ROUTE FACT itself (default via .4.1/.64.1) is a plane-gateway fact (lib-net.sh PLANE_GW), not a containment fact, and would still hold once nodes are flat as long as the same plane/gateway design is kept. Re-verify empirically post-flatten rather than assume; the citation just needs a currency note. LOW.
scripts/dc-mirror.sh:6 Doc-comment: "RUNS ON THE DC RACK HOST (e.g. vvr1-dc0), not on vcloud/voffice1." Same class as dc-rack-net.sh above but for the apt mirror service -- if "the rack host" no longer exists as a distinct entity, this script's stated execution host must be re-pointed to wherever the mirror service actually runs post-flatten (likely still a dedicated per-DC utility VM at the .4 octet, per lib-hosts.sh:96-100 REGION_HOST_SUFFIX/utility-band convention -- that utility VM is unaffected by containment removal, it just stops being INSIDE a nested host). LOW-MEDIUM -- doc-currency + confirm the utility VM's own libvirt parent changes from "inner" to "vcloud direct", nothing else.
scripts/maas-profile-assert.sh:39 Usage example lists voffice1,vvr1-dc0,vvr1-dc1 as the three MAAS-profile-bearing hosts whose region identity this script proves ("a machine count is NOT proof"). If the per-DC MAAS region (D-132 amendment) no longer lives ON the containment VM (because there is no containment VM) but instead on a flat per-DC utility VM, the usage example needs updating but the SCRIPT'S JOB (prove which region a profile resolves to, by rack identity) is unchanged and arguably MORE necessary post-flatten (more hosts look alike without the containment boundary as a visual/structural cue). LOW -- doc-currency; the script's function is robust to the topology change.
D-128/D-138 execution-host assumption (see Section 3, its own severity call-out below) vvr1-dcN is the D-138-RULED host for the Juju client + openstack CLI, and (per D-132 amendment, design-decisions.md:7161-7168) the per-DC MAAS region controller. Both rulings say "in the DC" / "on the rack" and CITE vvr1-dc0 transit-IP SSH as the concrete mechanism (runbooks/dc-dc-phase3-maas-enlist-deploy.md:424,430; phase4-juju-bundle-per-dc.md:165). THE OWED QUESTION THIS PASS MUST ANSWER, NOT ASSUME: with no containment VM, where do juju/openstack/the MAAS region run from? See Section 3. CRITICAL / BLOCKING -- this is not a mechanical rename; it is an unresolved architecture question that D-128/D-132/D-138 do not answer on their own once their named host disappears.
runbooks/dc-dc-teardown-rollback.md (8 hits; Paths A/B/C/M) Teardown paths name vvr1-dcN as the target of tofu destroy / virsh destroy / juju-controller-down recovery. Once the containment VM does not exist, these paths must be re-authored around whatever the new substrate shape is (destroying flat node VMs individually or via a module, not "destroy one containment VM = destroy the site"). The D-122 intent quoted in opentofu/main.tf:405 ("site-down = a single virsh destroy vvr1-dc0") is a SIMPLICITY PROPERTY that flattening explicitly TRADES AWAY -- teardown of N+1 individual VMs is inherently more steps than one. Flag as a design tradeoff for W0.4/W4.2 (module design should re-earn this simplicity via a tofu-module-scoped destroy, not lose it). MEDIUM-HIGH -- teardown is a frequently-exercised, high-consequence path (see D-061 register / docs/tool-index.md's own origin story) and losing "one command tears down a DC" without a replacement is a real regression.

3. The D-128/D-138 execution-host consequence -- flagged explicitly, per instructions

Today: vvr1-dcN simultaneously serves three ruled roles:

  1. D-123 Model B node host -- nested libvirt for the 9+1(+2) node VMs.
  2. D-132-amendment MAAS region+rack for its own DC -- installed "in the DC" so jujud's continuous MAAS-API dependency never crosses the Office1 fiber (SEC-010 preserved unamended by removing the cross-fiber requirement rather than exempting it).
  3. D-138 cloud-facing CLIENT host -- juju/openstack run FROM vvr1-dcN, reached via ssh -J voffice1 jessea123@<dc-rack-transit-ip>, because SEC-010 + D-052 make the node planes unreachable at L3 from voffice1, and D-138's own reasoning is explicit: "a MAAS rack proxies at the application layer and needs no kernel forwarding... that is TRUE of every Plane-2 tool then in use and FALSE of Juju, which dials the MACHINE at L3."

If vvr1-dcN is eliminated, all three roles lose their named host simultaneously, and they do NOT have to be re-homed together -- each has a different constraint:

  • Role 1 (node host) simply DISAPPEARS as a distinct thing -- vcloud's own libvirt takes over, no replacement host is needed.
  • Role 2 (MAAS region) needs SOME host inside "the DC's" network segment (wherever that segment's boundary now is) so jujud's L3 MAAS dependency stays local -- this could be a flat per-DC utility VM (the existing .6/REGION_HOST_SUFFIX convention already assumes a VM distinct from the role nodes, so it may not even need a new design, just a new libvirt parent).
  • Role 3 (Juju/openstack CLIENT) needs a host that can reach the flat node VMs at L3 for the full port set D-138 enumerated (22, 17070, 5000, 9292, 8774, 9696, 8776, 8778, 9876, 9311, 9511, 443+ -- design-decisions.md:7107). If node VMs are flat on vcloud and reachable from vcloud's own network without SEC-010's transit-drop boundary in the way, the client could plausibly run FROM VCLOUD ITSELF (collapsing D-128's Plane-1/Plane-2 split for this specific tool-class) -- but this is only true if the flattened topology does NOT recreate an equivalent transit/forwarding boundary between vcloud and the flat node VMs. If it DOES (e.g., to keep simulating the Roosevelt fiber-boundary realism the whole VR1 exercise exists to rehearse), then a replacement "DC-local client host" is still needed, and D-138's reasoning (why the client cannot live at the far side of that boundary) still applies verbatim to whatever plays the transit-boundary role post-flatten.

This is the single largest open question this dimension surfaces, and W0.3 cannot close it alone -- it is jointly a W0.2 (network/wiring: does a transit/SEC-010-equivalent boundary still exist post-flatten?) and W0.4 (target-topology: what replaces vvr1-dcN as a named host) question. Recorded here as the explicit hand-off the SCOPE doc's Section 4 instructs: "if there is no containment VM, where do juju/openstack run from?" has NO answer in the current repo -- D-128/D-132/D-138 all name vvr1-dcN as the concrete host, and none of them anticipates its removal. Whatever Phase 0 / W0.4 proposes as the target topology MUST include an explicit answer to this, stated as plainly as D-138's own ruling was stated, because it carries the same SEC-010/D-052 security-boundary weight D-138 was ruled to resolve -- getting it wrong reopens exactly the "narrow SEC-010 exception vs. re-plan the control-plane topology" choice D-132's amendment closed.

4. Non-consumers worth naming (to prevent Phase 4 double-counting)

  • scripts/lib-net.sh -- no containment-layer coupling; only D-143 (re-IP) touches it. Keep the two axes distinguishable per SCOPE-AND-EXECUTION-PLAN.md Section 7.
  • scripts/maas-role-tags.sh, scripts/dc-egress-check.sh, scripts/maas-profile-assert.sh logic (not comments) -- host-agnostic by construction (driven by lib-hosts.sh/env), so they need NO code change, only doc-comment currency once Section 3's question is answered.
  • The 66 design-decisions.md hits and ~17 CURRENT-STATE.md hits are the DECISION RECORD -- per GA-R1/append-only discipline these are not edited; the container-elim [ARCH] decision (owed, per SCOPE doc Section 7) will AMEND or SUPERSEDE the relevant entries (D-122/123 most directly), not rewrite them in place. Not a "consumer" in the code sense; flagged so Phase 4 does not propose editing history.

5. Summary counts for the bounded return

  • Distinct functional consumers identified (code/config, excluding decision-record docs and the stale worktree copy): 15 (table rows in Section 2, excluding the two doc-only rows for teardown-runbook and netbox-import which are procedural/data consumers, counted separately).
  • Highest severity: CRITICAL/BLOCKING -- the D-128/D-138 execution-host question (Section 3): no repo surface currently answers "where do juju/openstack run from with no containment VM", and D-132/D-138's own security reasoning (SEC-010, the app-layer-proxy vs. L3-dial distinction) applies to whatever replaces vvr1-dcN in that role.
  • HIGH severity (concrete, non-blocking but large): scripts/lib-hosts.sh power-address maps (every DC arm), scripts/site-headend-install.sh --host-nodes machinery (~140 lines becomes dead code for VR1), scripts/dc-rack-net.sh (rack-leg + D-131 DNS forwarder needs a new home), the outer/inner OpenTofu roots (cross-referenced to W0.1), and runbooks/dc-dc-teardown-rollback.md (loses the "one virsh destroy" simplicity D-122 intended, needs a re-earned equivalent).