#! /usr/bin/env perl

# Automated GRAM scheduler test
# Requires
# 1: postgres set up to allow current user to create/drop databases
# 2: globus_test package installed (for test creds)
# 3: GRAM, RFT, WSRF Java Container, globusrun-ws
# 4: perl
# 5: writable $GLOBUS_LOCATION/etc and tmp
# 6: GRAM and RFT setup packages in $GLOBUS_LOCATION/setup/globus
# 7: TCP port 3448 to be available

# When you run this script, it'll reconfigure the GRAM and RFT services,
# create an RFT database, and start a gridftp server and container. It will
# then run the submit tests with local output, staging, and streaming.
# When everything is done, it'll clean things up to their original state

use strict;
use Getopt::Long;

select(STDOUT);
$|=1;

my $setup_only = 0;
my $globus_location = $ENV{GLOBUS_LOCATION} || &fail("GLOBUS_LOCATION not set");
my $hostname = `globus-hostname`;
my $sysconfdir = "$globus_location/etc";
my $setupdir = "$globus_location/setup/globus";
my $database_schema = "$globus_location/share/globus_wsrf_rft/rft_schema.sql";
my $database_name = "GRAMTestDatabase";
my $gram_config_dir = "gram-service";
my $scheduler = "Fork";
my $tmpdir='/tmp';
my ($gridftp_logfile, $gridftp_pidfile,
    $gridftp_pid, $gridftp_host, $gridftp_port) =
            ("$tmpdir/gridftp-$$.log", "$tmpdir/gridftp-$$.pid");
my ($container_logfile, $container_pidfile,
    $container_pid, $container_host, $container_port) =
            ("$tmpdir/container-$$.log", "$tmpdir/container-$$.pid");
my $test_script_name = "submit_test.$$";
my ($test_script) = ("submit_test/$test_script_name");
my $descriptor = "$tmpdir/global-security-descriptor-$$.xml";
my $debug=0;
my @restore_descriptors = ();

GetOptions("setup-only|s" => \$setup_only);


%ENV = map { chomp $_; split(/=/, $_, 2) } split(/\n/,
    `. $globus_location/test/globus_test/testcred-setup.sh && /usr/bin/env`);

chomp($hostname);

# MAIN
END { if (! $setup_only) { &cleanup }  };

my $rc = &setup_ftp();

if ($rc eq "0") {
    $rc = &setup_rft();
}

if ($rc eq "0") {
    $rc = &setup_gram();
}

if ($rc eq "0") {
    $rc = &setup_gridmaps();
}

if ($rc eq "0") {
    $rc = &setup_container();
}

if ($rc eq "0") {
    $rc = &setup_test_script();
}

if ($rc eq "0" && !$setup_only) {
    $rc = &run_test_script();
}

if ($rc ne '0') {
    fail($rc);
}

sub setup_gridmaps
{
    local(*DESCRIPTOR);

    open (DESCRIPTOR, ">$descriptor");
    print DESCRIPTOR <<EOF;
<?xml version="1.0" encoding="UTF-8"?>
<securityConfig xmlns="http://www.globus.org">
    <gridmap value="$ENV{GRIDMAP}"/>
</securityConfig>
EOF
    close(DESCRIPTOR);
    return 0;
}

sub cleanup_gridmaps
{
    if (-f $descriptor && -o $descriptor) {
        unlink($descriptor);
    }
    return 0;
}

sub setup_gram
{
    my $result;

    $result = &backup_directory('gram-service');

    if ($result == 0) {
        my $dn = `grid-proxy-info -issuer`;
        chomp($dn);
        $result = &step("Setting up GRAM service",
            "cd \"$setupdir\" && setup-gram-service-common -staging-port 3448".
            " -staging-subject \"$dn\"");
        if ($result == 0) {
            $result = &exec_java_class(
                'org.globus.exec.service.exec.utils.FileMappingConfig', 
                '-scheduler', $scheduler,
                '-ftpServerHost', $gridftp_host,
                '-ftpServerPort', $gridftp_port,
                '-ftpProtocol', 'gsiftp',
                '-mapping', '/', '/');
        }
    }
    return $result;
}

sub cleanup_gram
{
    return &restore_directory('gram-service');
}

sub setup_rft
{
    my $result;

    $result = &step("Creating RFT Database", "createdb \"$database_name\" 2>&1");

    if ($result == 0) {
        $result = &step("Loading Database schema",
                "psql -d \"$database_name\" -f \"$database_schema\" 2>&1");
    }


    if ($result == 0) {
        $result = &backup_directory('globus_wsrf_rft');
    }

    if ($result == 0) {
        $result = &step("Running setup-rft",
                "cd \"$setupdir\" &&
                ./setup-rft -c \"jdbc:postgresql://$hostname/$database_name\"");
    }

    return $result;
}

sub cleanup_rft
{
    my $result;
    
    $result = &step("Dropping RFT Database", "dropdb \"$database_name\"");
    $result = &restore_directory("globus_wsrf_rft");
}

