# Changelog 2026-07-10 -- $DC parameterization for the 3 MAAS scripts (DOCFIX-166)

No live infrastructure touched -- script + test-harness changes only (prep
session; no jumphost/juju/MAAS access this session). Closes the CLI-wiring
half of tooling gap register item #15 sub-item 2 in
`docs/dc-dc-deployment-workflow.md` (full detail in
`runbooks/dc-dc-phase3-maas-enlist-deploy.md`'s "Known gaps" section, item
2), which blocked `scripts/reenroll-hosts.sh`, `scripts/carve-host-
interfaces.sh`, and `scripts/phase-00-maas-standup.sh` from being invoked
with a `$DC` argument at all -- DOCFIX-151 (2026-07-09) only gave the
selector *functions* to `lib-net.sh`/`lib-hosts.sh`; no consumer script
called them yet.

## Item

### 1. DOCFIX-166 -- `$DC` env var wiring in the 3 MAAS scripts
FILES: `scripts/reenroll-hosts.sh`, `scripts/carve-host-interfaces.sh`,
`scripts/phase-00-maas-standup.sh`, `tests/carve-host-interfaces/run-
tests.sh`, `tests/phase-00-maas-standup/run-tests.sh`, new `tests/reenroll-
hosts/run-tests.sh`.

WHAT: each script now reads an opt-in `$DC` env var immediately after
sourcing `lib-net.sh`/`lib-hosts.sh` (the earliest possible point, matching
the pattern `runbooks/dc-dc-phase3-maas-enlist-deploy.md`'s Step 1 already
documents) and, only if `$DC` is non-empty, calls `lib_net_select_dc "$DC"`
(all three scripts) and `lib_hosts_select_dc "$DC"` (`reenroll-hosts.sh` and
`carve-host-interfaces.sh` only -- `phase-00-maas-standup.sh` never sources
`lib-hosts.sh`, it has no host-identity dependency to select). This matches
the convention the selector functions and the Stage 4 runbook already
established (an explicit `$DC` env var passed positionally into each
function), rather than inventing a new `--dc` CLI-flag convention that does
not exist anywhere else in this repo.

```bash
DC="${DC:-}"
if [ -n "$DC" ]; then
  lib_net_select_dc "$DC"
  lib_hosts_select_dc "$DC"     # reenroll-hosts.sh / carve-host-interfaces.sh only
fi
```

Backward compatible by construction: unset `$DC` skips the `if` body
entirely, so every existing invocation (`bash scripts/reenroll-hosts.sh
--check`, `bash scripts/carve-host-interfaces.sh openstack0 --apply`, `bash
scripts/phase-00-maas-standup.sh`) runs byte-for-byte as it did before this
change -- confirmed by re-invoking each script with no `$DC` set and diffing
against pre-change behavior descriptions in `runbooks/phase-00-teardown-
maas-reset.md` (see VERIFIED below).

No second validation layer was added: because all three scripts already run
under `set -euo pipefail`, a selector function returning non-zero (dc2 at
the network layer; dc1 AND dc2 at the host layer) causes the script to exit
immediately with that same exit code -- which happens to line up with each
script's own "1 = fatal" convention, so the propagation is free.

