Files
ergo-proxmox-scripts/start-dependents.sh
T
ergosteur 0d55a6819d Start passthrough VM and its dependents by device identity, not address
Adding a PCIe card can renumber the buses behind it, so a hostpci entry
that names a PCI address can pass through the wrong device. These start
the VM only after finding each device by vendor/device/subsystem ID or
by its MAC-pinned interface, repoint its resource mapping if it moved,
and refuse to start on anything ambiguous. Its dependents then wait for
a port on it to answer before starting, so they find their shares up.
2026-10-25 02:29:49 -04:00

118 lines
3.8 KiB
Bash
Executable File

#!/bin/bash
# Start the guests that depend on the passthrough VM, once it is serving.
#
# Usage:
# ./start-dependents.sh [--check]
#
# Run at boot by dependents-start.service, after passthrough-vm-start.service
# has started the VM they need (a NAS, typically). The guests to start, in
# order, are listed in conf/dependents.conf; give each of them onboot: 0 so that
# only this starts them. VM and container IDs both work.
#
# Before starting anything it waits for WAIT_FOR (host:port) to accept a TCP
# connection, for up to WAIT_SECS seconds, because a VM that has started is not
# yet one that is serving. If it never answers the guests are started anyway,
# with a warning, which is no worse than not waiting at all.
#
# A guest that fails to start does not stop the others; the script exits
# non-zero at the end so the unit shows as failed. Guests already running are
# left alone. Nothing is started while prox-maintenance-mode.sh has autostart
# disabled.
#
# Options:
# --check Show what would be started and test WAIT_FOR once; start nothing
#
# The config is looked for relative to this script, so this works both in a
# checkout and deployed at /opt/ergosteur. Override with DEPENDENTS_CONFIG.
set -uo pipefail
# Print the header comment block: everything from line 2 up to the first blank line.
usage() {
sed -n '2,/^$/p' "$0" | sed -e 's/^#//' -e 's/^ //'
}
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
CONFIG=${DEPENDENTS_CONFIG:-$SCRIPT_DIR/conf/dependents.conf}
MAINT_STATE=/var/tmp/proxmox-autostart-backup.txt # written by prox-maintenance-mode.sh --enable
CHECK_ONLY=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) usage; exit 0 ;;
--check) CHECK_ONLY=true; shift ;;
*) echo "Error: unknown option '$1'." >&2; usage >&2; exit 1 ;;
esac
done
[[ -r $CONFIG ]] || { echo "Error: cannot read $CONFIG" >&2; exit 1; }
GUESTS=()
WAIT_FOR=""
WAIT_SECS=300
# shellcheck source=conf/dependents.conf.example
source "$CONFIG"
if [[ -e $MAINT_STATE ]]; then
echo "maintenance mode is on ($MAINT_STATE exists); not starting anything"
exit 0
fi
if [[ ${#GUESTS[@]} -eq 0 ]]; then
echo "no GUESTS listed in $CONFIG; nothing to do"
exit 0
fi
# True once host:port accepts a TCP connection.
answers() {
timeout 3 bash -c "exec 3<>/dev/tcp/${1%:*}/${1##*:}" 2>/dev/null
}
if [[ -n $WAIT_FOR ]]; then
if $CHECK_ONLY; then
if answers "$WAIT_FOR"; then
echo "$WAIT_FOR answers"
else
echo "$WAIT_FOR is not answering right now"
fi
else
echo "waiting up to ${WAIT_SECS}s for $WAIT_FOR"
deadline=$((SECONDS + WAIT_SECS))
until answers "$WAIT_FOR"; do
if (( SECONDS >= deadline )); then
echo "WARNING: $WAIT_FOR still not answering after ${WAIT_SECS}s; starting the guests anyway"
break
fi
sleep 5
done
answers "$WAIT_FOR" && echo "$WAIT_FOR answers"
fi
fi
failed=0
for id in "${GUESTS[@]}"; do
if [[ -e /etc/pve/qemu-server/$id.conf ]]; then
tool=qm conf=/etc/pve/qemu-server/$id.conf
elif [[ -e /etc/pve/lxc/$id.conf ]]; then
tool=pct conf=/etc/pve/lxc/$id.conf
else
echo "ERROR: no VM or container $id on this node" >&2
failed=1
continue
fi
if grep -q '^onboot: 1' "$conf"; then
echo "WARNING: $id has onboot: 1, so Proxmox starts it before the VM it needs; set onboot: 0"
fi
if [[ $($tool status "$id" 2>/dev/null) == "status: running" ]]; then
echo "$id is already running"
continue
fi
if $CHECK_ONLY; then
echo "would start $id ($tool)"
elif $tool start "$id"; then
echo "started $id ($tool)"
else
echo "ERROR: $tool start $id failed" >&2
failed=1
fi
done
exit "$failed"