#!/usr/bin/perl
#
# scan job execution log files from the SDSC Paragon system, and use convert2swf.pm
# to convert the data into the standard workload format.
#

use warnings;
use strict;
use Time::Local;
use POSIX;
use convert2swf;


############################
#
# DO THE CONVERSION:
#

my $tz_str    = "US/Pacific";
my $max_procs = 400;
my @queues    = ("interactive",
		 "q4t",
		 "q4s",
		 "q8s",
		 "qf8s",
		 "q16s",
		 "qf16s",
		 "q32s",
		 "qf32s",
		 "q64s",
		 "q256s",
		 "q32m",
		 "qf32m",
		 "q64m",
		 "qf64m",
		 "q128m",
		 "qf128m",
		 "q256m",
		 "qf256m",
		 "q1l",
		 "q32l",
		 "qf32l",
		 "q64l",
		 "qf64l",
		 "q128l",
		 "qf128l",
		 "q256l",
		 "qf256l",
		 "standby",
		 "fstandby",
		 "q1ll",
		 "q320m",
		 "holding",
		 "test"
		 );

$ENV{TZ} = $tz_str;
POSIX::tzset();

SWF::init( $tz_str, $max_procs, \@queues );

my $format_bugs;
my $jobs_data = get_jobs();

my ($zero, $maxtime) = SWF::convert( $jobs_data, $format_bugs );

my $header = generate_header( $zero, $maxtime, $max_procs );

SWF::print( $header );



#####################################
# SPECIFICS for SDSC Paragon workload

#
# the format of each entry includes the following space-separated fields:
# 0  ???
# 1  ???
# 2  ???
# 3  nodes
# 4  class (1=batch, 0=interactive)
# 5  CPU hours
# 6  charge rate for CPU hours
# 7  idle CPU hours
# 8  charge rate for idle CPU hours
# 9  under used - unused
# 10 charge rate - unused
# 11 total charge = sum (CPU * rates)
# 12 submit date
# 13 submit time
# 14 start date
# 15 start time
# 16 end date
# 17 end time
# 18 completion status (1=success)
# 19 queue time limit (CPU hours)
# 20 queue name

my $job_cnt;

sub get_jobs {
##############
# parse input format and create a hash for each job

   my @list_of_jobs;

   #
   # scan trace and collect job info
   #
   while (<>) {

       #
       # parse line and extract info
       #

       if (/^\s*$/) {          # empty line
           next;
       }

       $_ =~ /\s*(.*)\s*/;             # get rid of white space at ends
       $_ = $1;
       my @line = split('\s+');        # split into fields

       if ($#line != 20) {
           $format_bugs .= "format_problem $_\n";
           warn(">>>Format problem on $_\ngot $#line fields (should be 20)");
           next;
       }

       my %nj;

       #
       # set the desired fields as specified in convert2swf.pl
       #

       $nj{trace}  = $_;       # used for error messages

       # translate human readable start date and time into time array
       my $start;
       if ($line[14] ne "Unknown") {
	   my @begtmarr;
	   ($begtmarr[4],$begtmarr[3],$begtmarr[5]) = split('/',$line[14]);
	   ($begtmarr[2],$begtmarr[1],$begtmarr[0]) = split(':',$line[15]);
	   $begtmarr[4]--;
	   $start = &timelocal(@begtmarr);
       }
       else {
	   $start = -1;
       }

       $nj{start} = $start;

       # translate human readable end date and time into time array
       my $end;
       if ($line[16] ne "Unknown") {
	   my @endtmarr;
	   ($endtmarr[4],$endtmarr[3],$endtmarr[5]) = split('/',$line[16]);
	   ($endtmarr[2],$endtmarr[1],$endtmarr[0]) = split(':',$line[17]);
	   $endtmarr[4]--;
	   $end = &timelocal(@endtmarr);
       }
       else {
	   $end = -1;
       }

       $nj{end} = $end;

       # translate human readable submit date and time into time array
       # for interactive jobs, use start time
       my $submit;
       if ($line[4] == 1) {	# batch
	   if (($line[12] ne "Unknown") && ($line[12] ne "N/A")) {
	       my @subtmarr;
	       ($subtmarr[4],$subtmarr[3],$subtmarr[5]) = split('/',$line[12]);
	       ($subtmarr[2],$subtmarr[1],$subtmarr[0]) = split(':',$line[13]);
	       $subtmarr[4]--;
	       $submit = &timelocal(@subtmarr);
	   }
	   else {
	       $submit = -1;
	   }
       }
       else {			# interactive
	   $submit = $start;
       }

       $nj{submit} = $submit;

       # processors
       my $procs = $line[3];

       $nj{procs} = $procs;

       # CPU time is given as a total, in hours.
       # we need it per node in seconds...
       if ($procs > 0) {
	   my $cpu = ($line[5] * 3600) / $procs;

	   $nj{cpu} = $cpu;
       }

       $nj{status} = $line[18];

       $nj{user}   = $line[0];

       # queue and partition
       if ($line[4] == 0) {	# interactive

	   $nj{queue}     = "interactive";
	   $nj{partition} = 1;
       }
       else {
	   $nj{queue}     = $line[20];
	   $nj{partition} = 2;
       }

       $list_of_jobs[$job_cnt++] = \%nj;
   }

   return \@list_of_jobs;
}


sub generate_header {
   my ($zero, $maxtime, $max_procs) = @_;

   my ($sec, $min, $hr, $mday, $mon, $year, $wday, $yday, $isdst);

   $header = "; Version: 2.2
; Computer: Intel Paragon
; Installation: San Diego Supercomputer Center (SDSC)
;
; Acknowledge: Reagan Moore and Allen Downey
; Information: http://www.sdsc.edu/
;              http://www.cs.huji.ac.il/labs/parallel/workload/
;
";

   $header .= sprintf("; Conversion: Dror Feitelson (feit\@cs.huji.ac.il) %s\n",
                      strftime("%d %b %Y", localtime()));

   $header .= sprintf("; MaxJobs: %d\n", $job_cnt);
   $header .= sprintf("; MaxRecords: %d\n", $job_cnt);
   $header .= "; Preemption: No\n";

   $header .= sprintf("; UnixStartTime: %d\n", $zero);
   $header .= "; TimeZone: 0\n";
   $header .= sprintf("; TimeZoneString: %s\n", $tz_str);

   $header .= sprintf("; StartTime: %s\n",
          strftime("%a %b %d %H:%M:%S %Z %Y", localtime($zero)));

   $header .= sprintf("; EndTime:   %s\n",
          strftime("%a %b %d %H:%M:%S %Z %Y", localtime($maxtime)));

   $header .= "; MaxNodes: $max_procs (48 interactive, 352 batch, 6 service, 10 I/O)\n";
   $header .= "; MaxProcs: $max_procs\n";
   $header .= "; MaxPartitions: 2\n";
   $header .= "; Partitions: 1 = interactive partition (48  nodes)\n";
   $header .= "; Partitions: 2 = batch partition       (352 nodes)\n";
   $header .= "; Partitions: system also has service and I/O partitions that are not used to run jobs\n";

   return $header;
}
