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