#	Title:
#		DRMAA Job Scheduler Interface Module for GRAM (Version 1.0)
#
#	Author:
#		Alexander Saar (alexander.saar@hpi.uni-potsdam.de)
#
#		Hasso Plattner Institute for Software Systems Engineering
#		P.O. Box 14440 Potsdam
#
#	Version:
#		Beta Release 0.1
#
#	Description:	
#		This module provides a job scheduler for the Grid Resource 
#		Access and Management (GRAM) of the Globus Toolkit (GT).
#		The job scheduler implements the interface between GRAM and 
#		the Distributed Resource Management Application API (DRMAA).
#
#	License:		
#		This program is free software; you can redistribute it 
#		and/or modify it under the terms of the GNU General Public 
#		License (as published by the Free Software Foundation) 
#		version 2, dated June 1991.
#		This program is distributed in the hope that it will be 
#		useful, but WITHOUT ANY WARRANTY; without even the IMPLIED 
#		WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR 
#		PURPOSE.
#		See the terms and conditions of the GNU General Public 
#		License for more details. Please also read LICENSE.txt which 
#		is found in this source distribution.
#
#####################################################################

# GRAM module imports
use Globus::GRAM::Error; 
use Globus::GRAM::JobState; 
use Globus::GRAM::JobManager;
use Globus::Core::Paths;

# DRMAA module imports
use Schedule::DRMAAc qw/ :all /;

# package declaration
# NOTE: This package name must match the name of the .pm file!!
package Globus::GRAM::JobManager::DRMAA;

# parent package definition
@ISA = qw(Globus::GRAM::JobManager);

# remote program state values
$rps_vals = {
	$DRMAA_PS_UNDETERMINED         	=> 'DRMAA_PS_UNDETERMINED',
	$DRMAA_PS_QUEUED_ACTIVE        	=> 'DRMAA_PS_QUEUED_ACTIVE',
	$DRMAA_PS_SYSTEM_ON_HOLD       	=> 'DRMAA_PS_SYSTEM_ON_HOLD',
	$DRMAA_PS_USER_ON_HOLD         	=> 'DRMAA_PS_USER_ON_HOLD',
	$DRMAA_PS_USER_SYSTEM_ON_HOLD  	=> 'DRMAA_PS_USER_SYSTEM_ON_HOLD',
	$DRMAA_PS_RUNNING              	=> 'DRMAA_PS_RUNNING',
	$DRMAA_PS_SYSTEM_SUSPENDED     	=> 'DRMAA_PS_SYSTEM_SUSPENDED',
	$DRMAA_PS_USER_SUSPENDED       	=> 'DRMAA_PS_USER_SUSPENDED',
	$DRMAA_PS_USER_SYSTEM_SUSPENDED	=> 'DRMAA_PS_USER_SYSTEM_SUSPENDED',
	$DRMAA_PS_DONE                 	=> 'DRMAA_PS_DONE',
	$DRMAA_PS_FAILED               	=> 'DRMAA_PS_FAILED',
};

#
# module constructor
#
sub new
{
	my $proto = shift(@_);
	my $class = ref($proto) || $proto;
	my $self = $class->SUPER::new(@_);

	# check SGE_ROOT 
	my $sge = $ENV{SGE_ROOT};
	if(!defined($sge))
	{
	    warn "SGE_ROOT not found.";
	    die "SGE_ROOT needs to be set before running this script.";
	}

	return $self;
}

