#!/usr/bin/perl
#========================================================================
#
# $Id: wulfweb 17 2006-07-20 17:54:04Z rgb $
#
# See accompanying copyright and accompanying file COPYING
#
#========================================================================

 use lib qw( /usr/lib/perl );
 use FileHandle;
 use IO::Handle;
 use Getopt::Std;	# Single character options are plenty.

#========================================================================
# We parse the CL with getopt standard (single character)
#========================================================================
# Option list
 getopts('d:f:hi:p:t:v:');
 $verbose = 0;
 $wulfhosts = "$ENV{HOME}/.wulfhosts";
 $html = "wulf2html.html";
# Try a 10 second refresh cycle for such a long display
 $delay = 10;
# Assignments
 if($opt_d) {$delay = $opt_d;}
 if($opt_f) {$wulfhosts = $opt_f;}
 if($opt_h) {Usage();exit;}
 if($opt_i) {$includefile = $opt_i;}
 if($opt_t) {$type = $opt_t;}
 if($opt_v) {$verbose = $opt_v;}

# If leftovers, punt with Usage()
 $ARGC = @ARGV;
 if($ARGC > 1) {
   Usage("Incorrect number or type of arguments");
   exit;
 }
 if($ARGV[0]){
   $html = $ARGV[0];
 }
 $html = "$html";
 $html_tmp = "$html.tmp";

 if($type == ""){
   $type = 0;
 }

 if($verbose){
   print "Running wulf2html to create $html.\n";
 }

 # DATA ARRAYS
 # For each display type (numbered) we need header[$type][$i]
 # where the contents are the $i indexed column headers.
 # For each display type we ALSO need a data array of exactly the
 # same structure that contains the returns per host line.  There
 # is no easy way to build them, really.

 @numfields = (16,6,13,12,9,10,10);
 @header = (
[" Name "," Status "," Timestamp "," load1 "," load5 "," load15 "," rx bytes "," tx bytes "," si "," so "," pi "," po "," ctxt "," intr "," prun "," pblk "],
[" Name "," Status "," Timestamp "," load1 "," load5 "," load15 "],
[" Name "," Status "," Timestamp "," m-tot "," m-used "," m-free "," m-free* "," s-tot "," s-used "," s-free "," m-shr "," m-buf "," m-cch "],
[" Name "," IP "," Status "," Timestamp "," dev# "," rx_bts "," rx_pks "," rx_ers "," tx_bts "," tx_pks "," tx_ers "," tcp_sk "],
["  Name "," Status "," Timestamp "," CPU Model Name "," ClkM "," CchK "," Time "," Uptime "," Dty% "],
[" Name "," Status "," Timestamp "," PID "," Nice "," User "," Vsize "," RSS "," Time "," Command "],
[" Name "," Status "," Timestamp "," PID "," Nice "," User "," Vsize "," RSS "," Time "," Command "]
);

 #  nodedata[] will be overwritten.
 @nodedata = ();

 # A color table for load averages
 for($i=0;$i<100;$i++){
   $loadcolor[$i] = sprintf("#%02x00%02x",2*$i+28,227-2*$i);
 }

 if($includefile){
   # Start first html file
   if($verbose){
     print "Opening $includefile for use as a header.\n";
   }
   open(INCLUDE,"<$includefile") || print "Sorry, I couldn't open $includefile: $!\n";
   if($verbose){
     print "Done!\n";
   }
 } else {
   if($verbose) {
     print "No include file used.\n";
   }
 }
 $icnt = 0;
 while(<INCLUDE>){
   $include[$icnt] = $_;
   if($verbose){
     print "Including header:";
     print "$include[$icnt]";
   }
   $icnt++;
 }

 # Set local time for display.
 $time = localtime;

 if($verbose){
   print "Opening wulflogger stream.\n";
 }
 open WULFLOGGER,"wulflogger -t $type -d $delay -f $wulfhosts 2>&1 |" or die "Sorry, I couldn't open wulflogger process: $!\n";
 if($verbose){
   print "WULFLOGGER is now wulflogger -t $type -d $delay -f $wulfhosts\n";
   print "Done.\n";
 }

 # Start html file
 # Insert header
 # Start new html file
 $output = "";
 $output .= printheader($type,$icnt);
 # Insert table header
 $output .= tablehead($type);

 if($verbose){
   print "Opening temporary html file at $html_tmp\n";
 }
 open(WEBOUT,">$html_tmp") || die "Sorry, I couldn't open $html_tmp: $!\n";
 if($verbose){
    print "Done!\n";
 }
 print WEBOUT "$output";
 close WEBOUT;
 rename $html_tmp,$html;

 # Restart for real, this time (with delay).
 $output = "";
 $output .= printheader($type,$icnt);
 # Insert table header
 $output .= tablehead($type);

 while(<WULFLOGGER>){
   if($verbose){
     print "Loop: Read in $_";
   }
   chop;
   if(!/^#/){
     $output .= tableline($type,$_);
   } else {
     $output .= tablefoot();
     $output .= printfooter($hcnt);
     # Start new html file
     if($verbose){
       print "Opening temporary html file at $html_tmp\n";
     }
     open(WEBOUT,">$html_tmp") || die "Sorry, I couldn't open $html_tmp: $!\n";
     if($verbose){
       print "Done!\n";
     }
     print WEBOUT "$output";
     close WEBOUT;
     rename $html_tmp,$html;
     $output = "";

     $time = localtime;

     # Insert header
     $output .= printheader($type,$icnt);

     # Insert table header
     $output .= tablehead($type);
   }
 }

 exit;

