File Coverage

blib/lib/HPC/Runner/Slurm.pm
Criterion Covered Total %
statement 42 135 31.1
branch 0 40 0.0
condition 0 6 0.0
subroutine 14 17 82.3
pod n/a
total 56 198 28.2


line stmt bran cond sub pod time code
1             package HPC::Runner::Slurm;
2              
3 1     1   17401 use File::Path qw(make_path remove_tree);
  1         2  
  1         75  
4 1     1   642 use File::Temp qw/ tempfile tempdir /;
  1         16772  
  1         67  
5 1     1   483 use IO::File;
  1         724  
  1         153  
6 1     1   734 use IO::Select;
  1         1284  
  1         39  
7 1     1   5 use Cwd;
  1         1  
  1         46  
8 1     1   1044 use IPC::Open3;
  1         1956  
  1         42  
9 1     1   4 use Symbol;
  1         1  
  1         38  
10 1     1   521 use Template;
  1         15266  
  1         35  
11 1     1   759 use Log::Log4perl qw(:easy);
  1         37077  
  1         6  
12 1     1   1414 use DateTime;
  1         81579  
  1         39  
13 1     1   1174 use Data::Dumper;
  1         5568  
  1         83  
14 1     1   8 use List::Util qw/shuffle/;
  1         2  
  1         82  
15              
16 1     1   998 use Moose;
  1         350831  
  1         9  
17 1     1   5801 use namespace::autoclean;
  1         1  
  1         8  
