Newer
Older
openstack-caracal-ipv4 / scripts / phase-05-octavia-verify.sh
#!/usr/bin/env bash
# scripts/phase-05-octavia-verify.sh [MODEL]
#
# Read-only verify for phase-05 (Octavia enablement, D-021). Auto-detects where the
# phase is and reports accordingly; mutates NOTHING, safe to re-run:
#
#   CONFIG GATE (always): the LP#1937003 / Ceph-RBD preconditions --
#     octavia-diskimage-retrofit use-internal-endpoints=true, image-format=raw,
#     amp-image-tag set AND == octavia amp-image-tag. A mismatch HOLDs regardless of state.
#
#   then, by octavia/0 workload status:
#     blocked  -> PRE: charm-octavia resources must be EMPTY -> PROCEED (run 5.1 configure-resources)
#     active   -> POST: lb-mgmt net/subnet/sec-grp present + o-hm0 UP w/ fc00::/ ULA;
#                 then amphora image tagged $OTAG ACTIVE?
#                   no  -> CONTROL-PLANE-DONE (5.1 done; run 5.2 amphora pipeline)
#                   yes -> PASS (phase-05 EXIT GATE met)
#     other    -> HOLD (in flight / unexpected; re-check, do not re-fire 5.1)
#
# Usage:  source ~/admin-openrc && scripts/phase-05-octavia-verify.sh [MODEL]
# Exit:   0 PROCEED|CONTROL-PLANE-DONE|PASS | 1 HOLD/FAIL | 2 precondition
#
# Resolves dynamically; tags read from charm config (not hardcoded). Read-only. ASCII + LF.

set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true
IFS=$'\n\t'

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=scripts/lib-net.sh
. "$SCRIPT_DIR/lib-net.sh"

MODEL="${1:-openstack}"
RETRO="octavia-diskimage-retrofit"

FATAL=0
fail() { echo "FAIL: $*" >&2; FATAL=$((FATAL + 1)); }
pass() { echo "PASS: $*"; }

# --- preconditions ------------------------------------------------------------------
need_jq || exit 2
command -v openstack >/dev/null 2>&1 || { echo "FAIL: openstack client not found" >&2; exit 2; }
command -v juju      >/dev/null 2>&1 || { echo "FAIL: juju client not found" >&2; exit 2; }
[ -n "${OS_AUTH_URL:-}" ] || { echo "FAIL: OS_AUTH_URL unset -- 'source ~/admin-openrc' first" >&2; exit 2; }

J="$(juju status octavia -m "$MODEL" --format json 2>/dev/null || true)"
printf '%s' "$J" | jq -e '.applications.octavia.units."octavia/0"' >/dev/null 2>&1 \
  || { echo "FAIL: octavia/0 not found in model '$MODEL'" >&2; exit 2; }

echo "=== phase-05 octavia-enablement verify (read-only) ==="
echo

# --- CONFIG GATE (LP#1937003 + Ceph-RBD; always) -----------------------------------
echo "--- config gate (use-internal-endpoints / image-format / amp-image-tag match) ---"
UIE="$(juju config "$RETRO" use-internal-endpoints 2>/dev/null || true)"
IMGFMT="$(juju config "$RETRO" image-format 2>/dev/null || true)"
RTAG="$(juju config "$RETRO" amp-image-tag 2>/dev/null || true)"
OTAG="$(juju config octavia amp-image-tag 2>/dev/null || true)"
echo "  retrofit use-internal-endpoints=$UIE  image-format=$IMGFMT  amp-image-tag=$RTAG ; octavia amp-image-tag=$OTAG"
[ "$UIE" = "true" ] || fail "retrofit use-internal-endpoints=$UIE (need true; retrofit is metal-only -> internal glance)"
[ "$IMGFMT" = "raw" ] || fail "retrofit image-format=$IMGFMT (need raw; Ceph RBD fast-clone)"
{ [ -n "$RTAG" ] && [ "$RTAG" = "$OTAG" ]; } || fail "amp-image-tag mismatch retrofit='$RTAG' octavia='$OTAG' (LP#1937003)"
[ "$FATAL" -eq 0 ] && pass "config gate clear"
echo

