#!/usr/bin/perl -w

use strict;
use Getopt::Std;

sub factor_sequence;
sub usage;
sub run_tests;

my ($clients_range, $io_range, $meta_range, $branch, $cvsdate, $basepath,
        $mountpoint, $iterations, $verbose, $exp) = 
(undef, undef, undef, undef, undef, undef, undef, undef, undef, undef);

my %option_vars = (
        "c" => \$clients_range,
        "i" => \$io_range,
        "k" => \$meta_range,
        "b" => \$branch,
        "d" => \$cvsdate,
        "p" => \$basepath,
        "m" => \$mountpoint,
        "n" => \$iterations,
        "e" => \$exp,
        "v" => \$verbose);

my $operation = pop @ARGV;

if(!defined($operation))
{
    print "\n\nERROR: Missing an <operation> argument\n";
    usage;
}

my %opts = ();
getopts("c:i:k:b:p:m:n:e:vh", \%opts) || usage;

for (keys %opts)
{
    if(exists $option_vars{$_})
    {
        my $var = $option_vars{$_};
        $$var = $opts{$_};
    }
    else
    {
        usage;
    }
}

$clients_range = "1:1" if !defined($clients_range);
$io_range = "1:5" if !defined($io_range);
$meta_range = "1:1" if !defined($meta_range);
$branch = "HEAD" if !defined($branch);
$cvsdate = `date --iso-8601=date` if !defined($cvsdate);
$basepath = "." if !defined($basepath);
$iterations = 100 if !defined($iterations);
$exp = "meta" if !defined($exp);

my $benchpath = $basepath . "/BUILD-pvfs2-$branch/test/perfbase/benchmarks";

if(!defined($mountpoint))
{
    print "\n\nERROR: No mountpoint specified.\n";
    usage;
}

my @procs = factor_sequence($clients_range);
my @meta_servers = factor_sequence($meta_range);
my @io_servers = factor_sequence($io_range);

my $hostname=`uname -n`;
chomp($hostname);

run_tests;

sub myexec
{
    my $cmd = shift;
    my $error = shift;

    print "RUNNING: $cmd\n" if $verbose;
    system($cmd);
    if($? != 0)
    {
        print STDERR $error;
        exit $?
    }
}

sub check_exec
{
    my $cmd = shift;

    system("which $cmd > /dev/null 2>&1");
    if($? != 0)
    {
        print STDERR "\nScript requires $cmd to be in your PATH\n\n";
        exit $?;
    }
}

