Shutdown dependent on network activity ?

Status
Not open for further replies.

Child

Dabbler
Joined
Sep 25, 2011
Messages
44
Hi,

after I managed to get the wake on lan working on my freeNAS-Machine. I am trying to set up some kind of automatic shutdown. I'm able to do that through a cronjob and via "shutdown -p now". But I'd like to find a more sophisticated solution opposed to just shutting down the system regardless of whats happening.

So - is there a way to shutdown the system if there's not been any network activity on the NIC for lets say the last 2 hours?
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
The output from "systat -ifstat 1" is quite difficult to parse, so you probably want to look at using snmpget then use a "service" script similar to the one I wrote for idle SAS/SCSI disks here.

Enable SNMP in services, then use "snmpwalk -c public -v 2c localhost ifName" to determine the index (.1, .2) of your ethernet interface.

Then you can get the interface in/out stats using snmpget, eg. if your ethernet interface is index #1, "snmpget -c public -v 2c -Ov localhost ifInOctets.1 ifOutOctets.1".

Poll snmpget periodically, determine the delta between the previous poll and the current poll, and if it's zero for a repeated period of time then you can shutdown your server.

The following example should get you going, though I doubt you'll ever see zero activity on your ethernet interface for any prolonged period due to various heartbeats and other polling activity, in which case you may have to modify the criteria for shutting down the server eg. assume "idle traffic" as being up to, say, 5K/minute in either direction (in or out).

The example below assumes a 10KB in-bound and 2KB out-bound threshold in any particular time period, and would shutdown the server after 10 continuous 60 second periods (ie. 10 minutes) of "idle" or below-threshold traffic.

Code:
#!/bin/sh
#set -xv

TMPFILE=/tmp/ifoctets.dat

SNMP_COMMUNITY=public
SNMP_HOST=localhost
SNMP_INDEX=1
SNMP_GET="/usr/local/bin/snmpget -c $SNMP_COMMUNITY -v 2c -Ov $SNMP_HOST ifInOctets.$SNMP_INDEX ifOutOctets.$SNMP_INDEX"

TIMEOUT=10
TIMEDELAY=60

THRESHOLD_IN=10240
THRESHOLD_OUT=2048

echo "Count    In Delta    Out Delta"

COUNT=$TIMEOUT
OCT_IN2=0
OCT_OUT2=0

while [ true ]; do
	let OCT_IN1=OCT_IN2 >/dev/null
	let OCT_OUT1=OCT_OUT2 >/dev/null

	$SNMP_GET | tr -s '\n' ' ' > $TMPFILE
	read C1 OCT_IN2 C2 OCT_OUT2 < $TMPFILE

	let OCT_IN=OCT_IN2-OCT_IN1 >/dev/null
	let OCT_OUT=OCT_OUT2-OCT_OUT1 >/dev/null

#	if [ ${OCT_IN-0} -le 0 -a ${OCT_OUT-0} -le 0 ]; then
	if [ ${OCT_IN-0} -lt $THRESHOLD_IN -a ${OCT_OUT-0} -lt $THRESHOLD_OUT ]; then
		let COUNT=COUNT-1 >/dev/null
	else
		COUNT=${TIMEOUT}
	fi

	printf " %.3d  %11d  %11d\n" $COUNT $OCT_IN $OCT_OUT

	[ $COUNT -le 0 ] && break || sleep $TIMEDELAY
done

echo "SHUTTING DOWN SERVER"
#shutdown -p now
 

Child

Dabbler
Joined
Sep 25, 2011
Messages
44
Thank you :)
It's just - I'm pretty new to doing this stuff. So I only have a rough idea how to implement the script. Is there a How-To or sth like that? Most of the more precise topics on that are more about freeNAS 0,7(7).
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
Thank you :)
It's just - I'm pretty new to doing this stuff. So I only have a rough idea how to implement the script. Is there a How-To or sth like that? Most of the more precise topics on that are more about freeNAS 0,7(7).

You need to get shell access, are you familiar with Linux/Unix and PuTTY?

