Newer
Older
openstack-caracal-dc-dc / docs / dc-dc-deployment-workflow.md

DC-DC (VR1) deployment workflow overview

Purpose. A single followable checklist across the whole VR1 (two-DC virtual regional) buildout, spanning many sessions. docs/dc-dc-buildout-design.md is the WHY/WHAT (goals, gates, D-refs per phase); this doc is the HOW-TO-FOLLOW-IT sequence, cross-referenced to that plan, to D-100..D-110, and to what already exists in this repo vs what still needs to be authored. Update the "state" column as stages complete -- this doc is a tracker, not a one-time snapshot.

Governing constraint (unchanged throughout): minimize delta to Roosevelt. Reuse VR0 DC0 material wherever it transfers; the only deliberate no-Roosevelt-analog steps are the Section 9 shim register in the buildout design (OpenTofu node-VM creation, tc netem WAN simulation, single-unit controllers/rbd-mirror).


Stage 0 -- Decision ratification (BLOCKING)

Nothing in Stage 1 onward starts until this clears. D-100..D-110 are ALL PROPOSED, not ADOPTED (see docs/design-decisions.md).

  1. Operator reviews docs/dc-dc-buildout-design.md in full.
  2. Rule on each of D-100..D-110 individually (ADOPTED / AMENDED / REJECTED) -- not a blanket approval; each carries distinct, separable scope.
  3. Rule on the 6 open-redline items (buildout design Section 10):
    • Ceph size=3 vs a documented size=2 fallback (gated on Phase-0 disk math)
    • tc netem link profile (same-metro lean vs a specific Roosevelt target)
    • Whether D-102 folds into D-101, and whether D-109 stands alone
    • metal-admin admin-API ULA leg (lean: no)
    • COS per-DC-only vs an Office1 roll-up (lean: per-DC only)
    • Mirror sync topology (independent upstream pull per DC vs seed-from-peer)
  4. Record each ruling as an amendment in docs/design-decisions.md (same convention as every prior D-NNN ruling in this repo).

Exit gate: every D-100..D-110 is ADOPTED or explicitly deferred with a documented fallback. bash scripts/ledger-scan.sh should show none of them under "PROPOSED / OPEN decisions" (or only the explicitly-deferred ones).

State: NOT STARTED.


Stage 1 -- Phase 0: vcloud host preparation

Goal Turn the bare vcloud host into a substrate Office1 can deploy from.
Build Nested KVM enabled; libvirt storage pools + thin-provisioning; dark-fiber + per-site ISP-edge virtual networks; the six per-DC plane segments as virtual networks; MTU plan set on the underlay.
Gate Nested KVM verified; disk budget computed + Ceph size decision recorded; measured MTU recorded; virtual networks present and isolated as designed.
Owns D-100 (fabric), D-101/D-102 (MTU), buildout-design Section 3 (host/disk/MTU gates).
Reuse vs new NEW. No VR0-DC0 analog -- that testcloud IS the single vcloud host, already prepared; this is preparing the host to carry TWO independent DC substrates plus Office1.
Authoring status Runbook not started. opentofu/ SCAFFOLD started 2026-07-09: modules/dc-planes (six per-DC planes) + modules/mesh-link (dark-fiber legs) + modules/dc-storage-pool wired for DC1 + Office1 -- see opentofu/README.md for what's built vs deliberately deferred (node-VM/domain resources, DC2 CIDRs, netem). UNVALIDATED: no tofu binary available to run scripts/opentofu-validate.sh yet.

State: NOT STARTED.


Stage 2 -- Phase 1: Office1 headend standup

