#!/usr/bin/perl
#
# scan condensed job execution log files from the CM-5 system at LANL, and
# use convert2swf.pm to convert the data into the standard workload format.
#


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


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

my $tz_str    = "US/Mountain";
my $max_procs = 1024;
my @queues    = ("interactive",
		 "development",
		 "production",
		 "other",
		);


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 LANL CM5 workload

# the format of each entry includes the following space-separated fields:
#  0 job ID
#  1 submit date and time
#  2 start date and time
#  3 end date and time
#  4 requested nodes
#  5 nodes allocated
#  6 requested CPU time (sec)
#  7 used CPU time (sec)
#  8 requested memory (KB total)
#  9 used memory (KB total)
# 10 queue
# 11 dedicated (y/n)
# 12 user
# 13 project
# 14 executable
# 15 exit code
# 16 signal and explanation if killed

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;		# split into fields
	if ($#line < 15) {
	    warn(">>>Format problem on $_\ngot $#line fields (should be >= 15)");
	    next;
	}

	my %nj;

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

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


	# get submit, wait, and run times
	# translate human readable date and time into time array
	my @begtmarr;
	my $submit;
	if ($line[1] ne "unknown") {
	    my ($date,$time) = split('-',$line[1]);
	    ($begtmarr[5],$begtmarr[4],$begtmarr[3]) = split('/',$date);
	    ($begtmarr[2],$begtmarr[1],$begtmarr[0]) = split(':',$time);
	    $begtmarr[4]--;
	    $submit = &timelocal(@begtmarr);
	}
	else {
	    $submit = -1;
	}
	$nj{submit} = $submit;

	my $start;
	if ($line[2] ne "unknown") {
	    my ($date,$time) = split('-',$line[2]);
	    ($begtmarr[5],$begtmarr[4],$begtmarr[3]) = split('/',$date);
	    ($begtmarr[2],$begtmarr[1],$begtmarr[0]) = split(':',$time);
	    $begtmarr[4]--;
	    $start = &timelocal(@begtmarr);
	}
	else {
	    $start = -1;
	}
	$nj{start} = $start;

	if (($submit == -1) && ($start != -1)) {
	    $nj{submit} = $start;
	}

	my $end;
	if ($line[3] ne "unknown") {
	    my ($date,$time) = split('-',$line[3]);
	    ($begtmarr[5],$begtmarr[4],$begtmarr[3]) = split('/',$date);
	    ($begtmarr[2],$begtmarr[1],$begtmarr[0]) = split(':',$time);
	    $begtmarr[4]--;
	    $end = &timelocal(@begtmarr);
	}
	else {
	    $end = -1;
	}
	$nj{end} = $end;


	# number of processors
	my $procs = $line[5];
	$procs = -1 if ($procs eq "unknown");
	$nj{procs} = $procs;

	# CPU time
	my $cpu = $line[7];
	$cpu = -1 if ($cpu eq "unknown");
	$nj{cpu} = $cpu;

	# memory usage (total, not per proc)
	my $mem = $line[9];
	if ($mem eq "unknown") {
	    $mem = -1;
	}
	$nj{memtot} = $mem;

	# requested number of processors
	my $req_procs = $line[4];
	$req_procs = -1 if ($req_procs eq "unknown");
	$nj{req_procs} = $req_procs;

	# requested runtime
	my $req_time = $line[6];
	$req_time = -1 if ($req_time eq "unknown");
	$nj{req_time} = $req_time;

	# requested memory (total, not per proc)
	my $req_mem = $line[8];
	if ($req_mem eq "unknown") {
	    $req_mem = -1;
	}
	$nj{req_memtot} = $req_mem;

	# killed jobs indicated by additional field
	my $status;
	if (defined($line[16])) {
	    if ($line[16] !~ /^sig/) {
		$format_bugs .= "non_sig_exit_problem $_\n";
	    }
	    $status = 0;
	}
	else {
	    $status = 1;
	}
	$nj{status} = $status;

	# user
	my $user = $line[12];
	$user = -1 if ($user eq "unknown");
	$nj{user} = $user;

	# group
	my $group = $line[13];
	$group = -1 if ($group eq "unknown");
	$nj{group} = $group;

	# application
	my $app = $line[14];
	$app = -1 if ($app eq "unknown");
	$nj{app} = $app;

	# queue
	my $queue = $line[10];
	$queue = -1 if ($queue eq "unknown");
	$queue = 0  if ($queue eq "FOREIGN");
	$nj{queue} = $queue;

	$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: Thinking Machines CM-5
; Installation: Los Alamos National Lab (LANL)
; Acknowledge: Curt Canada
; Information: http://www.lanl.gov/
;              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: -25200\n";
    $header .= sprintf("; TimeZoneString: %s\n", $tz_str);

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

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

    $header .= "; MaxNodes: $max_procs\n";
    $header .= "; MaxProcs: $max_procs\n";

    $header .= "; Note: uses DJM for job management\n";
    $header .= "; Note: many jobs, especially smaller ones, ran in non-dedicated mode\n";

    return $header;
}

print ";\n";

