# overlays/dc-ha-scaleup.yaml
#
# D-121 (ADOPTED IN PART, 2026-07-15) VR1 HA scale-up overlay -- makes the
# decorative single-unit control plane REAL by scaling 14 services from
# num_units: 1 to 3. VR1-SPECIFIC: VR0/testcloud stays single-unit BY DESIGN
# (D-009), so this is an overlay, NOT a bundle.yaml edit -- same pattern as
# overlays/dc-dc-ipv6-family-matrix.yaml. Apply ALONGSIDE bundle.yaml's other
# overlays, e.g.:
# juju deploy ./bundle.yaml \
# --overlay overlays/octavia-pki.yaml \
# --overlay "overlays/${DC}-hostnames.yaml" \
# --overlay overlays/dc-ha-scaleup.yaml \
# -m "${DC_MODEL}"
#
# NOT YET APPLIED to any live model -- authored prep-only (STAGE 5 prep; the
# 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
# =====================================================================
#
# PLACEMENT / ANTI-AFFINITY (D-121 balance rule). Juju bundles have NO dedicated
# anti-affinity primitive; one-per-node is expressed POSITIONALLY -- a 3-entry
# `to:` list binds unit0->entry0, unit1->entry1, unit2->entry2. Every scaled app
# below uses the SAME three-control-node spread, so:
# - each app lands exactly one unit per control node (14 apps x 1 = ~14 API
# LXD containers per control node -- the even ~14/node density the 64 GiB
# control-node sizing was validated for, D-121); and
# - the RAM-heavy stateful trio (mysql-innodb-cluster already 3 in the base
# bundle, rabbitmq-server + vault scaled here) auto-balances to one replica
# per control node -- never two stacked on one node.
#
# THREE ITEMS BELOW GO BEYOND A LITERAL `num_units: 3` BUMP. They are NOT
# scope-creep: each is the same class of "mechanical 1->3 hides a gotcha" that
# D-121 itself flags (D-121 caught the placement rewrite; these are the two it
# under-states plus the one its own wording got wrong), and an overlay titled
# "make HA real" that shipped decorative HA would not meet its stated purpose.
# All three are called out in the delivery changelog, not slipped in:
# (A) rabbitmq-server min-cluster-size: 3. Per D-009 AMENDMENT (2026-07-02):
# "rabbitmq scale-up is NOT mechanical without min-cluster-size" -- without
# it the charm accepts client relations BEFORE the cluster forms (same race
# as the D-062 mysql formation failure). Roosevelt/VR1 delta = set it
# alongside num_units: 3. rabbitmq clusters natively (quorum), NO hacluster.
# (B) hacluster cluster_count: 3 (all 12 active hacluster subs + the re-added
# vault-hacluster). The base bundle pins cluster_count: 1 -- explicitly
# "decorative on single-unit testcloud" (D-009 / BUNDLEFIX-003). At 3 units
# with cluster_count still 1, pacemaker can bootstrap the VIP off a single
# node = the SAME decorative behavior D-121 exists to eliminate. 3 makes
# the charm wait for a real 3-node quorum before configuring resources.
# VERIFY at deploy (not fetched this session): confirm the hacluster
# 2.4/stable charm treats cluster_count as "peers required before the
# cluster forms / VIP binds" -- the base bundle explicitly pinned it to 1
# as decorative, and real 3-node quorum needs 3 regardless of the charm
# default. Flag if `juju config hacluster` disagrees.
# (C) vault-hacluster is DEFINED here, not just related. D-121's prose says it
# "exists among the hacluster subs" -- it does NOT: bundle.yaml line ~923
# has it COMMENTED OUT (removed by BUNDLEFIX-002 when vault was de-HA'd,
# C1). So the re-added `[vault:ha, vault-hacluster:ha]` relation would
# reference a non-existent app (provider-bundle-check rule 4 would FAIL).
# This overlay re-declares it, matching the other haclusters' shape.
#
# VAULT BACKEND (D-121 sub-ruling = v-a, RESOLVED 2026-07-15): vault stays
# MYSQL-backed (on the already-3-unit mysql-innodb-cluster via vault-mysql-router,
# unchanged) and scales to 3 -- NO etcd, NO Raft here. GATE (Stage 5, before
# commit): verify the deployed vault 1.8/stable charm actually does HA
# leader-election on the MySQL storage backend at 3 units. If it does NOT: do
# NOT fall back to etcd -- evaluate Raft integrated storage and route to D-068.
# 3-unit vault ALSO triples the manual-unseal burden (intersects OPEN SEC-003).
#
# 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 /
# compute nodes, NOT control-node containers); ceph-rbd-mirror stays 1 (D-108);
# memcached / designate-bind / glance-simplestreams-sync unchanged.
# =====================================================================
applications:
# ---- Clustered API charms (hacluster + dual VIP already wired in base) ----
# 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}}"]
glance:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
neutron-api:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
nova-cloud-controller:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
placement:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
cinder:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
openstack-dashboard:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
octavia:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
barbican:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
magnum:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
designate:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
ceph-radosgw:
num_units: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
# ---- RabbitMQ: native quorum clustering, NO hacluster. min-cluster-size is
# MANDATORY at multi-unit (D-009 AMENDMENT 2026-07-02, item A above). ----
rabbitmq-server:
num_units: 3
options:
min-cluster-size: 3
to: ["lxd:{{VR1_DC0_CONTROL_01}}", "lxd:{{VR1_DC0_CONTROL_02}}", "lxd:{{VR1_DC0_CONTROL_03}}"]
# ---- 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}}"]
# ---- 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
# other hacluster subs in bundle.yaml, with cluster_count: 3 (real HA). ----
vault-hacluster: {charm: hacluster, channel: 2.4/stable, options: {cluster_count: 3}, bindings: {'': metal-admin, ha: metal-internal, hanode: metal-internal, pacemaker-remote: metal-internal, peer-availability: metal-internal}}
# ---- cluster_count: 1 -> 3 for the 12 active hacluster subs (item B above).
# Overlay apply deep-merges options per-key -- the SAME mechanism
# dc-dc-ipv6-family-matrix.yaml already relies on (it supplies only
# `options:` for keystone et al. and depends on charm/channel/bindings
# surviving from base). So ONLY cluster_count is overridden here; each
# sub's base bindings/charm/channel are preserved. ----
keystone-hacluster: {options: {cluster_count: 3}}
glance-hacluster: {options: {cluster_count: 3}}
neutron-api-hacluster: {options: {cluster_count: 3}}
nova-cloud-controller-hacluster: {options: {cluster_count: 3}}
placement-hacluster: {options: {cluster_count: 3}}
openstack-dashboard-hacluster: {options: {cluster_count: 3}}
cinder-hacluster: {options: {cluster_count: 3}}
octavia-hacluster: {options: {cluster_count: 3}}
barbican-hacluster: {options: {cluster_count: 3}}
magnum-hacluster: {options: {cluster_count: 3}}
ceph-radosgw-hacluster: {options: {cluster_count: 3}}
designate-hacluster: {options: {cluster_count: 3}}
relations:
# ---- Re-add vault HA (removed by BUNDLEFIX-002; D-121 v-a re-adds it).
# vault already carries the `ha: metal-internal` binding in the base
# bundle, and vault-hacluster is declared above. ----
- [vault:ha, vault-hacluster:ha]