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

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