diff --git a/bundle.yaml b/bundle.yaml index ac94b92..a232960 100644 --- a/bundle.yaml +++ b/bundle.yaml @@ -71,14 +71,30 @@ # ----- Bindings for external-API-facing charms (public on provider) ---------- machines: + # VR1 role-separated (D-121 Option C: 3 control / 2 compute / 4 storage). Juju machine + # ids 0-8 are LOGICAL (not MAAS ids). Base carries vr1-dc0 tags; the dc1 deploy retags + # via overlays/vr1-dc1-machines.yaml. Per-role sub-tags (control/compute/storage) are + # applied to the MAAS nodes at Stage 4 (Fork-3); the DC tag scopes selection to one DC + # under the single Office1 region. The Juju CONTROLLER is a dedicated 10th VM (D-104 + # amendment), bootstrapped by its own tag -- NOT a bundle machine. + "0": + constraints: arch=amd64 tags=openstack-vr1-dc0,control + "1": + constraints: arch=amd64 tags=openstack-vr1-dc0,control + "2": + constraints: arch=amd64 tags=openstack-vr1-dc0,control + "3": + constraints: arch=amd64 tags=openstack-vr1-dc0,compute + "4": + constraints: arch=amd64 tags=openstack-vr1-dc0,compute + "5": + constraints: arch=amd64 tags=openstack-vr1-dc0,storage + "6": + constraints: arch=amd64 tags=openstack-vr1-dc0,storage + "7": + constraints: arch=amd64 tags=openstack-vr1-dc0,storage "8": - constraints: arch=amd64 tags=openstack - "9": - constraints: arch=amd64 tags=openstack - "10": - constraints: arch=amd64 tags=openstack - "11": - constraints: arch=amd64 tags=openstack + constraints: arch=amd64 tags=openstack-vr1-dc0,storage # ===================================================================== # Network-space bindings (D-052): EXPLICIT per-application blocks, no anchors. @@ -105,7 +121,7 @@ charm: mysql-innodb-cluster channel: 8.0/stable num_units: 3 - to: [lxd:8, lxd:9, lxd:10] + to: [lxd:0, lxd:1, lxd:2] bindings: '': metal-admin certificates: metal-internal @@ -119,7 +135,7 @@ charm: rabbitmq-server channel: 3.9/stable num_units: 1 - to: [lxd:10] + to: [lxd:0] bindings: '': metal-admin amqp: metal-internal @@ -137,7 +153,7 @@ # problem with 1.16 ruled out. See docs/D-068-vault-1.8-vs-1.16-analysis.md. BUNDLEFIX-010. channel: 1.8/stable num_units: 1 # 3 on Roosevelt (D-009); HA backend decided there (C1) - to: [lxd:11] + to: [lxd:0] bindings: '': metal-admin access: metal-internal @@ -179,7 +195,7 @@ charm: keystone channel: 2024.1/stable num_units: 1 # 3 on Roosevelt (D-009) - to: [lxd:8] + to: [lxd:0] options: vip: "10.12.4.50 10.12.8.50 10.12.12.50" # B1 front-loaded VIP; IS the catalog endpoint (B5, no os-public-hostname) use-policyd-override: true # as-built reconcile 2026-06-09 (origin untraced -- Review-later) @@ -218,7 +234,7 @@ charm: glance channel: 2024.1/stable num_units: 1 - to: [lxd:11] + to: [lxd:0] options: vip: "10.12.4.53 10.12.8.53 10.12.12.53" # B1 image-conversion: true # as-built; image conversion enabled (raw on Ceph-backed glance) @@ -252,7 +268,7 @@ charm: glance-simplestreams-sync channel: 2024.1/stable num_units: 1 - to: [lxd:8] + to: [lxd:1] options: # B4 amphora-pipeline use-internal-endpoints: true # use internal (IP) catalog endpoints use_swift: false # skip swift index; sidesteps radosgw object path for the amphora seed @@ -272,7 +288,7 @@ charm: nova-cloud-controller channel: 2024.1/stable num_units: 1 - to: [lxd:11] + to: [lxd:0] options: console-access-protocol: novnc network-manager: Neutron @@ -303,8 +319,8 @@ nova-compute: charm: nova-compute channel: 2024.1/stable - num_units: 3 - to: ["9", "10", "11"] + num_units: 2 + to: ["3", "4"] options: config-flags: default_ephemeral_format=ext4 enable-live-migration: true # now genuinely usable -- shared Ceph storage = memory-only migrate (B3) @@ -343,7 +359,7 @@ charm: placement channel: 2024.1/stable num_units: 1 - to: [lxd:11] + to: [lxd:0] options: vip: "10.12.4.59 10.12.8.59 10.12.12.59" # B1 bindings: @@ -376,7 +392,7 @@ charm: neutron-api channel: 2024.1/stable num_units: 1 - to: [lxd:9] + to: [lxd:0] options: enable-ml2-port-security: true flat-network-providers: physnet1 @@ -419,7 +435,7 @@ charm: ovn-central channel: 24.03/stable num_units: 3 - to: [lxd:8, lxd:9, lxd:10] + to: [lxd:0, lxd:1, lxd:2] bindings: '': metal-admin certificates: metal-internal @@ -439,11 +455,13 @@ options: ovn-bridge-mappings: physnet1:br-ex prefer-chassis-as-gw: true # B2 -- elects gateway chassis so tenant routers get external egress + # VR1 dc1: the 2 compute nodes' provider-public (enp2s0) MACs = 52:54:01:d1:NN:02, + # NN = node ordinal (compute-01=04, compute-02=05), pinned in opentofu/vr1-dc1-substrate. + # The charm picks whichever MAC is local per unit; ovn-chassis is subordinate to the + # 2-unit nova-compute, so only the compute hosts carry br-ex. bridge-interface-mappings: >- - br-ex:52:54:00:3d:fd:54 - br-ex:52:54:00:9d:63:77 - br-ex:52:54:00:89:7f:ce - br-ex:52:54:00:99:fc:c2 + br-ex:52:54:01:d1:04:02 + br-ex:52:54:01:d1:05:02 bindings: '': metal-admin amqp: metal-internal @@ -465,7 +483,7 @@ charm: cinder channel: 2024.1/stable num_units: 1 - to: [lxd:9] + to: [lxd:0] options: block-device: None glance-api-version: 2 @@ -509,7 +527,7 @@ charm: ceph-mon channel: squid/stable num_units: 3 - to: [lxd:8, lxd:9, lxd:10] + to: [lxd:0, lxd:1, lxd:2] options: source: *ceph-source expected-osd-count: 4 @@ -536,7 +554,7 @@ charm: ceph-osd channel: squid/stable num_units: 4 - to: ["8", "9", "10", "11"] + to: ["5", "6", "7", "8"] options: source: *ceph-source osd-devices: /dev/vdb # libvirt-attached, MAAS-untracked, wiped pre-deploy @@ -552,7 +570,7 @@ charm: ceph-radosgw channel: squid/stable num_units: 1 - to: [lxd:8] + to: [lxd:0] options: source: *ceph-source vip: "10.12.4.60 10.12.8.60 10.12.12.60" # B1 -- radosgw HA un-deferred for Roosevelt fidelity (decorative HA on testcloud) @@ -579,7 +597,7 @@ charm: openstack-dashboard channel: 2024.1/stable num_units: 1 - to: [lxd:10] + to: [lxd:0] options: debug: "false" vip: "10.12.4.58 10.12.8.58 10.12.12.58" # B1 -- browse HTTPS by IP (B5); ALLOWED_HOSTS must permit the VIP IP (verify at deploy) @@ -624,7 +642,7 @@ charm: octavia channel: 2024.1/stable num_units: 1 - to: [lxd:11] + to: [lxd:0] options: debug: false openstack-origin: *openstack-origin @@ -698,7 +716,7 @@ charm: barbican channel: 2024.1/stable num_units: 1 - to: [lxd:11] + to: [lxd:0] options: openstack-origin: *openstack-origin vip: "10.12.4.51 10.12.8.51 10.12.12.51" # B1 @@ -755,7 +773,7 @@ charm: magnum channel: 2024.1/stable num_units: 1 - to: [lxd:9] + to: [lxd:0] options: openstack-origin: *openstack-origin region: RegionOne @@ -813,7 +831,7 @@ charm: designate channel: 2024.1/stable num_units: 1 - to: [lxd:9] + to: [lxd:0] bindings: '': metal-admin dns-backend: metal-internal @@ -841,7 +859,7 @@ charm: designate-bind channel: 2024.1/stable num_units: 1 - to: [lxd:9] + to: [lxd:2] bindings: '': metal-admin dns-backend: metal-internal @@ -887,7 +905,7 @@ charm: ceph-rbd-mirror channel: squid/stable num_units: 1 - to: [lxd:10] + to: [lxd:0] bindings: '': metal-admin ceph-local: replication @@ -927,7 +945,7 @@ charm: memcached channel: latest/stable num_units: 1 - to: [lxd:8] + to: [lxd:0] bindings: '': metal-admin cache: metal-internal diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index f832c69..6634333 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -159,17 +159,38 @@ injecting a phantom `dashboard` binding -> `juju deploy` reject. Orphan removed; `yaml.safe_load` confirms the correct set restored; `provider-bundle-check.py` PASS. Pre-existing latent defect (app was prep-only, never deployed). - - **NEXT:** (1) commit+push this ruling batch (GA-R5: rulings land before - dependent work); (2) gated: add per-role MAAS tags (control/compute/storage) + - author the 10th controller VM in `opentofu/vr1-dc1-substrate` (apply with edge - egress OPEN); (3) render the 9-node role-separated `bundle.yaml` -- machines - block; RE-HOME the base placements (mysql/ovn/ceph-mon `lxd:8,9,10` -> control - IDs; nova-compute 3->2 -> compute IDs; ceph-osd -> the 4 storage IDs; singleton - utilities -> control IDs); re-scope the dc0-named tokens -- + extend the - validator + wire the `vr1-dc1-vips`/`dc-ha-scaleup` overlays + de-stale phase-4 - Step 4; (4) gated `juju bootstrap` (by controller tag, egress OPEN) -> deploy. - Deploy-time VERIFY-LIVE gates for the runbook: vault-3-unit HA-on-MySQL (D-121), - hacluster cluster_count:3, dc1 VIP validation, CRUSH failure-domain=host. + - **DONE 2026-07-24 (committed+pushed):** ruling batch `72cad5f`; validator spine + `c3970a9` (provider-bundle-check overlay-merge + per-DC bands + placement + invariants, harness 15/15); **the 9-node role-separated bundle RENDER** (bundle.yaml + machines "0"-"8" role+DC tags, all `to:` re-homed, nova-compute 3->2, ceph-osd on + 4 storage, ovn-chassis -> the 2 dc1 compute provider MACs; new + `overlays/vr1-dc1-machines.yaml` retag overlay; `dc-ha-scaleup` tokens rendered to + logical 0/1/2). VALIDATED: base + full dc1 deploy input (3 overlays, --dc vr1-dc1) + PASS, harness 15/15, gauntlet ALL GREEN (78), repo-lint 0-fail. Deployment-expansion + review (3 read-only reviewers) recorded `docs/audit/stage5-expansion-review-20260724.md`; + it confirmed relations/bindings/subordinates HOLD under role-sep (uniform 6-NIC, + SEC-011) and no channel/pin change is needed. + - **OPEN before deploy (from the expansion review):** RESOLVE-BEFORE-DEPLOY -- + (a) **vault VIP** (decorative-HA regression: vault scaled 3 + hacluster but NO vip + anywhere; D-036 says vault carries VIPs; needs a dc1-band VIP overlay + the vault-HA + VERIFY-LIVE); (b) **v4-only vs dual-stack phasing** (vr1-dc1-vips v4 and + dc-dc-ipv6-family-matrix v4+v6 both own `vip:` -> per-key REPLACE collision if both + applied; operator decides). FOLLOW-UP scripts/gates -- preflight.sh runs the checker + BARE (must pass the dc1 overlays + --dc vr1-dc1); pre-flight-checks.sh VR0-frozen + (no $DC selector, host_sysid-by-hostname); cloud-assert.sh false-passes decorative HA + on the 12 scaled API services; `juju bootstrap` needs a controller-tag constraint; + phase-4 Step 4 de-stale. VERIFY-LIVE gates: vault 1.8 HA-on-MySQL@3 (D-121), hacluster + cluster_count:3 semantics, Ceph pools size=3/min_size=2/failure-domain=host, + machines-block overlay merge (`--dry-run`). + - **NetBox->deploy coupling PROPOSED -> Chat (design seat).** A per-DC renderer/pipeline + that GENERATES the overlays + tfvars from NetBox IPAM (eliminating the hand-render + fragility + the VIP-overlay collision by construction, making the deploy gates + DC-aware) is being scoped as a new PROPOSED D-number (next-free per ledger-scan) in + Chat -- the escalation of the committee's Fork-2 deferred renderer. Prompt handed to + the operator 2026-07-24. + - **THEN, gated (per the D-104-amendment + the open items above):** per-role MAAS tags; + author + apply the 10th controller VM (egress OPEN); resolve the vault-VIP + phasing; + fix the deploy-gate scripts; `juju bootstrap` (controller tag, egress OPEN) -> deploy. Separately: DC0 full-mirror completes -> reachability gate -> stage close-out. - Position inside Stage 3: deploy step A EXECUTED 2026-07-19 (6/0/6 exact; convergence zero -- diff --git a/docs/audit/stage5-expansion-review-20260724.md b/docs/audit/stage5-expansion-review-20260724.md new file mode 100644 index 0000000..2c11628 --- /dev/null +++ b/docs/audit/stage5-expansion-review-20260724.md @@ -0,0 +1,114 @@ +# Deployment-expansion review -- vr1-dc1 (4-node hyperconverged -> 9-node role-separated) + +**Date:** 2026-07-24. **Branch:** `dc-dc-stage4-phase3-maas-deploy`. +**Convened by:** operator ("Have subagents deploy a deployment script review for +additional cross connections, updates, associations, and pinning updates that are +also needed based on the stack expansion"). + +**Status:** ADVISORY / audit record. Findings dispositioned below (handled in the +render / follow-up / VERIFY-LIVE / no-change). Adopts nothing. `docs/CURRENT-STATE.md` +remains the status authority (GA-R1). Companion to +`docs/audit/committee-20260724-track2-bundle-render.md`. + +Three independent read-only reviewers (relations/bindings/cross-DC; config/pins/channels; +deployment-scripts). Every load-bearing claim was re-checked against the repo before +recording. + +## A. Handled IN the render (this session; validated) + +- **ovn-chassis provider MACs** -- `bundle.yaml` carried VR0's 4 `openstack0-3` + provider MACs (`52:54:00:*`); re-homed to the 2 dc1 COMPUTE nodes' provider-public + (enp2s0) MACs `52:54:01:d1:04:02` / `52:54:01:d1:05:02` (ovn-chassis is subordinate + to the 2-unit nova-compute). NOTE: these are the substrate-PINNED values (scheme + `52:54:01:d1:NN:PP`, provider = NIC ordinal `:02`, `opentofu/vr1-dc1-substrate/main.tf`) + -- config-carried as-built, not inferred; still confirm at `juju deploy --dry-run`. +- **nova-compute num_units 3 -> 2** (2 compute nodes, D-121 Option C). +- **ceph-osd = 4** was already correct (matches R-3's 4 storage nodes); the stale + "ceph-osd 3" comment in `overlays/dc-ha-scaleup.yaml` was corrected to 4. +- **provider-bundle-check.py** ovn-chassis check: skip the VR0 Pattern-A MAC set on a + role-separated bundle (verify present + well-formed instead). + +## B. Follow-up (deploy scripts / gates -- NOT render-blocking, needed before their gate) + +- **`preflight.sh` gates the wrong artifact** -- runs `provider-bundle-check.py bundle.yaml` + BARE (base bundle, default dc0 bands, role tags absent so placement self-skips). Must + invoke the real dc1 deploy input: `--overlay overlays/vr1-dc1-machines.yaml --overlay + overlays/vr1-dc1-vips.yaml --overlay overlays/dc-ha-scaleup.yaml --dc vr1-dc1` (the + checker already supports this; only the call site is VR0-frozen). RENDER-ADJACENT. +- **`pre-flight-checks.sh` is VR0-frozen** -- never calls the `$DC` selectors; VIP band + vars are dc0 (and `unset` under the vr1-dc1 lib-net arm -> trips `set -u`); host loops + resolve by hostname (`host_sysid`) but VR1 MAAS hostnames are random -- must use + `host_sysid_by_bootmac "${HOST_BOOT_MAC[$h]}"`. Fails every dc1 node as written. +- **`cloud-assert.sh` false-passes decorative HA** -- only quorum-checks mysql (A2) + + ovn-central (A3); the 12 scaled hacluster-fronted API services get no pacemaker-quorum + / VIP-ownership assertion, so a VIP parked on one node (the exact `cluster_count`-still-1 + failure D-121 targets) passes clean. Needs a per-hacluster "3 online / VIP owned" check + before the post-deploy acceptance gate. +- **`juju bootstrap` unconstrained** (phase-4 Step 2) -- with 9 Ready nodes tagged + `openstack-vr1-dc1` in a region that sees both DCs, an unconstrained bootstrap can grab + a bundle node. Needs a distinct controller tag + `--constraints tags=` + (pairs with the D-104-amendment 10th VM). +- **phase-4 runbook drift** -- claims `lib_hosts_select_dc vr1-dc1` "fails loud until + populated"; it was populated 2026-07-23. Fold into the Step-4 de-stale. +- **Cross-DC (D-108) only the LOCAL leg wired** -- rbd-mirror `ceph-remote` (peer ceph-mon) + and radosgw multisite (realm/zonegroup/zone + sync) are absent; both are cross-model / + cross-controller (D-104) and staged as DR follow-ups per D-108's "prove one-way first". + +## C. VERIFY-LIVE gates (into the deploy runbook; not render changes) + +- **vault 1.8/stable real HA-on-MySQL at 3 units** (D-121's own GATE) -- confirm leader + election on the MySQL backend before commit; if it fails, evaluate Raft -> D-068, NEVER + fall back to etcd. +- **hacluster 2.4/stable `cluster_count`** -- confirm it means "peers required before VIP + binds"; else the VIP can bootstrap off one node (decorative HA). +- **Ceph pools land at defaults size=3 / min_size=2 / failure-domain=host** -- not set + anywhere; the D-121 R-3 rebuild-headroom argument silently depends on these holding on 4 + storage nodes. Post-deploy assertion. +- **ovn-chassis dc1 compute provider MACs** present locally (section A). + +## D. RESOLVE-BEFORE-DEPLOY (operator decisions; VIP-overlay layer, separate from the render) + +- **Vault VIP (decorative-HA regression).** `dc-ha-scaleup.yaml` scales vault -> 3 + + `vault-hacluster cluster_count: 3` + re-adds `[vault:ha, vault-hacluster:ha]`, but NO + `vip:` is set on vault anywhere (base or overlays) -- it was dropped with BUNDLEFIX-002 + when vault was de-HA'd. D-036 lists vault among the apps carrying provider+metal VIPs. + Without a VIP, kv consumers (barbican-vault -> `vault:secrets`) get one unit's + `vault_url`, not a stable HA endpoint = decorative HA. Fix = add a dc1-band vault VIP in + an overlay (needs a band octet) + the vault-HA VERIFY-LIVE (section C). designate is + VIP-less BY DESIGN (dnsaas), not a regression -- the contrast confirms the class. +- **v4-only vs dual-stack phasing.** `overlays/vr1-dc1-vips.yaml` (v4-only, concrete dc1 + bands) and `overlays/dc-dc-ipv6-family-matrix.yaml` (v4+v6, unrendered `{{TOKEN}}`s) BOTH + own `vip:` for the same 10 apps; overlay merge is per-key REPLACE, so applying both = + last-wins (dual-stack lost, or literal tokens deployed). Only bites if both are applied + together: a v4-first phase-4 deploy uses only vr1-dc1-vips (no collision). Operator + decides whether dc1 phase-4 is v4-only (defer IPv6) or dual-stack (forces reconciling + the two overlays into ONE vip string per app now). +- **machines-block overlay merge (`vr1-dc1-machines.yaml`)** -- documented juju behavior, + but no offline merger exists to prove it here; confirm at `juju deploy --dry-run`. + Fallback: inline the dc1 tags into a rendered dc1 bundle. + +## E. Confirmed CORRECT under the expansion (no change) + +- **All app relations / space bindings / subordinate associations HOLD under role + separation** -- because all 9 role nodes get the uniform 6-NIC set (SEC-011, uniform-NIC + ruling); ceph clients on control LXD, `octavia:ovsdb-cms -> data-tenant` + + `ovn-chassis-octavia` on control, ovn-chassis on the 2 compute nodes -- all satisfied. + (Caveat: holds ONLY while the uniform-6-NIC decision stands.) +- **No channel/pin change** required by 3-unit HA or role separation (D-002 pins hold). +- **`lib-hosts.sh` vr1-dc1 arm** is complete + measured (9 role nodes, octet bands, boot + MACs, per-DC tag) -- not a placeholder. +- **`provider-bundle-check.py` placement section** is role-aware + harnessed (this session). +- **`cloud-assert.sh` A2/A3** already expect 3 units (mysql/ovn). `ceph-osd=4`, + `expected-osd-count: 4`, `monitor-count: 3`, `reserved-host-memory: 8192` (D-040, ruled + retained) all correct. +- **dc-dc phase-3/4 runbooks are discover-live** (do not hardcode a node count); the + `openstack0-3` loops live only in the VR0 teardown/reenroll path, not the dc1 deploy path. + +## F. Design signal + +These findings motivate a NetBox->deploy coupling (a per-DC renderer/pipeline that +GENERATES the overlays + tfvars from NetBox IPAM, making the deploy gates DC-aware and +eliminating the hand-render fragility + the VIP-overlay collision by construction). Being +scoped as a new PROPOSED D-number (NetBox-deploy coupling; next-free per ledger-scan) in +the design-discussion seat (Chat). This is the escalation of the committee's Fork-2 +(deferred renderer tool). diff --git a/docs/changelog-20260724-stage5-committee-fork1.md b/docs/changelog-20260724-stage5-committee-fork1.md index 5fc78d3..8125078 100644 --- a/docs/changelog-20260724-stage5-committee-fork1.md +++ b/docs/changelog-20260724-stage5-committee-fork1.md @@ -91,6 +91,45 @@ **Revert:** `git checkout ~ -- scripts/provider-bundle-check.py tests/provider-bundle-check/run-tests.sh`. +## Item 7 -- vr1-dc1 bundle render (9-node role-separated) + retag overlay + +**What:** rendered `bundle.yaml` from VR0's 4-node hyperconverged layout to VR1 dc1's +9-node role-separated one: machines block "8"-"11" (`tags=openstack`) -> "0"-"8" with +role+DC tags (block style -- flow `{...}` would split the comma-tag); every app `to:` +re-homed (mysql/ovn-central/ceph-mon -> control 0/1/2; nova-compute 3->2 on compute 3/4; +ceph-osd -> storage 5/6/7/8; the 14 scaled apps -> control placeholder that the overlay +spreads; utilities -> control); ovn-chassis MACs -> the 2 dc1 compute provider NICs. +Rendered `overlays/dc-ha-scaleup.yaml` tokens -> logical control ids 0/1/2 (now +DC-neutral) + updated its header + fixed the stale ceph-osd-3 comment. New +`overlays/vr1-dc1-machines.yaml` (per-DC retag; machines-overlay merge is VERIFY-LIVE at +`juju deploy --dry-run`). +**Why:** the committee-adjudicated render (Fork 2 = hand-render now); unblocks the dc1 +Stage-5 deploy. Done as an asserted one-shot line-transform (preserves all D-NNN comments) +reviewed via git diff + the validator. +**Validation:** `provider-bundle-check.py bundle.yaml` (base standalone) PASS; the FULL dc1 +deploy input (`--overlay vr1-dc1-machines --overlay vr1-dc1-vips --overlay dc-ha-scaleup +--dc vr1-dc1`) PASS; harness 15/15; gauntlet ALL GREEN (78); repo-lint 0-fail. The +validator CAUGHT a real render bug (YAML flow-comma dropping the role tag) -- exactly its +purpose. +**Revert:** `git checkout ~ -- bundle.yaml overlays/dc-ha-scaleup.yaml` + +`git rm overlays/vr1-dc1-machines.yaml`. + +## Item 8 -- provider-bundle-check ovn-chassis role-sep branch + expansion review record + +**What:** (a) `provider-bundle-check.py` -- computes `role_sep` early; the ovn-chassis MAC +check now skips the VR0 openstack0-3 Pattern-A set on a role-separated bundle (verifies +present + well-formed instead), since dc chassis are the compute-node provider MACs. +(b) new `docs/audit/stage5-expansion-review-20260724.md` -- three read-only reviewer +verdicts (relations/bindings/cross-DC; config/pins; deploy-scripts), dispositioned +(handled / follow-up / VERIFY-LIVE / resolve-before-deploy / no-change). +**Why:** the operator-requested deployment-script expansion review; captures the +cross-connection/pinning/gate findings durably so the Chat design session (NetBox-deploy +coupling) reads them from the repo. +**Validation:** harness 15/15 (T9's role-sep fixture exercises the ovn role-sep branch); +gauntlet ALL GREEN (78). +**Revert:** `git checkout ~ -- scripts/provider-bundle-check.py` + +`git rm docs/audit/stage5-expansion-review-20260724.md`. + ## Next (not done this session; gated) Commit+push this batch (GA-R5: rulings land before dependent work). Then, each diff --git a/overlays/dc-ha-scaleup.yaml b/overlays/dc-ha-scaleup.yaml index 4262fb9..18ad8c8 100644 --- a/overlays/dc-ha-scaleup.yaml +++ b/overlays/dc-ha-scaleup.yaml @@ -16,19 +16,14 @@ # per-DC bundle deploy is two stages out). No live cloud reached this session. # # ===================================================================== -# ##### STAGE-4 BINDING REQUIRED -- READ BEFORE RENDER ##### -# Every `to:` target below is a {{TOKEN}} placeholder for one of the three -# Option C control nodes (vr1-dc0-control-01..03, D-121). The REAL Juju machine -# IDs do NOT exist yet -- they bind at Stage 4, AFTER MAAS enlists the Option C -# node VMs. Do NOT invent concrete `lxd:N` numbers here: a wrong number -# silently mis-places a unit and defeats the anti-affinity that is the entire -# HA value. Render this file (substitute the three tokens with the measured, -# enlisted control-node machine IDs) ONLY at Stage 4/5, exactly as -# dc-dc-ipv6-family-matrix.yaml's VIP tokens are rendered. The rendered -# bundle.yaml `machines:` block MUST define those same three IDs. -# {{VR1_DC0_CONTROL_01}} -> control node 1 machine id -# {{VR1_DC0_CONTROL_02}} -> control node 2 machine id -# {{VR1_DC0_CONTROL_03}} -> control node 3 machine id +# ##### RENDERED 2026-07-24 -- tokens bound to logical control ids 0/1/2 ##### +# The three {{VR1_DC0_CONTROL_0N}} tokens are RENDERED to the bundle.yaml machines +# block's LOGICAL control ids (0,1,2 -- the 3 control nodes, D-121 Option C). These +# ids are DC-neutral (both dc0 and dc1 use the same logical machines block; the DC +# tag is applied per-DC via overlays/vr1-dc1-machines.yaml), so this overlay is now +# DC-agnostic. The positional 3-entry `to:` lists give one-per-control-node +# anti-affinity (unit0->0, unit1->1, unit2->2); provider-bundle-check.py enforces the +# 3-distinct-machine spread on the MERGED bundle, so a token/id collision is caught. # ===================================================================== # # PLACEMENT / ANTI-AFFINITY (D-121 balance rule). Juju bundles have NO dedicated @@ -81,7 +76,7 @@ # # NOT changed here (deliberately, per D-121): mysql-innodb-cluster / ovn-central # / ceph-mon (already 3 in the base bundle); ceph-osd / nova-compute (scale-out, -# sized to node count -- Option C = ceph-osd 3, nova-compute 2, on the storage / +# sized to node count -- Option C = ceph-osd 4 (D-121 R-3 amendment), nova-compute 2, on the storage / # compute nodes, NOT control-node containers); ceph-rbd-mirror stays 1 (D-108); # memcached / designate-bind / glance-simplestreams-sync unchanged. # ===================================================================== @@ -92,51 +87,51 @@ # Each: 1 -> 3 units, one per control node (positional anti-affinity). keystone: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] glance: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] neutron-api: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] nova-cloud-controller: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] placement: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] cinder: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] openstack-dashboard: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] octavia: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] barbican: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] magnum: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] designate: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] ceph-radosgw: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] # ---- RabbitMQ: native quorum clustering, NO hacluster. min-cluster-size is # MANDATORY at multi-unit (D-009 AMENDMENT 2026-07-02, item A above). ---- @@ -144,13 +139,13 @@ num_units: 3 options: min-cluster-size: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] # ---- Vault: MySQL-backed (v-a), scale to 3, re-HA'd via vault-hacluster # (defined below) + the relation re-added at the foot of this file. ---- vault: num_units: 3 - to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"] + to: ["lxd:0", "lxd:1", "lxd:2"] # ---- vault-hacluster: RE-DECLARED (item C above). Removed by BUNDLEFIX-002 # when vault was de-HA'd; D-121 (v-a) brings it back. Shape matches the diff --git a/overlays/vr1-dc1-machines.yaml b/overlays/vr1-dc1-machines.yaml new file mode 100644 index 0000000..ceac025 --- /dev/null +++ b/overlays/vr1-dc1-machines.yaml @@ -0,0 +1,39 @@ +# overlays/vr1-dc1-machines.yaml +# +# Retag the 9-machine block from the vr1-dc0 base to vr1-dc1 for the dc1 deploy. +# ONE Office1 MAAS region sees BOTH DCs' nodes (D-104/D-123 single region), so the +# machines-block constraint must carry the DC-scoping tag or dc1's model could grab a +# dc0 node. The DC tag (openstack-vr1-dc1) selects THIS DC's nodes; the role sub-tag +# (control/compute/storage, applied at Stage 4 per Fork-3) selects the role. The base +# bundle.yaml is the vr1-dc0 source of truth (matches overlays/vr1-dc1-vips.yaml); +# this overlay + the VIP overlay are the two per-DC deltas for dc1. +# +# Applied (with the HA scale-up + VIP overlays): +# juju deploy ./bundle.yaml \ +# --overlay overlays/vr1-dc1-machines.yaml \ +# --overlay overlays/vr1-dc1-vips.yaml \ +# --overlay overlays/dc-ha-scaleup.yaml -m "${DC1_MODEL}" +# +# VERIFY-LIVE: confirm the machines-block override merges as expected at +# `juju deploy --dry-run` BEFORE deploy (no offline juju merger exists to prove it +# here). If it does not take, the fallback is inlining these dc1 tags into a rendered +# dc1 bundle (cheap) -- see committee-20260724-track2-bundle-render.md, Fork 2. +machines: + "0": + constraints: arch=amd64 tags=openstack-vr1-dc1,control + "1": + constraints: arch=amd64 tags=openstack-vr1-dc1,control + "2": + constraints: arch=amd64 tags=openstack-vr1-dc1,control + "3": + constraints: arch=amd64 tags=openstack-vr1-dc1,compute + "4": + constraints: arch=amd64 tags=openstack-vr1-dc1,compute + "5": + constraints: arch=amd64 tags=openstack-vr1-dc1,storage + "6": + constraints: arch=amd64 tags=openstack-vr1-dc1,storage + "7": + constraints: arch=amd64 tags=openstack-vr1-dc1,storage + "8": + constraints: arch=amd64 tags=openstack-vr1-dc1,storage diff --git a/scripts/provider-bundle-check.py b/scripts/provider-bundle-check.py index 431b1cf..2d029d0 100644 --- a/scripts/provider-bundle-check.py +++ b/scripts/provider-bundle-check.py @@ -113,6 +113,9 @@ sys.stderr.write("ERROR: cannot merge overlay %s: %s\n" % (ov, e)); return 2 PROVIDER, ADMIN, INTERNAL = (ipaddress.ip_network(x) for x in DC_BANDS[args.dc]) apps = (doc or {}).get("applications", {}) or {} + machines = (doc or {}).get("machines", {}) or {} + roles = {str(mid): _role_of(spec) for mid, spec in machines.items()} + role_sep = any(roles.values()) # role-separated (VR1) vs hyperconverged base (VR0) fails, oks = [], [] def pub(s): return ((s or {}).get("bindings", {}) or {}).get("public") @@ -160,11 +163,19 @@ if not bim: continue macs = set(MAC_RE.findall(bim.lower())) - missing = EXPECT_CHASSIS_MACS - macs - if missing: - fails.append("%s missing chassis MAC(s): %s" % (n, ", ".join(sorted(missing)))) + if role_sep: + # VR1 role-separated: the VR0 openstack0-3 Pattern-A set (D-057/D-060) does not + # apply -- dc chassis are the compute-node provider MACs. Verify present + well-formed. + if macs: + oks.append("%s bridge-interface-mappings: %d well-formed MAC(s) (role-sep; VR0 set N/A)" % (n, len(macs))) + else: + fails.append("%s bridge-interface-mappings has no valid MAC" % n) else: - oks.append("%s bridge-interface-mappings: all 4 chassis MACs present (incl openstack0)" % n) + missing = EXPECT_CHASSIS_MACS - macs + if missing: + fails.append("%s missing chassis MAC(s): %s" % (n, ", ".join(sorted(missing)))) + else: + oks.append("%s bridge-interface-mappings: all 4 chassis MACs present (incl openstack0)" % n) # -- 4. relations: existing apps, explicit endpoints (the magnum-shared-db class) -- rels = (doc or {}).get("relations", []) or [] @@ -235,9 +246,7 @@ # un-rendered bundle has none, so the checks self-skip (base stays green). # Run on the MERGED bundle (base + overlays) to see the scaled apps' real # num_units/to:. Catches the decorative-HA paths juju itself won't error on. - machines = (doc or {}).get("machines", {}) or {} - roles = {str(mid): _role_of(spec) for mid, spec in machines.items()} - if any(roles.values()): + if role_sep: pf0 = len(fails) n_ctl = sum(1 for r in roles.values() if r == "control") n_cmp = sum(1 for r in roles.values() if r == "compute")