# Very simple, fully automatic, using @header array defined above.
sub tablehead {

 my $tabletype = shift;
 my $i;
 my $buffer = "";
 $buffer .= "
 <table border=\"2\" cellpadding=\"2\">
  <tr>
";
 for($i = 0;$i < $numfields[$tabletype];$i++){
   $buffer .= "   <td align=\"center\"> $header[$tabletype][$i] </td>\n";
 }
 $buffer .= "  </tr>\n";

 return($buffer);

}     

sub tableline {

 my $tabletype = shift;
 my $i;
 my $j;
 my $nodedatalen;
 my $cmdline;
 my $modelname;
 my $buffer = "";
 $_ = shift;
 @nodedata =  split(/\s+/,$_);
 if($nodedata[1] =~ m/down/) {
   $buffer .= "
   <tr bgcolor=\"#000000\">
     <td align=\"center\"><font color=\"FFFFFF\"><b>$nodedata[0]</b></font></td>
";
   for($i = 1;$i < $numfields[$tabletype];$i++){
     $buffer .= "     <td align=\"center\"><font color=\"FFFFFF\"><b>down</b></font></td>\n";
   }
   $buffer .= "   </tr>\n";
 } else {
   # Transform nodedata as required for disply.
   # $tabletype = 4.  CPU model name is several words (alas)
   # I expect we have to count in from the end.
   if($tabletype == 4){
     $nodedatalen = @nodedata;  # the number of fields
     $modelname = $nodedata[3]; # model starts in field three
     $i = $nodedatalen - $numfields[$tabletype]-1; # number of words
     $j = 4;
     while($i > 0){
       $modelname .= " $nodedata[$j]";
       $j++;
       $i--;
     }
     $i = 3;
     $nodedata[$i] = $modelname;
     while($j<$nodedatalen){
       $nodedata[$i+1] = $nodedata[$j];
       $j++;
       $i++;
     }
     $nodedata[6] .= " $nodedata[7]";
     $nodedata[7] = $nodedata[8];
     $nodedata[8] = $nodedata[9];
   }


   # $tabletype = 6, where we have to collect the rest of the
   # output command lines (of unknown length).
   if($tabletype == 6){
     $nodedatalen = @nodedata;  # the number of fields
     $cmdline = $nodedata[$numfields[$tabletype]-1];
     $i = $numfields[$tabletype];
     while($i < $nodedatalen){
       $cmdline .= " $nodedata[$i]";
       $i++;
     }
     $nodedata[$numfields[$tabletype]-1] = $cmdline;
   }

   if($tabletype == 3){
     # network has an extra "status" field"
     $nodedata[3] = int($nodedata[3]);
   } else {
     $nodedata[2] = int($nodedata[2]);
   }

   # To the extent that we do specialized/cool things like use
   # different colors, we'll have to use a sort of case switch here.
   if($tabletype == 0 || $tabletype == 1){
     $colorindex = int($nodedata[3]*25);
     if($colorindex > 99){
       $colorindex = 99;
     }
     $bgcolor = "$loadcolor[$colorindex]";
     $fgcolor = "FFFF00";
   } elsif($tabletype == 3){
     # We'll try a bit of a colormap on networking too.
     # 100BT \approx 90MB/sec (usually) so 1MB -> 1
     $packettot = $nodedata[5] + $nodedata[8];
     $colorindex = int($packettot/100000);
     if($colorindex > 99){
       $colorindex = 99;
     }
     $bgcolor = "$loadcolor[$colorindex]";
     $fgcolor = "FFFF00";
   } elsif($tabletype == 5 || $tabletype == 6){
     $_ = $nodedata[$numfields[$tabletype]-1];
     if(/Idle/){
       $bgcolor = "FFFFFF";
       $fgcolor = "000000";
     } else {
       $bgcolor = "FFFF55";
       $fgcolor = "000000";
     }
   } else {
       $bgcolor = "FFFFFF";
       $fgcolor = "000000";
   }
   $buffer .= "<tr bgcolor=$bgcolor>\n";
   for($i = 0;$i < $numfields[$tabletype];$i++){
     $buffer .= "     <td align=\"center\"><font color=$fgcolor><b>$nodedata[$i]</b></font></td>\n";
   }
   $buffer .= "  </tr>\n";
 }
 return $buffer;

}

