Template NM WAN pieces: netplan split, cellular keyfile, new host variables
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KqhjP7nud5gdBg25zCiRk9
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
wifis:
|
||||
@WIFI_WAN_IFACE@:
|
||||
renderer: NetworkManager
|
||||
optional: true
|
||||
dhcp4: true
|
||||
# IPv6 is deliberate as of 2026-07-12: NM does SLAAC/DHCPv6 itself.
|
||||
# Before this it only worked by accident — dracut's runtime catch-all
|
||||
# /run/systemd/network/zzzz-dracut-default.network had networkd
|
||||
# co-managing wlan0 (second DHCPv4 client + surprise IPv6); deploy.sh
|
||||
# now masks that file. Privacy (temporary) addresses stay off so hbd
|
||||
# and DNS see one stable source address per uplink.
|
||||
dhcp6: true
|
||||
ipv6-privacy: false
|
||||
# never-default: see the Starlink stanza above — same reasoning, applies to every
|
||||
# van-failover-managed WAN.
|
||||
networkmanager:
|
||||
passthrough:
|
||||
ipv4.never-default: "true"
|
||||
access-points:
|
||||
"Wapana":
|
||||
auth:
|
||||
key-management: "psk"
|
||||
password: "6e1335fd97165a7d2618bec19824be363a2766d7765f91aa14773d871eaa59dc"
|
||||
@@ -33,26 +33,3 @@ network:
|
||||
networkmanager:
|
||||
passthrough:
|
||||
ipv4.never-default: "true"
|
||||
wifis:
|
||||
@WIFI_WAN_IFACE@:
|
||||
renderer: NetworkManager
|
||||
optional: true
|
||||
dhcp4: true
|
||||
# IPv6 is deliberate as of 2026-07-12: NM does SLAAC/DHCPv6 itself.
|
||||
# Before this it only worked by accident — dracut's runtime catch-all
|
||||
# /run/systemd/network/zzzz-dracut-default.network had networkd
|
||||
# co-managing wlan0 (second DHCPv4 client + surprise IPv6); deploy.sh
|
||||
# now masks that file. Privacy (temporary) addresses stay off so hbd
|
||||
# and DNS see one stable source address per uplink.
|
||||
dhcp6: true
|
||||
ipv6-privacy: false
|
||||
# never-default: see the Starlink stanza above — same reasoning, applies to every
|
||||
# van-failover-managed WAN.
|
||||
networkmanager:
|
||||
passthrough:
|
||||
ipv4.never-default: "true"
|
||||
access-points:
|
||||
"Wapana":
|
||||
auth:
|
||||
key-management: "psk"
|
||||
password: "6e1335fd97165a7d2618bec19824be363a2766d7765f91aa14773d871eaa59dc"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# NetworkManager connection for the cellular WAN (see failover/config.json, van-failover).
|
||||
# Created by deploy.sh only when absent; never rewritten. No modem needs to be present.
|
||||
# never-default: van-failover is the sole owner of the default route (same as the Wi-Fi/Starlink profiles).
|
||||
[connection]
|
||||
id=@CELLULAR_CONN_NAME@
|
||||
uuid=@CELLULAR_UUID@
|
||||
type=gsm
|
||||
autoconnect=true
|
||||
|
||||
[gsm]
|
||||
apn=@CELLULAR_APN@
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
route-metric=300
|
||||
never-default=true
|
||||
|
||||
[ipv6]
|
||||
method=auto
|
||||
@@ -9,6 +9,7 @@
|
||||
# # POSTCHECK_WAIT=<s>: seconds retried probes may wait in total (default 15; 0 = single attempt)
|
||||
# ./deploy.sh --classify-unit <ActiveState> <Result> <ExecMainStatus> # prints ok|finished|wait|warn (used by post_check; for tests)
|
||||
# ./deploy.sh --classify-radio # reads `iw dev <if> info` text on stdin, prints beaconing|silent (no ssid line = silent; used by post_check; for tests)
|
||||
# ./deploy.sh --render-wan-netplan # rendered /etc/netplan/50-van-wan.yaml (ethernet part + wifis stanza when WIFI_WAN_IFACE is set)
|
||||
# ./deploy.sh render <file> # preview a templated file on stdout
|
||||
# ./deploy.sh --redact # filter stdin, masking secret values (used by --diff; for tests)
|
||||
# Idempotent. Netplan (wlan0 + Starlink NIC = NM-managed WANs; eth0 is a LAN
|
||||
@@ -25,18 +26,20 @@ VANLINK_CONF=${VANLINK_CONF:-/etc/vanlink.conf}
|
||||
|
||||
# Must be defined (non-empty) in the host file; deploy aborts naming any missing.
|
||||
REQUIRED_VARS=(
|
||||
WIFI_5G_IFACE WIFI_2G_IFACE LAN_IFACE STARLINK_IFACE WIFI_WAN_IFACE
|
||||
WIFI_5G_IFACE WIFI_2G_IFACE LAN_IFACE STARLINK_IFACE
|
||||
GATEWAY_IP GATEWAY_PREFIX GATEWAY_NETWORK DHCP_RANGE_START DHCP_RANGE_END
|
||||
DNS_RESOLVERS MODEM_USB_VENDOR AP_SSID_5G AP_SSID_2G AP_5G_RADIO
|
||||
ENABLE_COCKPIT ENABLE_LI3 ENABLE_NVME_WATCH ENABLE_HA ENABLE_FRIGATE
|
||||
ENABLE_NM_WAN
|
||||
)
|
||||
# May be empty or absent; NVME_* are required only when ENABLE_NVME_WATCH=1.
|
||||
OPTIONAL_VARS=(
|
||||
WIFI_WAN_IFACE CELLULAR_CONN_NAME CELLULAR_APN
|
||||
ONBOARD_LAN_IFACE NVME_DEVICE NVME_FS_DEVICE
|
||||
USB_BACKUP_ROOT_PART USB_BACKUP_MOUNT SD_BACKUP_UUID
|
||||
)
|
||||
# Computed in load_conf; usable as @TOKEN@ like the rest.
|
||||
DERIVED_VARS=(ONBOARD_LAN_UNMANAGED)
|
||||
DERIVED_VARS=(ONBOARD_LAN_UNMANAGED CELLULAR_UUID)
|
||||
TOKEN_VARS=("${REQUIRED_VARS[@]}" "${OPTIONAL_VARS[@]}" "${DERIVED_VARS[@]}")
|
||||
TOKEN_RE='@[A-Z][A-Z0-9_]*@'
|
||||
|
||||
@@ -53,7 +56,7 @@ load_conf() { # load_conf <file> — source and validate a host config
|
||||
[ -n "${!v:-}" ] || missing+=("$v")
|
||||
done
|
||||
[ ${#missing[@]} -eq 0 ] || die "$f is missing required variable(s): ${missing[*]}"
|
||||
for flag in ENABLE_COCKPIT ENABLE_LI3 ENABLE_NVME_WATCH ENABLE_HA ENABLE_FRIGATE; do
|
||||
for flag in ENABLE_COCKPIT ENABLE_LI3 ENABLE_NVME_WATCH ENABLE_HA ENABLE_FRIGATE ENABLE_NM_WAN; do
|
||||
case ${!flag} in
|
||||
0|1) ;;
|
||||
*) die "$f: $flag must be 0 or 1 (got '${!flag}')" ;;
|
||||
@@ -64,6 +67,7 @@ load_conf() { # load_conf <file> — source and validate a host config
|
||||
|| die "$f: ENABLE_NVME_WATCH=1 needs NVME_DEVICE and NVME_FS_DEVICE"
|
||||
fi
|
||||
ONBOARD_LAN_UNMANAGED=${ONBOARD_LAN_IFACE:+;interface-name:$ONBOARD_LAN_IFACE}
|
||||
CELLULAR_UUID=$(uuidgen -s -n @dns -N "vanlink-${CELLULAR_CONN_NAME:-}" 2>/dev/null || true)
|
||||
}
|
||||
|
||||
render() { # render <file> -> stdout, with @TOKEN@ placeholders substituted
|
||||
@@ -74,6 +78,16 @@ render() { # render <file> -> stdout, with @TOKEN@ placeholders substituted
|
||||
sed "${args[@]}" "$1"
|
||||
}
|
||||
|
||||
# The netplan is two templates: ap/50-van-wan.yaml (ethernet/Starlink) and
|
||||
# ap/50-van-wan-wifi.yaml (the wifis: stanza, appended verbatim only for hosts
|
||||
# with a Wi-Fi uplink, so a host without one gets no empty/invalid wifis: block).
|
||||
render_wan_netplan() { # ap/50-van-wan.yaml, plus the wifis stanza when the host has a Wi-Fi uplink
|
||||
render ap/50-van-wan.yaml
|
||||
if [ -n "${WIFI_WAN_IFACE:-}" ]; then
|
||||
render ap/50-van-wan-wifi.yaml
|
||||
fi
|
||||
}
|
||||
|
||||
check_rendered() { # check_rendered <src> <rendered-file> — fail on a leftover @TOKEN@
|
||||
local left
|
||||
left=$(grep -oE "$TOKEN_RE" "$2" | sort -u | tr '\n' ' ') || true
|
||||
@@ -277,6 +291,7 @@ case "${1:-}" in
|
||||
--redact) # ./deploy.sh --redact — stdin through redact_secrets (no config, no root)
|
||||
redact_secrets
|
||||
exit 0 ;;
|
||||
--render-wan-netplan) load_conf "$VANLINK_CONF"; render_wan_netplan; exit 0 ;;
|
||||
--classify-unit) # ./deploy.sh --classify-unit <ActiveState> <Result> <ExecMainStatus>
|
||||
classify_unit "${2:-}" "${3:-}" "${4:-}"
|
||||
exit 0 ;;
|
||||
@@ -365,10 +380,10 @@ echo "== netplan drift check =="
|
||||
# see note below), so it's easy to edit ap/50-van-wan.yaml and forget the
|
||||
# manual `netplan apply` step. Warn loudly rather than silently drifting.
|
||||
if [ ! -f /etc/netplan/50-van-wan.yaml ]; then
|
||||
warn "netplan: /etc/netplan/50-van-wan.yaml is missing — repo config was never deployed. Run: sudo ./deploy.sh render ap/50-van-wan.yaml | sudo tee /etc/netplan/50-van-wan.yaml && sudo netplan generate && sudo netplan apply"
|
||||
elif ! diff -q <(render ap/50-van-wan.yaml) /etc/netplan/50-van-wan.yaml >/dev/null 2>&1; then
|
||||
warn "netplan: /etc/netplan/50-van-wan.yaml differs from ap/50-van-wan.yaml (rendered). Deploy manually: sudo ./deploy.sh render ap/50-van-wan.yaml | sudo tee /etc/netplan/50-van-wan.yaml && sudo netplan generate && sudo netplan apply"
|
||||
diff -u /etc/netplan/50-van-wan.yaml <(render ap/50-van-wan.yaml) || true
|
||||
warn "netplan: /etc/netplan/50-van-wan.yaml is missing — repo config was never deployed. Run: sudo ./deploy.sh --render-wan-netplan | sudo tee /etc/netplan/50-van-wan.yaml && sudo netplan generate && sudo netplan apply"
|
||||
elif ! diff -q <(render_wan_netplan) /etc/netplan/50-van-wan.yaml >/dev/null 2>&1; then
|
||||
warn "netplan: /etc/netplan/50-van-wan.yaml differs from ap/50-van-wan.yaml (rendered). Deploy manually: sudo ./deploy.sh --render-wan-netplan | sudo tee /etc/netplan/50-van-wan.yaml && sudo netplan generate && sudo netplan apply"
|
||||
diff -u /etc/netplan/50-van-wan.yaml <(render_wan_netplan) || true
|
||||
fi
|
||||
|
||||
echo "== access point =="
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
"wans": [
|
||||
{ "name": "wifi", "device": "@WIFI_WAN_IFACE@", "metric": 100 },
|
||||
{ "name": "starlink", "device": "@STARLINK_IFACE@", "metric": 200 },
|
||||
{ "name": "cellular", "connection": "Koodo", "metric": 300 }
|
||||
{ "name": "cellular", "connection": "@CELLULAR_CONN_NAME@", "metric": 300 }
|
||||
]
|
||||
}
|
||||
|
||||
+5
-1
@@ -5,7 +5,7 @@ WIFI_5G_IFACE=wlp1s0 # MediaTek MT7925E (PCIe); 2.4GHz AP is the sepa
|
||||
WIFI_2G_IFACE=wlx3c3332002066 # D-Link USB dongle
|
||||
LAN_IFACE=enP1p17s0
|
||||
STARLINK_IFACE=enP2p33s0
|
||||
WIFI_WAN_IFACE=wlan0 # not present on this host (kept for the failover config)
|
||||
WIFI_WAN_IFACE= # none: no spare radio (the R5C's two radios are both APs)
|
||||
ONBOARD_LAN_IFACE=
|
||||
|
||||
GATEWAY_IP="10.42.1.1"
|
||||
@@ -33,3 +33,7 @@ ENABLE_LI3=1
|
||||
ENABLE_NVME_WATCH=0 # no NVMe on this board
|
||||
ENABLE_HA=1
|
||||
ENABLE_FRIGATE=1
|
||||
ENABLE_NM_WAN=1
|
||||
|
||||
CELLULAR_CONN_NAME=Koodo
|
||||
CELLULAR_APN=sp.koodo.com
|
||||
|
||||
@@ -12,7 +12,7 @@ WIFI_5G_IFACE=wlp1s0 # 5GHz AP radio (hostapd)
|
||||
WIFI_2G_IFACE=wlx000000000000 # 2.4GHz AP radio (hostapd-2g)
|
||||
LAN_IFACE=enp1s0 # wired LAN port, bridged into br0
|
||||
STARLINK_IFACE=enp2s0 # Starlink uplink (NetworkManager WAN)
|
||||
WIFI_WAN_IFACE=wlan0 # Wi-Fi uplink (NetworkManager WAN)
|
||||
WIFI_WAN_IFACE=wlan0 # optional: Wi-Fi uplink (NetworkManager WAN); empty = none
|
||||
ONBOARD_LAN_IFACE= # optional: extra onboard LAN port bridged into br0 (e.g. eth0); empty = none
|
||||
|
||||
# --- Access point -------------------------------------------------------------
|
||||
@@ -47,3 +47,7 @@ ENABLE_LI3=0 # Li3 house-battery BLE -> MQTT service
|
||||
ENABLE_NVME_WATCH=0 # NVMe timeout watchdog (needs an NVMe drive)
|
||||
ENABLE_HA=0 # Home Assistant + ESPHome (podman quadlets)
|
||||
ENABLE_FRIGATE=0 # Frigate NVR (podman quadlet)
|
||||
ENABLE_NM_WAN=1 # NetworkManager owns the WANs (netplan, cellular profile) via --apply-network
|
||||
|
||||
CELLULAR_CONN_NAME=Koodo # optional: NM name of the cellular connection (also used by van-failover)
|
||||
CELLULAR_APN=sp.koodo.com # optional: cellular APN
|
||||
|
||||
@@ -33,3 +33,7 @@ ENABLE_LI3=0
|
||||
ENABLE_NVME_WATCH=0 # boots from mmcblk0; no NVMe fitted
|
||||
ENABLE_HA=1
|
||||
ENABLE_FRIGATE=0
|
||||
ENABLE_NM_WAN=1
|
||||
|
||||
CELLULAR_CONN_NAME=Koodo
|
||||
CELLULAR_APN=sp.koodo.com
|
||||
|
||||
@@ -37,7 +37,8 @@ fi
|
||||
# (plain `install` would ship the placeholder unrendered — the 2026-09-23 failure)
|
||||
# vanrouter.js: installed by the loop in the cockpit block; 50-van-wan.yaml: reference copy,
|
||||
# rendered by hand via `deploy.sh render` (deploy.sh only warns when it differs)
|
||||
exempt=" cockpit/vanrouter/vanrouter.js ap/50-van-wan.yaml "
|
||||
# 50-van-wan-wifi.yaml, cellular.nmconnection: installed by dedicated code, not `install_rendered <src> /abs/dst` literal lines
|
||||
exempt=" cockpit/vanrouter/vanrouter.js ap/50-van-wan.yaml ap/50-van-wan-wifi.yaml ap/cellular.nmconnection "
|
||||
for f in $(./deploy.sh --templates); do
|
||||
case "$exempt" in *" $f "*) continue ;; esac
|
||||
if grep -qE "^[[:space:]]*install_rendered +$f[[:space:]]" deploy.sh; then
|
||||
@@ -62,9 +63,11 @@ expect van ap/nftables.conf 'ip saddr 10.42.1.0/24'
|
||||
expect wan ap/nftables.conf 'ip saddr 10.42.0.0/24'
|
||||
expect van ap/van-br0-setup.service 'brctl addif br0 enP1p17s0'
|
||||
expect wan ap/van-br0-setup.service 'brctl addif br0 enx00e04c331140'
|
||||
expect van failover/config.json '"device": "wlan0"'
|
||||
expect van router-status/config.json '"wifi_wan_iface": "wlan0"'
|
||||
expect van failover/van-wlan-watchdog.service 'van-wlan-watchdog wlan0'
|
||||
expect van failover/config.json '"device": ""'
|
||||
expect wan failover/config.json '"device": "wlan0"'
|
||||
expect van router-status/config.json '"wifi_wan_iface": ""'
|
||||
expect van failover/van-wlan-watchdog.service 'ExecStart=/usr/local/sbin/van-wlan-watchdog'
|
||||
expect wan failover/van-wlan-watchdog.service 'van-wlan-watchdog wlan0'
|
||||
expect van ap/van-ap-unmanaged.conf 'interface-name:enP1p17s0'
|
||||
expect wan ap/van-ap-unmanaged.conf 'interface-name:enx00e04c331140;interface-name:eth0'
|
||||
expect wan ap/23-van-lan-eth0.network 'Name=eth0'
|
||||
@@ -202,4 +205,27 @@ for row in "beaconing:$r_ok" "silent:$r_bad" "silent:"; do
|
||||
if [ "$got" = "$want" ]; then ok "--classify-radio -> $want"; else bad "--classify-radio gave '$got', want '$want'"; fi
|
||||
done
|
||||
|
||||
# 14. the netplan split leaves the Pi 5's rendered WAN netplan byte-identical, and the R5C (no Wi-Fi WAN) omits wifis
|
||||
old=$(mktemp)
|
||||
git show 223652b:ap/50-van-wan.yaml > "$old"
|
||||
want=$(VANLINK_CONF=hosts/wan.conf ./deploy.sh render "$old")
|
||||
tmpw=$(mktemp)
|
||||
# render_wan_netplan is internal; expose it through --render-wan-netplan (documented in the header)
|
||||
VANLINK_CONF=hosts/wan.conf ./deploy.sh --render-wan-netplan > "$tmpw"
|
||||
if [ "$want" = "$(cat "$tmpw")" ]; then ok "wan.conf: split netplan renders identical to the old single template"; else bad "wan.conf: netplan render changed by the split"; diff <(echo "$want") "$tmpw" | head -10 | sed 's/^/ /'; fi
|
||||
if VANLINK_CONF=hosts/van.conf ./deploy.sh --render-wan-netplan | grep -q '^ wifis:'; then bad "van.conf: wifis stanza must be omitted (WIFI_WAN_IFACE empty)"; else ok "van.conf: no wifis stanza"; fi
|
||||
rm -f "$tmpw" "$old"
|
||||
|
||||
# 15. cellular connection name comes from the host config
|
||||
expect van failover/config.json '"connection": "Koodo"'
|
||||
expect wan failover/config.json '"connection": "Koodo"'
|
||||
|
||||
# 16. cellular keyfile template renders a valid NM gsm profile with a stable uuid
|
||||
for h in van wan; do
|
||||
k=$(VANLINK_CONF=hosts/$h.conf ./deploy.sh render ap/cellular.nmconnection)
|
||||
for pat in '^id=Koodo$' '^type=gsm$' '^apn=sp.koodo.com$' '^route-metric=300$' '^never-default=true$' '^uuid=[0-9a-f-]{36}$'; do
|
||||
if echo "$k" | grep -qE "$pat"; then ok "$h: cellular keyfile has $pat"; else bad "$h: cellular keyfile lacks $pat"; fi
|
||||
done
|
||||
done
|
||||
|
||||
exit "$fail"
|
||||
|
||||
Reference in New Issue
Block a user