File Coverage

blib/lib/HPC/Runner/PBS.pm
Criterion Covered Total %
statement 42 92 45.6
branch 0 14 0.0
condition n/a
subroutine 14 17 82.3
pod n/a
total 56 123 45.5


line stmt bran cond sub pod time code
1             package HPC::Runner::PBS;
2              
3 1     1   12388 use File::Path qw(make_path remove_tree);
  1         1  
  1         57  
4 1     1   664 use File::Temp qw/ tempfile tempdir /;
  1         15189  
  1         51  
5 1     1   405 use IO::File;
  1         623  
  1         85  
6 1     1   379 use IO::Select;
  1         1019  
  1         33  
7 1     1   5 use Cwd;
  1         1  
  1         37  
8 1     1   429 use IPC::Open3;
  1         2093  
  1         39  
9 1     1   4 use Symbol;
  1         1  
  1         37  
10 1     1   445 use Template;
  1         13399  
  1         28  
11 1     1   678 use Log::Log4perl qw(:easy);
  1         32224  
  1         4  
12 1     1   1132 use DateTime;
  1         69170  
  1         32  
13 1     1   562 use Data::Dumper;
  1         4104  
  1         60  
14 1     1   5 use List::Util qw/shuffle/;
  1         1  
  1         66  
15             # use IPC::Cmd qw/can_run/;
16              
17 1     1   519 use Moose;
  1         303379  
  1         5  
18 1     1   4520 use namespace::autoclean;
  1         2  
  1         8  
