rbbt-util 5.33.0 → 5.33.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/rbbt/hpc/orchestrate/batches.rb +9 -2
- data/lib/rbbt/hpc/orchestrate.rb +31 -3
- data/lib/rbbt/util/cmd.rb +1 -1
- data/lib/rbbt/util/misc/exceptions.rb +15 -3
- data/lib/rbbt/workflow/definition.rb +6 -1
- data/lib/rbbt/workflow/integration/nextflow.rb +32 -14
- data/lib/rbbt/workflow/step/dependencies.rb +23 -3
- data/lib/rbbt/workflow/step/info.rb +1 -1
- data/lib/rbbt/workflow/step/run.rb +3 -2
- data/lib/rbbt/workflow/util/orchestrator.rb +9 -5
- data/lib/rbbt/workflow.rb +3 -5
- data/share/rbbt_commands/workflow/task +13 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ffb05883cf1e7eda628395b50967322b42240ace100fb73a2f4c4a1ed5e8d27
|
4
|
+
data.tar.gz: 8b25c002f68b88dc0ba1f09c041258ee0ca15d4fc6f8dc3df5de37ea2473d059
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b74e527336830eecb1cad52bb13cbcb335c734a18e9261d87cfba6da93422cf94a826e97d43c6ef1d37d122623408230d80a037a9a91000fa95a6cf9c818cf6
|
7
|
+
data.tar.gz: 8b8de5c12aac8dfae5f68018cb5aae4023b78993fa78aa7cf93cacc16eea4c2b959f75190ead6862502ecc75ae3ccd9cc462b1db26930943447a758f1f359cd9
|
@@ -11,7 +11,7 @@ module HPC
|
|
11
11
|
iii :END_BATCHES
|
12
12
|
else
|
13
13
|
n = batch.dup
|
14
|
-
n[:deps] = n[:deps].collect{|b| b[:top_level] }
|
14
|
+
n[:deps] = n[:deps].collect{|b| b[:top_level] } if n[:deps]
|
15
15
|
iif n
|
16
16
|
end
|
17
17
|
end
|
@@ -79,7 +79,14 @@ module HPC
|
|
79
79
|
|
80
80
|
batches.each do |batch|
|
81
81
|
jobs = batch[:jobs]
|
82
|
-
all_deps = jobs.collect{|d| job_dependencies(d) }.flatten.uniq
|
82
|
+
all_deps = jobs.collect{|d| job_dependencies(d) }.flatten.uniq - jobs
|
83
|
+
|
84
|
+
minimum = all_deps
|
85
|
+
all_deps.each do |dep|
|
86
|
+
minimum -= job_dependencies(dep)
|
87
|
+
end
|
88
|
+
|
89
|
+
all_deps = minimum
|
83
90
|
deps = all_deps.collect do |d|
|
84
91
|
(batches - [batch]).select{|batch| batch[:jobs].collect(&:path).include? d.path }
|
85
92
|
end.flatten.uniq
|
data/lib/rbbt/hpc/orchestrate.rb
CHANGED
@@ -14,8 +14,14 @@ module HPC
|
|
14
14
|
options.delete "detach"
|
15
15
|
options.delete "jobname"
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
if options[:orchestration_rules]
|
18
|
+
rules = YAML.load(Open.read(options[:orchestration_rules]))
|
19
|
+
elsif Rbbt.etc.slurm["default.yaml"].exists?
|
20
|
+
rules = YAML.load(Open.read(Rbbt.etc.slurm["default.yaml"]))
|
21
|
+
else
|
22
|
+
rules = {}
|
23
|
+
end
|
24
|
+
|
19
25
|
IndiferentHash.setup(rules)
|
20
26
|
|
21
27
|
batches = HPC::Orchestration.job_batches(rules, job)
|
@@ -26,7 +32,29 @@ module HPC
|
|
26
32
|
raise "No batch without unmet dependencies" if top.nil?
|
27
33
|
batches.delete top
|
28
34
|
job_options = options.merge(top[:rules])
|
29
|
-
|
35
|
+
|
36
|
+
if top[:deps].nil?
|
37
|
+
batch_dependencies = []
|
38
|
+
else
|
39
|
+
top_jobs = top[:jobs]
|
40
|
+
|
41
|
+
batch_dependencies = top[:deps].collect{|d|
|
42
|
+
target = d[:top_level]
|
43
|
+
canfail = false
|
44
|
+
|
45
|
+
top_jobs.each do |job|
|
46
|
+
canfail = true if job.canfail_paths.include?(target.path)
|
47
|
+
end
|
48
|
+
|
49
|
+
if canfail
|
50
|
+
'canfail:' + batch_ids[d].to_s
|
51
|
+
else
|
52
|
+
batch_ids[d].to_s
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
job_options.merge!(:batch_dependencies => batch_dependencies )
|
30
58
|
job_options.merge!(:manifest => top[:jobs].collect{|d| d.task_signature })
|
31
59
|
|
32
60
|
if options[:dry_run]
|
data/lib/rbbt/util/cmd.rb
CHANGED
@@ -75,7 +75,7 @@ module CMD
|
|
75
75
|
|
76
76
|
string = ""
|
77
77
|
options.each do |option, value|
|
78
|
-
raise "Invalid option key: #{option.inspect}" if option.to_s !~ /^[a-z_0-9
|
78
|
+
raise "Invalid option key: #{option.inspect}" if option.to_s !~ /^[a-z_0-9\-=.]+$/i
|
79
79
|
#raise "Invalid option value: #{value.inspect}" if value.to_s.include? "'"
|
80
80
|
value = value.gsub("'","\\'") if value.to_s.include? "'"
|
81
81
|
|
@@ -32,8 +32,6 @@ class DependencyError < Aborted
|
|
32
32
|
def initialize(msg)
|
33
33
|
if defined? Step and Step === msg
|
34
34
|
step = msg
|
35
|
-
workflow = step.path.split("/")[-3]
|
36
|
-
new_msg = [workflow, step.short_path, step.messages.last] * " - "
|
37
35
|
new_msg = [step.path, step.messages.last] * ": "
|
38
36
|
super(new_msg)
|
39
37
|
else
|
@@ -42,6 +40,21 @@ class DependencyError < Aborted
|
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
43
|
+
class DependencyRbbtException < RbbtException
|
44
|
+
def initialize(msg)
|
45
|
+
if defined? Step and Step === msg
|
46
|
+
step = msg
|
47
|
+
|
48
|
+
new_msg = nil
|
49
|
+
new_msg = [step.path, step.messages.last] * ": "
|
50
|
+
|
51
|
+
super(new_msg)
|
52
|
+
else
|
53
|
+
super(msg)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
45
58
|
class DontClose < Exception; end
|
46
59
|
|
47
60
|
class KeepLocked < Exception
|
@@ -64,4 +77,3 @@ class StopInsist < Exception
|
|
64
77
|
@exception = exception
|
65
78
|
end
|
66
79
|
end
|
67
|
-
|
@@ -92,7 +92,12 @@ module Workflow
|
|
92
92
|
self.dependencies = self.dependencies - [dep]
|
93
93
|
Open.rm_rf self.files_dir if Open.exist? self.files_dir
|
94
94
|
FileUtils.cp_r dep.files_dir, self.files_dir if Open.exist?(dep.files_dir)
|
95
|
-
|
95
|
+
|
96
|
+
if dep.overriden
|
97
|
+
Open.link dep.path, self.tmp_path
|
98
|
+
else
|
99
|
+
Open.ln_h dep.path, self.tmp_path
|
100
|
+
end
|
96
101
|
|
97
102
|
case remove.to_s
|
98
103
|
when 'true'
|
@@ -4,7 +4,7 @@ module Workflow
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def self.nextflow_includes(file)
|
7
|
-
Open.read(file).scan(/^include\s*{\s*(
|
7
|
+
Open.read(file).scan(/^include\s*{\s*([^\s]*?)\s+.*?}\s*from\s+["'](.*?)["'](?:\s*params.*)?/).collect{|p| p}.uniq
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.nextflow_recursive_params(file)
|
@@ -48,25 +48,43 @@ module Workflow
|
|
48
48
|
task name => result do
|
49
49
|
work = file('work')
|
50
50
|
profile = config :profile, :nextflow
|
51
|
+
config_file = config :config, :nextflow
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
nextflow_inputs = {}
|
54
|
+
|
55
|
+
inputs.zip(inputs.fields).collect do |v,f|
|
56
|
+
v = if String === v && m = v.match(/^JOB_FILE:(.*)/)
|
57
|
+
file(m[1])
|
58
|
+
elsif v.nil?
|
59
|
+
Rbbt::Config.get(['nextflow', f] * "_", 'default', f)
|
60
|
+
else
|
61
|
+
v
|
62
|
+
end
|
63
|
+
|
64
|
+
if f.to_s.include?("-")
|
65
|
+
p,_sep, section = f.to_s.partition("-")
|
66
|
+
name = [section, p] * "."
|
57
67
|
else
|
58
|
-
|
68
|
+
name = f
|
59
69
|
end
|
70
|
+
|
71
|
+
nextflow_inputs[name] = v
|
60
72
|
end
|
61
73
|
|
62
|
-
inputs.replace new_inputs
|
63
|
-
|
64
74
|
Misc.in_dir file('stage') do
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
75
|
+
|
76
|
+
cmd = "nextflow "
|
77
|
+
|
78
|
+
cmd += " -C #{config_file}" if config_file
|
79
|
+
|
80
|
+
cmd += " run"
|
81
|
+
|
82
|
+
cmd += " -work-dir #{work} -ansi-log false"
|
83
|
+
|
84
|
+
cmd += " -profile #{profile}" if profile
|
85
|
+
|
86
|
+
|
87
|
+
cmd("#{cmd} #{file}", nextflow_inputs.merge('add_option_dashes' => true))
|
70
88
|
end
|
71
89
|
|
72
90
|
output_file = file(output).glob.first if output
|
@@ -107,7 +107,26 @@ class Step
|
|
107
107
|
canfail = ComputeDependency === job && job.canfail?
|
108
108
|
end
|
109
109
|
|
110
|
-
|
110
|
+
raise_dependency_error(job) if job.error? and not canfail
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.raise_dependency_error(job)
|
114
|
+
begin
|
115
|
+
if job.get_exception
|
116
|
+
klass = job.get_exception.class
|
117
|
+
else
|
118
|
+
klass = Kernel.const_get(info[:exception][:class])
|
119
|
+
end
|
120
|
+
rescue
|
121
|
+
Log.exception $!
|
122
|
+
raise DependencyError, job
|
123
|
+
end
|
124
|
+
|
125
|
+
if (klass <= RbbtException)
|
126
|
+
raise DependencyRbbtException, job
|
127
|
+
else
|
128
|
+
raise DependencyError, job
|
129
|
+
end
|
111
130
|
end
|
112
131
|
|
113
132
|
def log_dependency_exec(dependency, action)
|
@@ -169,7 +188,7 @@ class Step
|
|
169
188
|
|
170
189
|
if dependency.error?
|
171
190
|
log_dependency_exec(dependency, :error)
|
172
|
-
|
191
|
+
raise_dependency_error dependency
|
173
192
|
end
|
174
193
|
|
175
194
|
if dependency.streaming?
|
@@ -368,7 +387,7 @@ class Step
|
|
368
387
|
seen_paths << step.path
|
369
388
|
begin
|
370
389
|
Step.prepare_for_execution(step) unless step == self
|
371
|
-
rescue DependencyError
|
390
|
+
rescue DependencyError, DependencyRbbtException
|
372
391
|
raise $! unless canfail_paths.include? step.path
|
373
392
|
end
|
374
393
|
next unless step.dependencies and step.dependencies.any?
|
@@ -493,6 +512,7 @@ class Step
|
|
493
512
|
return true if @overriden
|
494
513
|
return true if dependencies.select{|dep| dep.overriden? }.any?
|
495
514
|
info[:archived_info].each do |f,i|
|
515
|
+
next if Symbol === i
|
496
516
|
return true if i[:overriden] || i["overriden"]
|
497
517
|
end if info[:archived_info]
|
498
518
|
return false
|
@@ -437,7 +437,7 @@ class Step
|
|
437
437
|
end
|
438
438
|
end # END SYNC
|
439
439
|
res
|
440
|
-
rescue DependencyError
|
440
|
+
rescue DependencyError, DependencyRbbtException
|
441
441
|
exception $!
|
442
442
|
rescue LockInterrupted
|
443
443
|
raise $!
|
@@ -484,7 +484,7 @@ class Step
|
|
484
484
|
if dofork
|
485
485
|
fork(true) unless started?
|
486
486
|
|
487
|
-
join unless done?
|
487
|
+
join unless done? or dofork == :nowait
|
488
488
|
else
|
489
489
|
run(true) unless started?
|
490
490
|
|
@@ -496,6 +496,7 @@ class Step
|
|
496
496
|
|
497
497
|
def fork(no_load = false, semaphore = nil)
|
498
498
|
raise "Can not fork: Step is waiting for proces #{@pid} to finish" if not @pid.nil? and not Process.pid == @pid and Misc.pid_exists?(@pid) and not done? and info[:forked]
|
499
|
+
Log.debug "Fork to run #{self.path}"
|
499
500
|
sout, sin = Misc.pipe if no_load == :stream
|
500
501
|
@pid = Process.fork do
|
501
502
|
Signal.trap(:TERM) do
|
@@ -71,6 +71,7 @@ module Workflow
|
|
71
71
|
|
72
72
|
default_resources.each{|k,v| resources[k] ||= v } if default_resources
|
73
73
|
|
74
|
+
resources = {:cpus => 1} if resources.empty?
|
74
75
|
resources
|
75
76
|
end
|
76
77
|
|
@@ -83,7 +84,8 @@ module Workflow
|
|
83
84
|
|
84
85
|
def self.candidates(workload, rules)
|
85
86
|
if rules.empty?
|
86
|
-
candidates = workload.
|
87
|
+
candidates = workload.
|
88
|
+
select{|k,v| v.empty? }.
|
87
89
|
collect{|k,v| k }.
|
88
90
|
reject{|k| k.done? }
|
89
91
|
else
|
@@ -107,7 +109,8 @@ module Workflow
|
|
107
109
|
|
108
110
|
attr_accessor :available_resources, :resources_requested, :resources_used, :timer
|
109
111
|
|
110
|
-
def initialize(timer = 5, available_resources =
|
112
|
+
def initialize(timer = 5, available_resources = nil)
|
113
|
+
available_resources = {:cpus => Etc.nprocessors } if available_resources.nil?
|
111
114
|
@timer = timer
|
112
115
|
@available_resources = IndiferentHash.setup(available_resources)
|
113
116
|
@resources_requested = IndiferentHash.setup({})
|
@@ -155,7 +158,7 @@ module Workflow
|
|
155
158
|
log = job_rules[:log] if job_rules
|
156
159
|
log = Log.severity if log.nil?
|
157
160
|
Log.with_severity log do
|
158
|
-
job.produce(false,
|
161
|
+
job.produce(false, :nowait)
|
159
162
|
end
|
160
163
|
end
|
161
164
|
end
|
@@ -228,8 +231,9 @@ module Workflow
|
|
228
231
|
|
229
232
|
new_workload = {}
|
230
233
|
workload.each do |k,v|
|
231
|
-
next if k.done?
|
232
|
-
new_workload[k] = v.reject{|d| d.done? || (d.error? && ! d.recoverable_error?)}
|
234
|
+
next if k.done? || k.error? || k.aborted?
|
235
|
+
#new_workload[k] = v.reject{|d| d.done? || ((d.error? || d.aborted?) && ! d.recoverable_error?)}
|
236
|
+
new_workload[k] = v.reject{|d| d.done? || d.error? || d.aborted?}
|
233
237
|
end
|
234
238
|
workload = new_workload
|
235
239
|
sleep timer
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -423,7 +423,7 @@ module Workflow
|
|
423
423
|
|
424
424
|
# jobname => true sets the value of the input to the name of the job
|
425
425
|
if task.input_options
|
426
|
-
jobname_input = task.input_options.select{|i,o| o[:jobname]}.collect{|i,o| i }.first
|
426
|
+
jobname_input = task.input_options.select{|i,o| o[:jobname] }.collect{|i,o| i }.first
|
427
427
|
else
|
428
428
|
jobname_input = nil
|
429
429
|
end
|
@@ -476,14 +476,14 @@ module Workflow
|
|
476
476
|
end
|
477
477
|
end
|
478
478
|
|
479
|
+
input_values = task.take_input_values(inputs)
|
479
480
|
if real_inputs.empty? && Workflow::TAG != :inputs && ! overriden
|
480
481
|
step_path = step_path taskname, jobname, [], [], extension
|
481
|
-
input_values = task.take_input_values(inputs)
|
482
482
|
else
|
483
|
-
input_values = task.take_input_values(inputs)
|
484
483
|
step_path = step_path taskname, jobname, input_values, dependencies, extension
|
485
484
|
end
|
486
485
|
|
486
|
+
|
487
487
|
job = get_job_step step_path, task, input_values, dependencies
|
488
488
|
job.workflow = self
|
489
489
|
job.clean_name = jobname
|
@@ -503,8 +503,6 @@ module Workflow
|
|
503
503
|
|
504
504
|
def _job(taskname, jobname = nil, inputs = {})
|
505
505
|
|
506
|
-
_inputs = IndiferentHash.setup(inputs.dup)
|
507
|
-
|
508
506
|
task_info = task_info(taskname)
|
509
507
|
task_inputs = task_info[:inputs]
|
510
508
|
persist_inputs = inputs.values_at(*task_inputs)
|
@@ -191,7 +191,7 @@ The `recursive_clean` cleans all the job dependency steps recursively.
|
|
191
191
|
-rcl--recursive_clean Clean the last step and its dependencies to recompute the job completely
|
192
192
|
-uaj--update_all_jobs Consider all dependencies when checking for updates, even when they have no info files
|
193
193
|
--fork Run job asyncronously and monitor progress. It monitors detached processes as well
|
194
|
-
--orchestrate Run the job through the orchestrator
|
194
|
+
--orchestrate* Run the job through the orchestrator
|
195
195
|
--detach Run job asyncronously and detach process
|
196
196
|
--exec Run job with no persistence
|
197
197
|
-O--output* Save job result into file
|
@@ -441,7 +441,17 @@ begin
|
|
441
441
|
job.fork
|
442
442
|
elsif options[:orchestrate]
|
443
443
|
require 'rbbt/workflow/util/orchestrator'
|
444
|
-
|
444
|
+
rules = case options[:orchestrate]
|
445
|
+
when 'none', 'open', 'default'
|
446
|
+
nil
|
447
|
+
else
|
448
|
+
YAML.parse(Open.read(options[:orchestrate]))
|
449
|
+
end
|
450
|
+
if rules
|
451
|
+
Workflow::Orchestrator.process rules, job
|
452
|
+
else
|
453
|
+
Workflow::Orchestrator.process job
|
454
|
+
end unless job.done?
|
445
455
|
else
|
446
456
|
job.run(:stream)
|
447
457
|
res = job
|
@@ -577,10 +587,10 @@ when Step
|
|
577
587
|
elsif detach
|
578
588
|
exit! 0
|
579
589
|
else
|
590
|
+
res.join
|
580
591
|
if %w(float integer string boolean).include?(res.result_type.to_s)
|
581
592
|
out.puts res.load
|
582
593
|
else
|
583
|
-
res.join
|
584
594
|
Open.open(res.path, :mode => 'rb') do |io|
|
585
595
|
Misc.consume_stream(io, false, out)
|
586
596
|
end if Open.exist?(res.path) || Open.remote?(res.path) || Open.ssh?(res.path)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbt-util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.33.
|
4
|
+
version: 5.33.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|