#
# job submit function
#
sub submit
{
	my $self = shift;
    my $description = $self->{JobDescription};
    
    print STDERR 'Entering DRMAA submit.';

    # save job description for debugging
#   $description->save("/tmp/job_description.$$");
 
	# init DRMAA session
        my( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_init( undef );
        die Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
        warn "DRMAA session initialized \n";

    #get new job template
	my ( $error, $jt, $diagnosis ) = Schedule::DRMAAc::drmaa_allocate_job_template();
	if ($error)
	{
		warn Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
		return Globus::GRAM::Error::JOB_EXECUTION_FAILED;
	}
	warn "job template allocated \n";

	# check executable 
	if( $description->executable() eq '')
    {
		return Globus::GRAM::Error::RSL_EXECUTABLE();
    }
    elsif(! -f $description->executable())
    {
		return Globus::GRAM::Error::EXECUTABLE_NOT_FOUND();
    }
    elsif(! -x $description->executable())
    {
		return Globus::GRAM::Error::EXECUTABLE_PERMISSIONS();
    }
	else
	{
		# set command
        	$executable = $description->executable();
	
		( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_set_attribute( $jt, $Schedule::DRMAAc::DRMAA_REMOTE_COMMAND, $executable );
		if ($error)
		{
		    warn "setting executable $executable failed";
		    warn Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;			
	            return Globus::GRAM::Error::JOB_EXECUTION_FAILED;
		}
	}
	
	# processing arguments
#	my @arguments = $description->arguments();
	
	# checking arguments
#   foreach(@arguments)
#   {
#       if(ref($_))
#		{
#		    return Globus::GRAM::Error::RSL_ARGUMENTS;
#		}
#   }
    
#   if($arguments[0])
#   {
#       foreach(@arguments)
#       {
#           $_ =~ s/\\/\\\\/g;
#	    $_ =~ s/\$/\\\$/g;
#	    $_ =~ s/"/\\\"/g; #"
#	    $_ =~ s/`/\\\`/g; #`
	     
#	     	push @args, '"' . $_ . '" ';
#        }
        
        # set job attributes
#		( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_set_vector_attribute( $jt, $DRMAA_V_ARGV, [ @args ] );
#		if ($error)
#		{
#			warn Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
#			return Globus::GRAM::Error::JOB_EXECUTION_FAILED;
#		}
#    }
    
	# finally run the job
	( $error, $jobid, $diagnosis ) = Schedule::DRMAAc::drmaa_run_job( $jt );
	if ($error)
	{
		warn Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
		return Globus::GRAM::Error::JOB_EXECUTION_FAILED;
	}
	
	print STDERR "Job submitted with ID: $jobid.\n";
	return { JOB_ID => $jobid, JOB_STATE => Globus::GRAM::JobState::PENDING };
}

#
# job poll function
#
sub poll
{
	my $self = shift;
    my $description = $self->{JobDescription};
    my $jobid = $description->jobid();

    print STDERR "Polling job $jobid.\n";

        # init DRMAA session
        my( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_init( undef );
        die Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
        warn "DRMAA session initialized \n";
    
    my ( $error, $remoteps, $diagnosis ) = Schedule::DRMAAc::drmaa_job_ps( $jobid );
    # Problem: Error is set if job status is not available
    #if ($error)
    #{
    #	warn "Error while polling: " . Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis;
    #	return Globus::GRAM::Error::LOCAL_SCHEDULER_ERROR();
    #}
	
    $_ = $rps_vals->{$remoteps};
    print STDERR "Polled status: " . $_ . "\n";
	
	if ($remoteps == $Schedule::DRMAAc::DRMAA_PS_UNDETERMINED)
	{
		$self->log("Status can not be determined. Telling job manager to ignore this poll.");
		return {};
	}
	elsif (/DONE/)
	{
		return { JOB_STATE => Globus::GRAM::JobState::DONE };
	}
	elsif (/SUSPENDED/)
	{
		return { JOB_STATE => Globus::GRAM::JobState::SUSPENDED };
	}
	elsif (/RUNNING/)
	{
		return { JOB_STATE => Globus::GRAM::JobState::ACTIVE };
	}
	elsif ($remoteps == $Schedule::DRMAAc::DRMAA_PS_QUEUED_ACTIVE)
	{
		return { JOB_STATE => Globus::GRAM::JobState::PENDING };
	}
	elsif (/FAILED/)
	{
		return { JOB_STATE => Globus::GRAM::JobState::FAILED };
	}
	else
	{
		$self->log("Received unknown state. Telling job manager to ignore this poll.");
		return {};
	}
	
	# no valid Globus constant for *_HOLD states
}

#
# job cancel function
#
sub cancel
{
	my $self = shift;
    my $description = $self->{JobDescription};
    my $jobid = $description->jobid();

    print STDERR "Cancel job $job_id.";
    
	# init DRMAA session
        my( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_init( undef );
        die Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
        warn "DRMAA session initialized \n";

    ( $error, $diagnosis ) = Schedule::DRMAAc::drmaa_control( $jobid, $Schedule::DRMAAc::DRMAA_CONTROL_TERMINATE );
    if ($error)
    {
    	warn Schedule::DRMAAc::drmaa_strerror( $error ) . "\n" . $diagnosis if $error;
		return Globus::GRAM::Error::JOB_CANCEL_FAILED();
	}
	else
	{
		return { JOB_STATE => Globus::GRAM::JobState::FAILED };
	}
}

1; # Ancient Druid Custom
