# 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]