sub tablefoot {

 my $buffer = "";
 $buffer .= " </table>\n";
 return $buffer;

}     

sub printheader {
 my $tabletype = shift;
 my $hcnt = shift;
 my $i;
 my $buffer = "";
 # $hcnt = shift;
 # Insert header
 for($i=0;$i<$hcnt;$i++){
   $buffer .= "$include[$i]";
 }
 $buffer .= "\n<hr>\n";
 $buffer .= "
<meta http-equiv=\"refresh\" content=\"$delay\">
Time: $time<br>";
 if($tabletype == 0 || $tabletype == 1){
   $buffer .= "
Load Average Color Map:
 <table>
  <tr>
";
   $buffer .= "<td bgcolor=\"$loadcolor[0]\"><font color=\"FFFF00\"><b>0.0</b></font></td>";
   for($i = 1;$i<24;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[24]\"><font color=\"FFFF00\"><b>1.0</b></font></td>";
   for($i = 25;$i<49;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[49]\"><font color=\"FFFF00\"><b>2.0</b></font></td>";
   for($i = 50;$i<74;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[74]\"><font color=\"FFFF00\"><b>3.0</b></font></td>";
   for($i = 75;$i<99;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[99]\"><font color=\"FFFF00\"><b>4.0+</b></font></td>";
   $buffer .= "
  </tr>
 </table>
 <hr>
";
 } elsif($tabletype == 3){
   $buffer .= "
Total Bytes/Second Color Map:
 <table>
  <tr>
";
   $buffer .= "<td bgcolor=\"$loadcolor[0]\"><font color=\"FFFF00\"><b>&nbsp;&nbsp;0 MB/sec&nbsp;</b></font></td>";
   for($i = 1;$i<24;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[24]\"><font color=\"FFFF00\"><b>&nbsp;2.5 MB/sec&nbsp;</b></font></td>";
   for($i = 25;$i<49;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[49]\"><font color=\"FFFF00\"><b>&nbsp;5.0 MB/sec&nbsp;</b></font></td>";
   for($i = 50;$i<74;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[74]\"><font color=\"FFFF00\"><b>&nbsp;7.5 MB/sec&nbsp;</b></font></td>";
   for($i = 75;$i<99;$i++){
     $buffer .= "<td bgcolor=\"$loadcolor[$i]\"></td>";
   }
   $buffer .= "<td bgcolor=\"$loadcolor[99]\"><font color=\"FFFF00\"><b>&nbsp;10+ MB/sec</b></font></td>";
   $buffer .= "
  </tr>
 </table>
 <hr>
";
 }

 return $buffer;

}

sub printfooter {

 my $hcnt;
 my $buffer;
 $hcnt = shift;
 if($hcnt){
   $buffer .= "</html>\n";
 }
 return $buffer;

}

sub Usage {

 my $message = shift;
 if($message) {print STDERR "Error: $message\n";}
 print STDERR "
Usage:
  wulf2html [-d delay] [-f wulfhosts] [-i includefile]
      [-p path] [-t type] [-h] [-v level] filename
  -d delay sets the delay used for both wulflogger and page updates.
  -f wulfhosts to specify a particular wulfhosts file.
  -i include file (which must contain html to go before the statistics 
     table in the created web page).
  -t type to control wulflogger \"type\" of display.
  -h displays help/usage
  -v level controls verbosity and debugging output

  filename (required) is the web page created.

All file names should be specified by their whole path.

  Supported types
     0 vmstat
     1 loadavg (default)
     2 memory
";
 exit;
}