18             extends 'HPC::Runner::Scheduler';
19             with 'MooseX::SimpleConfig';
20              
21             # For pretty man pages!
22             $ENV{TERM}='xterm-256color';
23             our $VERSION = '2.57';
24              
25             =head1 NAME
26              
27             HPC::Runner::Slurm - Job Submission to Slurm
28              
29             =head1 SYNOPSIS
30              
31             Please see the indepth documentation at L<HPC::Runner::Usage>.
32              
33             package Main;
34             extends 'HPC::Runner::Slurm';
35              
36             Main->new_with_options(infile => '/path/to/commands');
37              
38             This module is a wrapper around sbatch and can be used to submit arbirtary bash commands to slurm.
39              
40             It has two levels of management. The first is the main sbatch command, and the second is the actual job, which runs commands in parallel, controlled by HPC::Runner::Threads or HPC::Runner::MCE.
41              
42             It supports job dependencies. Put in the command 'wait' to tell slurm that some job or jobs depend on some other jobs completion. Put in the command 'newnode' to tell HPC::Runner::Slurm to submit the job to a new node.
43              
44             The only necessary option is the --infile.
45              
46             =head2 Submit Script
47              
48             cmd1
49             cmd2 && cmd3
50             cmd4 \
51             --option cmd4 \
52             #Tell HPC::Runner::Slurm to put in some job dependencies.
53             wait
54             cmd5
55             #Tell HPC::Runner::Slurm to pass things off to a new node, but this job doesn't depend on the previous
56             newnode
57             cmd6
58              
59             =head2 get_nodes
60              
61             Get the nodes from sinfo if not supplied
62              
63             If the nodelist is supplied partition must be supplied
64              
65             =cut
66              
67             sub BUILD {
68 0     0     my $self = shift;
69 0           $self->logname('slurm_logs');
70 0           $self->log($self->init_log);
71             }
72              
73             sub get_nodes{
74 0     0     my($self) = @_;
75              
76 0           $DB::single=2;
77             # #Fix - had this backwards
78 0 0         return if $self->slurm_decides;
79 0 0 0       if($self->nodelist && !$self->partition){
80 0           print "If you define a nodelist you must define a partition!\n";
81 0           die;
82             }
83              
84 0           my @s = `sinfo -r`;
85 0           my $href;
86              
87 0           foreach my $s (@s) {
88 0           my @nodes = ();
89 0           my $noderef = [];
90 0 0         next if $s =~ m/^PARTITION/i;
91 0           my @t = ($s =~ /(\S+)/g);
92 0           $t[0] =~ s/\*//g;
93 0 0         next unless $t[1] =~ m/up/;
94              
95 0           my $nodes = $t[5];
96              
97             #list of nodes
98 0 0         if($nodes =~ m/\[/){
99 0           my($n) = ($nodes =~ m/\[(\S+)\]/g);
100 0           my @n = split(",", $n);
101              
102 0           foreach my $nt (@n) {
103 0 0         if($nt =~ m/-/){
104 0           my(@m) = ($nt =~ m/(\d+)-(\d+)/g);
105 0           push(@$noderef, ($m[0]..$m[1]));
106             }
107             else{
108 0           my($m) = ($nt =~ m/(\d+)/g);
109 0           push(@$noderef, $m);
110             }
111             }
112             }
113             else{ #only one node
114 0           my($m) = ($nodes =~ m/(\d+)/g);
115 0           push(@$noderef, $m);
116             }
117              
118 0 0         if(exists $href->{$t[0]}){
119 0           my $aref = $href->{$t[0]};
120 0 0         push(@$aref, @$noderef) if $noderef;
121 0           $href->{$t[0]} = $aref;
122             }
123             else{
124             # $href->{$t[0]} = \@nodes;
125 0           $href->{$t[0]} = $noderef;
126             }
127             }
128              
129             #Got the nodes lets find out which partition has the most nodes
130             #Unless we already have a defined partition, then we don't care
131              
132 0           my $holder = 0;
133 0           my $bpart;
134              
135 0           while(my($part, $nodes) = each %{$href}){
  0            
136 0 0         next unless $nodes;
137 0 0         next unless ref($nodes) eq "ARRAY";
138              
139 0           @$nodes = map { $part.$_ } @$nodes;
  0            
140              
141 0 0         if(scalar @$nodes > $holder){
142 0           $holder = scalar @$nodes;
143 0           $bpart = $part;
144             }
145             }
146              
147             #Allow for user defined partition and/or partition/nodelist
148             #Also randomize nodelist so we quit hammering the first node
149 0           $DB::single=2;
150              
151 0 0 0       if($self->partition && $self->nodelist){
    0          
152 0           $DB::single=2;
153 0           return;
154             }
155             elsif($self->partition){
156 0           $DB::single=2;
157 0           my @shuffle = shuffle @{$href->{$self->partition}};
  0            
158             # $self->nodelist($href->{$self->partition});
159 0           $self->nodelist(\@shuffle);
160 0           return;
161             }
162              
163 0           $self->partition($bpart);
164             # $self->nodelist($href->{$bpart});
165 0           my @shuffle = shuffle @{$href->{$bpart}};
  0            
166 0           $self->nodelist(\@shuffle);
167             }
168              
169              
170              
171             =head2 submit_slurm()
172              
173             Submit jobs to slurm queue using sbatch.
174              
175             This subroutine was just about 100% from the following perlmonks discussions. All that I did was add in some logging.
176              
177             http://www.perlmonks.org/?node_id=151886
178             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
179              
180             perl command.pl 1
181             perl command.pl 2
182             #so on and so forth
183              
184             =cut
185              
186             sub submit_slurm{
187 0     0     my $self = shift;
188              
189 0           my ($infh,$outfh,$errfh);
190 0           $errfh = gensym(); # if you uncomment this line, $errfh will
191             # never be initialized for you and you
192             # will get a warning in the next print
193             # line.
194 0           my $cmdpid;
195 0           eval{
196 0           $cmdpid = open3($infh, $outfh, $errfh, "sbatch ".$self->slurmfile);
197             };
198 0 0         die $@ if $@;
199              
200 0           my $sel = new IO::Select; # create a select object
201 0           $sel->add($outfh,$errfh); # and add the fhs
202              
203 0           my($stdout, $stderr, $jobid);
204              
205 0           while(my @ready = $sel->can_read) {
206 0           foreach my $fh (@ready) { # loop through them
207 0           my $line;
208             # read up to 4096 bytes from this fh.
209 0           my $len = sysread $fh, $line, 4096;
210 0 0         if(not defined $len){
    0          
211             # There was an error reading
212             #$self->log->fatal("Error from child: $!");
213 0           $self->log_main_messages('fatal', "Error from child: $!");
214             } elsif ($len == 0){
215             # Finished reading from this FH because we read
216             # 0 bytes. Remove this handle from $sel.
217 0           $sel->remove($fh);
218 0           next;
219             } else { # we read data alright
220 0 0         if($fh == $outfh) {
    0          
221 0           $stdout .= $line;
222             #$self->log->info($line);
223 0           $self->log_main_messages('debug', $line)
224             } elsif($fh == $errfh) {
225 0           $stderr .= $line;
226             #$self->log->error($line);
227 0           $self->log_main_messages('error', $line);
228             } else {
229             #$self->log->fatal("Shouldn't be here!\n");
230 0           $self->log_main_messages('fatal', "Shouldn't be here!");
231             }
232             }
233             }
234             }
235              
236 0           waitpid($cmdpid, 1);
237 0           my $exitcode = $?;
238              
239 0 0         ($jobid) = $stdout =~ m/Submitted batch job (\d.*)$/ if $stdout;
240 0 0         if(!$jobid){
241 0           print "No job was submitted! Please check to make sure you have loaded modules shared and slurm!\nFull error is:\t$stderr\n$stdout\nEnd Job error";
242 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";
243 0           $self->submit_to_slurm(0);
244             }
245             else{
246 0           push(@{$self->jobref->[-1]}, $jobid);
  0            
247 0           print "Submitting job ".$self->slurmfile."\n\tWith Slurm jobid $jobid\n";
248             }
249             }
250              
251             =head1 AUTHOR
252              
253             Jillian Rowe, C<< <jillian.e.rowe at gmail.com> >>
254              
255             =head1 BUGS
256              
257             Please report any bugs or feature requests to C<bug-runner-init at rt.cpan.org>, or through
258             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=HPC-Runner-Slurmm>. I will be notified, and then you'll
259             automatically be notified of progress on your bug as I make changes.
260              
261              
262             =head1 SUPPORT
263              
264             You can find documentation for this module with the perldoc command.
265              
266             perldoc HPC::Runner::Slurm
267              
268              
269             You can also look for information at:
270              
271             =over 4
272              
273             =item * RT: CPAN's request tracker (report bugs here)
274              
275             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=HPC-Runner-Slurm>
276              
277             =item * AnnoCPAN: Annotated CPAN documentation
278              
279             L<http://annocpan.org/dist/HPC-Runner-Slurm>
280              
281             =item * CPAN Ratings
282              
283             L<http://cpanratings.perl.org/d/HPC-Runner-Slurm>
284              
285             =item * Search CPAN
286              
287             L<http://search.cpan.org/dist/HPC-Runner-Slurm/>
288              
289             =back
290              
291             =head1 Acknowledgements
292              
293             Before Version 2.41
294              
295             This module was originally developed at and for Weill Cornell Medical
296             College in Qatar within ITS Advanced Computing Team. With approval from
297             WCMC-Q, this information was generalized and put on github, for which
298             the authors would like to express their gratitude.
299              
300             As of Version 2.41:
301              
302             This modules continuing development is supported by NYU Abu Dhabi in the Center for Genomics and Systems Biology.
303             With approval from NYUAD, this information was generalized and put on bitbucket, for which
304             the authors would like to express their gratitude.
305              
306              
307             =head1 LICENSE AND COPYRIGHT
308              
309             Copyright 2014 Weill Cornell Medical College in Qatar.
310              
311             This program is free software; you can redistribute it and/or modify it
312             under the terms of the the Artistic License (2.0). You may obtain a
313             copy of the full license at:
314              
315             L<http://www.perlfoundation.org/artistic_license_2_0>
316              
317             Any use, modification, and distribution of the Standard or Modified
318             Versions is governed by this Artistic License. By using, modifying or
319             distributing the Package, you accept this license. Do not use, modify,
320             or distribute the Package, if you do not accept this license.
321              
322             If your Modified Version has been derived from a Modified Version made
323             by someone other than you, you are nevertheless required to ensure that
324             your Modified Version complies with the requirements of this license.
325              
326             This license does not grant you the right to use any trademark, service
327             mark, tradename, or logo of the Copyright Holder.
328              
329             This license includes the non-exclusive, worldwide, free-of-charge
330             patent license to make, have made, use, offer to sell, sell, import and
331             otherwise transfer the Package with respect to any patent claims
332             licensable by the Copyright Holder that are necessarily infringed by the
333             Package. If you institute patent litigation (including a cross-claim or
334             counterclaim) against any party alleging that the Package constitutes
335             direct or contributory patent infringement, then this Artistic License
336             to you shall terminate on the date that such litigation is filed.
337              
338             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
339             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
340             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
341             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
342             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
343             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
344             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
345             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
346              
347              
348             =cut
349              
350             __PACKAGE__->meta->make_immutable;
351             #use namespace::autoclean;
352              
353             1;