Once you have shell access (as root), paste in the code above anywhere you have write access, ie. your storage, give it execute access with "chmod +x ethIdle.sh") then execute the script, eg. "./ethIdle.sh" and watch the output. Obviously if you do this over ssh, your monitoring will cause network activity so to get a more realistic impression of your "idle" network throughput you will want to run it from the console on the server (option #9 from the menu).

You'll need to adapt this into a service yourself, use the aforementioned post as a guide.
 

Child

Dabbler
Joined
Sep 25, 2011
Messages
44
I'm familiar with putty - at least I am able to access the system through ssh.
So - I can just go ahead paste the code into a single file on my win7-Desktop, move that onto the NAS-Volume and execute through shell directly though the freeNAS machine, right?

Well - I guess I'll give it a try tomorrow. It's already 1 am were I am ;)
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
So - I can just go ahead paste the code into a single file on my win7-Desktop, move that onto the NAS-Volume and execute through shell directly though the freeNAS machine, right?

Yes, just make sure the file you save to your Desktop is in UNIX format and not Windows/PC format.

I would recommend installing TextPad (it's free) if you don't have a decent text editor on your PC - when you go to "Save As...", choose UNIX as the File Format.

move that onto the NAS-Volume and execute through shell directly though the freeNAS machine, right?

If you've got your storage mounted via a network share on your Windows PC, you should be able to save your text file directly to your storage then execute it "in place" on FreeNAS, so no need to copy it (it's only a test script, and working with this file saving approach makes making subsequent changes *much* easier!)
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
Well - I guess I'll give it a try tomorrow. It's already 1 am were I am ;)

FYI I'll have limited web access between now and Sunday night (UK time) but if you post back your results I'll pick it up as soon as poss.
 

Child

Dabbler
Joined
Sep 25, 2011
Messages
44
Your help is much appreciated :) Thank you.

Okay - so I'm not in a big rush to get this done ;)
But nevertheless I tried the script and it works absolutely fine. I've changed the variables a little bit - mostly for testing purpose. Didn't want to wait for 10 minutes.

So it looks like this:

Code:
#!/bin/sh
#set -xv

TMPFILE=/tmp/ifoctets.dat

SNMP_COMMUNITY=public
SNMP_HOST=localhost
SNMP_INDEX=1
SNMP_GET="/usr/local/bin/snmpget -c $SNMP_COMMUNITY -v 2c -Ov $SNMP_HOST ifInOctets.$SNMP_INDEX ifOutOctets.$SNMP_INDEX"

TIMEOUT=2
TIMEDELAY=30

THRESHOLD_IN=20480
THRESHOLD_OUT=10240


echo "Delay between polls: $TIMEDELAY sec" 
echo "Count    In Delta    Out Delta"

COUNT=$TIMEOUT
OCT_IN2=0
OCT_OUT2=0

while [ true ]; do
	let OCT_IN1=OCT_IN2 >/dev/null
	let OCT_OUT1=OCT_OUT2 >/dev/null

	$SNMP_GET | tr -s '\n' ' ' > $TMPFILE
	read C1 OCT_IN2 C2 OCT_OUT2 < $TMPFILE

	let OCT_IN=OCT_IN2-OCT_IN1 >/dev/null
	let OCT_OUT=OCT_OUT2-OCT_OUT1 >/dev/null

#	if [ ${OCT_IN-0} -le 0 -a ${OUT_OUT-0} -le 0 ]; then
	if [ ${OCT_IN-0} -lt $THRESHOLD_IN -a ${OUT_OUT-0} -lt $THRESHOLD_OUT ]; then
		let COUNT=COUNT-1 >/dev/null
	else
		COUNT=${TIMEOUT}
	fi

	printf " %.3d  %11d  %11d\n" $COUNT $OCT_IN $OCT_OUT

	[ $COUNT -le 0 ] && break || sleep $TIMEDELAY
done

echo -e "Inbound $THRESHOLD_IN and outbound $THRESHOLD_OUT thresholds have been reached $TIMEOUT times. \nSystem is shutting down."
exit 0
#shutdown -p now


