#!/bin/bash # CI VM IP reporter — called by ci-report-ip.service at boot. # # Phase C extension: reads guestinfo.ip-assignment from the VMX (written by the # host orchestrator before vmrun start). If present, applies a static IP to # the primary NIC and reports it back immediately via guestinfo.ci-ip, skipping # DHCP entirely. If absent, falls back to the original DHCP-detect behaviour. # # The VMCI channel (vmware-rpctool) does not require TCP/IP to be initialised — # it communicates via the VMware virtual hardware interface. This means the # static IP can be configured before network-online.target, so by the time SSH # starts listening the NIC already has the correct address. # # Install: # sudo cp ci-report-ip.sh /usr/local/bin/ci-report-ip.sh # sudo chmod +x /usr/local/bin/ci-report-ip.sh # sudo cp ci-report-ip.service /etc/systemd/system/ci-report-ip.service # sudo systemctl daemon-reload # sudo systemctl enable ci-report-ip.service RPCTOOL=/usr/bin/vmware-rpctool [ -x "$RPCTOOL" ] || exit 0 # ── Check for pre-assigned IP from orchestrator ─────────────────────────────── assigned_ip=$("$RPCTOOL" "info-get guestinfo.ip-assignment" 2>/dev/null | tr -d '[:space:]') if [[ "$assigned_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then netmask=$("$RPCTOOL" "info-get guestinfo.netmask" 2>/dev/null | tr -d '[:space:]') gateway=$("$RPCTOOL" "info-get guestinfo.gateway" 2>/dev/null | tr -d '[:space:]') # Resolve primary NIC (first non-loopback). iface=$(ip -o link show | awk -F': ' '$2 != "lo" {print $2; exit}') if [ -z "$iface" ]; then echo "ci-report-ip: no non-loopback interface found" | systemd-cat -t ci-report-ip exit 1 fi # Compute prefix length from netmask (default /24). prefix=24 if [ -n "$netmask" ]; then prefix=$(python3 -c \ "import ipaddress; print(ipaddress.IPv4Network('0.0.0.0/$netmask',strict=False).prefixlen)" \ 2>/dev/null) || prefix=24 fi # Flush any existing config (DHCP or stale static) and apply. ip addr flush dev "$iface" 2>/dev/null || true ip addr add "${assigned_ip}/${prefix}" dev "$iface" ip link set "$iface" up if [[ "$gateway" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then ip route add default via "$gateway" dev "$iface" 2>/dev/null || true fi # Report IP back to host via guestinfo.ci-ip. "$RPCTOOL" "info-set guestinfo.ci-ip $assigned_ip" 2>/dev/null echo "ci-report-ip: applied static IP $assigned_ip/$prefix via guestinfo" \ | systemd-cat -t ci-report-ip exit 0 fi # ── Fallback: DHCP — wait for IP and report ─────────────────────────────────── # Retries for up to 60 s to tolerate open-vm-tools re-init after UUID change # (linked clones get a new UUID — vmware-rpctool may fail briefly on first boot). for i in $(seq 1 30); do IP=$(ip -4 route get 1.0.0.0 2>/dev/null \ | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' \ | head -1) if [ -n "$IP" ]; then if "$RPCTOOL" "info-set guestinfo.ci-ip $IP" 2>/dev/null; then echo "ci-report-ip: set guestinfo.ci-ip=$IP via DHCP (attempt $i)" \ | systemd-cat -t ci-report-ip exit 0 fi fi sleep 2 done echo "ci-report-ip: failed to set guestinfo.ci-ip after 30 attempts" \ | systemd-cat -t ci-report-ip exit 1