# overlays/vr1-dc0-machines.yaml
#
# The vr1-dc0 half of the hand-maintained per-DC overlay pair. Created 2026-07-29
# (item 3.8). Its sibling is overlays/vr1-dc1-machines.yaml.
#
# WHY THIS FILE EXISTS AT ALL, given that bundle.yaml's `machines:` block already
# carries dc0's tags: `overlays/${DC}-machines.yaml` is a path that TWO consumers
# already name -- scripts/preflight.sh's P2 overlay assembly and
# runbooks/dc-dc-phase6-designate-cos-magnum.md. Both guard it with `[ -f ]` or the
# equivalent, so while the dc0 file did not exist they SILENTLY SKIPPED it: P2 went
# green having validated a different input than the deploy would use. Creating the
# file closes that hole from the artifact side rather than by adding another guard.
#
# THIS OVERLAY IS `applications:`-ONLY -- IT RETAGS NOTHING.
# bundle.yaml's machines block is the vr1-dc0 source of truth and already reads
# `tags=openstack-vr1-dc0,<role>` for ids 0-8, so dc0 needs no machines-block delta;
# only dc1 does (that is what its sibling overlay is for). Do not add a `machines:`
# block here to make the pair look symmetric -- it would duplicate the base values in
# a second place and create a drift surface for no gain. The filename is therefore
# slightly broader than "machines": it is the per-DC HAND-MAINTAINED overlay. Renaming
# it would require editing scripts/preflight.sh, which this file does not own.
#
# ---- ovn-chassis bridge-interface-mappings: WHY IT LIVES HERE ----
# MOVED OUT OF bundle.yaml 2026-07-29 (item 3.8). The base bundle carried vr1-DC1's
# MACs while being documented as dc0's source of truth, so a dc0 deploy matched no
# local MAC, ovn-chassis built no br-ex mapping, provider egress was dead on dc0
# compute, and no gate failed (provider-bundle-check only COUNTS well-formed MACs; it
# never checks the prefix, and `if not bim: continue` makes ABSENCE a silent skip).
# Governing text: the D-124 coupling entry's 2026-07-27 correction in
# docs/design-decisions.md -- "ovn-chassis bridge-interface-mappings is a PER-DC value
# and must become renderer OUTPUT into the per-DC overlay ... It must also move OUT of
# the base bundle for the 2026-07-25 symmetric-overlay ruling to be truthful."
# It is placed in the MACHINES overlay, not the vips overlay, because
# overlays/vr1-dc0-vips.yaml is GENERATED by scripts/render-dc-overlays.py and says
# "do not hand-edit" -- a key added there is clobbered on the next render. When the
# renderer is extended to emit this value (the "renderer OUTPUT" half of the same
# correction), it should emit INTO this file, not into the vips file.
#
# ---- THE MAC VALUES: MEASURED, AND NOT WHERE TWO REPO RECORDS SAY THEY ARE ----
# vr1-dc0's MACs are NON-schematic (measured 52:54:00:* values), unlike dc1's derivable
# 52:54:01:d1:NN:PP scheme, so every one of them has to be read from a capture.
#
# SOURCE OF TRUTH: opentofu/vr1-dc0-substrate/main.tf, the `vr1_dc0_nodes` map. Its
# own comment fixes the ordering: "Order = vr1_dc0_node_nics order (metal-admin,
# provider-public, metal-internal, data-tenant, storage, replication)" -- metal-admin
# FIRST (:77 "metal-admin FIRST = PXE/boot plane"). So the provider-public MAC is
# element [1], the SECOND entry, never the first:
# vr1-dc0-compute-01 (main.tf:109-112) -> [0] 52:54:00:1b:19:e6 metal-admin/PXE
# [1] 52:54:00:8c:2a:8c provider-public <-- used
# vr1-dc0-compute-02 (main.tf:113-116) -> [0] 52:54:00:18:ab:b4 metal-admin/PXE
# [1] 52:54:00:50:48:88 provider-public <-- used
#
# CROSS-CHECKED against two independent captures:
# docs/audit/inner-plan-20260721-macpin.txt:36-72 / :85-123 -- the tofu plan that
# pinned them, listing each domain's interfaces in vr1_dc0_node_nics order.
# docs/audit/stage5-committee-raw-20260727.md:323 -- live MAAS, 18/18 nodes:
# "`enp1s0..enp6s0` + `br-ex`. `enp2s0` correctly carries no link (D-100
# provider-raw); `br-ex` is a bridge over `enp2s0`". NIC position 2 = provider-
# public = the br-ex member. This is the same positional slot dc1 encodes as `:02`.
#
# !! TWO REPO RECORDS ARE WRONG ABOUT THIS AND WILL MISLEAD THE NEXT READER !!
# - docs/audit/stage5-unmeasured-register-20260727.md, row U5, states dc0's
# "compute provider-public MACs" are 52:54:00:1b:19:e6 / 52:54:00:18:ab:b4 and
# notes they match scripts/lib-hosts.sh:121-122. They DO match lib-hosts -- but
# lib-hosts.sh:121-122 is HOST_BOOT_MAC, the metal-admin/PXE NIC ([0]), NOT
# provider-public. U5 labels the boot MACs as provider MACs.
# - the D-124 coupling correction says this value should be "sourced from
# lib-hosts.sh". lib-hosts.sh carries ONLY HOST_BOOT_MAC and therefore CANNOT
# source it; the substrate main.tf is the only in-repo source for NIC [1].
# Taking either record at face value would have put the PXE MAC in br-ex -- br-ex
# bridged onto the boot/metal-admin NIC instead of the provider NIC. Both are logged
# as findings; neither file is edited here.
#
# The charm picks whichever MAC is local per unit and ignores non-matching entries;
# ovn-chassis is subordinate to the 2-unit nova-compute, so exactly these two compute
# hosts carry br-ex. Exactly 2 MACs belong here -- a third means a wrong node.
#
# VERIFY-LIVE (this file is a NEW merge surface -- assert it at `juju deploy --dry-run`
# BEFORE any deploy; no offline juju merger exists to prove it here). The dry-run must
# show base `ovn-chassis` keeping BOTH of its existing options alongside the new one:
# ovn-bridge-mappings: physnet1:br-ex
# prefer-chassis-as-gw: true (B2 -- gateway-chassis election)
# This overlay sets only `bridge-interface-mappings` under `options`. That is safe IF
# juju merges the `options` MAP key-by-key, and unsafe if it REPLACES the map -- in
# which case the physnet mapping and the B2 gateway election both vanish from the
# deploy input and provider egress dies, by a different route than the defect this
# file exists to fix. Key-by-key is near-certain (the whole *-vips.yaml architecture
# depends on it -- those overlays set `options.vip` on apps carrying other base
# options) but it is NOT PROVEN HERE: provider-bundle-check.py's `_deep_merge` merges
# key-by-key by construction, so validating through the checker cannot tell the two
# behaviours apart.
# >>> CORRECTED 2026-08-02 (DOCFIX-208): "Only the live dry-run can" IS WRONG, and a
# reader following it would have asserted nothing. MEASURED at both verbosities
# (docs/audit/stage5-dryrun-dc0-20260802.txt): `juju deploy --dry-run` prints NO
# application options AT ALL -- greps for bridge-interface-mappings, physnet1,
# openstack-origin, cloud-archive and prefer-chassis all return ZERO, and --debug adds
# only the per-machine constraint lines. The merge is provable ONLY after deploy, from
# the deployed application's own config. The assertion now lives at
# runbooks/dc-dc-phase4-juju-bundle-per-dc.md Step 4.4, run the moment `juju deploy`
# returns and before the model settles. Assert it explicitly; do not assume. <<<
#
# Applied as part of the vr1-dc0 per-DC set (R6: this set lands BEFORE
# overlays/dc-ha-scaleup.yaml, never in the same command as it):
# juju deploy ./bundle.yaml \
# --overlay overlays/vr1-dc0-vips.yaml \
# --overlay overlays/vr1-dc0-machines.yaml \
# --overlay overlays/vr1-dc0-octavia-pki.yaml \
# -m "${DC_MODEL}"
applications:
ovn-chassis:
options:
bridge-interface-mappings: >-
br-ex:52:54:00:8c:2a:8c
br-ex:52:54:00:50:48:88
# ---- UCA SOURCE -> THE IN-DC MIRROR (RULED 2026-07-31, GA-R5: "Point origin/source
# at the mirrored UCA, per-DC overlay"). ----
#
# WHY: the charms otherwise add `deb http://ubuntu-cloud.archive.canonical.com/ubuntu
# jammy-updates/caracal main`, which is MEASURED UNREACHABLE from a node (000) under
# D-107's node-level airgap, while the mirrored copy answers 200 with real content.
# juju's `apt-mirror` model-config rewrites only the UBUNTU archive, never the UCA
# source a charm adds -- so this is the only place it can be pointed.
#
# THIS IS A PER-DC VALUE and therefore CANNOT live in bundle.yaml: the mirror address
# differs per DC. dc1 gets NO equivalent block -- its apt-cacher-ng forwards whatever
# URL it is handed, so `cloud:jammy-caracal` keeps working there. That asymmetry is
# D-135's experiment RESULT (full mirror rewrites every non-Ubuntu source; proxy
# rewrites none), not a defect to "fix" for symmetry.
#
# SCOPE DERIVED FROM THE CHARM SCHEMAS, not hand-listed: 15 apps whose charm accepts an
# origin key AND resolves to a UCA pocket -- 12 `openstack-origin` (explicit
# cloud:jammy-caracal or the charm default `caracal`) + 3 ceph `source`. Every other app
# defaults to `distro` (the Ubuntu archive only) and needs nothing.
#
# >>> REFUTED BY THE LIVE DEPLOY, 2026-08-02. THE CLAIM BELOW IS FALSE AND IT BLOCKED
# THE dc0 BUNDLE DEPLOY. DO NOT RESTORE IT. <<<
# The struck claim: "NO `|key` SUFFIX IS NEEDED: measured, the UCA signing key is already
# on the nodes at /etc/apt/trusted.gpg.d/ubuntu-keyring-2012-cloud-archive.gpg, and
# debmirror preserved the upstream Release/InRelease signatures, so the mirrored copy
# verifies as-is." The debmirror half is TRUE -- the signatures are intact. The KEY half
# is false on a freshly-deployed node, and it is the half that matters.
# MEASURED on `ceph-osd/0` at first boot: /etc/apt/trusted.gpg.d/ holds ONLY
# ubuntu-keyring-2012-cdimage.gpg and ubuntu-keyring-2018-archive.gpg. There is NO
# cloud-archive keyring. apt therefore fails the mirrored UCA source:
# W: GPG error: http://10.12.8.4/cloud-archive jammy-updates/caracal InRelease:
# NO_PUBKEY 5EDB1B62EC4926EA
# E: The repository '... InRelease' is not signed.
# subprocess.CalledProcessError: Command '['apt-get','update']' returned exit 100
# -> `hook failed: "install"` on every charm this block repoints.
# THE MECHANISM: that keyring file is shipped by the `ubuntu-cloud-keyring` PACKAGE,
# which the charm installs as a side effect of the `cloud:<series>-<release>` origin
# path. Handing the charm a RAW `deb ...` line bypasses that path, so the key never
# arrives. The original claim was almost certainly measured on a node from the
# 2026-07-31 deploy -- which used `cloud:jammy-caracal` and therefore HAD installed the
# keyring -- and then generalised to a fresh node, where it does not hold. Classic
# instrument-currency: true when taken, an artifact of the prior deploy, false here.
# THE FIX IS THE CHARM-DOCUMENTED ONE, and both halves are quoted from the charms:
# openstack charms -- `openstack-origin` accepts "deb <URL>|<PUB KEY>" (keystone's own
# config description). NO separate `key` option exists on these charms.
# ceph charms -- a SEPARATE `key` option, whose description accepts "a GPG key in
# ASCII armor format, including BEGIN and END markers or a keyid".
# USE THE ASCII-ARMORED BLOCK, NEVER A BARE KEYID: a keyid makes charm-helpers fetch
# from keyserver.ubuntu.com, which a D-107-airgapped node CANNOT reach. The armored
# block is written locally and needs no egress.
# KEY PROVENANCE, measured not downloaded from the internet: the mirror itself serves
# `ubuntu-cloud-keyring` 2021.03.26 at
# http://10.12.8.4/ubuntu/pool/main/u/ubuntu-keyring/ubuntu-cloud-keyring_2021.03.26_all.deb
# (path obtained from `apt-get download --print-uris` ON A NODE -- note the source
# package is `ubuntu-keyring`, NOT `ubuntu-cloud-keyring`), sha512 matching apt's own
# declared digest, and its keyring contains exactly the missing key:
# pub rsa4096/5EDB1B62EC4926EA 2012-08-14
# fingerprint 391A9AA2147192839E9DB0315EDB1B62EC4926EA
# uid "Canonical Cloud Archive Signing Key <ftpmaster@canonical.com>"
# ROOSEVELT DELTA, and the reason this is not just a dc0 bug: ANY airgapped DC that
# mirrors the UCA must carry the archive's TRUST as deliberately as its CONTENT.
# Pointing the URL at a mirror is half the job; the 2026-07-31 ruling did that half.
#
# LOGGED, NOT CHANGED HERE: `ovn-central`'s charm default is `source: zed`, NOT caracal.
# It therefore points at a UCA pocket the dc0 mirror does not carry (jammy-updates/caracal
# only). Repointing it would change its RELEASE, not just its URL, so it is a separate
# question and is left alone.
barbican:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
cinder:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
designate:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
glance:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
keystone:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
magnum:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
neutron-api:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
nova-cloud-controller:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
nova-compute:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
octavia:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
openstack-dashboard:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
placement:
options:
openstack-origin: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
ceph-mon:
options:
source: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
ceph-osd:
options:
source: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"
ceph-radosgw:
options:
source: "deb http://10.12.8.4/cloud-archive jammy-updates/caracal main"