When executed through shell on the machine it works flawlessly (have to remove the exit und the # at the end to get the shutdown).
Because Soccer's on today I have had not much time - I just quickly added the script into a cronjob. But that did not work.

So I've to look into that a bit more.

EDIT: Ah I just forgot to change the end of the script. It works as a cronjob now :) Great!
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
When executed through shell on the machine it works flawlessly (have to remove the exit und the # at the end to get the shutdown).

Great, glad it works for you! :)

EDIT: Ah I just forgot to change the end of the script. It works as a cronjob now :) Great!

Running this under cron really isn't a good idea, as you run the risk of it not running at all or running too often - you don't want multiple copies of this script running all at the same time, so you either put in a measure which detects if another instance is already running which terminates the new instance immediately, or (as suggested in my first post) you convert the script so that it runs as a "service" which means it runs at boot up and then runs all the time (a "daemon").

Of course if it were possible to schedule a job that started at boot up using cron - which is possible with some versions of cron, including the version in FreeBSD 8.2/FreeNAS but not supported by the GUI - then for most people it wouldn't be necessary to go to the lengths of writing "service" scripts. Sadly we don't currently have that option, who knows maybe in the future.
 

Child

Dabbler
Joined
Sep 25, 2011
Messages
44
Okay - well I can't see how the script could be activeted through cron more often than once. I guess the only possible way would that the script never reaches shutdown until the next scheduled cronjob for the script. But since I only run this script at 0:30 am I'm thinking that the shutdown should allways work - there's a 24 hour window for it to shutdown the machine. And 30 min off no-traffic seems very likely at some point.

Or am I missing something? I content with the way it works right now - it shut down the NAS the last two nights without any issues at hand.
Besides - I kinda don't understand fully what I'd have to do to convert this into a "service".
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
I guess the only possible way would that the script never reaches shutdown until the next scheduled cronjob for the script.

Yep.

But since I only run this script at 0:30 am I'm thinking that the shutdown should allways work - there's a 24 hour window for it to shutdown the machine. And 30 min off no-traffic seems very likely at some point.

Fair enough.

Or am I missing something? I content with the way it works right now - it shut down the NAS the last two nights without any issues at hand.
Besides - I kinda don't understand fully what I'd have to do to convert this into a "service".

*Starting* the job will be interesting - unless you always turn on your server at precisely the same time each day, and can schedule this job to start a few minutes later. If your server comes on at random times, I can't see how you can schedule the start of the shutdown job so that it is constantly monitoring the network from startup, unless of course you start the script every minute of every hour of every day (but immediately terminate all duplicate instances other than the first, which would be trivial to implement, but quite nasty as hacks go!)

Of course, if you only want to shut down the server starting from 0:30 and never want to shut it down before that time, then as you are with cron. Although adding a sanity check to avoid multiple instances "just in case" wouldn't be a bad idea, there's a number of ways of achieving this - touching a "lock" file would be the simplest approach, then checking for the existence of this lock file at the start of the script, exiting immediately if found.

Besides - I kinda don't understand fully what I'd have to do to convert this into a "service".

Follow the link in the first post - you need a script that starts and stops the service (pretty easy, basically just change the name of that given in the example) which then calls the main long-running shutdown script (the only major addition to what you have so far is to create a "pid" file - see the example for details). Then you just configure it into your system by enabling it in rc.conf, and commit your changes to the read-only partition - all explained in the example.
 

Dutchy

Cadet
Joined
Sep 4, 2011
Messages
7
Many thanks Milhouse, your script works wonderful!
I've been thinking about writing a script for my own but yours is really elegant and saved me a lot of trail and error.
Now, if I only could get wake on lan to work properly..
 

Ozzmosis

Cadet
Joined
Nov 25, 2011
Messages
1
Hello...

So I am pretty new to everything Linux, FreeBSD and FreeNAS. I have installed FreeNAS 7 on a machine, and its working great except that I want to shutdown the server when there is no activity as per the script posted by Child.

I have gone and tried to get this script to work but I was getting errors related to snmpget, which after lots of head scratching, I found that its not in /usr/local/bin where it should be. I found bsnmpget instead. So I tried to modify the script, and it really isn't working. More head scratching. I'm bald now. haha

It finally dawned on me that this script is written for FreeNAS 8 and not 7, and this should be why its not working.

I was wondering if a person would be so kind as to assist me in getting this script working in FreeNAS 7.

