File Coverage

blib/lib/HPC/Runner/Command/submit_jobs/Utils/Scheduler/ParseInput.pm
Criterion Covered Total %
statement 15 109 13.7
branch 0 42 0.0
condition 0 6 0.0
subroutine 5 15 33.3
pod 7 10 70.0
total 27 182 14.8


line stmt bran cond sub pod time code
1             package HPC::Runner::Command::submit_jobs::Utils::Scheduler::ParseInput;
2              
3 1     1   559 use Moose::Role;
  1         2  
  1         6  
4 1     1   4342 use List::MoreUtils qw(natatime);
  1         2  
  1         10  
5 1     1   593 use Storable qw(dclone);
  1         3  
  1         47  
6 1     1   6 use Memoize;
  1         2  
  1         40  
7 1     1   6 use Data::Dumper;
  1         2  
  1         866  
8              
9             =head1 HPC::Runner::App::Scheduler::ParseInput
10              
11             Parse the infile for HPC params, jobs, and batches
12              
13             =head2 Subroutines
14              
15             =head3 parse_file_slurm
16              
17             Parse the file looking for the following conditions
18              
19             lines ending in `\`
20             wait
21             nextnode
22              
23             Batch commands in groups of $self->cpus_per_task, or smaller as wait and nextnode indicate
24              
25             =cut
26              
27             sub parse_file_slurm {
28 0     0 1   my $self = shift;
29              
30 0 0         my $fh = IO::File->new( $self->infile, q{<} )
31             or print "Error opening file "
32             . $self->infile . " "
33             . $!; # even better!
34              
35 0           $self->reset_cmd_counter;
36 0           $self->reset_batch_counter;
37 0           $self->check_add_to_jobs;
38              
39             #If we pass in commandline afterok
40             #This is not supported within a file
41             #HPC afterok=thing1,thing2 -> Not supported
42              
43 0 0         if ( $self->has_afterok ) {
44 0           $self->jobs->{ $self->jobname }->submitted = 1;
45 0           $self->jobs->{ $self->jobname }->scheduler_ids = $self->afterok;
46              
47 0           my $oldjob = $self->jobname;
48 0           $self->increase_jobname();
49 0           $self->deps($oldjob);
50             }
51              
52 0           while (<$fh>) {
53 0           my $line = $_;
54 0 0         next unless $line;
55 0 0         next unless $line =~ m/\S/;
56 0           $self->process_lines($line);
57             }
58              
59 0           close($fh);
60              
61 0           $self->post_process_parse_file_slurm;
62             }
63              
64             =head3 post_process_file_slurm
65              
66             =cut
67              
68             sub post_process_parse_file_slurm {
69 0     0 0   my $self = shift;
70              
71 0           $self->check_for_commands;
72 0 0         if ( !$self->sanity_check_schedule ) {
73 0           return;
74             }
75 0           $self->schedule_jobs;
76 0           $self->print_table_schedule_info;
77 0           $self->chunk_commands;
78             }
79              
80             =head3 check_for_commands
81              
82             Check all jobs to make sure they have commands
83              
84             =cut
85              
86             sub check_for_commands {
87 0     0 1   my $self = shift;
88              
89 0           my @keys = keys %{ $self->jobs };
  0            
90              
91 0           $self->reset_cmd_counter;
92 0           $self->reset_batch_counter;
93              
94 0           foreach my $key (@keys) {
95              
96             # next if $self->jobs->{$key}->count_cmds;
97 0 0         next if $self->jobs->{$key}->cmd_counter;
98 0           delete $self->jobs->{$key};
99 0           delete $self->graph_job_deps->{$key};
100             }
101             }
102              
103             =head3 process_lines
104              
105             Iterate through all lines in the job file
106             1. Sanity check - can't use nohup or push commands to background
107             2. Check for HPC meta - #HPC
108             3. Check for Note meta
109              
110             =cut
111              
112             sub process_lines {
113 0     0 1   my $self = shift;
114 0           my $line = shift;
115              
116 0           $self->check_sanity($line);
117 0           $self->process_hpc_meta($line);
118 0           $self->check_note_meta($line);
119              
120 0 0         return if $line =~ m/^#/;
121              
122             #Do I need this?
123             #$self->check_add_to_jobs();
124              
125 0           $self->check_lines_add_cmd($line);
126             }
127              
128             =head3 check_lines_add_cmd
129              
130             Append to the command
131              
132             We check for a few cases
133              
134             1. A line that is terminated by the usual newline character
135              
136             echo "hello!"
137              
138             2. A multiline command in the usual bash sense
139              
140             echo "goodbye!" && \
141             echo "not again!"
142              
143             3. The command is wait. Submit jobs we already have to the scheduler, and any jobs after 'wait', depend upon jobs before 'wait' finishing.
144              
145             wait
146              
147             4. Deprecated! The command is 'newnode' on a line by itself. Submit all the previous jobs, but no dependenciies. Instead please use '#HPC commands_per_node' within your job file.
148              
149             #HPC jobname=job01
150             #HPC commands_per_node=1
151             #HPC cpus_per_task=1
152              
153             gzip VERY_LARGE_FILE
154             gzip OTHER_VERY_LARGE_FILE
155              
156             =cut
157              
158             sub check_lines_add_cmd {
159 0     0 1   my $self = shift;
160 0           my $line = shift;
161              
162 0 0         return unless $line;
163              
164 0           $self->add_cmd($line);
165              
166 0 0         if ( $line =~ m/\\$/ ) {
    0          
167 0           return;
168             }
169              
170             #If we're using 'wait' its linear deps
171             elsif ( $self->match_cmd(qr/^wait$/) ) {
172 0           $self->clear_cmd;
173 0           $self->check_add_to_jobs;
174 0           my $oldjob = $self->jobname;
175 0           $self->increase_jobname();
176 0           $self->deps($oldjob);
177             }
178              
179             #TODO Get rid of this
180 0 0         $self->job_files->{ $self->jobname }->print( $self->cmd ) if $self->has_cmd;
181 0 0         $self->jobs->{ $self->jobname }->inc_cmd_counter if $self->has_cmd;
182              
183 0           $self->inc_cmd_counter;
184 0           $self->clear_cmd;
185             }
186              
187             =head3 check_sanity
188              
189             Do some sanity checks. So far we only check for nohup, because nohup confuses schedulers.
190              
191             #TODO Add check for when line ends with &. This also confuses schedulers
192              
193             =cut
194              
195             sub check_sanity {
196              
197             #TODO Integrate this with DBM::Deep jobs -> everything will be in there
198 0     0 1   my $self = shift;
199 0           my $line = shift;
200              
201             #Do a sanity check for nohup
202 0 0         if ( $line =~ m/^nohup / ) {
203 0           die print
204             "You cannot submit jobs to the queue using nohup! Please remove nohup and try again.\n";
205             }
206             }
207              
208             =head3 check_note_meta
209              
210             Check for lines starting with #TASK - used to pass per process task_tags
211              
212             =cut
213              
214             sub check_note_meta {
215 0     0 1   my $self = shift;
216 0           my $line = shift;
217              
218 0 0         return unless $line =~ m/^#TASK/;
219 0           $self->add_cmd($line);
220             }
221              
222             =head3 process_hpc_meta
223              
224             allow for changing parameters mid through the script
225              
226             #Job1
227             echo "this is job one" && \
228             bin/dostuff bblahblahblah
229              
230             #HPC cpu_per_task=12
231              
232             echo "This is my new job with new HPC params!"
233              
234             Make sure our hpc variables are current for filling in the template
235             #HPC cpus_per_task=1
236             to
237             #SBATCH --cpus-per-task=1
238              
239             =cut
240              
241             #TODO This should be done in parse_input
242              
243             sub process_hpc_meta {
244 0     0 1   my $self = shift;
245 0           my $line = shift;
246              
247 0 0         return unless $line =~ m/^#HPC/;
248 0           chomp($line);
249              
250 0           my ( $t1, $t2 ) = parse_meta($line);
251              
252 0 0         if ( !$self->can($t1) ) {
253 0           print "Option $t1 is an invalid option!\n";
254 0           return;
255             }
256              
257 0           my $jobname = $self->jobname;
258              
259             #Only process jobnames
260 0 0 0       if ( $t1 eq 'jobname' || $t1 eq 'deps' ) {
261 0           $self->$t1($t2);
262 0           return;
263             }
264              
265 0 0         if ( $jobname eq 'hpcjob_001' ) {
266              
267             # Could also just be using global defs... $self->app_log->warn('You have not
268             # defined a job name. It is best practice to defined jobnames, but we will
269             # define hpcjob_001 for you.');
270 0           $self->apply_global_directives( $t1, $t2 );
271 0           $self->apply_job_directives( $t1, $t2 );
272             }
273             else {
274 0           $self->apply_job_directives( $t1, $t2 );
275             }
276              
277 0           push( @{ $self->jobs->{ $self->jobname }->{hpc_meta} }, $line );
  0            
278             }
279              
280             sub apply_global_directives {
281 0     0 0   my $self = shift;
282 0           my $t1 = shift;
283 0           my $t2 = shift;
284              
285 0 0 0       if ( $t1 && $self->can($t1) ) {
286 0           $self->$t1($t2);
287             }
288             }
289              
290             sub apply_job_directives {
291 0     0 0   my $self = shift;
292 0           my $t1 = shift;
293 0           my $t2 = shift;
294              
295 0 0         return unless $self->jobs->{ $self->jobname };
296 0 0         if ( $self->jobs->{ $self->jobname }->can($t1) ) {
297 0           $self->jobs->{ $self->jobname }->$t1($t2);
298             }
299             else {
300             ##Warnings
301 0           $self->app_log->warn( 'You tried to assign ' . $t1 . ' to ' . $t2 );
302             }
303             }
304              
305             memoize('parse_meta');
306              
307             sub parse_meta {
308             my $line = shift;
309             my ( @match, $t1, $t2 );
310              
311             @match = $line =~ m/ (\w+)=(.+)$/;
312             ( $t1, $t2 ) = ( $match[0], $match[1] );
313              
314             return ( $t1, $2 );
315             }
316              
317             1;