VERIFIED (this workstation; no jq installed, no live jumphost/MAAS/juju
access -- same environment gap DOCFIX-151's changelog documented):

- Manual, jq-independent invocation of all three scripts confirms the exact
  intended matrix (the selector calls happen before `need_jq`/any MAAS call,
  so these do not need jq or a fake `maas` at all):
  - `DC` unset -> unaffected (rc identical to before this change in every
    script).
  - `DC=dc0` -> explicit no-op, byte-for-byte same as unset.
  - `DC=dc1` -> `reenroll-hosts.sh` and `carve-host-interfaces.sh` FAIL LOUD
    at `lib_hosts_select_dc` ("no enrolled hosts yet..."), before touching
    MAAS; `phase-00-maas-standup.sh` (network-selector only) no-ops (D-101:
    dc1 inherits dc0's plane literals) and proceeds to its own MAAS calls.
  - `DC=dc2` -> all three FAIL LOUD at `lib_net_select_dc` first ("no
    assigned network literals yet..."), before `lib_hosts_select_dc` is ever
    reached in the two scripts that also call it.
  - `DC=bogus` -> all three FAIL LOUD ("unknown DC 'bogus'").
- `bash scripts/repo-lint.sh`: 0 fail, 1 documented legacy WARN (unchanged).
- `bash scripts/run-tests-all.sh`: new `reenroll-hosts` suite added (none
  existed before); it reports `FAIL: jq required`, identically to its
  sibling suites `carve-host-interfaces` and `phase-00-maas-standup` (both
  already jq-gated, unrelated to this change) and to roughly twenty other
  suites in the same gauntlet run that all fail for the same reason on this
  jq-less Windows workstation. Did NOT run a `git stash` A/B comparison this
  time (unlike DOCFIX-151's changelog) because the working tree currently
  carries OTHER in-flight, uncommitted changes from parallel agent sessions
  tonight (per the operator's own instruction that multiple agents are
  working concurrently) -- stashing would have touched their work, not just
  this delivery's. Instead, correctness was verified directly (the
  jq-independent invocation matrix above), and the only new gauntlet
  observation is the addition of the `reenroll-hosts` suite itself
  (previously absent, so it could not have been part of any prior baseline)
  failing for the same pre-existing, already-documented reason as its
  siblings -- not a regression this delivery introduced.
- Added `$DC` test cases to both existing harnesses
  (`tests/carve-host-interfaces/run-tests.sh`,
  `tests/phase-00-maas-standup/run-tests.sh`): a `DC=dc0` explicit no-op
  case (must match the existing default-case assertions), and fail-loud
  cases for the blocked DC values, using a new `run_dc()` helper that skips
  fixture setup entirely (these calls exit at the selector, before the
  script ever reaches `need_jq`/a MAAS call, so they do not depend on the
  fixtures OR on jq being installed -- confirmed by invoking them directly,
  bypassing the harness's own jq gate, per the VERIFIED bullet above).
- Created `tests/reenroll-hosts/run-tests.sh` (no harness existed for this
  script before). Scoped narrowly to what this delivery actually changed:
  the `$DC` fail-loud/no-op matrix, plus a smoke test that `--check`'s
  read-only reporting is unaffected when `$DC` is unset. Does not attempt to
  cover the create/poll/tag mutation paths (those need an interactive
  password prompt and a much heavier MAAS fixture set -- out of scope for a
  targeted parameterization change). Uses a minimal fake `maas` returning an
  empty machines list plus `< /dev/null` on stdin so a bug that ever reached
  the real interactive password prompt would fail loudly (EOF) instead of
  hanging the harness.

REVERT: `git checkout HEAD~ -- scripts/reenroll-hosts.sh scripts/carve-
host-interfaces.sh scripts/phase-00-maas-standup.sh
tests/carve-host-interfaces/run-tests.sh
tests/phase-00-maas-standup/run-tests.sh && rm -rf tests/reenroll-hosts`
(safe -- purely additive/opt-in; unset `$DC` behavior is provably unchanged,
so no other script or runbook step that does not set `$DC` depends on any of
this).

## What this delivery does NOT close (be precise)

Tooling gap register item #15 has SEVEN sub-findings (enumerated in
`runbooks/dc-dc-phase3-maas-enlist-deploy.md`'s "Known gaps" section). This
delivery closes ONLY the CLI-invocation half of sub-item 2 (the three
scripts not accepting `$DC` at all). It does NOT:

- Invent any per-DC literal. No dc1/dc2 hostname, octet, boot MAC, or
  VIRSH_POWER_ADDRESS was added to `lib-hosts.sh`. `lib_hosts_select_dc`
  still fails loud for both dc1 and dc2, exactly as DOCFIX-151 left it --
  that data gap is a separate, later delivery item (the Stage 4 runbook's
  own delivery checklist), not something this change works around.
- Make `phase-00-maas-standup.sh`'s `PLANES` table dc2-aware. That table is
  a hardcoded D-052/D-053 literal, not derived from `lib-net.sh`'s flat
  vars -- selecting `dc1` happens to be correct today only because D-101
  makes dc1's real target identical to dc0's, not because the script reads
  dc1-specific data. Once dc2 gets real NetBox CIDRs (gap #3), this table
  will need its own parameterization pass.
- Close sub-items 1 (no OpenTofu rack-controller module), 3 (node
  count/sizing undecided), 4 (dc2 blocked at the network-selector layer --
  unrelated to this delivery, pre-existing), 5 (per-DC mirror ownership
  unconfirmed), 6 (VM-host-discovered MAAS status unconfirmed), or 7
  (`HOST_TAG`/boot-fabric-name literals are DC0-specific). All six remain
  OPEN, unchanged by this delivery.

## Next actionable step

`docs/dc-dc-deployment-workflow.md` item #15 and `runbooks/dc-dc-phase3-
maas-enlist-deploy.md`'s gap #2 / Steps 5 and 8 updated to reflect the
partial closure (see those files' diffs). The next real dependency for
sub-item 2's FULL closure is Stage 4's own execution: once it measures real
per-DC hostnames/octets/boot MACs and a real per-DC `PLANES` table update
gets authored, `lib-hosts.sh` and `phase-00-maas-standup.sh`'s literal table
go from "selector wired but fails loud" to "selector wired and populated" --
no further change to the three scripts themselves should be required for
that.