The only other thing I need to work on is getting WOL working. I have to look into the hardware. Not sure if my WOL cable is correct, as i had to make one... Another topic.

Thanks!
 

ProtoSD

MVP
Joined
Jul 1, 2011
Messages
3,348
Ozzmosis,

I'm really not trying to be rude, but you should really repost your request in the FreeNAS 7 Forums, FreeNAS 7 Forums. If we start cross posting FreeNAS 7 questions in these forums it makes it really difficult for people to keep track of what version is being discussed, as just happened with you, and things become chaotic very fast.

Thanks
 

Ismael Duarte

Contributor
Joined
Jun 13, 2011
Messages
154
I could use this script, It seems to be very usefull.
But is not working for me, the NAS is shutting down even when I'm uploading a big file.
Maybe the issue is when i type "snmpget -c public -v 2c -Ov localhost ifInOctets.1 ifOutOctets.1" or "snmpwalk -c public -v 2c localhost ifName" I always get "Timeout: No Response from localhost"

What am I doing wrong?

Thank you!
 

char1iecha1k

Cadet
Joined
Feb 19, 2012
Messages
3
Hi Milhouse

I have tried making a service similar to the one you used with disk spindown however I am having difficulty modifying it. The additional bit of code (I have modified from the sasidle script) and added to the ethidle script is below

Code:
# Being called by rc
if [ ! -z $RC_PID ]; then
        if [ -z $PROCMAIN ]; then
                export PROCMAIN=YES;
                echo test
                exit 0
        fi
        echo $$ >/var/run/ethidled.pid
fi


on boot $RC_PID is populated but PROCMAIN isnt so it always runs whatever command is in the nested if statement and exits.

Also how does this spawn a new process? when testing it sits there waiting for the script to complete before finishing the boot process and allowing me back onto the console, thankfully there is no traffic at this time so it always finishes.
 

Milhouse

Guru
Joined
Jun 1, 2011
Messages
564
You've removed the line that spawns the long running process, and replaced it with "echo test". Go back to the sasidle original, and you'll see that it executes the following:
Code:
zpool iostat ${POOL} ${INTERVAL} | /bin/sh $0 ${ALL_ARGS} | logger -i -t sasidle &


which is to say the output from zpool iostat is piped as input into the "current" script (identified by the special argument, $0) with the output being piped in to logger (so that messages appear in /var/log/messages), and the whole sequence of commands is then run in the background (&). Since PROCMAIN has been set and exported, this second invocation of the script will NOT execute the content of the nested "if". The reason for this "hack" is to ensure the long running process (the backgrounded command sequence) is run with the correct command processor (/bin/sh) so that it can be identified and stopped/restarted by the service control script.

Replacing the line "echo test" with something like "/bin/sh $0 ${ALL_ARGS} | logger -i -t ethidle &" should probably do the trick for what you need.
 

char1iecha1k

Cadet
Joined
Feb 19, 2012
Messages
3
Thanks for the quick reply,

That $0 and the & where the missing bits of info that had me confused.

EDIT
when I first posted this reply I wrote the following below, but since posting I think I realise that isnt necessary as you start and stop the service using the start stop feature and thus dont need to run it interactively.

However there is one other part I dont understand now. When the script gets executed by RC, $RC_PID is populated and $PROCMAIN isnt so the background task gets executed. Now in that background task $RC_PID is populated and $PROCMAIN is so it makes the pid and finishes the script. But if you then run it interactively $RC_PID isnt populated so that whole block of code is skipped and thus it goes on to execute the rest of the script (even though the background one is running). Do you not need a clause before to check whether it is running in the background like so?

Code:
if [ -e /var/run/ethidled.pid ]; then
        echo Process all ready running in the background
        exit 0
fi
# Being called by rc
if [ ! -z $RC_PID ]; then
        if [ -z $PROCMAIN ]; then
                export PROCMAIN=YES;
                /bin/sh $0 ${ALL_ARGS} | logger -i -t ethidle &
                exit 0
        fi
        echo $$ >/var/run/ethidled.pid
fi


Also how do you start stop the daemon after it has booted?
 
Status
Not open for further replies.
Top