#!/usr/bin/perl

use strict;
use IO::Handle;
use Getopt::Long;
use Pod::Usage;

my $hostname = `$ENV{GLOBUS_LOCATION}/bin/globus-hostname`;
chomp $hostname;

my ($verbose,   $factory,       $startLoad,     $endLoad) =
   (0,          "$hostname",    1,              0);
my ($startParallelism,  $endParallelism,    $duration,  $factoryType) =
   (1,                  0,                  900,          "Fork");
my ($delegate,  $poll,  $rslPath,   $help,  $man) =
   (0,          0,      "",         0,      0);

my $executable = "$ENV{GLOBUS_LOCATION}/test/globus_wsrf_gram_service_java_test_throughput/gram-throughput-tester";

GetOptions(
    "verbose!"              => \$verbose,
    "f|factory=s"           => \$factory,
    "type=s"                => \$factoryType,
    "l|start-load=s"        => \$startLoad,
    "m|end-load=s"          => \$endLoad,
    "p|start-parallelism=s" => \$startParallelism,
    "q|end-parallelism=s"   => \$endParallelism,
    "d|duration=s"          => \$duration,
    "g|delegate!"           => \$delegate,
    "poll!"                 => \$poll,
    "rsl=s"                 => \$rslPath,
    "help"                  => \$help,
    "man"                   => \$man)
    or pod2usage(2);

if ($help or $man) {
    pod2usage(2) if $help;
    pod2usage(1) if $man;
}

if ($endLoad < $startLoad)
{
        $endLoad = $startLoad;
}

if ($endParallelism < $startParallelism)
{
        $endParallelism = $startParallelism;
}

if ($verbose) {
    print   "Factory: $factory\n"
          . "Type: $factoryType\n"
          . "Delegate: $delegate\n"
          . "Start Load: $startLoad\n"
          . "End Load: $endLoad\n"
          . "Start Parallelism: $startParallelism\n"
          . "End Parallelism: $endParallelism\n"
          . "Duration: $duration\n"
          . "Poll: $poll\n"
          . "RSL: $rslPath\n";
}

STDOUT->autoflush(1);
TESTLOG->autoflush(1);

for (my $lIndex=$startLoad; $lIndex<=$endLoad; $lIndex*=2)
{
  for (my $pIndex=$startParallelism; $pIndex<=$endParallelism; $pIndex*=2)
  {
    if ($verbose)
    {
        print "------------------------------------\n";
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
            = localtime(time);
        printf  "Starting test at %4d-%02d-%02d %02d:%02d:%02d\n",
                $year+1900,$mon+1,$mday,$hour,$min,$sec;
        print   "Load: $lIndex\n"
              . "Parallelism: $pIndex\n"
              . "Duration: $duration\n";
    }
    my $testOutputFile = "throughput-test-L" . $lIndex . "-P" . $pIndex .".log";
    my $testExec = "$executable "
                 . "--factory $factory "
                 . "--type $factoryType "
                 . "--load $lIndex "
                 . "--parallelism $pIndex "
                 . "--test-duration $duration "
                 . "--delegate " . ($delegate?"true ":"false ")
                 . "--poll " . ($poll?"true ":"false ");
        if (length($rslPath) > 0)
        {
            $testExec .= " --rsl $rslPath";
        }
                 #. "--job-duration $jobDuration "

    open TESTLOG, ">$testOutputFile" or die "Error: unable to open file "
        . $testOutputFile . "for writing";
    open EXEC, "$testExec |" or die "Error: unable to execute command \'"
        . $testExec . "\'";
        while (<EXEC>) {
            if ($verbose) {
                print;
            }
            print TESTLOG;
        }
    close EXEC;
    close TESTLOG;
  }
}
END{};
1;

=head1 NAME

run-throughput-tests.pl - MJS throughput test script

=head1 SYNOPSIS

Performs a constrained parameter study using the gram-throughput-tester
application.

run-throughput-tests.pl [options]

Options:

    --factory=<constact string>         MJFS service address
                                        (default is local hostname)
    --type=<type>                       MJFS type (default is "Fork")
    --start-load=<load>                 Specify the first test case's
                                        per-client job load (default is 1)
    --end-load=<load>                   Specify the last test case's
                                        per-client job count
                                        (default is start load)
    --start-parallelism=<parallelism>   Specify the first test case's
                                        number of client threads
                                        (default is 1)
    --end-parallelism=<parallelism>     Specify the last test case'snumber
                                        of client threads
                                        (default is start parallelism)
    --duration=<duration>               Specify the duration of each test
                                        case in seconds (default is 900)
    --delegate                          Do delegation for each job.
    --poll                              Poll for job state instead of using
                                        notifications.
    --rsl=<rsl>                         Specify rsl to use for each job.
    --help                              Print short usage
    --man                               Print long usage

=head1 OPTIONS

=over 8

=item B<--factory>

Specify the Managed Job Factory Service address of the form
[protocol://]host[:[port]][/service path/service name].  The default value is
the hostname determined by globus-hostname.

=item B<--type>

Specify the factory type that jobs are submitted to.  The factory type
represents the type of queue scheduler that the factory (MMJFS) is associated
with.  Commone values are "Fork", "PBS", "LSF", and "Condor" (default
is "Fork").

=item B<--start-load>

Specify the first test case's per-client job count (default is 1).
The job load is the number of jobs to maintain at the service for each client
thread.  Test cases will be run starting with startload and ending with endload 
by powers of 2.  For example, if one specifies -startload=4 and -endload=40,
test cases will be run with job loads of 4, 8, 16, and 32.

=item B<--end-load>

Specify the last test case's per-client job count, rounded to the last power of
2 (default is 1).
The job load is the number of jobs to maintain at the service for each client
thread.  Test cases will be run starting with startload and ending with endload 
by powers of 2.  For example, if one specifies -startload=4 and -endload=40,
test cases will be run with job loads of 4, 8, 16, and 32.

=item B<--parallelism>

Specify the number of client threads.  Each client thread will be required to
maintain the current test case's job load for the duration specified with the
duration options.

=item B<--start-load>

Specify the first test case's number of client threads.  Each client thread will
be required to maintain the current test case's job load for the duration
specified with the duration option. The default is 1.

=item B<--end-load>

Specify the first test case's number of client threads.  Each client thread will
be required to maintain the current test case's job load for the duration
specified with the duration option. The default is 1.

=item B<--duration>

Specify the duration of each test case in seconds.  The duration is the length
of time each client thread is to continue to maintain the current test case's
job load.  This correspondes to gram-throughput-tester's --test-duration option.

=item B<--delegate>

Specify whether delegation should be performed.

=item B<--poll>

Specify whether polling should be performed instead of notifications for
updating the client's copy of the job state data.

=item B<--rsl>

Specify the RSL to use for each job in lieu of the default sleep job.

=item B<--help>

Print short usage.

=item B<--man>

Print long usage.

=cut
