rbbt-util 5.30.13 → 5.31.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f5bed0870bd27e4ef4d95a01ad49c4e6fb9e64a6e89eeb82e613e4523b4956f
4
- data.tar.gz: efa0cdc96054711ad3069c3ead053b68dba25198cab1a76163d3b21b5ddf93fb
3
+ metadata.gz: 71eb12f214343e48d8b2fdbcf5886e382dd3c60c8faeb89625cfb9aa56a5ad08
4
+ data.tar.gz: cf8f167c63aecfd2f389650ea9c0baeac59c769c9258bdedc973bc58d8a845e4
5
5
  SHA512:
6
- metadata.gz: 6e1039b390a000f83c29a419294e87d960f6de97e3a6bd341f3772fec279ec0619bbdaf43ced206d83125c02d8e3617e83a365c089b2e4a7463f85c26f12bde2
7
- data.tar.gz: 45f167302faebbfed96b6babe0f5f5941d2722ecd6557b38dc82a997ab75b300ba11abf393def4414b9388fc101b778072119e49a719243d2fc9fd110c69c5b6
6
+ metadata.gz: a8a23f6a6ab179cb2d4a23ad4556e8f5d5cb1aee600e51b9205a6545c62e364353504adf94cafe8b0e9878881d67c14a27b95724587e1ec109157ad749bdf43d
7
+ data.tar.gz: 2d44d126bf3c7963a2846ebac4a749f1c124b82d94b925a1f1c6d2d4ff371ace5b29d86ee80f581c9aab7368b8d4a403687520dd62289c4da964e00263e65065
data/lib/rbbt/hpc.rb CHANGED
@@ -1,3 +1,6 @@
1
1
  require 'rbbt-util'
2
2
  require 'rbbt/util/cmd'
3
+ require 'rbbt/hpc/batch'
4
+ require 'rbbt/hpc/orchestrate'
3
5
  require 'rbbt/hpc/slurm'
