#!/bin/sh # Make config.boot carry `vif 53 disable` while the RUNNING config keeps the # 10 gig up. Run on the master. Idempotent. # # WHY THIS IS AWKWARD # # The convention is that BOTH routers' config.boot hold `vif 53 disable`, so a # reboot in any order comes up unable to claim the cloned MAC # (f0:9f:c2:12:9b:4f) and vrrp-wan-reconcile then enables it on whichever box # holds the VIP. The master's RUNNING config must have it enabled -- it is # carrying the WAN -- so config.boot and the running config must deliberately # disagree. # # VyOS gives no clean way to express that. `save` writes the RUNNING config, # not the candidate: setting the node, saving, then discarding was tested in # labsim and config.boot came back without `disable`, the WAN untouched. So the # only route is to genuinely disable it, save that, and re-enable -- a real, # brief interruption of the 10 gig. # # WHAT THE INTERRUPTION ACTUALLY COSTS # # Not the internet, on a healthy pair: pppoe0 is up on the master and the # failover route falls to it while bond0.53 is down, which is exactly the # behaviour T5 proves in labsim (LAN back in 5s). Traffic moves to the slower # line and back. Expect a few seconds, plus however long the ISP takes to # re-issue the DHCP lease afterwards. # # IF THIS SCRIPT DIES HALFWAY it leaves the master with bond0.53 disabled -- # which vrrp-wan-reconcile repairs within 30s ("MASTER with bond0.53 disabled # -> enabling"). The failure mode is bounded by design, not by luck. # # sudo /config/vif53-pin-boot-disable do it # sudo /config/vif53-pin-boot-disable --check report only, change nothing VIF=53 BOOT=/config/config.boot cfg() { /opt/vyatta/bin/vyatta-op-cmd-wrapper show configuration commands 2>/dev/null; } boot_has() { awk "/vif ${VIF} {/,/^ }/" "$BOOT" 2>/dev/null | grep -qc disable 2>/dev/null; } boot_disable_count() { awk "/vif ${VIF} {/,/^ }/" "$BOOT" 2>/dev/null | grep -c disable; } run_disable_count() { cfg | grep -c "vif ${VIF} disable"; } report() { printf ' running config disable : %s\n' "$(run_disable_count)" printf ' config.boot disable : %s\n' "$(boot_disable_count)" printf ' bond0.%s address : %s\n' "$VIF" \ "$(ip -4 addr show "bond0.${VIF}" 2>/dev/null | sed -n 's/.*inet \([0-9.]*\).*/\1/p')" } if [ "${1:-}" = "--check" ]; then report; exit 0; fi if [ "$(boot_disable_count)" -ge 1 ]; then echo " config.boot already pins 'vif ${VIF} disable' -- nothing to do" report exit 0 fi # Refuse on a box that is not carrying the WAN: there the running config should # already have `disable`, and a plain `save` is all that is needed. Doing the # dance here would be pointless downtime. if [ "$(run_disable_count)" -ge 1 ]; then echo " this box already has 'vif ${VIF} disable' in the running config;" echo " a plain 'save' is enough and costs nothing. Not touching the WAN." exit 0 fi # Serialise against vrrp-wan-reconcile by taking ITS lock. Without this the two # commit at the same time and VyOS refuses one of them with "Configuration # system temporarily locked due to another commit in progress" -- observed in # labsim, where the `save` landed but the RE-ENABLE did not, leaving the master # with its 10 gig down. The reconciler uses `flock -n` and simply skips a tick # it cannot get, so holding this is cheap and safe. exec 9>/run/vrrp-wan.lock if ! flock -w 60 9; then echo " could not take /run/vrrp-wan.lock within 60s -- is a commit stuck?" echo " refusing to race vrrp-wan-reconcile for the config lock." exit 1 fi logger -t vif53-pin "pinning 'vif ${VIF} disable' into config.boot -- bond0.${VIF} will bounce" echo " bouncing bond0.${VIF} to get 'disable' into config.boot..." t0=$(date +%s) # 9>&- so the config session's long-lived unionfs-fuse child does not inherit # the lock fd and hold it for ever -- the same trap vrrp-wan-reconcile documents. /bin/vbash 9>&- <