Newer
Older
openstack-caracal-ipv4 / scripts / phase-06-mgmt-vm.sh
#!/usr/bin/env bash
# scripts/phase-06-mgmt-vm.sh
#
# Phase-06 Step 6.2 (MUTATION; allocates a pool FIP): create capi-mgmt-v2
# (gp.large / ubuntu-24.04-noble on capi-mgmt-net), poll ACTIVE, attach a floating IP from
# provider-ext, resolve the tenant (fixed) IP, and persist BOTH to ~/capi-mgmt-net.env
# (the single source for 6.3-6.6 + phase-07; NEITHER value is deterministic per rebuild --
# DOCFIX-038, never hardcode). D-056 flagged-mutation script; human-gated by invocation.
#
# DOCFIX-054: FIP attach is IDEMPOTENT here -- if the VM already has a floating IP (looked up
# via its neutron port), it is REUSED; a new FIP is allocated only when none is attached. The
# do-doc block allocated unconditionally (a re-run would leak a second FIP).
#
# Tunables via env: VM PROJ EXT NET SG KEYPAIR FLAVOR IMAGE ENVFILE POLL_TRIES POLL_SLEEP
# Requires: jumphost; admin-openrc; openstack; jq; python3; scripts/resolve_tenant_ip.py.
# Usage:  source ~/admin-openrc && bash scripts/phase-06-mgmt-vm.sh
# Exit:   0 VM ACTIVE + FIP attached + env persisted | 1 gate/resolve fail | 2 precondition
# ASCII + LF.

set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RESOLVE="$SCRIPT_DIR/resolve_tenant_ip.py"

VM="${VM:-capi-mgmt-v2}"
PROJ="${PROJ:-capi-mgmt}"
EXT="${EXT:-provider-ext}"
NET="${NET:-capi-mgmt-net}"
SG="${SG:-capi-mgmt-sg}"
KEYPAIR="${KEYPAIR:-capi-mgmt-key}"
FLAVOR="${FLAVOR:-gp.large}"
IMAGE="${IMAGE:-ubuntu-24.04-noble}"
ENVFILE="${ENVFILE:-$HOME/capi-mgmt-net.env}"
POLL_TRIES="${POLL_TRIES:-40}"; POLL_SLEEP="${POLL_SLEEP:-15}"

for c in openstack jq python3; do command -v "$c" >/dev/null 2>&1 || { echo "FAIL: $c not found" >&2; exit 2; }; done
[ -f "$RESOLVE" ] || { echo "FAIL: helper $RESOLVE not found" >&2; exit 2; }
if [ -z "${OS_AUTH_URL:-}" ] && [ -f "$HOME/admin-openrc" ]; then
  # shellcheck disable=SC1091
  . "$HOME/admin-openrc"
fi
[ -n "${OS_AUTH_URL:-}" ] || { echo "FAIL: OS_AUTH_URL unset and no ~/admin-openrc" >&2; exit 2; }
openstack token issue >/dev/null 2>&1 || { echo "FAIL: no scoped token (admin-openrc)" >&2; exit 2; }

# 1. VM verify-or-create
if openstack server show "$VM" -f value -c id >/dev/null 2>&1; then
  echo "[SKIP] server $VM exists"
else
  echo "[..] creating $VM ($FLAVOR / $IMAGE on $NET)"
  openstack server create --image "$IMAGE" --flavor "$FLAVOR" \
    --network "$NET" --security-group "$SG" --key-name "$KEYPAIR" "$VM" >/dev/null
  echo "[OK] $VM create submitted"
fi

# 2. poll ACTIVE (fail fast on ERROR)
echo "=== poll $VM -> ACTIVE ==="
ST=""
for i in $(seq 1 "$POLL_TRIES"); do
  ST=$(openstack server show "$VM" -f value -c status 2>/dev/null || echo '?')
  echo "[$i] status=$ST"
  case "$ST" in
    ACTIVE) break ;;
    ERROR)  echo "GATE FAIL: $VM entered ERROR"; exit 1 ;;
  esac
  sleep "$POLL_SLEEP"
done
[ "$ST" = ACTIVE ] || { echo "GATE FAIL: $VM not ACTIVE after $POLL_TRIES tries"; exit 1; }

# 3. floating IP -- idempotent (DOCFIX-054): reuse via the VM's port, else allocate + associate
PORT=$(openstack port list --server "$VM" -f value -c ID | head -1)
[ -n "$PORT" ] || { echo "GATE FAIL: no neutron port for $VM"; exit 1; }
FIP=$(openstack floating ip list --port "$PORT" -f value -c "Floating IP Address" 2>/dev/null | head -1)
if [ -n "$FIP" ]; then
  echo "[SKIP] $VM already has floating IP $FIP (reusing)"
else
  FIP=$(openstack floating ip create "$EXT" -f value -c floating_ip_address)
  [ -n "$FIP" ] || { echo "GATE FAIL: FIP allocation returned empty"; exit 1; }
  openstack server add floating ip "$VM" "$FIP"
  echo "[OK] allocated + associated FIP $FIP"
fi

# 4. tenant (fixed) IP = the server address that is NOT the FIP (tested .py helper)
TENANT_IP=$(openstack server show "$VM" -f json | FIP="$FIP" python3 "$RESOLVE")
[ -n "$TENANT_IP" ] || { echo "GATE FAIL: could not resolve tenant IP for $VM"; exit 1; }

# 5. persist both (single source; neither deterministic per rebuild -- DOCFIX-038)
umask 077
printf 'MGMT_FIP=%s\nMGMT_TENANT_IP=%s\n' "$FIP" "$TENANT_IP" | tee "$ENVFILE"
echo "=== confirm ==="
openstack server show "$VM" -f value -c status -c addresses
echo "Summary: $VM ACTIVE; FIP=$FIP TENANT=$TENANT_IP persisted to $ENVFILE"