6
+ require 'rbbt/hpc/lsf'
@@ -0,0 +1,623 @@
1
+ module HPC
2
+ class SBATCH < Exception;
3
+ attr_accessor :directory
4
+ def initialize(directory)
5
+ @directory = directory
6
+ end
7
+ end
8
+
9
+ module TemplateGeneration
10
+ def exec_cmd(job, options = {})
11
+ env_cmd = Misc.process_options options, :env_cmd
12
+ development = Misc.process_options options, :development
13
+
14
+ job_cmd = self.rbbt_job_exec_cmd(job, options)
15
+
16
+ if options[:singularity]
17
+
18
+ group, user, user_group, scratch_group_dir, projects_group_dir = options.values_at :group, :user, :user_group, :scratch_group_dir, :projects_group_dir
19
+
20
+ singularity_img, singularity_opt_dir, singularity_ruby_inline = options.values_at :singularity_img, :singularity_opt_dir, :singularity_ruby_inline
21
+
22
+ singularity_cmd = %(singularity exec -e -B #{singularity_opt_dir}:/singularity_opt/ -B /apps/)
23
+
24
+ if contain = options[:contain]
25
+ contain = File.expand_path(contain)
26
+ singularity_cmd << %( -C -H "#{contain}" \
27
+ -B "#{options[:batch_dir]}" \
28
+ -B /scratch/tmp \
29
+ #{ group != user_group ? "-B /gpfs/projects/#{user_group}" : "" } \
30
+ -B #{scratch_group_dir} \
31
+ -B #{projects_group_dir} \
32
+ -B "#{singularity_ruby_inline}":"#{contain}/.ruby_inline":rw \
33
+ -B ~/git:"#{contain}/git":ro \
34
+ #{Open.exists?('~/.rbbt/software/opt/')? '-B ~/.rbbt/software/opt/:"/opt/":ro' : '' } \
35
+ -B ~/.rbbt:"#{contain}/home/":ro)
36
+ singularity_cmd << " #{singularity_img} "
37
+ end
38
+ env_cmd ||= ""
39
+ env_cmd << " TMPDIR='#{contain}/.rbbt/tmp' "
40
+ end
41
+
42
+ if env_cmd
43
+ exec_cmd = %(env #{env_cmd} rbbt)
44
+ else
45
+ exec_cmd = %(rbbt)
46
+ end
47
+
48
+ exec_cmd << "--dev '#{development}'" if development
49
+
50
+ exec_cmd = singularity_cmd + exec_cmd if singularity_cmd
51
+
52
+ exec_cmd
53
+ end
54
+
55
+ def rbbt_job_exec_cmd(job, options)
56
+
57
+ jobname = job.clean_name
58
+ workflow = job.workflow
59
+ task = job.task_name
60
+
61
+ Misc.add_defaults options, :jobname => jobname
62
+
63
+ task = Symbol === job.overriden ? job.overriden : job.task_name
64
+
65
+ if job.overriden
66
+ override_deps = job.rec_dependencies.
67
+ select{|dep| Symbol === dep.overriden }.
68
+ collect do |dep|
69
+
70
+ name = [dep.workflow.to_s, dep.task_name] * "#"
71
+ [name, dep.path] * "="
72
+ end * ","
73
+
74
+ options[:override_deps] = override_deps
75
+ end
76
+
77
+ # Save inputs into inputs_dir
78
+ inputs_dir = Misc.process_options options, :inputs_dir
79
+ saved = Step.save_job_inputs(job, inputs_dir) if inputs_dir
80
+ options[:load_inputs] = inputs_dir if saved && saved.any?
81
+
82
+ saved.each do |input|
83
+ options.delete input
84
+ end if saved
85
+
86
+ cmds = CMD.process_cmd_options options.merge(:add_option_dashes => true)
87
+
88
+ <<-EOF.strip
89
+ workflow task #{workflow} #{task} #{cmds}
90
+ EOF
91
+ end
92
+
93
+ def header(options)
94
+ header =<<-EOF
95
+ #!/bin/bash
96
+ EOF
97
+
98
+ header
99
+ end
100
+
101
+ def batch_options(job, options)
102
+ IndiferentHash.setup(options)
103
+
104
+ batch_options = IndiferentHash.setup({})
105
+
106
+ keys = [
107
+ :batch_dir,
108
+ :batch_modules,
109
+ :batch_name,
110
+ :contain,
111
+ :contain_and_sync,
112
+ :copy_image,
113
+ :drbbt,
114
+ :env_cmd,
115
+ :exclusive,
116
+ :highmem,
117
+ :manifest,
118
+ :nodes,
119
+ :queue,
120
+ :singularity,
121
+ :sync,
122
+ :task_cpus,
123
+ :time,
124
+ :user_group,
125
+ :wipe_container,
126
+ :workdir,
127
+ ]
128
+
129
+ keys.each do |key|
130
+ next if options[key].nil?
131
+ batch_options[key] = Misc.process_options options, key
132
+ end
133
+
134
+ batch_dir = batch_options[:batch_dir]
135
+
136
+ batch_name = File.basename(batch_dir)
137
+ inputs_dir = File.join(batch_dir, 'inputs_dir')
138
+
139
+ keys_from_config = [
140
+ :queue,
141
+ :highmem,
142
+ :exclusive,
143
+ :env_cmd,
144
+ :user_group,
145
+ :singularity_img,
146
+ :singularity_opt_dir,
147
+ :singularity_ruby_inline,
148
+ :singularity
149
+ ]
150
+
151
+ keys_from_config.each do |key|
152
+ next unless batch_options.include? key
153
+ default_value = Rbbt::Config.get(key, "batch_#{key}", "batch")
154
+ next if default_value.nil?
155
+ Misc.add_defaults batch_options, default_value
156
+ end
157
+
158
+ user = batch_options[:user] ||= ENV['USER'] || `whoami`.strip
159
+ group = batch_options[:group] ||= File.basename(File.dirname(ENV['HOME']))
160
+ batch_options[:scratch_group_dir] = File.join('/gpfs/scratch/', group)
161
+ batch_options[:projects_group_dir] = File.join('/gpfs/projects/', group)
162
+
163
+ if batch_options[:contain_and_sync]
164
+ if batch_options[:contain].nil?
165
+ contain_base = Rbbt::Config.get(:contain_base_dir, :batch_contain, :batch, :default => "/scratch/tmp/rbbt-[USER]")
166
+ contain_base = contain_base.sub('[USER]', user)
167
+ random_file = TmpFile.random_name
168
+ batch_options[:contain] = File.join(contain_base, random_file)
169
+ end
170
+
171
+ batch_options[:sync] ||= "~/.rbbt/var/jobs"
172
+ batch_options[:wipe_container] ||= 'post'
173
+ end
174
+
175
+ if batch_options[:contain] && ! batch_options[:singularity]
176
+ options[:workdir_all] = batch_options[:contain]
177
+ end
178
+
179
+ Misc.add_defaults batch_options,
180
+ :batch_name => batch_name,
181
+ :inputs_dir => inputs_dir,
182
+ :queue => 'debug',
183
+ :nodes => 1,
184
+ :step_path => job.path,
185
+ :task_cpus => 1,
186
+ :time => '2min',
187
+ :env_cmd => '_JAVA_OPTIONS="-Xms1g -Xmx${MAX_MEMORY}m"',
188
+ :singularity_img => ENV["SINGULARITY_IMG"] || "~/rbbt.singularity.img",
189
+ :singularity_ruby_inline => ENV["SINGULARITY_RUBY_INLINE"] || "~/.singularity_ruby_inline",
190
+ :singularity_opt_dir => ENV["SINGULARITY_OPT_DIR"] || "~/singularity_opt",
191
+ :workdir => Dir.pwd
192
+
193
+ exec_cmd = exec_cmd(job, batch_options)
194
+ rbbt_cmd = rbbt_job_exec_cmd(job, options)
195
+
196
+ Misc.add_defaults batch_options,
197
+ :exec_cmd => exec_cmd,
198
+ :rbbt_cmd => rbbt_cmd
199
+
200
+ batch_dir = batch_options[:batch_dir]
201
+
202
+ Misc.add_defaults batch_options,
203
+ :fout => File.join(batch_dir, 'std.out'),
204
+ :ferr => File.join(batch_dir, 'std.err'),
205
+ :fjob => File.join(batch_dir, 'job.id'),
206
+ :fdep => File.join(batch_dir, 'dependencies.list'),
207
+ :fcfdep => File.join(batch_dir, 'canfail_dependencies.list'),
208
+ :fexit => File.join(batch_dir, 'exit.status'),
209
+ :fsync => File.join(batch_dir, 'sync.log'),
210
+ :fsexit => File.join(batch_dir, 'sync.status'),
211
+ :fcmd => File.join(batch_dir, 'command.batch')
212
+
213
+ batch_options
214
+ end
215
+
216
+ def meta_data(options)
217
+ meta =<<-EOF
218
+ #MANIFEST: #{(options[:manifest] || []) * ", "}
219
+ #DEPENDENCIES: #{(options[:dependencies] || []) * ", "}
220
+ #EXEC_CMD: #{options[:exec_cmd]}
221
+ #CMD: #{options[:rbbt_cmd]}
222
+ #STEP_PATH: #{options[:step_path]}
223
+ EOF
224
+
225
+ meta = meta.split("\n").reject{|line| line =~ /: $/} * "\n"
226
+ meta
227
+ end
228
+
229
+ def load_modules(modules = [])
230
+ modules = modules.split(/,\s*/) if String === modules
231
+
232
+ str = ""
233
+ modules.each do |mod|
234
+ str << "module load #{ mod }" << "\n"
235
+ end if modules
236
+
237
+ str
238
+ end
239
+
240
+ def batch_system_variables
241
+ <<-EOF
242
+ let MAX_MEMORY="$(grep MemTotal /proc/meminfo|grep -o "[[:digit:]]*") / 1024"
243
+ EOF
244
+ end
245
+
246
+ def prepare_environment(options = {})
247
+ modules = options[:batch_modules]
248
+
249
+ prepare_environment = ""
250
+
251
+ functions = ""
252
+
253
+ if contain = options[:contain]
254
+ contain = File.expand_path(contain)
255
+ functions +=<<-EOF
256
+ function batch_erase_contain_dir(){
257
+ rm -Rfv '#{contain}' 2>1 >> '#{options[:fsync]}'
258
+ }
259
+ EOF
260
+
261
+ prepare_environment +=<<-EOF
262
+ if ls -A '#{contain}' &> /dev/null ; then
263
+ empty_contain_dir="false"
264
+ else
265
+ empty_contain_dir="true"
266
+ fi
267
+ EOF
268
+
269
+ prepare_environment +=<<-EOF if options[:wipe_container] == 'force'
270
+ batch_erase_contain_dir()
271
+ EOF
272
+ end
273
+
274
+ if sync = options[:sync]
275
+ source = if options[:singularity]
276
+ File.join(options[:contain], '.rbbt/var/jobs')
277
+ elsif options[:contain]
278
+ File.join(options[:contain], 'var/jobs')
279
+ else
280
+ '~/.rbbt/var/jobs/'
281
+ end
282
+
283
+ source = File.expand_path(source)
284
+ sync = File.expand_path(sync)
285
+ functions +=<<-EOF
286
+ function batch_sync_contain_dir(){
287
+ mkdir -p "$(dirname '#{sync}')"
288
+ rsync -avztAXHP --copy-unsafe-links "#{source}/" "#{sync}/" 2>1 >> '#{options[:fsync]}'
289
+ sync_es="$?"
290
+ echo $sync_es > '#{options[:fsexit]}'
291
+ find '#{sync}' -type l -ls | awk '$13 ~ /^#{sync.gsub('/','\/')}/ { sub("#{source}", "#{sync}", $13); print $11, $13 }' | while read A B; do rm $A; ln -s $B $A; done
292
+ }
293
+ EOF
294
+ end
295
+
296
+ if options[:singularity]
297
+
298
+ group, user, user_group, scratch_group_dir, projects_group_dir = options.values_at :group, :user, :user_group, :scratch_group_dir, :projects_group_dir
299
+
300
+ singularity_img, singularity_opt_dir, singularity_ruby_inline = options.values_at :singularity_img, :singularity_opt_dir, :singularity_ruby_inline
301
+
302
+ prepare_environment +=<<-EOF
303
+ # Load singularity modules
304
+ module load intel/2018.1
305
+ module load singularity
306
+ mkdir -p "#{singularity_opt_dir}"
307
+ EOF
308
+
309
+ if contain
310
+
311
+ prepare_environment +=<<-EOF
312
+ # Prepare container for singularity
313
+ mkdir -p "#{contain}/.rbbt/etc/"
314
+
315
+ for dir in .ruby_inline git home; do
316
+ mkdir -p "#{contain}/$dir"
317
+ done
318
+
319
+ for tmpd in persist_locks produce_locks R_sockets sensiblewrite sensiblewrite_locks step_info_locks tsv_open_locks; do
320
+ mkdir -p "#{contain}/.rbbt/tmp/$tmpd"
321
+ done
322
+
323
+ # Copy environment
324
+ cp ~/.rbbt/etc/environment #{contain}/.rbbt/etc/
325
+
326
+ # Set search_paths
327
+ echo "singularity: /singularity_opt/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" > #{contain}/.rbbt/etc/search_paths
328
+ echo "rbbt_user: /home/rbbt/.rbbt/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
329
+ echo "outside_home: #{contain}/home/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
330
+ echo "group_projects: #{projects_group_dir}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
331
+ echo "group_scratch: #{scratch_group_dir}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
332
+ echo "user_projects: #{projects_group_dir}/#{user}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
333
+ echo "user_scratch: #{scratch_group_dir}/#{user}/{PKGDIR}/{TOPLEVEL}/{SUBPATH}" >> #{contain}/.rbbt/etc/search_paths
334
+ EOF
335
+ end
336
+ end
337
+
338
+ batch_system_variables + load_modules(modules) + "\n" + functions + "\n" + prepare_environment
339
+ end
340
+
341
+ def execute(options)
342
+ exec_cmd, job_cmd = options.values_at :exec_cmd, :rbbt_cmd
343
+
344
+ <<-EOF
345
+ step_path=$(
346
+ #{exec_cmd} #{job_cmd} --printpath
347
+ )
348
+ exit_status=$?
349
+
350
+ [[ -z $BATCH_JOB_ID ]] || #{exec_cmd} workflow write_info --recursive --force=false --check_pid "$step_path" batch_job $BATCH_JOB_ID
351
+ [[ -z $BATCH_SYSTEM ]] || #{exec_cmd} workflow write_info --recursive --force=false --check_pid "$step_path" batch_system $BATCH_SYSTEM
352
+ EOF
353
+ end
354
+
355
+ def sync_environment(options = {})
356
+ sync_environment = ""
357
+
358
+ if options[:sync]
359
+ sync_environment +=<<-EOF
360
+ if [ $exit_status == '0' ]; then
361
+ batch_sync_contain_dir
362
+ else
363
+ sync_es=$exit_status
364
+ fi
365
+ EOF
366
+ end
367
+
368
+ sync_environment
369
+ end
370
+
371
+ def cleanup_environment(options = {})
372
+ cleanup_environment = ""
373
+ if options[:sync]
374
+ if options[:wipe_container] == 'force'
375
+ cleanup_environment +=<<-EOF
376
+ batch_erase_contain_dir
377
+ EOF
378
+ elsif options[:wipe_container] == 'post' || options[:wipe_container] == 'both'
379
+ cleanup_environment +=<<-EOF
380
+ if [ $sync_es == '0' -a $empty_contain_dir == 'true' ]; then
381
+ batch_erase_contain_dir
382
+ fi
383
+ EOF
384
+ end
385
+ end
386
+ cleanup_environment
387
+ end
388
+
389
+ def coda(options)
390
+ coda =<<-EOF
391
+ echo $exit_status > '#{options[:fexit]}'
392
+ EOF
393
+
394
+ if options[:sync]
395
+ coda +=<<-EOF
396
+ if [ $sync_es == '0' ]; then
397
+ exit $exit_status
398
+ else
399
+ exit $sync_es
400
+ fi
401
+ EOF
402
+ else
403
+ coda +=<<-EOF
404
+ exit $exit_status
405
+ EOF
406
+ end
407
+
408
+ coda
409
+ end
410
+
411
+ def job_template(job, options = {})
412
+ batch_options = batch_options job, options
413
+
414
+ header = self.header(batch_options)
415
+
416
+ meta_data = self.meta_data(batch_options)
417
+
418
+ prepare_environment = self.prepare_environment(batch_options)
419
+
420
+ execute = self.execute(batch_options)
421
+
422
+ sync_environment = self.sync_environment(batch_options)
423
+
424
+ cleanup_environment = self.cleanup_environment(batch_options)
425
+
426
+ coda = self.coda(batch_options)
427
+
428
+ <<-EOF
429
+ #{header}
430
+
431
+ # #{Log.color :green, "0. Meta-data"}
432
+ #{meta_data}
433
+
434
+ # #{Log.color :green, "1. Prepare environment"}
435
+ #{prepare_environment}
436
+
437
+ # #{Log.color :green, "2. Execute"}
438
+ #{execute}
439
+
440
+ # #{Log.color :green, "3. Sync and cleanup environment"}
441
+ #{sync_environment}
442
+ #{cleanup_environment}
443
+
444
+ # #{Log.color :green, "4. Exit"}
445
+ #{coda}
446
+ EOF
447
+ end
448
+
449
+ def prepare_submision(template, batch_dir, clean_batch_job = false, batch_dependencies = [])
450
+ Open.mkdir batch_dir
451
+ fcmd = File.join(batch_dir, 'command.batch')
452
+ fdep = File.join(batch_dir, 'dependencies.list')
453
+ fcfdep = File.join(batch_dir, 'canfail_dependencies.list')
454
+
455
+ Open.write(fcmd, template)
456
+
457
+ %w(std.out std.err job.id job.status dependencies.list canfail_dependencies.list exit.status sync.log inputs_dir).each do |filename|
458
+ path = File.join(batch_dir, filename)
459
+ Open.rm_rf path if File.exists? path
460
+ end if clean_batch_job
461
+
462
+ batch_dependencies = [] if batch_dependencies.nil?
463
+
464
+ canfail_dependencies = batch_dependencies.select{|dep| dep =~ /^canfail:(\d+)/ }.collect{|dep| dep.partition(":").last}
465
+ dependencies = batch_dependencies.reject{|dep| dep =~ /^canfail:(\d+)/ }
466
+
467
+ Open.write(fdep, dependencies * "\n") if dependencies.any?
468
+ Open.write(fcfdep, canfail_dependencies * "\n") if canfail_dependencies.any?
469
+
470
+ fcmd
471
+ end
472
+
473
+
474
+ def run_job(job, options = {})
475
+ system = self.to_s.split("::").last
476
+
477
+ batch_base_dir, clean_batch_job, remove_batch_dir, procpath, tail, batch_dependencies, dry_run = Misc.process_options options,
478
+ :batch_base_dir, :clean_batch_job, :remove_batch_dir, :batch_procpath, :tail, :batch_dependencies, :dry_run,
479
+ :batch_base_dir => File.expand_path(File.join('~/rbbt-batch'))
480
+
481
+ workflow = job.workflow
482
+ task_name = job.task_name
483
+
484
+ TmpFile.with_file(nil, remove_batch_dir, :tmpdir => batch_base_dir, :prefix => "#{system}_rbbt_job-#{workflow.to_s}-#{task_name}-") do |batch_dir|
485
+ Misc.add_defaults options,
486
+ :batch_dir => batch_dir,
487
+ :inputs_dir => File.join(batch_dir, "inputs_dir")
488
+
489
+ options[:procpath_performance] ||= File.join(batch_dir, "procpath##{procpath.gsub(',', '#')}") if procpath
490
+
491
+ template = self.job_template(job, options.dup)
492
+
493
+ fcmd = prepare_submision(template, options[:batch_dir], clean_batch_job, batch_dependencies)
494
+
495
+ batch_job = run_template(batch_dir, dry_run)
496
+
497
+ return batch_job unless tail
498
+
499
+ t_monitor = Thread.new do
500
+ self.follow_job(batch_dir, :STDERR)
501
+ end
502
+ self.wait_for_job(batch_dir)
503
+ t_monitor.raise Aborted
504
+ return unless Open.read(File.join(batch_dir, 'exit.status')).strip == '0'
505
+ path = Open.read(File.join(batch_dir, 'std.out')).strip
506
+ if Open.exists?(path) && job.path != path
507
+ Log.info "Path of BATCH job #{path} is different from original job #{job.path}. Stablishing link."
508
+ Open.ln path, job.path
509
+ Open.ln path + '.info', job.path + '.info' if Open.exists?(path + '.info')
510
+ Open.ln path + '.files', job.path + '.files' if Open.exists?(path + '.files')
511
+ end
512
+ batch_job
513
+
514
+ end
515
+ end
516
+
517
+ def follow_job(batch_dir, tail = true)
518
+ fjob = File.join(batch_dir, 'job.id')
519
+ fout = File.join(batch_dir, 'std.out')
520
+ ferr = File.join(batch_dir, 'std.err')
521
+ fexit = File.join(batch_dir, 'exit.status')
522
+ fstatus = File.join(batch_dir, 'job.status')
523
+
524
+ job = Open.read(fjob).strip if Open.exists?(fjob)
525
+
526
+ if job && ! File.exists?(fexit)
527
+ begin
528
+ status_txt = job_status(job)
529
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
530
+ STDERR.puts status_txt
531
+ lines = status_txt.split("\n").length
532
+ rescue
533
+ if ! File.exists?(fexit)
534
+ STDERR.puts Log.color(:magenta, "Job #{job.to_i} not done and not running. STDERR:")
535
+ STDERR.puts Open.read(ferr)
536
+ end
537
+ return
538
+ end
539
+ end
540
+
541
+ if File.exists?(fexit)
542
+ exit_status = Open.read(fexit)
543
+ if exit_status.to_i == 0
544
+ STDERR.puts Log.color(:magenta, "Job #{job} done with exit_status 0. STDOUT:")
545
+ STDERR.puts Open.read(fout)
546
+ else
547
+ STDERR.puts Log.color(:magenta, "Job #{job.to_i} done with exit_status #{exit_status}. STDERR:")
548
+ STDERR.puts Open.read(ferr)
549
+ end
550
+ return
551
+ end
552
+
553
+ if tail
554
+ Log.severity = 10
555
+ while ! File.exists? fout
556
+ if job
557
+ STDERR.puts
558
+ Log.clear_line(STDERR)
559
+ STDERR.write Log.color(:magenta, "Waiting for Output")
560
+ 3.times do
561
+ STDERR.write Log.color(:magenta, ".")
562
+ sleep 1
563
+ end
564
+ status_txt = job_status(job)
565
+ lines.times do
566
+ Log.clear_line(STDERR)
567
+ end
568
+ Log.clear_line(STDERR)
569
+ STDERR.puts Log.color(:magenta, "Status [#{job.to_i}]:")
570
+ STDERR.puts status_txt
571
+ lines = status_txt.split("\n").length
572
+ end
573
+ end
574
+ STDERR.puts
575
+ Log.clear_line(STDERR)
576
+ STDERR.puts Log.color(:magenta, "Output:")
577
+ begin
578
+ status_txt = job_status(job)
579
+ Open.write(fstatus, status_txt) unless status_txt.nil? || status_txt.empty?
580
+ out = CMD.cmd("tail -f '#{fout}'", :pipe => true) if File.exists?(fout) and not tail == :STDERR
581
+ err = CMD.cmd("tail -f '#{ferr}'", :pipe => true) if File.exists?(ferr)
582
+
583
+ terr = Misc.consume_stream(err, true, STDERR) if err
584
+ tout = Misc.consume_stream(out, true, STDOUT) if out
585
+
586
+ sleep 3 while job_status(job).include? job.to_s
587
+ rescue Aborted
588
+ ensure
589
+ begin
590
+ terr.exit if terr
591
+ tout.exit if tout
592
+ err.close if err
593
+ err.join if err
594
+ rescue Exception
595
+ end
596
+
597
+ begin
598
+ out.close if out
599
+ out.join if out
600
+ rescue Exception
601
+ end
602
+ end
603
+ end
604
+ end
605
+
606
+ def wait_for_job(batch_dir, time = 1)
607
+ fexit = File.join(batch_dir, 'exit.status')
608
+ fjob = File.join(batch_dir, 'job.id')
609
+ job = Open.read(fjob) if Open.exists?(fjob)
610
+
611
+ while ! Open.exists?(fexit)
612
+ sleep time
613
+ end
614
+ end
615
+
616
+ end
617
+
618
+ module BATCH
619
+ extend HPC::TemplateGeneration
620
+ end
621
+
622
+ end
623
+