Goal Stand up the operator's deployment headend first, before any DC.
Build MAAS region controller; OpenTofu; NetBox (vcloud-local, importer extended for multi-DC + dual-stack); GitBucket (vcloud-local mirror); Tailscale front door (Office1 only).
Gate MAAS region reachable; NetBox authoritative + populated (planes, per-DC v4, ULA/GUA carve); GitBucket serving; OpenTofu reaches vcloud host libvirt; Tailscale confirmed to Office1 only.
Owns D-103 (Office1 VMs are OpenTofu-created), D-107 (headend is not a core-service provider).
Reuse vs new NEW infra (Office1 didn't exist for VR0 DC0), but the practices transfer directly: NetBox-as-IPAM-apex discipline, the netbox/*-import.py pattern, and this repo's own git/GitBucket hosting model are all precedent, just extended.
Authoring status Runbook not started. opentofu/modules/base-image + cloudinit-vm (2026-07-09) give the VM-creation mechanism for the MAAS-region/NetBox/GitBucket service VMs -- neither instantiated yet: no image source chosen, and the real user_data/meta_data/network_config content for any of the three VMs hasn't been designed. See opentofu/README.md.

State: NOT STARTED.


Stage 3 -- Phase 2: OpenTofu builds each DC substrate (from Office1)

Goal From Office1, script each DC's "physical" layer into existence.
Build Per DC: OpenTofu creates node-VM libvirt domains (SHIM, Section 9), the DC's plane networks, and its OPNsense edge; registers the DC libvirt host to that DC's MAAS rack controller as a virsh VM-host.
Gate MAAS rack controller per DC up; MAAS discovers the OpenTofu-created node VMs; edge routing + simulated ISP uplink up; netem parameters applied and measured.
Owns D-103 (seam), D-100 (edges).
Reuse vs new NEW (no OpenTofu/multi-rack precedent in VR0 DC0). Explicitly tagged shim: node-VM creation has no Roosevelt analog (physical racking + BMC enlistment replaces it there) -- do not treat as reusable production IaC.
Authoring status Runbook not started. The plane/link/pool layer of opentofu/ (see Stage 1) is reusable here too. modules/node-vm (2026-07-09) covers the MAAS-managed OpenStack node VM pattern (blank disk, PXE-boot) -- not yet instantiated (node count/sizing is a pending Phase-0 decision). Still missing for THIS stage: the virsh-VM-host-to-MAAS-rack registration and the OPNsense edge itself (OPNsense needs the DIFFERENT cloud-init/base-image VM pattern, not node-vm's PXE pattern -- still unbuilt). See opentofu/README.md.

State: NOT STARTED.


Stage 4 -- Phase 3: MAAS enlist / commission / deploy (per DC)

Goal Bring the discovered node VMs to a deployed OS, ready for Juju.
Build Per DC: MAAS commissions + deploys the node VMs; provider NIC left RAW (br-ex discipline, D-100); VLAN-103 metal-internal bridge stack pre-built by MAAS.
Gate Nodes deployed; six planes present per node with correct fabrics/VLANs; provider NIC raw; PXE (v4) working; per-DC mirror reachable; NTP from edge working.
Owns D-103, D-100 (br-ex), D-107 (mirror + NTP).
Reuse vs new PARTIAL reuse. runbooks/phase-00-teardown-maas-reset.md carries the MAAS commission/deploy + br-ex discipline this repo already validated for a single rack; the per-DC multi-rack registration is new, the deploy mechanics are precedent.
Authoring status No dc-dc runbook yet; adapt phase-00's MAAS section per-DC.

State: NOT STARTED.


Stage 5 -- Phase 4: Juju controller + OpenStack bundle (per DC) -- HIGH REUSE

Goal A running independent OpenStack cloud per DC.
Build Per DC: bootstrap the single-unit Juju controller in-DC; deploy the Caracal bundle; Vault init with a per-DC independent root, issuing v4 + IPv6-SAN certs; apply the family matrix (ULA on data/storage/replication, dual-stack metal-internal API, GUA provider).
Gate preflight.sh PASS before add-model; post-deploy cloud-assert.sh --capture per DC; controller backup taken + stored; Ceph-over-v6 and geneve-over-v6 verified.
Owns D-104 (controllers), D-109 (Vault), D-101 (families).
Reuse vs new This is the ground-truth/copy point (docs/dc-dc-buildout-design.md line 12: seeded from openstack-caracal-ipv4 at 8813efc). bundle.yaml and runbooks/phase-01..08-*.md are the per-DC template -- run essentially the existing VR0 DC0 deploy loop TWICE (once per DC), adapted for: independent Keystones (no cross-DC federation), the IPv6 family matrix (D-101/D-102), and per-DC Vault roots (D-109) instead of the single shared root.
Authoring status Existing phase-01..08 runbooks are the starting point; need a per-DC parameterization pass (D-101 family matrix, D-109 Vault) before reuse, not a from-scratch write.

State: NOT STARTED. This is where the existing repo tooling (preflight.sh, cloud-assert.sh, scripts/lib-net.sh/lib-hosts.sh, run-logged.sh) does the most direct work -- confirmed repo-agnostic as of the 2026-07-09 sweep, so they apply to either DC's clone/session without modification.


Stage 6 -- Phase 5: DR wiring and failover drill

Goal Prove Tier-A recoverability, one-way first, then two-way.
Build Replication-plane peering (ULA); radosgw multisite zonegroup (two DCs as zones); rbd-mirror for the Glance pool; cinder-backup to the replicated object store; consistency groups where needed. Run the failover/failback drill.
Gate One-way drill clean (RTO/RPO measured); then two-way; controller restore drill (D-104) exercised; RTO recorded as a measured output, not an SLA.
Owns D-108 (replication), D-104 (control-plane backup/restore in the drill).
Reuse vs new Ground truth: docs/dc-dc-replication-DR-seed.md (the original Tier-A/bidirectional/cinder-backup+rbd-mirror decision) -- D-108 corrected its carrier from the seed's v4 replication plane to IPv6-only ULA. The failover/failback drill skeleton is already drafted (buildout design Section 8): confirm DC1 down -> promote Glance -> restore Cinder -> rebuild instances -> record RTO/RPO; failback is split-brain-safe (no auto-resume, controlled window to flip primary back).
Authoring status Skeleton exists; needs a runbook with command-level steps + harness.

State: NOT STARTED.


Stage 7 -- Phase 6: Designate, COS, Magnum/CAPI (per DC)

Goal Complete the service set in both DCs.
Build Per DC: Designate (designate-bind) with per-DC zones + A/AAAA per the D-008 bootstrap order; COS observability; Magnum/CAPI with in-cloud single-homed capi-mgmt (domain-setup re-run per D-046).
Gate Designate resolving A + AAAA; FQDN-SAN certs valid (D-019/D-021 root cause closed); COS scraping; a CAPI workload cluster comes up per DC.
Owns D-106 (Designate), D-105 (CAPI/COS).
Reuse vs new HIGH REUSE for CAPI/Magnum: runbooks/phase-06-incloud-mgmt-cluster.md + phase-07-conductor-graft.md + phase-08-workload-cluster-acceptance.md are direct precedent, per-DC. Designate is a REVERSAL, not new build: VR0 DC0 deferred it (D-019, "DNS scope reduction"); D-106 reactivates it (supersedes D-019, reactivates the original D-008 bootstrap order) -- read D-008 and D-019 together before writing this stage's runbook, the static-/etc/hosts workaround they document goes away.
Authoring status No dc-dc runbook yet; adapt phase-06/07/08 per-DC, write Designate reactivation fresh.

State: NOT STARTED.


Cross-cutting discipline (applies at every stage, every session)

  • Session bootstrap, every session: git pull -> bash scripts/repo-lint.sh (0 fail / 1 documented legacy WARN expected) -> bash scripts/ledger-scan.sh to reconcile open decisions/next-free numbers against docs/session-ledger.md.
  • Any session that will mutate the cloud: bash scripts/run-logged.sh <label> first (as-executed log).
  • Before any add-model-like mutation: bash scripts/preflight.sh PASS.
  • After any deploy/restart/change, and as an incident-triage first step: bash scripts/cloud-assert.sh (add --capture at real deploy-completion milestones for the committed BOM).
  • Before touching ANY built surface: grep docs/design-decisions.md for the governing D-NNN. PROPOSED/OPEN means the operator has not ruled -- present options, do not implement.
  • Every deliverable (script, runbook, doc): repo-lint clean, the touched script's harness green (write one if missing), a changelog entry with a per-item revert, and a docs/session-ledger.md update.
  • Before assigning a new D-/DOCFIX-/BUNDLEFIX-NNN: bash scripts/ledger-scan.sh for next-free (it is the numbering authority, not repo-lint's informational count).
  • Hard rule reminder: no inferred values -- every IP/ID/name/scope is measured this session or carried from confirmed as-built; verify before mutate, always; destructive/secret-handling steps are individually gated, never batched.

Tooling gap register (audited 2026-07-09; update as items close)

Confirmed by grepping the actual repo (scripts/, tests/, netbox/, runbooks/), not inferred from the design doc alone. Ordered roughly by "you'll hit this first," not by size -- #1 is cheap and unblocks a reuse claim made elsewhere in this doc;

#2 is the single largest build.

  1. $DC selector convention (BLOCKS Stage 5's reuse claim). scripts/lib-net.sh and lib-hosts.sh are hardcoded to ONE deployment's literals (one set of six plane CIDRs, one KEYSTONE_VIP_DEFAULT, a fixed 4-host list) -- confirmed by reading both files directly, no DC parameter exists anywhere in them. Running the "reused" phase-01..08 runbooks against DC2 today would silently reuse DC1's values. Needs the same treatment as the 2026-07-09 $REPO convention: a $DC (or --dc) selector, with both libraries becoming DC-keyed lookups instead of flat constants. Do this BEFORE writing the Stage 5 runbook, or the "high reuse" claim above is not actually true operationally.
  2. OpenTofu -- network/pool/node-VM/cloud-init layer scaffolded 2026-07-09 (was: zero files). opentofu/ now has: modules/dc-planes (six per-DC planes), modules/mesh-link (the D-100 dark-fiber triangle legs), modules/dc-storage-pool, modules/node-vm (blank disk, PXE-boot MAAS-managed node VM, the D-103 pattern for Stage 3), and modules/base-image + modules/cloudinit-vm (the OTHER VM pattern -- pre-built image + cloud-init, for Office1's own service VMs). DC1 + Office1 pool/plane wiring is instantiated in root main.tf; node-vm and cloudinit-vm are NOT (node sizing and Office1 VM image/config choices are both pending decisions -- neither invented here). Provider schema was confirmed against the actual current docs AND real example .tf files (not memory) -- it differs materially from older common examples: nested structures are attribute-style objects (key = { ... }), not classic HCL blocks. A real bug from the first pass was found and fixed: dc-planes/mesh-link/dc-storage-pool originally used block syntax for libvirt_network's domain/mtu and libvirt_pool's target; corrected once real provider examples surfaced the actual convention -- discovered precisely BECAUSE building the domain module forced fetching real example code instead of doc-summaries alone. Still missing, deliberately deferred (see opentofu/README.md): OPNsense's own bootability via cloudinit-vm is NOT confirmed (FreeBSD, not the same cloud-init/NoCloud guarantee as Linux images); the per-device boot-order attribute's exact internal shape (flagged UNVERIFIED in node-vm/main.tf, an inference by pattern not a confirmed field); and tc netem application. UNVALIDATED -- no tofu binary was available to run scripts/opentofu-validate.sh this session; run it on a connected machine before trusting any of this.
  3. NetBox multi-DC/dual-stack import pipeline. netbox/ has exactly two scripts (ipv4-prefixes-import.py, ipv6-mark-reserved.py) -- single-DC v4 import and v6-reservation-marking only. D-101 requires DC2's supernet, the org ULA /48, and per-DC GUA carves to be NetBox-authoritative via "the extended import pipeline" -- that pipeline is not written. Blocks Stage 0's exit condition (literals must be real, not placeholders) and Stage 2.
  4. OPNsense edge deployment/config, and netem WAN-simulation -- (b)/(c) were RESEARCHED 2026-07-09 (opentofu/README.md's dedicated section, sourced against OPNsense's own docs/forum/GitHub), (d) is still a real gap. Each site (Office1, DC1, DC2) gets its own independent OPNsense edge with a simulated ISP uplink (D-100); OpenTofu owns creating the edge VMs (D-103). Blocks Stage 1 (Office1 edge) and Stage 3 (per-DC edges) gates, which require netem parameters "applied and measured."
    • (a) Network substrate -- DONE. opentofu/modules/mesh-link creates the inter-site L2 segments (DC1<->DC2, DC1<->Office1, DC2<->Office1) an OPNsense VM's interfaces would eventually attach to.
    • (b)+(c) The OPNsense VM/image AND its config -- RESEARCHED, confirmed as ONE pattern, not built yet. Cloud-init is confirmed unreliable on OPNsense (FreeBSD) -- OPNsense's own forum consensus, not just a hedge. The real native mechanism is the Configuration Importer (official docs): scans an attached volume for /conf/config.xml in a 2-3 second boot-time window; ISO9660 support was added to it specifically for VM/cloud automation (attach a config ISO as a secondary CD-ROM) -- mechanically IDENTICAL to modules/cloudinit-vm's shape (a secondary volume attached as cdrom), just a different payload (a plain ISO9660 image with /conf/config.xml, not a NoCloud seed). Use the nano image (pre-installed, serial-ready), not the installer images. Still needed to actually build it: a preprocessing script for the nano image's decompress+convert (OpenTofu's volume-download mechanism won't do that itself), a way to build the config.xml ISO, and the actual config.xml content (real design work). No image source formally chosen yet either. Full sourcing in opentofu/README.md.
    • (d) tc netem WAN-simulation -- a wholly separate mechanism, not an OPNsense setting. No Terraform/OpenTofu-native equivalent exists at all (confirmed against the libvirt_network schema, gap #2); it is a Linux tc qdisc setting applied to the host-side bridge interface after the network exists, needing either a provisioner or a small companion script -- neither written. The exact parameters (latency/jitter/loss/ rate) are themselves still an unruled D-100 open sub-item (gap #11), so this needs a decision before it can be scripted, not just scripting.
  5. Ceph replication tooling (radosgw multisite + rbd-mirror). No script in the repo touches either (checked; osd-blank-check.sh is unrelated). D-108's entire mechanism -- zonegroup setup, rbd-mirror daemon peering, the failover/failback drill commands -- is net-new. Blocks Stage 6.
  6. Designate reactivation is a bundle change, not just a runbook. runbooks/phase-01-bundle-deploy.md states the bundle explicitly ships "NO designate (D-019)" -- reactivating it per D-106 means editing bundle.yaml itself (a built-surface change, gated behind D-106 leaving PROPOSED), in addition to writing the reactivation runbook. Blocks Stage 7.
  7. MTU/geneve budget + Ceph disk-budget calculators. D-102 and buildout-design Section 3 both describe measurement-then-computation gates (host L2 MTU -> geneve-over-v6 overhead; measured per-node OSD footprint -> size=3 vs size=2 fallback) that are manual arithmetic in prose today, not a script. Blocks the Stage 1 Phase-0 gates.
  8. The "developed IPv6 plan" D-101 cites does not exist in this repo. D-101's context line references "the developed family-follows-reachability IPv6 plan (in-project, not yet committed)" as prior art -- grepped the whole repo, it is not present as a standalone doc anywhere. If the exact Phase A/B/C detail behind D-101/D-102 is needed by a future session, it currently is NOT retrievable from the repo alone. Not a blocker for Stage 0 ratification of what IS written, but a real content gap if finer detail is needed later.
  9. Runbook debt. Confirmed zero DC-DC-specific runbooks exist. Stage 1, 2, 3, 6 need net-new runbooks; Stage 5 and 7 need an adaptation pass over phase-01..08 / phase-06/07/08 (Stage 5's pass is gated on #1 above).
  10. Harness debt. This repo's own delivery rule -- no script change ships without its tests/<name>/run-tests.sh harness -- applies to everything in #2-7 above. None exist yet because none of those scripts exist yet; expect roughly 5-6 new test suites alongside whatever gets built.
  11. Decision sub-items still "leaning," not ruled, that block the runbooks depending on them even after Stage 0 closes the big items: exact netem parameters (D-100), exact NetBox literals for DC2/ULA/GUA (D-101), whether D-102 folds into D-101, COS per-DC-vs-Office1-rollup (D-105), mirror sync topology (D-107).

Where things stand today (2026-07-09) -- update this as stages close

Layer Status
Design (dc-dc-buildout-design.md) DONE, PROPOSED
Decisions (D-100..D-110) DRAFTED, ALL PROPOSED -- Stage 0 is the very next action
DR mechanism seed DONE (dc-dc-replication-DR-seed.md), superseded-carrier corrected into D-108
Stage 0-7 runbooks NOT YET WRITTEN -- see Tooling gap register above for the itemized breakdown
opentofu/ (networks/pools/node-VM PXE/cloud-init-VM patterns) SCAFFOLDED 2026-07-09, UNVALIDATED (no tofu binary available this session) -- see gap #2 and opentofu/README.md
OPNsense image+config mechanism RESEARCHED 2026-07-09 (config.xml ISO via the Configuration Importer, nano image) -- not yet built, see opentofu/README.md
opentofu/ (netem, DC2 planes) NOT DONE -- deliberately deferred, see opentofu/README.md
Reusable as-is, repo-agnostic (2026-07-09 sweep) bundle.yaml, phase-01..08 VR0 DC0 runbooks (Stage 5/7 template, once DC-parameterized -- gap #1), preflight.sh, cloud-assert.sh, repo-lint.sh, run-logged.sh, ledger-scan.sh
lib-net.sh / lib-hosts.sh NOT yet DC-agnostic -- single-deployment hardcoded literals; see gap #1
Environment/skill docs (environment.md, README.md) Still describe VR0 DC0/v1 single-DC scope wholesale -- flagged, not yet rewritten for VR1; not a blocker for Stage 0 but will need doing before/during Stage 1