sub setup_ftp
{
    my $result;

    $result = &step("Starting GridFTP server",
            "globus-gridftp-server -s > \"$gridftp_logfile\" 2>/dev/null &
            echo \$! > $gridftp_pidfile");

    if ($result == 0) {
        local(*GRIDFTP_PIDFILE);

        open(GRIDFTP_PIDFILE, "<$gridftp_pidfile");
        chomp($gridftp_pid=<GRIDFTP_PIDFILE>);
        close(GRIDFTP_PIDFILE);

        print "Getting GridFTP server port...";
        while (-z $gridftp_logfile && kill(0, ($gridftp_pid))) {
            sleep(1);
        }

        if (! kill(0, ($gridftp_pid))) {
            $result = "GridFTP server died";
            print "failed\n";
        } else {
            local(*GRIDFTP_LOGFILE);

            open(GRIDFTP_LOGFILE, "<$gridftp_logfile");

            while (<GRIDFTP_LOGFILE>) {
                if (/^Server listening at ([^:]+):(\d+)/) {
                    $gridftp_host = $1;
                    $gridftp_port = $2;
                    last;
                }
            }
            close(GRIDFTP_LOGFILE);

            if (defined($gridftp_host) && defined($gridftp_port)) {
                print "ok\n";
                $result = 0;
            } else {
                $result = "Could not determine GridFTP host:port";
                print "failed\n";
            }
        }
    }
    return $result;
}

sub cleanup_ftp
{
    if (defined($gridftp_pid)) {
        kill 'TERM', ($gridftp_pid);
        unlink ($gridftp_logfile, $gridftp_pidfile);
    }
}

sub setup_container
{
    my $result;

    $result = &step("Starting SOAP Service Container",
            "globus-start-container -p 3448 -containerDesc $descriptor > \"$container_logfile\" &
            echo \$! > $container_pidfile");

    if ($result == 0) {
        local(*CONTAINER_PIDFILE);

        open(CONTAINER_PIDFILE, "<$container_pidfile");
        chomp($container_pid=<CONTAINER_PIDFILE>);
        close(CONTAINER_PIDFILE);

        print "Getting SOAP Service Container port...";

        while (-z $container_logfile && kill(0, ($container_pid))) {
            sleep(1);
        }

        if (! kill(0, ($container_pid))) {
            $result = "SOAP Service Container died";
            print "failed\n";
        } else {
            local(*CONTAINER_LOGFILE);

            open(CONTAINER_LOGFILE, "<$container_logfile");
            while (<CONTAINER_LOGFILE>) {
                if (m|https?://([^:]+):(\d+)/wsrf/services/ManagedJobFactoryService|) {
                    $container_host = $1;
                    $container_port = $2;

                    last;
                }
            }
            close(CONTAINER_LOGFILE);

            if (defined($container_host) && defined($container_port)) {
                print "ok\n";

                $result = 0;
            } else {
                $result = "Could not determine SOAP Container host:port";
                print "failed\n";
            }
        }
    }

    return $result;
}

sub cleanup_container
{
    if (defined($container_pid)) {
        kill 'TERM', ($container_pid);
        unlink ($container_logfile, $container_pidfile);
    }
}

sub setup_test_script {
    my $result;

    $result = &step("Creating test script", 
        "./make-test-script.pl -host $container_host -port $container_port \ -scheduler $scheduler -gridftp-port $gridftp_port -streaming $test_script");

    return $result;
}

sub cleanup_test_script {
    unlink($test_script);
}

sub run_test_script {

    my $dn = `grid-proxy-info -issuer`;
    chomp($dn);

    system("./run-test-script.pl submit_test $test_script_name -subject \"$dn\"");
}

sub cleanup
{
    &cleanup_test_script();
    sleep(1);
    &cleanup_container();
    sleep(1);
    &cleanup_gram();
    sleep(1);
    &cleanup_ftp();
    sleep(1);
    &cleanup_rft();
    sleep(1);
    &cleanup_gridmaps();
}

sub backup_directory
{
    my $dir = shift;
    my $tarball="/tmp/$dir-$$.tar";
    my $result;

    if (-d "$sysconfdir/$dir") {
        print "Backing up $sysconfdir/$dir...";
        $result = `cd \"$sysconfdir\" && tar cf \"$tarball\" \"$dir\"`;

        if ($? != 0) {
            chomp $result;
            print "failed\n";
            return ("Failed: $result");
        } else {
            print "ok\n";
            return 0;
        }
    }
}

sub restore_directory
{
    my $dir = shift;
    my $tarball="/tmp/$dir-$$.tar";
    my $result;

    if (-f $tarball) {
        print "Restoring $sysconfdir/$dir...";

        $result = `cd \"$sysconfdir\" && rm -rf \"$sysconfdir/$dir\" && 
                tar xf \"$tarball\"`;
        if ($? != 0) {
            chomp $result;
            print "failed\n";
            return "Failed: $result";
        } else {

            print "ok\n";
            unlink($tarball);

            return 0;
        }
    }
}

sub exec_java_class
{
    my $cmd = join(' ', 'java', @_);
    my $result;
    
    print "Adding FS mapping...";

    $result = `. \"$sysconfdir\"/globus-devel-env.sh && $cmd`;

    if ($? == 0) {
        print "ok\n";
        return 0;
    } else {
        print "failed\n";

        chomp($result);
        return "Failed: $result";
    }
}

sub fail
{
    if (int($rc) != $rc) {
        print STDERR "$_[0]\n";
        $rc = 1;
    } else {
        print STDERR "FAILED: $rc\n";
    }
    exit $rc;
}

sub step
{
    my ($task, $cmd) = @_;

    print "$task...";

    print "\nRunning $cmd\n" if $debug;

    my $result = `$cmd`;

    print "\nCommand result is $result\n" if $debug;
    if ($? != 0) {
        chomp $result;

        print "failed\n";

        return "Failed: $result";
    } else {
        print "ok\n";

        return 0;
    }
}
