Have you ever had the problem you want your Synology NAS to be powered off automatically when it's not in use but you did not set up an automated shutdown cycle because of you could not be sure whether there are still some network related jobs up and running?
Hey, my name is "Tux" and this tutorial will show you how you can control your Synology NAS via bash script in a more power saving way so that it will automatically shutdown due to a specific amount of data (not) being transferred over specific Synology NAS' network interface(s) within a specific amount of time.
Note
Important note!
Important note!
sshpass
-command and just do the according following small one-line modification on both of the lines: Script modification for certificate authentication
sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host"…
Basic pre-information
127.0.0.1
) unless you'll find an SNMP-client package for DSM to install. That said, it is necessary to put the script onto a remote Linux system (Ubuntu 18.04 server-VM on VMware ESXi-host in my case) which on the other hand keeps your Synology NAS save from ending in a shutdown loop in case of script misconfiguration. Of course you could also place an Ubuntu-VM onto the Synology NAS itself when using Synology's Virtual Machine Manager, this would make some code lines inside the script unnecessary (e.g. PING commands) but would work anyway, so feel free to adjust the script if you want or leave it untouched in any case, it does not affect the script's functionality.
This tutorial is based on the following two systems which was tested successfully according to its functionality:
System preparation
Synology NAS
Now make sure that the root
user on your Synology NAS has permissions to login directly. To accomplish this, please follow this tutorial.
Linux SNMP-client system
$ apt install snmp
Optional: On your Linux client download and install the sshpass package:
$ apt-get install sshpass
Now, before go closer to the implementation, it is important to know about best practice on how to start the first time with this script:
First of all, like already mentioned, you already had to decide whether it makes sense for you to use the script at all because it is network traffic dependent only. This basically means, when a specific amount of data is not being transferred over the Synology NAS' network ports over a specific amount of time, you can reliably assume that the Synology NAS is not in use (which you could not when using any other physical data interface other than LAN). As in my case, all my interactions with my Synology NAS are always heavy network traffic dependent (automated weekly backup jobs, copying large/a lot of files over SMB/NFS, media server, and so on…) and I want to have my Synology NAS powered off when none of those jobs are running. So this script is exactly what I want to have because it lets me automate the Synology NAS' power control completely.
So now, if you are willing to use the script too (which I assume you do when you've reached over here), you have to decide how long you want to have your Synology NAS running idle before it shuts down automatically. To give you an idea, in my case, I took one hour (for the script that would be a value of 3600
seconds) and a shutdown-limit of 500 MB (for the script that would be a value of 500000000
Bytes) for every single of the Synology NAS' network interfaces.
This means: If the amount of data being transferred over any of the Synology NAS' network ports does not exceed 500 MB within one hour, the Synology NAS is going to shutdown.
For your specific case you have to decide two things:
Note: You can even do a log history cycle over night or even a few days if you want because I've implemented a counter which will increase after every loop pass. This counter value is also being added to the filename(s) being created, just study the script a little bit and you'll find out what it does if you are interested in it.
Now there are a few more things to mention:
You may think that I was doing it too granular when getting SNMP values twice for every of my four network interfaces and even differentiate incoming and outgoing traffic. Of course you could also add up all values together twice and then do a single substraction but when you leave the script almost untouched and use it like I did you will be more flexible if, for example, you would want to exclude a specific network interface from being monitored for whatever reason.
Now, let's stop messing around and just start with the script!
First, according to official Synology MIB documents, to find out the specific SNMP-OID and the according network interface name per network interface, from an SNMP client Linux system, execute the following command (for <mySNMPCommunity>
set the community name value as configured recently):
$ snmpwalk -v2c -c "<mySNMPCommunity>" "<mySynologyIPAddress>" .1.3.6.1.2.1.31.1.1.1.1
Now customize all required script parameters (starting with <my…
) on the following script, including replacing the SNMP-OIDs which you should got now from the command output above. The following script is based on four network interfaces, just customize the amount if you have more/less network interfaces to process.
#!/bin/bash # # By default this script assumes 4 network interfaces to process. # If you have to process more/less than 4 network interfaces please look out for the three "##++/--" characters # inside the script code which are marking where you have to do the according modifications (basically it is just # adding/removing existing codelines). # # --------------------------- GENERAL SETTINGS --------------------------- # Startup delay in seconds: sleep 5 # Synology NAS SNMP community: community="<myCommunity>" # Synology NAS IP address: host="<myIPAddress>" # Synology SSH port number: hostSSHPort="<mySSHPort>" # Synology NAS root password: hostRootPassword="<myRootPassword>" # Synology NAS' data shutdown-limit per network interface in bytes: shutdownLimit="<myShutdownLimit>" # Sleeptime between stage 1 and stage 2 in seconds: sleepTime="<mySleeptime>" # Declare a general associative array: declare -A array # # Set interface name(s) for the according Synology network interfaces to be monitored. # You are free to choose a random names without any effection on the scripts' functionality # but it is recommended to match the typical descriptions like the ones you'll get when you search # through SNMP tables, e.g. "eth(x)". # Note: It is also possible to monitor virtual network interfaces. # Example: # if1="eth0" if1="<myNetworkInterfaceName1>" if2="<myNetworkInterfaceName2>" if3="<myNetworkInterfaceName3>" if4="<myNetworkInterfaceName4>" ##++/-- # # Declare OID variables for [IN]coming network traffic: # Example: # declare "$if1"_IN_OID_var="iso.3.6.1.2.1.31.1.1.1.6.3" declare "$if1"_IN_OID_var="<mySynologyNetworkInterface1_IN_OID>" declare "$if2"_IN_OID_var="<mySynologyNetworkInterface2_IN_OID>" declare "$if3"_IN_OID_var="<mySynologyNetworkInterface3_IN_OID>" declare "$if4"_IN_OID_var="<mySynologyNetworkInterface4_IN_OID>" ##++/-- # Set OID variables for [IN]coming network traffic: if1_IN_OID="$if1"_IN_OID_var if2_IN_OID="$if2"_IN_OID_var if3_IN_OID="$if3"_IN_OID_var if4_IN_OID="$if4"_IN_OID_var ##++/-- # Declare OID variables for [OUT]going network traffic: declare "$if1"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>" declare "$if2"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>" declare "$if3"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>" declare "$if4"_OUT_OID_var="<mySynologyNetworkInterface1_OUT_OID>" ##++/-- # Set OID variables for [OUT]going network traffic: if1_OUT_OID="$if1"_OUT_OID_var if2_OUT_OID="$if2"_OUT_OID_var if3_OUT_OID="$if3"_OUT_OID_var if4_OUT_OID="$if4"_OUT_OID_var ##++/-- # # --------------------------- OPTIONAL SETTINGS --------------------------- # # --------------------- File logging ------------------------- # Activate "file logging" ( ["y"]es / ["n"]o )? (Default = no) # BEWARE: If activated, according to your settings, this may # create a lot of files in the according user's home folder! # You should activate this only for testing/analyzing purposes! fileLogging="n" # --------------------- Soft mode ------------------------- # Activate "test mode" ( ["y"]es / ["n"]o )? (Default = no) # If activated, the script will never execute the shutdown # command for the Synology NAS. Therefore the NAS will never # physically shut down but will return an echo value "powerOff!". # Activate this option for command line testing purposes if needed. testMode="n" # --------------------- Curfew time mode ------------------------- # Activate "Curfew time mode" ( ["y"]es / ["n"]o )? (Default = no) # If activated, you can enter a specific weekday number (MO=1, SU=7), # a specific start- and an end-hour in which the Synology NAS will # be prevented from shut down, for example if you want to specifiy # a time range for your regularly data backup cycles where you want # this script to be inactive. # Note: It would not make sense to go more granular than hours, # so minutes and even seconds have been left over. # Feel free to adjust the script if you really want minutes and/or # seconds to be implemented. # Also feel free to add more than a single curfew time mode, in this # case please adjust the script for your specific requirements. curfewTimeMode="n" curfewTimeDay="<myCurfewTimeDay>" curfewTimeStartHour="<myCurfewTimeStartHour>" curfewTimeEndHour="<myCurfewTimeEndHour>" softMode="n" # --------------------------------------------------------- # # --------------------------- START SCRIPT LOOP --------------------------- # while true; do # Test mode check: if [ "$testMode" == "y" ]; then echo "testModeEnabled!" && echo $(date) fi # Increase loop counter: let "loopCounter=loopCounter+1" echo "Loop-No.: "$loopCounter"" && echo $(date) # # *** Stage 1 *** # # Check whether the system is reachable over network... ping -c 1 "$host" > /dev/null hostPingResult="$?" # ...if yes: case "$hostPingResult" in 0) echo "Stage 1: case 0: pingResponse!" && echo $(date) # Get the system uptime since last boot in seconds: hostUptimeCheck=$(sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host" "awk '{print \$1}' /proc/uptime") # If system uptime since first successful PING response is less than 120 seconds, assume it has recently (re)booted. # In this case wait for 60 seconds to make sure the system's SNMP-services are ready: if [ "${hostUptimeCheck%.*}" -lt "120" ]; then echo "System has recently (re)booted, sleep for 60 seconds!" && echo $(date) sleep 60 fi # Get SNMP stage 1 values for the according eth(x)-interfaces for [IN]coming/[OUT]going traffic since the last system reboot. # # *** [IN]coming traffic *** array["$if1"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_IN_OID}) array["$if2"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_IN_OID}) array["$if3"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_IN_OID}) array["$if4"_IN_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_IN_OID}) ##++/-- # # *** [OUT]going traffic array["$if1"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_OUT_OID}) array["$if2"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_OUT_OID}) array["$if3"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_OUT_OID}) array["$if4"_OUT_stage1]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_OUT_OID}) ##++/-- # Sleeptime between stage 1 and stage 2 (between measuring points): echo "Entering stage 2 in "$sleepTime" seconds!" && echo $(date) sleep "$sleepTime" # # *** Stage 2 *** ping -c 1 "$host" > /dev/null hostPingResult="$?" case "$hostPingResult" in 0) echo "Stage 2: case 0: pingResponse!" && echo $(date) # Get SNMP stage 2 values for the according eth(x)-interfaces for [IN]coming/[OUT]going traffic since the last system reboot. # # *** [IN]coming traffic *** array["$if1"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_IN_OID}) array["$if2"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_IN_OID}) array["$if3"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_IN_OID}) array["$if4"_IN_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_IN_OID}) ##++/-- # # *** [OUT]going traffic array["$if1"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if1_OUT_OID}) array["$if2"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if2_OUT_OID}) array["$if3"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if3_OUT_OID}) array["$if4"_OUT_stage2]=$(snmpget -v2c -Oqv -c "$community" "$host" ${!if4_OUT_OID}) ##++/-- # # Check whether the system has rebooted between stage 1 and stage 2: if [ "${array["$if1"_IN_stage1]}" -le "${array["$if1"_IN_stage2]}" ] && \ [ "${array["$if2"_IN_stage1]}" -le "${array["$if2"_IN_stage2]}" ] && \ [ "${array["$if3"_IN_stage1]}" -le "${array["$if3"_IN_stage2]}" ] && \ [ "${array["$if4"_IN_stage1]}" -le "${array["$if4"_IN_stage2]}" ] && \ [ "${array["$if1"_OUT_stage1]}" -le "${array["$if1"_OUT_stage2]}" ] && \ [ "${array["$if2"_OUT_stage1]}" -le "${array["$if2"_OUT_stage2]}" ] && \ [ "${array["$if3"_OUT_stage1]}" -le "${array["$if3"_OUT_stage2]}" ] && \ [ "${array["$if4"_OUT_stage1]}" -le "${array["$if4"_OUT_stage2]}" ]; then ##++/-- # # Calculate the effective amount of transferred [IN]coming and [OUT]going data per network interface: array["$if1"_diff_IN]=$((${array["$if1"_IN_stage2]} - ${array["$if1"_IN_stage1]})) array["$if2"_diff_IN]=$((${array["$if2"_IN_stage2]} - ${array["$if2"_IN_stage1]})) array["$if3"_diff_IN]=$((${array["$if3"_IN_stage2]} - ${array["$if3"_IN_stage1]})) array["$if4"_diff_IN]=$((${array["$if4"_IN_stage2]} - ${array["$if4"_IN_stage1]})) array["$if1"_diff_OUT]=$((${array["$if1"_OUT_stage2]} - ${array["$if1"_OUT_stage1]})) array["$if2"_diff_OUT]=$((${array["$if2"_OUT_stage2]} - ${array["$if2"_OUT_stage1]})) array["$if3"_diff_OUT]=$((${array["$if3"_OUT_stage2]} - ${array["$if3"_OUT_stage1]})) array["$if4"_diff_OUT]=$((${array["$if4"_OUT_stage2]} - ${array["$if4"_OUT_stage1]})) ##++/-- # # File logging: if [ "$fileLogging" == "y" ]; then printf "${array["$if1"_diff_IN]}" > ~/"$if1"_LoopNo"$loopCounter"_IN_transferredData.txt printf "${array["$if2"_diff_IN]}" > ~/"$if2"_LoopNo"$loopCounter"_IN_transferredData.txt printf "${array["$if3"_diff_IN]}" > ~/"$if3"_LoopNo"$loopCounter"_IN_transferredData.txt printf "${array["$if4"_diff_IN]}" > ~/"$if4"_LoopNo"$loopCounter"_IN_transferredData.txt printf "${array["$if1"_diff_OUT]}" > ~/"$if1"_LoopNo"$loopCounter"_OUT_transferredData.txt printf "${array["$if2"_diff_OUT]}" > ~/"$if2"_LoopNo"$loopCounter"_OUT_transferredData.txt printf "${array["$if3"_diff_OUT]}" > ~/"$if3"_LoopNo"$loopCounter"_OUT_transferredData.txt printf "${array["$if4"_diff_OUT]}" > ~/"$if4"_LoopNo"$loopCounter"_OUT_transferredData.txt ##++/-- fi # # Check per network interface if data shutdown-limit has been exceeded (if yes, shutdown the System): if [ "${array["$if1"_diff_IN]}" -lt "$shutdownLimit" ] && \ [ "${array["$if2"_diff_IN]}" -lt "$shutdownLimit" ] && \ [ "${array["$if3"_diff_IN]}" -lt "$shutdownLimit" ] && \ [ "${array["$if4"_diff_IN]}" -lt "$shutdownLimit" ] && \ [ "${array["$if1"_diff_OUT]}" -lt "$shutdownLimit" ] && \ [ "${array["$if2"_diff_OUT]}" -lt "$shutdownLimit" ] && \ [ "${array["$if3"_diff_OUT]}" -lt "$shutdownLimit" ] && \ [ "${array["$if4"_diff_OUT]}" -lt "$shutdownLimit" ]; then ##++/-- # Curfew time mode check: case "$curfewTimeMode" in y) # Check current day and hour: currentDay=$(date +%u) currentHour=$(date +%H) # Check whether current day and hour is within curfew time: if [ "$currentDay" == "$curfewTimeDay" ] && [ "$currentHour" -ge "$curfewTimeStartHour" ] && [ "$currentHour" -le "$curfewTimeEndHour" ]; then softMode="y" echo "softModeEnabled!" && echo $(date) # Test mode check: elif [ "$testMode" == "n" ]; then softMode="n" fi ;; n) ;; esac # Soft mode check: case "$softMode" in n) # Test mode check: if [ "$testMode" == "n" ]; then sshpass -p "$hostRootPassword" ssh -p "$hostSSHPort" root@"$host" 'sudo poweroff' # After "powerOff" command was executed wait 30 seconds before continuing to make sure all system's network interfaces were shut down properly and therefore will not send any PING reply anymore. # This prevents this script from entering into "Stage 1" unnecessarily again: echo "hardMode powerOff! Sleep for 30 seconds!" && echo $(date) sleep 30 fi echo "(testmode)hardMode powerOff!" && echo $(date) ;; y) echo "softMode powerOff!" && echo $(date) ;; esac fi else echo "System has rebooted between stage 1 and stage 2!" && echo $(date) fi ;; 1) echo "Stage 2: case 1: noPingResponse!" && echo $(date) ;; 2) echo "Stage 2: case 2: pingError!" && echo $(date) ;; esac ;; 1) echo "Stage 1: case 1: noPingResponse!" && echo $(date) ;; 2) echo "Stage 1: case 2: pingError!" && echo $(date) ;; esac # Delay before repeating the script to prevent uneccessary load. echo "Sleep for 60 seconds before repeating loop!" && echo $(date) sleep 60 echo "endOfLoop!" && echo $(date) done;
After configuring all of the script's parameters according to your requirements you have to put the script onto your Linux SNMP-client system. Then make the script executable:
$ chmod +x <myScript>.sh
Then run the script manually to do all required testings to figure out your specific settings for later automation.
After those testings you should have figured out the matching script values for your personal requirements. So after configuring the script according to your requirements open the crontab editor on your Linux SNMP-client system:
$ crontab -e
Now, to make the script run automatically on system reboot, which would be best practice in most cases, just insert the following line to the crontab file to add the specific cronjob:
@reboot /<myPathToMyScript>/<myScript>.sh
…or alternatively, if you want to log the script's output (stdout and stderr) into a file permanently (the logfile will be deleted after every system reboot when it is saved into the /tmp
directory, you have to put it into another path (e.g. /home/<myUser>
) if you don't want it to be deleted on reboot):
@reboot sleep 30 && /<myPathToMyScript>/<myScript>.sh >> /tmp/<myScript>.log 2>&1
Now save it and quit the crontab editor.
This way the script will start automatically everytime you reboot your Linux SNMP-client system and your networkload based Synology NAS shutdown script has successfully been set up.
Appreciate my work?
Buy me a coffee or PayPal