#!/usr/bin/env bash # lab-lib.sh — helpers shared by vm-create.sh / vm-destroy.sh / vm-deploy.sh. # Source it; it never executes anything on its own. Topology single-source: # lab/ips.env. Disks: per-VM qcow2 with backing_file to the shared read-only # alpine-base.qcow2 (fast creates, ~MBs of delta storage; the base MUST NOT be # modified once children exist — vm-create only re-syncs it when the laptop # source hash differs). # shellcheck disable=SC2034 LAB_LIB="$(readlink -f "${BASH_SOURCE[0]}")" : "${REPO:=$(cd "$(dirname "$LAB_LIB")/.." && pwd)}" export REPO log() { printf 'pflab: %s\n' "$*"; } warn() { printf 'pflab WARN: %s\n' "$*" >&2; } die() { printf 'pflab FAIL: %s\n' "$*" >&2; exit 1; } lab_env() { [ -f "$REPO/lab/ips.env" ] || die "missing $REPO/lab/ips.env" # shellcheck disable=SC1091 . "$REPO/lab/ips.env" # bare-IP derivatives (route/via fields) PF0_LAN_IP=${PF0_LAN%%/*}; PF0_WAN_IP=${PF0_WAN%%/*}; PF0_WAN2_IP=${PF0_WAN2%%/*} CLI_IP=${CLI%%/*}; SRV_IP=${SRV%%/*}; GW2_WAN_IP=${GW2_WAN%%/*}; GW2_WAN2_IP=${GW2_WAN2%%/*} LAB_SEEDS="$REPO/lab/seeds" VMS="pf0 cli srv gw2" } SSH_OPTS=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 -o LogLevel=ERROR -o ControlMaster=auto -o ControlPersist=120 -o "ControlPath=$HOME/.ssh/cm-pflab-%r@%h:%p") vm_name() { echo "pftest-$1"; } vm_ip() { local v=${1^^}_SSH; [ -n "${!v-}" ] || die "unknown vm alias: $1"; echo "${!v}"; } # Run command(s) on deb. deb() { ssh "${SSH_OPTS[@]}" "$DEB_USER@$DEB" "$@"; } deb_cmd() { deb "bash -s" <<<"$1"; } # Run a shell script inside a lab VM (nested ssh from deb with the lab key). vm_cmd() { local ip=$1; deb "ssh -i $LAB_KEY_DEB ${SSH_OPTS[*]} root@$ip 'bash -s'" <<<"$2"; } # --- template rendering: replace @KEY@ tokens from environment + extra K=V --- RENDER_KEYS=( DEB DEB_USER PF0_MGMT PF0_LAN PF0_WAN PF0_WAN2 CLI CLI_GW SRV SRV_GW GW2_WAN2 GW2_WAN PF0_SSH CLI_SSH SRV_SSH GW2_SSH PF0_LAN_IP PF0_WAN_IP PF0_WAN2_IP CLI_IP SRV_IP GW2_WAN_IP GW2_WAN2_IP MGMT_NET NET_PF_LAN NET_PF_WAN NET_PF_WAN2 PF_LAN_HOSTIP PF_WAN_HOSTIP PF_WAN2_HOSTIP PF_NET_PREFIX VM_VCPUS VM_MEM_MB PF0_MAC_MGMT PF0_MAC_LAN PF0_MAC_WAN PF0_MAC_WAN2 CLI_MAC_MGMT CLI_MAC_LAN SRV_MAC_MGMT SRV_MAC_WAN GW2_MAC_WAN GW2_MAC_WAN2 ROOT_PASS LAB_PACKAGES IMG_DIR BASE_IMG SEED_DIR DEPLOY_DIR BOOT_DIR LAB_KEY_DEB LAB_KEY_LAPTOP LAPTOP_SSH_FRAG BASE_SRC_REL ) _esc() { local s=$1; s=${s//\\/\\\\}; s=${s//&/\\&}; s=${s//\~/\\~}; s=${s//$'\n'/\\n}; printf '%s' "$s"; } render() { # render