Considering this script was written around static DNS entries, it’ll take some work to implement WoL.
It would be nice for enterprise, but not a necessity for homelab.
I’ll probably let it alone.
Considering this script was written around static DNS entries, it’ll take some work to implement WoL.
It would be nice for enterprise, but not a necessity for homelab.
I’ll probably let it alone.
For future, one trick in RouterOS script is to use comment to store data. So MAC could be stored as comment on your static DNS records. Also you could do same IP/name lookup indirectly using find in DHCP leases or via ARP.
Finally had some time to troubleshoot the script. I had to reverse some of the logic in the code, in order to achieve what I wanted. Also, I had to add the Date/Time vars to each function so the values were correct inside the function.
I was able to manage around an hour of battery runtime on a 1000va UPS, plus the benefit of unattended graceful shutdowns on each host.
Here’s the working script. I hope it helps someone else, even though it’s edge-case.
# [Variables]
:global pwrOutageTime
:local stage
:local threshold
:local srcAddr 192.168.254.1
:local server pve-node1
:local ifaceArray [:toarray "ether7,ether6,ether5,ether4,ether3,ether2"]
:local mtikArray [:toarray "hap2,hap1,rb2,crs2,crs1"]
:local srvDomain srv.domain.local
:local mgmtDomain mgmt.domain.local
:local emailAddr email@example.com
:local linuxCmd "sudo shutdown now"
:local mtikCmd "/system shutdown"
:local srvPing [/ping src-address=$srcAddr address="$server.$srvDomain" count=1]
:local idName [/system identity get name]
/system ups
:local upsName [get ([find]->0) name]
:local upsMon [monitor $upsName once as-value]
:local upsOnline ($upsMon->"on-line")
:local upsRTLeft ($upsMon->"runtime-left")
:local upsLoad ($upsMon->"load")
/system clock
:local cTime [get time]
# [Functions]
:local funcLogWarning do={
:local threshold $1
:local upsRTLeft $2
:local upsLoad $3
:log warning "A/C power interrupted - UPS is on auxiliary power"
:log info " - Current load: $upsLoad %\
\n - Runtime remaining: $upsRTLeft\
\n - Runtime threshold: $threshold"
}
:local funcEmailWarning do={
:local emailAddr $1
:local threshold $2
:local upsRTLeft $3
:local upsLoad $4
/system clock
:local cDate [get date]
:local cTime [get time]
:local cTZone [get time-zone-name]
:tool e-mail send to=$emailAddr\
subject="UPS ALERT: A/C power interrupted"\
body="UPS is on auxiliary power.\
\n\nAuto-shutdown sequence will commence when the runtime threshold is reached.\
\n\n\nUPS Information:\
\n---------------------\
\nCurrent load: $upsLoad %\
\nRuntime remaining: $upsRTLeft\
\nRuntime threshold: $threshold\
\n\n\nDate: $cDate\
\nTime: $cTime ($cTZone)"
}
:local funcLogShutdown do={
:local host $1
:local stage $2
:local threshold $3
:local upsRTLeft $4
/system clock
:local cDate [get date]
:local cTime [get time]
:local cTZone [get time-zone-name]
:log warning "A/C power interrupted - UPS is on auxiliary power\
\n - Runtime threshold ($threshold) has been reached"
:log info " - Runtime remaining: $upsRTLeft\
\n - Stage $stage: Initiating $host shutdown on $cDate at $cTime ($cTZone)"
}
:local funcEmailShutdown do={
:local emailAddr $1
:local stage $2
:local threshold $3
:local upsRTLeft $4
:local upsLoad $5
/system clock
:local cDate [get date]
:local cTime [get time]
:local cTZone [get time-zone-name]
:tool e-mail send to=$emailAddr\
subject="UPS ALERT: Auto-shutdown - $device"\
body="UPS is on auxiliary power.\
\n\nUPS runtime threshold has been reached.\
\n\nAuto-shutdown - Stage $stage:\
\nIncremental shutdown of $device and running services has been initiated.\
\n\n\nUPS Information:\
\n---------------------\
\nCurrent load: $upsLoad %\
\nRuntime remaining: $upsRTLeft\
\nRuntime threshold: $threshold\
\n\n\nDate: $cDate\
\nTime: $cTime ($cTZone)"
}
:local funcShutdown do={
:local srcAddr $1
:local host $2
:local domain $3
:local cmd $4
/system ssh-exec src-address=$srcAddr address="$host.$domain" command=$cmd
}
:local funcNodeIfaceMonitor do={
:local ifaceArray $1
:local nodeArray [:toarray ""]
:foreach iface in=$ifaceArray do={
:local ifaceRunning [/interface get [find name~"$iface"] value-name=running]
:local ifaceName [/interface get [find name~"$iface"] value-name=name]
:local underscorePos [:find $ifaceName "_"]
:if ($underscorePos != nil) do={ :set ifaceName [:pick $ifaceName ($underscorePos + 1) [:len $ifaceName]] }
:if ($ifaceRunning = true) do={ :set nodeArray ($nodeArray, $ifaceName) }
}
:return $nodeArray
}
# [Variables to call functions]
:local nodeArray [$funcNodeIfaceMonitor $ifaceArray]
# [Code]
:if ($upsOnline = false) do={
:if ($pwrOutageTime = "") do={ :set pwrOutageTime $cTime }
:if ($srvPing > 0) do={
:set stage 1
:set threshold 00:10:00
:if ($upsRTLeft <= $threshold) do={
/system scheduler disable auxiliary-auto-shutdown
$funcEmailShutdown $emailAddr $stage $threshold $upsRTLeft $upsLoad device="PVE-Node1"
$funcLogShutdown $server $stage $threshold $upsRTLeft
$funcShutdown $srcAddr $server $srvDomain $linuxCmd
:delay 60s
/system scheduler enable auxiliary-auto-shutdown
} else={
$funcLogWarning $threshold $upsRTLeft $upsLoad
:if ($cTime < ($pwrOutageTime + 20s)) do={ $funcEmailWarning $emailAddr $threshold $upsRTLeft $upsLoad }
}
} else={
:if ($nodeArray != "") do={
:set stage 2
:set threshold 00:15:00
:if ($upsRTLeft <= $threshold) do={
/system scheduler disable auxiliary-auto-shutdown
$funcEmailShutdown $emailAddr $stage $threshold $upsRTLeft $upsLoad device="RKE2 cluster"
:foreach node in=$nodeArray do={
$funcLogShutdown $node $stage $threshold $upsRTLeft
$funcShutdown $srcAddr $node $srvDomain $linuxCmd
:delay 3s
}
:delay 90s
/system scheduler enable auxiliary-auto-shutdown
} else={
$funcLogWarning $threshold $upsRTLeft $upsLoad
:if ($cTime < ($pwrOutageTime + 20s)) do={ $funcEmailWarning $emailAddr $threshold $upsRTLeft $upsLoad }
}
} else={
:set stage 3
:set threshold 00:05:00
:if ($upsRTLeft <= $threshold) do={
$funcEmailShutdown $emailAddr $stage $threshold $upsRTLeft $upsLoad device="MikroTik devices"
:foreach mtik in=$mtikArray do={
$funcLogShutdown $mtik $stage $threshold $upsRTLeft
$funcShutdown $srcAddr $mtik $mgmtDomain $mtikCmd
:delay 3s
}
:local rootDev $idName
$funcLogShutdown $rootDev $stage $threshold $upsRTLeft
:delay 10s
/system shutdown
} else={
$funcLogWarning $threshold $upsRTLeft $upsLoad
:if ($cTime < ($pwrOutageTime + 20s)) do={ $funcEmailWarning $emailAddr $threshold $upsRTLeft $upsLoad }
}
}
}
} else={ :set pwrOutageTime "" }