# Changelog 2026-07-09 -- MTU/geneve budget + Ceph disk-budget calculators (DOCFIX-162)

No live infrastructure touched -- read-only, arithmetic-only shell scripts
plus test harnesses only. Closes tooling gap register item #7 in
`docs/dc-dc-deployment-workflow.md`, which named the measurement-then-
computation gates in D-101 and buildout-design Section 3 as manual
arithmetic in prose today, not a script -- blocking the Stage 1 Phase-0
gates in `runbooks/dc-dc-phase0-vcloud-prep.md` Step 3.

**Numbering note:** several other agents are working the tooling gap
register in parallel this session, all uncommitted -- to avoid a
collision on the next-free DOCFIX number, this delivery uses the literal
placeholder `DOCFIX-162` everywhere a real number would go (this file,
the gap-register update, the session-ledger entry). The orchestrating
session assigns the real sequential number and find-replaces it at
integration/commit time.

## Item

### 1. DOCFIX-162 -- `scripts/dc-dc-mtu-geneve-budget.sh`
FILES: `scripts/dc-dc-mtu-geneve-budget.sh`, new
`tests/dc-dc-mtu-geneve-budget/run-tests.sh`.

WHAT: turns D-101's ("Tenant/MTU sub-policy", folded in from D-102)
geneve-over-v6 MTU arithmetic into a runnable, read-only calculator.
Required `--underlay-mtu <bytes>` (no default -- the measured host L2 MTU
per Step 3's own `ip -o link show | awk '{print $2, $0}' | grep -i mtu`
check); optional `--jumbo-threshold` (default 9000, overridable for policy
exploration only). Computes: `>= 9000` -> jumbo, tenant MTU stays 1500;
otherwise `tenant_mtu = underlay_mtu - 56` (56 = 40 IPv6 outer header + 8
UDP + 8 Geneve base, quoted verbatim from D-101), matching D-101's own
worked example exactly (1500 - 56 = 1444). Added one sanity floor NOT
itself part of D-101's text: if the computed tenant MTU falls below 1280
(the IPv6 protocol's own minimum link MTU, RFC 8200 Sec 5), the verdict is
FAIL rather than a silent PASS -- clearly labeled in the script's header as
a separate, defensible addition, not D-101 arithmetic. Prints a
human-readable computation, a PASS/FAIL verdict, and a single
`MTU-BUDGET: ...` summary line for pasting into a changelog/as-executed
log, matching what Step 3 asks the operator to record.

VERIFIED:
- New harness `tests/dc-dc-mtu-geneve-budget/run-tests.sh`: 19/19 PASS.
  Covers the exact D-101 worked example (1500 -> 1444, both the
  human-readable line and the summary line reproduce verbatim), jumbo
  input (9000 and above) correctly leaves tenant MTU at 1500, the jumbo
  threshold boundary (exactly-9000 vs 8999), missing `--underlay-mtu`
  fails loud (rc 2, clear message), non-numeric/zero/negative
  `--underlay-mtu` and bad `--jumbo-threshold` all fail loud (rc 2), the
  IPv6-minimum-MTU sanity floor triggers FAIL (rc 1) at 1300 and 1280
  underlay, `--help` exits 0 and cites both D-101 and the 56-byte
  overhead, and an unknown flag fails loud (rc 2).
- Manually re-ran the worked example, jumbo case, and floor case directly
  (not just through the harness) to confirm output text before writing the
  harness's regexes against it.
- `bash scripts/repo-lint.sh`: 0 fail, 1 documented legacy WARN (unchanged
  D-001..018 non-ASCII carve-out; nothing this delivery touched).

REVERT: `rm scripts/dc-dc-mtu-geneve-budget.sh && rm -rf
tests/dc-dc-mtu-geneve-budget` (safe -- no other script calls this one
yet; it is a standalone calculator, not sourced by anything).

### 2. DOCFIX-162 -- `scripts/dc-dc-ceph-disk-budget.sh`
FILES: `scripts/dc-dc-ceph-disk-budget.sh`, new
`tests/dc-dc-ceph-disk-budget/run-tests.sh`.

WHAT: turns buildout-design Section 3 / D-101's Ceph size=3-vs-size=2
disk-budget arithmetic into a runnable, read-only calculator. Required
inputs, ALL with no default (fails loud if any is missing): `--total-disk
<SIZE>` (measured total disk, Step 2's `df`/`lsblk` output), `--dc1-nodes
<N>` / `--dc1-per-node-osd <SIZE>` and `--dc2-nodes <N>` /
`--dc2-per-node-osd <SIZE>` (measured or firmly planned per-DC Ceph OSD
node count and per-node raw footprint -- already reflecting each
cluster's own size=3 replication as it would actually consume disk), and
`--backup-overhead-fraction <FRACTION>` (a decimal fraction of the
combined DC1+DC2 Ceph total representing radosgw backup pools + cross-DC
multisite duplication + Glance rbd-mirror targets + images + per-DC
mirrors -- quoted directly from Section 3's own itemization). This
fraction is NOT specified as a hard number anywhere in the repo (checked
`docs/design-decisions.md` and `docs/dc-dc-buildout-design.md`
directly) -- so it has NO default and the script refuses to run without
it explicitly passed; its header documents that it must be estimated from
real Ceph/radosgw-admin usage measurements once DC1's cluster exists.
Sizes accept plain byte integers or 1024-based `Ki/Mi/Gi/Ti`/`K/M/G/T`
suffixes (matching `df -h` binary units).

Computes `required = (dc1_total + dc2_total) * (1 + overhead_fraction)`
against the measured total disk. If it fits: PASS, prints the margin. If
not: FAIL, prints the shortfall, and states -- but does NOT compute or
silently apply -- that size=2 is the ONLY documented fallback per D-101 /
Section 3, and that adopting it is a decision the OPERATOR must explicitly
log (its own decision note / changelog entry). Deliberately does NOT
invent a size=2 disk-requirement number: the real per-node footprint under
size=2 depends on a distinct measurement this repo does not have yet, and
fabricating a scaling factor would be exactly the unmeasured-arithmetic
this repo's discipline forbids. Prints a `CEPH-BUDGET: ...` summary line
for the same changelog/as-executed-log use as the MTU script.

VERIFIED:
- New harness `tests/dc-dc-ceph-disk-budget/run-tests.sh`: 16/16 PASS.
  Covers a constructed size=3-fits numeric example (3 nodes x 500G per DC,
  30% overhead, 10TiB measured total -> ~3.80TiB required, PASS with
  ~6.19TiB margin, exact summary line asserted) and a constructed
  size=3-does-not-fit example (same Ceph footprint against a 2TiB measured
  total -> FAIL, exact shortfall asserted, size=2 fallback named,
  explicit-logged-decision language asserted, never applied silently);
  each of the six required arguments fails loud (rc 2) when missing,
  specifically confirming `--backup-overhead-fraction` has no default and
  blocks the run alone; bad numeric/size formats (non-integer node count,
  non-decimal fraction, bad size suffix) all fail loud (rc 2); a
  plain-byte total-disk value produces the same PASS verdict as its
  1024-based-suffix equivalent (10995116277760 bytes == 10Ti); `--help`
  exits 0 and cites Section 3 and the no-default-fraction rule; an unknown
  flag fails loud (rc 2).
- Manually re-ran both constructed examples directly to confirm output
  text before writing the harness's regexes against it.
- `bash scripts/repo-lint.sh`: 0 fail, 1 documented legacy WARN (unchanged).

REVERT: `rm scripts/dc-dc-ceph-disk-budget.sh && rm -rf
tests/dc-dc-ceph-disk-budget` (safe -- standalone, nothing else calls it
yet).

## Next actionable step

Tooling gap register item #7 CLOSED (mechanism). Both calculators are
read-only and take every varying input as a required, explicitly-measured
argument -- no invented defaults anywhere, matching hard rule 2. Neither
has been run against the real vcloud host yet (no live infrastructure this
session, prep-only per the operator's own instruction) -- that first real
run is part of executing `runbooks/dc-dc-phase0-vcloud-prep.md` Step 3
tomorrow morning: measure the real interface MTU and feed it to
`dc-dc-mtu-geneve-budget.sh`, and once Step 2's `df`/`lsblk` numbers and a
real (or firmly planned) per-node OSD footprint exist, feed those to
`dc-dc-ceph-disk-budget.sh`. The `--backup-overhead-fraction` input
specifically remains a genuine open item until DC1's Ceph cluster exists
and reports real radosgw/rbd-mirror pool usage (Section 3's own point:
"the reserve split ... is set in Phase 0 from the measured DC0 per-node
OSD footprint") -- this delivery gives the arithmetic a home, not a real
number to plug into it yet.