# --- octavia workload state ---------------------------------------------------------
OSTATE="$(printf '%s' "$J" | jq -r '.applications.octavia.units."octavia/0"."workload-status".current // "unknown"')"
OMSG="$(printf '%s' "$J" | jq -r '.applications.octavia.units."octavia/0"."workload-status".message // ""')"
echo "--- octavia/0 workload: $OSTATE ($OMSG) ---"

# helper: count non-empty Name rows of a charm-octavia-tagged list
count_tagged() { "$@" --tags charm-octavia -f value -c Name 2>/dev/null | grep -c . || true; }

if [ "$OSTATE" = "blocked" ]; then
  # PRE: resources must be empty (idempotency baseline)
  n_net="$(count_tagged openstack network list)"
  n_sub="$(count_tagged openstack subnet list)"
  n_sg="$(count_tagged openstack security group list)"
  echo "  charm-octavia resources: networks=$n_net subnets=$n_sub sec-groups=$n_sg (expect 0/0/0 pre-run)"
  if [ "$n_net" -ne 0 ] || [ "$n_sub" -ne 0 ] || [ "$n_sg" -ne 0 ]; then
    fail "octavia blocked but charm-octavia resources already exist -- inconsistent; investigate before re-firing"
  fi
  echo
  if [ "$FATAL" -ne 0 ]; then echo "Summary: HOLD/FAIL -- $FATAL gate(s) failed."; exit 1; fi
  echo "Summary: PROCEED -- config gate clear, octavia awaiting configure-resources (resources empty)."
  echo "  Next: juju run octavia/leader configure-resources -m $MODEL --wait=20m  (Step 5.1)"
  exit 0

elif [ "$OSTATE" = "active" ]; then
  # POST: control-plane resources + o-hm0
  n_net="$(count_tagged openstack network list)"
  n_sub="$(count_tagged openstack subnet list)"
  n_sg="$(count_tagged openstack security group list)"
  echo "  charm-octavia resources: networks=$n_net subnets=$n_sub sec-groups=$n_sg (expect >=1 each)"
  { [ "$n_net" -ge 1 ] && [ "$n_sub" -ge 1 ] && [ "$n_sg" -ge 1 ]; } \
    || fail "octavia active but charm-octavia lb-mgmt resources missing (net/sub/sg = $n_net/$n_sub/$n_sg)"
  OHM0="$(juju exec --unit octavia/0 -m "$MODEL" -- 'ip -br addr show o-hm0' </dev/null 2>/dev/null || true)"
  if printf '%s' "$OHM0" | grep -q 'UP' && printf '%s' "$OHM0" | grep -q 'fc00:'; then
    pass "o-hm0 UP with fc00::/ IPv6-ULA"
  else
    fail "o-hm0 not UP with an fc00::/ ULA addr (got: ${OHM0:-<none>})"
  fi
  # 5.2: amphora image
  AMPH_ACTIVE="$(openstack image list --tag "$OTAG" -f value -c Status 2>/dev/null | grep -xc active || true)"
  echo "  images tagged $OTAG with Status=active: $AMPH_ACTIVE"
  echo
  if [ "$FATAL" -ne 0 ]; then echo "Summary: HOLD/FAIL -- $FATAL gate(s) failed."; exit 1; fi
  if [ "$AMPH_ACTIVE" -ge 1 ]; then
    echo "Summary: PASS -- phase-05 EXIT GATE met (octavia active; lb-mgmt resources + o-hm0 up; ACTIVE amphora tagged $OTAG)."
    exit 0
  fi
  echo "Summary: CONTROL-PLANE-DONE -- Step 5.1 complete; no ACTIVE amphora image yet."
  echo "  Next: run Step 5.2 (amphora image pipeline), then re-run this for PASS."
  exit 0

else
  echo
  echo "Summary: HOLD -- octavia/0 workload '$OSTATE' (not blocked/active). In flight or unexpected;"
  echo "  re-check; do NOT re-fire configure-resources on a transient (appendix-A)."
  [ "$FATAL" -ne 0 ] && exit 1
  exit 1
fi