]> git.donarmstrong.com Git - uiuc_igb_scripts.git/blob - dqsub
add support for array options
[uiuc_igb_scripts.git] / dqsub
1 #!/usr/bin/perl
2 # dqsub submits jobs using qsub with better options
3 # and is released under the terms of the GNU GPL version 3, or any
4 # later version, at your option. See the file README and COPYING for
5 # more information.
6 # Copyright 2014 by Don Armstrong <don@donarmstrong.com>.
7
8
9 use warnings;
10 use strict;
11
12 use Getopt::Long;
13 # use Pod::Usage;
14
15 =head1 NAME
16
17 dqsub - submits jobs using qsub with better options
18
19 =head1 SYNOPSIS
20
21 dqsub [options]
22
23  Options:
24    --queue, -q Queue to use
25    --interactive, -I call qsub interactively
26    --nodes nodes to use
27    --array array mode (one of 'chdir' or 'xargs' or '')
28    --array-from file to read arrays from (default STDIN)
29    --ppn processors per node to use
30    --mem memory to request
31    --dir Directory to run the script in (default current directory)
32    --debug, -d debugging level (Default 0)
33    --help, -h display this help
34    --man, -m display manual
35
36 =head1 OPTIONS
37
38 =over
39
40 =item B<--array>
41
42 This describes how dqsub will generate array jobs.
43
44 If no B<--array> is given, then the command and any additional
45 arguments given will be run using qsub.
46
47 If B<--array> is C<chdir>, then each line of the input given in
48 B<--array-from> will be used as a directory and the command and any
49 additional arguments given will run in each directory.
50
51 IF B<--array> is C<xargs>, then each line of the input given will be
52 considered to be an additional argument which will be given to the
53 command run in the current directory.
54
55 =item B<--array-from>
56
57 File to read array arguments from. If not provided, and B<--array> is
58 given, arguments will be read from STDIN.
59
60 =item B<--debug, -d>
61
62 Debug verbosity. (Default 0)
63
64 =item B<--help, -h>
65
66 Display brief usage information.
67
68 =item B<--man, -m>
69
70 Display this manual.
71
72 =back
73
74 =head1 EXAMPLES
75
76 dqsub
77
78 =cut
79
80 use IO::File;
81 use Cwd qw(getcwd abs_path);
82 use vars qw($DEBUG);
83
84 my %options = (nodes           => 1,
85                ppn             => 2,
86                mem             => '2G',
87                debug           => 0,
88                help            => 0,
89                man             => 0,
90                interactive     => 0,
91               );
92
93 GetOptions(\%options,
94            'queue|q=s',
95            'interactive|I!',
96            'nodes=i',
97            'array=s',
98            'array_from|array-from=s',
99            'ppn|processors-per-node=i',
100            'mem|memory=s',
101            'dir=s',
102            'debug|d+','help|h|?','man|m');
103
104 # pod2usage() if $options{help};
105 # pod2usage({verbose=>2}) if $options{man};
106
107 $DEBUG = $options{debug};
108
109 my @USAGE_ERRORS;
110 if (not @ARGV and not $options{interactive}) {
111     push @USAGE_ERRORS,"You must provide a command to run";
112 }
113 if (defined $options{array} and $options{array} !~ /^(?:|chdir|xargs)$/i) {
114     push @USAGE_ERRORS,"--array must be one of chdir, xargs or '' if provided";
115     $options{array} = lc($options{array});
116     if ($options{array} eq '') {
117         $options{array} = undef;
118     }
119 }
120 if ($options{interactive} and @ARGV) {
121     push @USAGE_ERRORS,"Don't provide commands when you're asking for an interactive shell";
122 }
123
124 # pod2usage(join("\n",@USAGE_ERRORS)) if @USAGE_ERRORS;
125 print STDERR join("\n",@USAGE_ERRORS) and exit 1 if @USAGE_ERRORS;
126
127 # OK. Generate the options to qsub which we'll be using
128 my @qsub_options = generate_qsub_options(\%options);
129
130 if ($options{interactive}) {
131     print STDERR 'running: qsub '.join(' ',@qsub_options) if $DEBUG;
132     exec('qsub',@qsub_options);
133 } else {
134     my @array = ();
135     if ($options{array}) {
136         @array = read_array_options(\%options) if $options{array};
137         # the -t option gives the range of elements for an array job
138         push @qsub_options,'-t','1-'. scalar @array;
139     }
140     call_qsub(\@qsub_options,write_qsub_script(\%options,\@ARGV,\@array));
141 }
142
143 sub generate_qsub_options{
144     my ($options) = @_;
145     my @qo;
146     if (defined $options->{queue} and length $options->{queue}) {
147         push @qo,'-q',$options->{queue};
148     }
149     if (defined $options->{dir}) {
150         push @qo,'-d',abs_path($options->{dir});
151     } else {
152         push @qo,'-d',getcwd;
153     }
154     ## handle the -l options
155     my @l;
156     push @l, 'nodes='.$options->{nodes};
157     if (defined $options->{ppn}) {
158         $l[$#l] .= ':ppn='.$options->{ppn};
159     }
160     if ($options->{mem}) {
161         push @l,'mem='.$options->{mem};
162     }
163     push @qo,'-l',join(',',@l) if @l;
164     if ($options->{interactive}) {
165         push @qo,'-I';
166     }
167     return @qo;
168 }
169
170 sub read_array_options{
171     my ($options) = @_;
172     my $fh = \*STDIN;
173     if (defined $options->{array_from}) {
174         $fh = IO::File->new(defined $options->{array_from}) or
175             die "Unable to open $options->{array_from} for reading: $!";
176     }
177     my @array;
178     for (<$fh>) {
179         chomp;
180         push @array,$_;
181     }
182     return @array;
183 }
184
185 sub call_qsub {
186     my ($qsub_options,$script) = @_;
187     my $qsub_fh;
188     open $qsub_fh,'|-','qsub',@{$qsub_options},'-' or
189         die "Unable to start qsub: $!";
190     print {$qsub_fh} $script or
191         die "Unable to print to qsub: $!";
192     close($qsub_fh) or
193         die "Unable to close qsub filehandle: $!";
194 }
195
196 sub write_qsub_script {
197     my ($opt,$arg,$array) = @_;
198
199     my $script = "#!/bin/bash\n";
200     my $command = join(' ',map {qq('$_')} @{$arg});
201         $script .= <<EOF;
202 # this script was written by dqsub
203 EOF
204     if (defined $opt->{array}) {
205         my $array_opt = join("\n",@{$array});
206         $script .= <<EOF;
207 OPT=\$(sed -n -e "\$PBS_ARRAYID p"<<'_HERE_DOC_END'
208 $array_opt
209 _HERE_DOC_END_
210 )
211 EOF
212         if ($opt->{array} eq 'chdir') {
213             $script .= <<EOF;
214 cd "\$OPT";
215 exec $command;
216 EOF
217         } else {
218             $script .= <<EOF;
219 exec $command "\$OPT";
220 EOF
221         }
222         die "--array is currently not implemented";
223     } else {
224         $script .= <<EOF;
225 # there's no array, so just executing the command with arguments
226 exec $command;
227 EOF
228     }
229     return $script;
230 }
231
232
233 __END__