19              
20             extends 'HPC::Runner::Scheduler';
21             #extends 'HPC::Runner::Slurm';
22             with 'MooseX::SimpleConfig';
23              
24             our $VERSION = '0.10';
25              
26             # For pretty man pages!
27             $ENV{TERM}='xterm-256color';
28              
29             =encoding utf-8
30              
31             =head1 NAME
32              
33             HPC::Runner::PBS - Submit jobs to a PBS job scheduler.
34              
35             =head1 DESCRIPTION
36              
37             HPC::Runner::PBS is a wrapper around qsub and can be used to submit arbirtary bash commands to PBS.
38              
39             It has two levels of management. The first is the main qsub command, and the second is the actual job, which runs commands in parallel, controlled by HPC::Runner::Threads or HPC::Runner::MCE.
40              
41             It supports job dependencies. Put in the command 'wait' to tell PBS that some job or jobs depend on some other jobs completion. Put in the command 'newnode' to tell HPC::Runner::PBS to submit the job to a new node.
42              
43             The only necessary option is the --infile, and --queue if you wish to run on a queue besides s48.
44              
45             The bulk of this code is extended from HPC::Runner::Slurm.
46              
47             =head2 queue
48              
49             Same as the partition in HPC::Runner::Slurm
50              
51             =cut
52              
53             has 'queue' => (
54             is => 'rw',
55             isa => 'Str|Undef',
56             required => 0,
57             documentation => q{PBS queue for job submission. Defaults is none, pbs decides.},
58             predicate => 'has_queue',
59             clearer => 'clear_queue',
60             );
61              
62             has '+partition' =>(
63             isa => 'Str | Undef',
64             #alias => 'queue',
65             #predicate => 'has_queue',
66             #clearer => 'clear_queue',
67             );
68              
69             =head2 walltime
70              
71             Define PBS walltime
72              
73             =cut
74              
75             has 'walltime' => (
76             is => 'rw',
77             isa => 'Str',
78             required => 1,
79             default => '04:00:00',
80             predicate => 'has_walltime',
81             clearer => 'clear_walltime,'
82             );
83              
84             =head2 mem
85              
86             =cut
87              
88             has 'mem' => (
89             is => 'rw',
90             isa => 'Str|Undef',
91             predicate => 'has_mem',
92             clearer => 'clear_mem',
93             required => 0,
94             documentation => q{Supply a memory limit},
95             );
96              
97             =head2 template_file
98              
99             actual template file
100              
101             One is generated here for you, but you can always supply your own with --template_file /path/to/template
102              
103             =cut
104              
105             has '+template_file' => (
106             is => 'rw',
107             isa => 'Str',
108             default => sub {
109             my $self = shift;
110              
111             my($fh, $filename) = tempfile();
112              
113             my $tt =<<EOF;
114             #!/bin/bash
115             #
116             #PBS -N [% JOBNAME %]
117             [% IF self.has_queue %]
118             #PBS -q [% self.queue %]
119             [% END %]
120             #PBS -l nodes=1:ppn=[% CPU %]
121             [% IF self.has_walltime %]
122             #PBS -l walltime=[% self.walltime %]
123             [% END %]
124             #PBS -j oe
125             #PBS -o localhost:[% OUT %]
126             [% IF self.has_mem %]
127             #PBS -l mem=[% self.mem %]
128             [% END %]
129              
130             [% IF AFTEROK %]
131             #PBS -W depend=afterok:[% AFTEROK %]
132             [% END %]
133              
134             [% IF MODULE %]
135             [% FOR d = MODULE %]
136             module load [% d %]
137             [% END %]
138             [% END %]
139              
140             [% COMMAND %]
141             EOF
142              
143             print $fh $tt;
144             $DB::single=2;
145             return $filename;
146             },
147             );
148              
149             =head1 SUBROUTINES/METHODS
150              
151             =cut
152              
153             =head2 run()
154              
155             First sub called
156             Calling system module load * does not work within a screen session!
157              
158             =cut
159              
160             sub run {
161 0     0     my $self = shift;
162              
163 0           $DB::single=2;
164              
165 0           $self->logname('pbs_logs');
166 0           $self->log($self->init_log);
167 0           $DB::single=2;
168              
169 0 0         if($self->serial){
170 0           $self->procs(1);
171             }
172              
173 0           $self->check_files;
174 0           $self->parse_file_slurm;
175 0           $DB::single=2;
176             }
177              
178             sub get_nodes {
179 0     0     my($self) = @_;
180              
181             #$self->partition($self->queue) if $self->partition eq "";
182              
183 0           $DB::single=2;
184              
185 0           $self->nodelist([]);
186              
187 0           $DB::single=2;
188             }
189              
190             =head2 submit_slurm()
191              
192             Submit jobs to PBS using qsub
193              
194             Uses almost the same logic as submit_slurm, so we'll keep that.
195              
196             This subroutine was just about 100% from the following perlmonks discussions. All that I did was add in some logging.
197              
198             http://www.perlmonks.org/?node_id=151886
199             You can use the script at the top to test the runner. Just download it, make it executable, and put it in the infile as
200              
201             perl command.pl 1
202             perl command.pl 2
203             #so on and so forth
204              
205             =cut
206              
207             sub submit_slurm{
208 0     0     my $self = shift;
209              
210 0           my ($infh,$outfh,$errfh);
211 0           $errfh = gensym(); # if you uncomment this line, $errfh will
212             # never be initialized for you and you
213             # will get a warning in the next print
214             # line.
215 0           my $cmdpid;
216 0           eval{
217 0           $cmdpid = open3($infh, $outfh, $errfh, "qsub ".$self->slurmfile);
218             };
219 0 0         die $@ if $@;
220              
221 0           my $sel = new IO::Select; # create a select object
222 0           $sel->add($outfh,$errfh); # and add the fhs
223              
224 0           my($stdout, $stderr, $jobid);
225              
226 0           while(my @ready = $sel->can_read) {
227 0           foreach my $fh (@ready) { # loop through them
228 0           my $line;
229             # read up to 4096 bytes from this fh.
230 0           my $len = sysread $fh, $line, 4096;
231 0 0         if(not defined $len){
    0          
232             # There was an error reading
233             #$self->log->fatal("Error from child: $!");
234 0           $self->log_main_messages('fatal', "Error from child: $!");
235             } elsif ($len == 0){
236             # Finished reading from this FH because we read
237             # 0 bytes. Remove this handle from $sel.
238 0           $sel->remove($fh);
239 0           next;
240             } else { # we read data alright
241 0 0         if($fh == $outfh) {
    0          
242 0           $stdout .= $line;
243             #$self->log->info($line);
244 0           $self->log_main_messages('debug', $line);
245             } elsif($fh == $errfh) {
246 0           $stderr .= $line;
247             #$self->log->error($line);
248 0           $self->log_main_messages('error', "There was an error!\n".$line);
249             } else {
250             #$self->log->fatal("Shouldn't be here!\n");
251 0           $self->log_main_messages('fatal', "We shouldn't be here, something has gone wrong\n");
252             }
253             }
254             }
255             }
256              
257 0           waitpid($cmdpid, 1);
258 0           my $exitcode = $?;
259              
260             #($jobid) = $stdout =~ m/(\w.*)$/ if $stdout;
261 0           ($jobid) = $stdout;
262 0           chomp($jobid);
263              
264 0 0         if(!$jobid){
265 0           print "No job was submitted! Please check your things!\nFull error is:\t$stderr\n$stdout\nEnd Job error";
266 0           print "Submit scripts will be written, but will not be submitted to the queue. Please look at your files in ".$self->outdir." for more information\n";
267 0           $self->submit_to_slurm(0);
268             }
269             else{
270 0           push(@{$self->jobref->[-1]}, $jobid);
  0            
271 0           print "Submitting job ".$self->slurmfile."\n\tWith PBS jobid $jobid\n";
272             }
273             #Fix for jobs not showing up...
274 0           sleep(5);
275             }
276              
277              
278             __PACKAGE__->meta->make_immutable;
279              
280             1;
281              
282             __END__
283              
284              
285             =head1 SYNOPSIS
286              
287             use HPC::Runner::PBS;
288              
289             =head1 AUTHOR
290              
291             Jillian Rowe E<lt>jillian.e.rowe@gmail.comE<gt>
292              
293             =head1 Acknowledgements
294              
295             This module was originally developed at and for NYU Abu Dhabi in the Center for Genomics and Systems Biology.
296             With approval from NYUAD, this information was generalized and put on github, for which
297             the authors would like to express their gratitude.
298              
299             =head1 COPYRIGHT
300              
301             Copyright 2015- Jillian Rowe
302              
303             =head1 LICENSE
304              
305             This library is free software; you can redistribute it and/or modify
306             it under the same terms as Perl itself.
307              
308             =head1 SEE ALSO
309              
310             =cut