#!/usr/bin/perl


#
# This script is a component of Warewulf,
# http://www.runlevelzero.net/greg/warewulf
#
#########################################################################
#
# Copyright (c) 2003, The Regents of the University of California, through
# Lawrence Berkeley National Laboratory (subject to receipt of any
# required approvals from the U.S. Dept. of Energy).  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# The GNU GPL Document can be found at:
# http://www.gnu.org/copyleft/gpl.html
#
#########################################################################
#
#	this module is written and maintained by:
#	Geoff Galitz
#	galitz@berkeley.edu, geoff@galitz.org
#	Oct 4, 2005
#
#	version 1.1
#
#
#	This is the Wake-On-Lan module for warewulf.  It powers down
#	idle nodes and wakes sleeping nodes when there is demand for
#	more processors.
#

BEGIN {
	push (@INC, "/usr/lib/warewulf/");
}

use Warewulf::Config;
use Warewulf::Status;
use Warewulf::Util;
use Unix::Syslog qw(:macros);
use Unix::Syslog qw(:subs);

#use Schedule::DRMAAc;  # for DRMAA use

# declare and initialize some variables

$NODE=""; # real name of a node
$NODEN="";	# nodename holder
$STATE="0";	# state of the node 
$COUNTER="0";	# loop counter
$STATUS="";	# idle or not
$WAKE=0;	# WAKE up node flag
$i=0;		# loop counter
$j=0;		# loop counter
$k=0;		# loop counter
$l=0;		# loop counter
$IDLEC=0;	# how many idle nodes
$DOWNER="";	# Nodes that are down
my @DOWNNODES;  # array containing down nodes info



if ( -f '/var/run/wol.pid' ) {
	print "wol daemon already running!\n";
	open(PID, "/var/run/wol.pid");
	$pid = <PID>;
	close PID;
	print "PID=$pid\n";
	exit 1;
}
 fork and exit;

$pid = $$;
open(PID, '> /var/run/wol.pid')
   or warn "Could not create PID file at: '/var/run/wol.pid'\n";
print PID $pid;
close PID;


# opand and configure the syslog interface
openlog wol, LOG_PID,  LOG_LOCAL7;
syslog LOG_INFO, "wol starting";



#create an array/hash thing
#initialize the  array of nodes
%nodearray = &node_status();
foreach (sort keys %nodearray) {
	#$NODELIST = join " ", $_, $NODELIST;
	@TMP_NODESTAT = split;
	push @NODESTAT, [@TMP_NODESTAT];
}

# get the size of the array for futher mucking around
$NODE_INIT=@NODESTAT;
#print "NODE_INIT = $NODE_INIT\n";

#initialize the state of the nodes in the array
#I wish there was a way to do this in one pass.
while ( $i < $NODE_INIT) {
	$NODESTAT[$i][1] = "$STATE";
	$i++;
}

while (1) {
	$WAKE=0;
	$j = 0;
	$IDLEC=0;
	%nodestatus = &node_status();
	foreach (sort keys %nodestatus) {
		$NODEN = $_;
		@SYSNODES = split (/:/,$NODEN);
		$NODE = $SYSNODES[1];
	#$NODEN = $nodestatus{$_}{NODENAME};
	#syslog LOG_INFO, "reading %s", $SYSNODES[1];
		for (@NODESTAT and $j < $NODE_INIT) {
	#syslog LOG_INFO, "$NODESTAT[$j][0] = %s and $NODEN = %s",$NODESTAT[$j][0], $NODEN;
			if ($NODESTAT[$j][0] = $NODEN) {
				#may need to change the READY tag
				#if ($nodestatus{$NODEN}{CPUUTIL} < 1 and $nodestatus{$NODEN}{NODESTATUS} eq "READY") {
	#syslog LOG_INFO, "CPU for %s  = %s", $NODEN, $nodestatus{$NODEN}{CPUUTIL};
	#syslog LOG_INFO, "STATE for %s  = %s", $NODEN, $nodestatus{$NODEN}{NODESTATUS};
				if ($nodestatus{$NODEN}{CPUUTIL} < 1 and $nodestatus{$NODEN}{NODESTATUS} eq "READY") {
				# if we are here, then the node is idle
				# here we keep track of how long a node is idle for
					$IDLEC++;
					#print "IDLEC = $IDLEC\n";
					$k = $NODESTAT[$j][1];
					$k++;
					$NODESTAT[$j][1] = $k;
					#print "==IDLE $NODESTAT[$j][0] :: $NODESTAT[$j][1]\n";
	#syslog LOG_INFO, "reading %s with status %s", $NODEN, $nodestatus{$_}{NODESTATUS};
					$j++;
					if ( $k >= 60 and $IDLEC > 1 ) {
						#system ("/usr/bin/rsh $NODEN '/bin/echo ASLEEP > /.nodestatus'");
						#sleep 1;
						system ("/usr/bin/rsh $NODE /sbin/shutdown -h now");
	#					syslog LOG_INFO, "Putting node %s to sleep", $NODEN;
						syslog LOG_INFO, "Putting node %s to sleep", $NODE;
					} else {
						sleep 1;
					}
					#print "REPEAT == IDLE $NODESTAT[$j][0] :: $NODESTAT[$j][1]\n";
					last;
				} else {
				# if we are there, then the node is non-IDLE
					#$k = 0;
					#$NODESTAT[$j][1] = $k;
					$NODESTAT[$j][1] = 0;
					#print "==NON IDLE $NODESTAT[$j][0] :: $NODESTAT[$j][1]\n";
					$j++;
					last;
				}
			} 
		}

	}
#	$COUNTER++;
#	sleep 2;

	$l = 0;
	while (@NODESTAT and $l < $NODE_INIT) {
	sleep 1;
	# here we determine if we need more nodes
		#syslog LOG_INFO, "STAT = %s %s ",$NODESTAT[$l][0],  $NODESTAT[$l][1];
		if ( $NODESTAT[$l][1] != 0 ) {
			$WAKE++;
			#syslog LOG_INFO, "Some nodes are IDLE and waiting.";
			#syslog LOG_INFO, "Incrementing WAKE, WAKE = %d", $WAKE;
			last;
		} else {
			$WAKE = 0;
			$l++;
		}
	}
	if ($WAKE eq 0) {
	sleep 1;
	# here we acutally wake new nodes
		%nodestatus = &node_status();
		foreach (sort keys %nodestatus) {
		$NODEN = $_;
		@SYSNODES = split (/:/,$NODEN);
		$NODE = $SYSNODES[1];
			if ($nodestatus{$NODEN}{NODESTATUS} ne "READY") {
				syslog LOG_INFO, "Looking for nodes to wake...";
				$DOWNER=`wwlist -q -d --config "hardware addr"`;  
				@DOWNNODES = split(' ',$DOWNER);
				if ( ! $DOWNNODES[0] ) {
					syslog LOG_INFO, "There are no sleeping nodes to wake. All nodes are busy, down or unavailable.";
					sleep 60;
				} else {
					system ("ether-wake $DOWNNODES[1]");
					syslog LOG_INFO, "Attempting to wake %s MAC ADDR: %s", $DOWNNODES[0], $DOWNNODES[1];
					sleep 60;
					last;
					# add counter to wake multiple nodes
				}
			}
		}
	} else {
		sleep 2;
	}
}

syslog LOG_INFO, "wol shutting down.";
closelog;