sub run_tests()
{
    check_exec("mpiexec");
    check_exec("mpdtrace");

    if(!exists $ENV{"PVFS2TAB_FILE"})
    {
        print STDERR "PVFS2TAB_FILE must be set.\n\n";
        exit 1;
    }

    my $mpdprocs = `mpdtrace`;
    if(!defined($mpdprocs) || $mpdprocs eq "")
    {
        print STDERR "mpdtrace found no mpd processes.\n\n";
        exit 1;
    }

    my @mpdprocs_list = split(/\n/, $mpdprocs);

    if(! -d "/tmp/mpi-test-data")
    {
        myexec("mkdir /tmp/mpi-test-data",
               "Failed to create directory: /tmp/mpi-test-data\n");
    }

    for my $meta (@meta_servers)
    {
        for my $io (@io_servers)
        {
            check_exec("$hostname-restart-servers");

            myexec("$hostname-restart-servers $basepath/INSTALL-pvfs2-$branch $io $meta",
                   "Failed to restart servers with:\n" .
                   "meta servers: $meta\nio servers: $io\n" .
                   "branch: $branch\n\n");

            for my $proc (@procs)
            {
                my $datfile = "/tmp/perftest-$operation-c$proc-m$meta-i$io-$branch.dat";

                myexec("mpiexec -1 -n $proc -env PVFS2TAB_FILE \"" . 
                       $ENV{PVFS2TAB_FILE} . "\" $benchpath/$operation-test " .
                       "-d $mountpoint -n $iterations > $datfile",
                       "Test run failed: proc: $proc, meta: $meta, io: $io\n");

                open(WEBINPUT,
                     "|./perfbase-web-input -o $operation -m $meta -i $io " .
                     "-f ./experiments/pvfs2-op-input.xml -t $branch > /tmp/perfbase_input_result 2>&1");
                
                if($? != 0)
                {
                    print STDERR "Can't fork perfbase-web-input: $!\n" .
                                 "Failed to submit test results to perfbase " .
                                 "for run: proc: $proc, " .
                                 "meta: $meta, io: $io\n\n" .
                                 `cat /tmp/perfbase_input_result`;
                    exit 1;
                }

                if($verbose)
                {
                    print "RUNNING: ./perfbase-web-input -o $operation " .
                          "-m $meta -i $io -f " .
                          "./experiments/pvfs2-$exp-input.xml -t $branch\n";
                }

                open(WEBINPUT,
                     "|./perfbase-web-input -o $operation -m $meta -i $io " .
                     "-f ./experiments/pvfs2-$exp-input.xml -t $branch > /tmp/perfbase_input_result 2> /tmp/perfbase_input_error");
                
                my $datresults = `cat $datfile`;
                print WEBINPUT $datresults;
                
                my $res = close WEBINPUT; 
                if(!$res)
                {
                    print STDERR "Failed to submit results to perfbase: $! $?\n" .
                                 "for run: proc: $proc, meta: $meta, io: $io\n";
                    exit 1;
                }

                my $error_bytes=`cat /tmp/perfbase_input_error | wc -c`;
                if($? != 0 || $error_bytes != 0)
                {
                    print STDERR "Can't fork perfbase-web-input: $!\n" .
                                 "Failed to submit test results to perfbase " .
                                 "for run: proc: $proc, " .
                                 "meta: $meta, io: $io\n\n" .
                                 `cat /tmp/perfbase_input_result`;
                    exit 1;
                }

            }
        }
    }
}

sub factor_sequence()
{
    my $inc = 1;
    my ($first, $last);
    my $seq = shift;

    if($seq =~ /(\d+):(\d+):(\d+)/)
    {
        $first = $1;
        $inc = $2;
        $last = $3;
    }
    else
    {
        $seq =~ /(\d+):(\d+)/;
        $first = $1;
        $last = $2;
    }

    my @seq_array = ();

    for(my $i = $first; $i <= $last; $i += $inc)
    {
        push(@seq_array, $i);
    }
    return @seq_array;
}

sub usage()
{
    print << "USAGE"

usage: $0 [options] -<operation>

<operation> must be the operation to test, where <operation>-test is
the executable to run.  All such executables must take the same parameter
set: -d <pvfs2 mountpoint> -i <iterations>.  The <operation>-test program
should perform the operation as many times as specified in <iterations>
and print to stdout on a separate line for each iteration:

<iteration> <time (seconds)>


OPTIONS:

Range options must be in the form <min>:<max> or <min>:<inc>:<max> 
where the test will be run from <min> to <max>, 
incrementing by <inc> after each iteration.
<inc> defaults to 1 if not specified.

          -h             : Help.
          -c <range>     : the range of mpi processes to use.  
                           Defaults to 1:1.
          -i <range>     : the range of io servers to use.  
                           Defaults to 1:4.
          -k <range>     : the range of meta servers to use.  
                           Defaults to 1:1.
          -b <branch>    : the name of the CVS branch to use.  
                           Defaults to HEAD.
          -d <date>      : iso-8601 valid date string to 
                           use for CVS checkout.  
                           Defaults to latest.
          -p <base path> : path to base pvfs2 test directory.  Defaults to \$PWD.
                           This should contain {BUILD,INSTALL}-pvfs2-* dirs.
          -m <dir>       : pvfs2 mount point (passed to test program)
          -n <iter>      : number of iterations (passed to test program).
                           Defaults to 100.
          -e <exp>       : perfbase experiment to submit the test results.
                           This should only include the experiment component
                           of the name.
                           Defaults to 'meta'
          -v             : Verbose.  Show commands as they are being
                           executed.

USAGE
;;

    exit 1;
}

#
# Local variables:
# c-indent-level: 4
# c-basic-offset: 4
# End:
# vim: ft=perl ts=8 sts=4 sw=4 expandtab
