--account, -A Account name to use
--join, -J join error and output streams (default)
--name, -N Name of the job
+ --precommand Optional command to run before each command
--debug, -d debugging level (Default 0)
--help, -h display this help
--man, -m display manual
Whether to join STDOUT and STDERR. On by default; disable with
C<--nojoin>.
+=item B<--batch>
+
+Which batch system to use. If sbatch exists, assume it's slurm,
+otherwise, PBS.
+
=item B<--debug, -d>
Debug verbosity. (Default 0)
GetOptions(\%options,
'queue|q=s',
+ 'batch=s',
'interactive|I!',
'nodes=i',
'array=s',
'array_per_job|array-per-job=i',
'array_slot_limit|array-slot-limit=i',
'array_all_in_one_job|array-all-in-one-job!',
- 'ppn|processors-per-node=i',
+ 'ppn|cpus|processors-per-node=i',
'account|A=s',
'join|J!',
'mem|memory=s',
'pmem|process_mem|process-mem=s',
'pvmem|process_virtual_mem|process-virtiual-mem=s',
'max_file|max-file|file=s',
+ 'precommand|pre-command|pre_command=s',
'dir=s',
'name=s',
'debug|d+','help|h|?','man|m');
push @USAGE_ERRORS,"Don't provide commands when you're asking for an interactive shell";
}
+if (not defined $options{batch}) {
+ qx{which sbatch >/dev/null 2>&1};
+ if ($? == 0) {
+ $options{batch} = 'slurm'
+ } else {
+ $options{batch} = 'pbs'
+ }
+}
+
+if ($options{batch} !~ /^pbs|slurm$/) {
+ push @USAGE_ERRORS,"Unsupported batch system '$options{batch}'; ".
+ "supported systems are pbs or slurm";
+}
+
# pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
if (@USAGE_ERRORS) {
print STDERR map {"$_\n"} @USAGE_ERRORS;
exit 1;
}
+
+my $JOB_SUBMITTER = 'qsub';
# OK. Generate the options to qsub which we'll be using
-my @qsub_options = generate_qsub_options(\%options,\@ARGV);
+my @qsub_options;
+if ($options{batch} eq 'pbs') {
+ @qsub_options = generate_qsub_options(\%options,\@ARGV);
+ $JOB_SUBMITTER = 'qsub';
+} elsif ($options{batch} eq 'slurm') {
+ @qsub_options = generate_slurm_options(\%options,\@ARGV);
+ $JOB_SUBMITTER = 'sbatch';
+} else {
+ die "Unsupported batch system '$options{batch}'";
+}
+
if ($options{interactive}) {
- print STDERR 'running: qsub '.join(' ',@qsub_options) if $DEBUG;
- exec('qsub',@qsub_options);
+ print STDERR 'running: '.$JOB_SUBMITTER.' '.join(' ',@qsub_options) if $DEBUG;
+ if ($options{batch} eq 'pbs') {
+ exec($JOB_SUBMITTER,@qsub_options);
+ } else {
+ exec('srun',@qsub_options,$ENV{SHELL}//'bash');
+ }
+
} else {
my @array = ();
if ($options{array}) {
if ($options{array_all_in_one_job}) {
$options{array_per_job} = scalar @array;
} else {
- push @qsub_options,'-t','1-'. ceil(scalar @array / $options{array_per_job});
+ if ($options{batch} eq 'pbs') {
+ push @qsub_options,'-t';
+ } else {
+ push @qsub_options,'-a';
+ }
+ push @qsub_options,'1-'. ceil(scalar @array / $options{array_per_job});
if ($options{array_slot_limit}) {
$qsub_options[$#qsub_options] .= '%'.$options{array_slot_limit};
}
}
}
+ if ($options{batch} eq 'pbs') {
+ push @qsub_options,'-';
+ }
call_qsub(\@qsub_options,write_qsub_script(\%options,\@ARGV,\@array));
}
if (defined $options->{queue} and length $options->{queue}) {
push @qo,'-q',$options->{queue};
}
- if (defined $options->{dir}) {
- push @qo,'-d',abs_path($options->{dir});
- } else {
- push @qo,'-d',getcwd;
- }
## handle the -l options
my @l;
push @l, 'nodes='.$options->{nodes};
@{$args}[0..min($#{$args},2)]);
}
# join error and output streams
- if ($options->{join}) {
+ if ($options->{join}) {
push @qo,'-j','oe';
}
return @qo;
}
+sub generate_slurm_options{
+ my ($options,$args) = @_;
+ my @qo;
+ if (defined $options->{queue} and length $options->{queue}) {
+ push @qo,'-p',$options->{queue};
+ }
+ ## handle the -l options
+ if (defined $options->{account}) {
+ push @qo,'-A',$options->{account};
+ }
+ my %options_map =
+ (mem => 'mem',
+ ppn => 'cpus-per-task',
+ time => 'time',
+ cputime => 'cput',
+ host => 'host',
+ pmem => 'pmem',
+ pvmem => 'pvmem',
+ max_file => 'file',
+ );
+ for my $k (keys %options_map) {
+ if ($options->{$k}) {
+ push @qo,'--'.$options_map{$k}.'='.$options{$k};
+ }
+ }
+ if ($options{mem}) {
+ push @qo,'--mem='.$options{mem};
+ }
+ if ($options->{interactive}) {
+ push @qo,'--pty';
+ }
+ if ($options->{name}) {
+ push @qo,'-J',$options->{name};
+ } else {
+ push @qo,'-J',join('_',
+ map {my $a = $_; $a =~ s/[^a-zA-Z0-9]*//g; $a;}
+ @{$args}[0..min($#{$args},2)]);
+ }
+ return @qo;
+}
+
sub read_array_options{
my ($options) = @_;
my $fh = \*STDIN;
sub call_qsub {
my ($qsub_options,$script) = @_;
my $qsub_fh;
- open $qsub_fh,'|-','qsub',@{$qsub_options},'-' or
- die "Unable to start qsub: $!";
+ open $qsub_fh,'|-',$JOB_SUBMITTER,@{$qsub_options} or
+ die "Unable to start $JOB_SUBMITTER: $!";
print {$qsub_fh} $script or
- die "Unable to print to qsub: $!";
+ die "Unable to print to $JOB_SUBMITTER: $!";
close($qsub_fh) or
- die "Unable to close qsub filehandle: $!";
+ die "Unable to close $JOB_SUBMITTER filehandle: $!";
}
sub write_qsub_script {
my ($opt,$arg,$array) = @_;
my $script = "#!/bin/bash\n";
- my $command = join(' ',map {qq('$_')} @{$arg});
+ my $command = join(' ',map {$_ =~ /\s/?qq('$_'):$_} @{$arg});
$script .= <<EOF;
# this script was written by dqsub
+EOF
+ # if there is a precommand, write it out
+ if ($opt->{precommand}) {
+ $script .=<<EOF;
+# this is the precommand _BEGIN_
+$opt->{precommand}
+# precommand _END_
+EOF
+ }
+ my $directory = getcwd;
+ if (defined $opt->{dir}) {
+ $directory = abs_path($opt->{dir});
+ }
+ # we really should be quoting this instead
+ $script .=<<EOF;
+# change to the working directory
+cd "$directory";
EOF
if (defined $opt->{array}) {
my @subshell = ('','');
my $array_opt = join("\n",@{$array});
my $max_array = scalar @{$array};
my $apjm1 = $opt->{array_per_job} - 1;
+ $script .= <<EOF;
+if [ -n "\$PBS_ARRAYID" ]; then
+ MYARRAYID="\${PBS_ARRAYID:=1}"
+else
+ MYARRAYID="\${SLURM_ARRAY_TASK_ID:=1}"
+fi;
+EOF
if ($opt->{array_per_job} > 1) {
# we will use subshells if there are more than one array
# items per job
$script .= <<EOF;
for i in \$(seq 1 $opt->{array_per_job}); do
# in some cases, the jobs aren't going to come out evenly. Handle that.
-JOBNUM=\$(( \${PBS_ARRAYID:=1} * $opt->{array_per_job} + \$i - $opt->{array_per_job} ))
+JOBNUM=\$(( \${MYARRAYID:=1} * $opt->{array_per_job} + \$i - $opt->{array_per_job} ))
if [ \$JOBNUM -le $max_array ]; then
OPT=\$(sed -n -e "\$JOBNUM p"<<'_HERE_DOC_END_'
EOF
} else {
$script .= <<EOF;
-OPT=\$(sed -n -e "\${PBS_ARRAYID:=1} p"<<'_HERE_DOC_END_'
+OPT=\$(sed -n -e "\${MYARRAYID:=1} p"<<'_HERE_DOC_END_'
EOF
}
$script .= <<EOF;