Newer
Older
openstack-caracal-dc-dc / tests / dc-node-v6-carve / run-tests.sh
#!/usr/bin/env bash
# tests/dc-node-v6-carve/run-tests.sh -- offline harness for scripts/dc-node-v6-carve.py.
# Stubs `maas` with a fakebin returning controlled JSON. Never touches live MAAS.
# Exit: 0 all pass | 1 any case failed.  ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
ok()  { PASS=$((PASS+1)); printf '  PASS  %s\n' "$1"; }
bad() { FAIL=$((FAIL+1)); printf '  FAIL  %s\n' "$1"; shift; [ $# -gt 0 ] && printf '%s\n' "$*" | sed 's/^/        /'; }

# mkbin <dir> <v6:full|none|novlan> <nodev6:none|correct|wrong> <tag:ok|missing>
mkbin() {
  local d="$1"; mkdir -p "$d/fakebin"
  cat > "$d/fakebin/maas" <<FB
#!/usr/bin/env bash
if [ "\$2" = "subnets" ]; then python3 "$d/subnets.py"; exit 0; fi
if [ "\$2" = "machines" ]; then python3 "$d/machines.py"; exit 0; fi
if [ "\$2" = "interface" ]; then echo '{}'; exit 0; fi
exit 1
FB
  chmod +x "$d/fakebin/maas"
  cat > "$d/subnets.py" <<PY
import json
mode = "$2"
s = [{"cidr": "10.12.8.0/22", "id": 6, "vlan": {"id": 5005}},
     {"cidr": "10.12.4.0/22", "id": 7, "vlan": {"id": 5189}}]
if mode == "full":
    s += [{"cidr": "fd50:840e:74e2:220::/64", "id": 21, "vlan": {"id": 5005}},
          {"cidr": "2602:f3e2:f02:10::/64",   "id": 23, "vlan": {"id": 5189}}]
elif mode == "novlan":
    s += [{"cidr": "fd50:840e:74e2:220::/64", "id": 21, "vlan": {"id": 5005}}]
print(json.dumps(s))
PY
  cat > "$d/machines.py" <<PY
import json
nodev6, tag = "$3", "$4"
links_admin = [{"mode": "static", "ip_address": "10.12.8.121",
                "subnet": {"cidr": "10.12.8.0/22", "vlan": {"id": 5005}}}]
links_brex  = [{"mode": "static", "ip_address": "10.12.4.121",
                "subnet": {"cidr": "10.12.4.0/22", "vlan": {"id": 5189}}}]
if nodev6 == "correct":
    links_admin.append({"mode": "static", "ip_address": "fd50:840e:74e2:220::121",
                        "subnet": {"cidr": "fd50:840e:74e2:220::/64", "vlan": {"id": 5005}}})
    links_brex.append({"mode": "static", "ip_address": "2602:f3e2:f02:10::121",
                       "subnet": {"cidr": "2602:f3e2:f02:10::/64", "vlan": {"id": 5189}}})
elif nodev6 == "wrong":
    links_admin.append({"mode": "static", "ip_address": "fd50:840e:74e2:220::999",
                        "subnet": {"cidr": "fd50:840e:74e2:220::/64", "vlan": {"id": 5005}}})
m = {"hostname": "n1", "system_id": "aaa", "status_name": "Ready",
     "tag_names": (["virtual", "openstack-vr1-dc0"] if tag == "ok" else ["virtual"]),
     "interface_set": [
        {"name": "enp1s0", "id": 216, "links": links_admin},
        {"name": "enp2s0", "id": 223, "links": []},
        {"name": "br-ex",  "id": 404, "links": links_brex}]}
print(json.dumps([m]))
PY
}

run() { # run <want_rc> <regex> <label> <dir> <action> [args...]
  local want="$1" re="$2" label="$3" d="$4"; shift 4
  local out rc
  out="$(cd "$REPO" && PATH="$d/fakebin:$PATH" python3 scripts/dc-node-v6-carve.py "$@" 2>&1)"; rc=$?
  if [ "$rc" = "$want" ] && grep -qE "$re" <<<"$out"; then ok "$label"
  else bad "$label (rc=$rc want=$want)" "$(printf '%s' "$out" | tail -5)"; fi
}

echo "== dc-node-v6-carve =="

D="$TMP/none"; mkbin "$D" full none ok
run 1 'planned v6 links : 2' "T1 uncarved node plans one v6 link per v4-carrying iface" "$D" check vr1-dc0

# THE OCTET MIRROR IS TEXTUAL. .121 -> ::121, never the hex 0x121 or hex(121)=0x79.
run 1 'fd50:840e:74e2:220::121' "T2 mirror is TEXTUAL: .121 -> ::121" "$D" check vr1-dc0
out="$(cd "$REPO" && PATH="$D/fakebin:$PATH" python3 scripts/dc-node-v6-carve.py check vr1-dc0 2>&1)"
if ! grep -qE '::(79|121121)' <<<"$out"; then ok "T3 no hex conversion of the octet"
else bad "T3 octet was hex-converted" "$out"; fi

# The RAW provider NIC (D-100) has no v4 link and must be SKIPPED, not errored --
# and br-ex, which carries provider-public instead, must be INCLUDED.
if ! grep -q 'enp2s0' <<<"$out" && grep -q 'br-ex' <<<"$out"; then
  ok "T4 raw provider NIC skipped (no v4 link); br-ex included (has one)"
else bad "T4 provider-NIC/br-ex handling wrong" "$out"; fi

D="$TMP/done"; mkbin "$D" full correct ok
run 0 'PASS: dc-node-v6-carve' "T5 fully-carved fixture PASSES -- the gate can go green" "$D" check vr1-dc0

D="$TMP/wrong"; mkbin "$D" full wrong ok
run 1 'REFUSING to add a second address' \
  "T6 an interface with a DIFFERENT v6 refuses rather than adding a second" "$D" check vr1-dc0

D="$TMP/novlan"; mkbin "$D" novlan none ok
run 1 'NO v6 subnet on that vlan' \
  "T7 a vlan with no v6 subnet is a NAMED error, not a silent skip" "$D" check vr1-dc0

# A tag that matches nothing must REFUSE, never read as "0 planned, all clean".
D="$TMP/notag"; mkbin "$D" full none missing
run 2 'Refusing to fall back to a heuristic' \
  "T8 no tagged machines REFUSES (exit 2) rather than reporting a clean zero" "$D" check vr1-dc0

# apply without --commit must write nothing and say so.
D="$TMP/dry"; mkbin "$D" full none ok
run 0 'DRY RUN -- nothing was written' "T9 apply is DRY by default" "$D" apply vr1-dc0

# --- PROFILE RESOLUTION (2026-07-30) ---------------------------------------
# This script was ENV-BLIND: default "admin", --profile only, no environment
# read. Every bash tool here honours MAAS_PROFILE, so `export MAAS_PROFILE=...`
# left THIS tool silently targeting OFFICE1 -- the unrecoverable direction
# during the D-132 q1 per-DC region migration. These assertions pin all three
# resolution paths AND require the run to SAY which region it targets, because a
# capture that does not name the profile cannot be audited for wrong-region work.
pchk(){ # pchk <desc> <expected-substring> <env-value-or-empty> [extra args...]
  local desc="$1" want="$2" envv="$3"; shift 3
  local out
  if [ -n "$envv" ]; then out="$(MAAS_PROFILE="$envv" python3 "$REPO/scripts/dc-node-v6-carve.py" check vr1-dc0 "$@" 2>&1)"
  else out="$(env -u MAAS_PROFILE python3 "$REPO/scripts/dc-node-v6-carve.py" check vr1-dc0 "$@" 2>&1)"; fi
  case "$out" in *"$want"*) echo "  PASS  $desc"; PASS=$((PASS+1)) ;;
                 *) echo "  FAIL  $desc (wanted '$want' in: $(printf '%s' "$out" | head -1))"; FAIL=$((FAIL+1)) ;; esac
}
pchk "T10 MAAS_PROFILE env is honoured"          "profile 'env-region' (from MAAS_PROFILE env)" "env-region"
pchk "T11 --profile overrides the environment"   "profile 'flag-region' (from --profile)"       "env-region" --profile flag-region
pchk "T12 no env and no flag falls back to admin" "profile 'admin' (from built-in default)"     ""

# An ABSENT maas CLI must REFUSE (exit 2), not traceback (exit 1). A traceback is
# indistinguishable from a real failure and breaks the REFUSE contract.
OUT="$(PATH=/nonexistent-for-test:/usr/bin:/bin python3 "$REPO/scripts/dc-node-v6-carve.py" check vr1-dc0 2>&1)"; RC=$?
if [ "$RC" = 2 ]; then echo "  PASS  T13 absent maas CLI REFUSES with exit 2"; PASS=$((PASS+1));
else echo "  FAIL  T13 absent maas CLI REFUSES with exit 2 (got $RC)"; FAIL=$((FAIL+1)); fi
case "$OUT" in *Traceback*) echo "  FAIL  T14 absent maas CLI does not traceback"; FAIL=$((FAIL+1)) ;;
               *) echo "  PASS  T14 absent maas CLI does not traceback"; PASS=$((PASS+1)) ;; esac

echo
echo "RESULT: PASS=$PASS FAIL=$FAIL"
[ "$FAIL" -eq 0 ] || exit 1