rbbt-util 5.30.3 → 5.30.8
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.rb +111 -26
- data/lib/rbbt/hpc/slurm.rb +21 -11
- data/lib/rbbt/persist.rb +1 -1
- data/lib/rbbt/resource/path.rb +8 -2
- data/lib/rbbt/tsv/manipulate.rb +1 -1
- data/lib/rbbt/util/open.rb +3 -1
- data/lib/rbbt/workflow.rb +2 -2
- data/lib/rbbt/workflow/accessor.rb +4 -4
- data/lib/rbbt/workflow/examples.rb +5 -1
- data/lib/rbbt/workflow/remote_workflow/remote_step.rb +5 -0
- data/lib/rbbt/workflow/step.rb +13 -6
- data/lib/rbbt/workflow/step/accessor.rb +11 -1
- data/lib/rbbt/workflow/step/run.rb +11 -6
- data/lib/rbbt/workflow/usage.rb +1 -1
- data/lib/rbbt/workflow/util/archive.rb +1 -1
- data/lib/rbbt/workflow/util/provenance.rb +9 -1
- data/share/Rlib/util.R +8 -2
- data/share/rbbt_commands/migrate +1 -1
- data/share/rbbt_commands/slurm/list +33 -6
- data/share/rbbt_commands/workflow/server +9 -5
- 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: be64e4af9e06d88f1188ab447f63ea37296d562663ed05945de890d4addd4ce1
|
4
|
+
data.tar.gz: c3c984994184f2b980dfb699b5809d88f41eda8f16201c5c403a20850fc4e9e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd1e8251c15b80c44c36e9b270db74aa1aadcc2d2c01ed1b613e715a0b1cc3ca23ecd4d936afd37697de1c1dbb476afd0efecf57a2ddefbbddec5eb81a902ff6
|
7
|
+
data.tar.gz: 33e7e4a1c9a22c9900da0dbb2e4530c4b360fc3ef2f3b3abb8fbc72fd657f4d10f63d1064e721c227b7900760b98e6d98acbc776e4cf476107635c96d64bd006
|
data/lib/rbbt/hpc/orchestrate.rb
CHANGED
@@ -5,7 +5,10 @@ module HPC
|
|
5
5
|
def self.job_rules(rules, job)
|
6
6
|
workflow = job.workflow.to_s
|
7
7
|
task_name = job.task_name.to_s
|
8
|
+
task_name = job.overriden.to_s if Symbol === job.overriden
|
9
|
+
|
8
10
|
defaults = rules["defaults"] || {}
|
11
|
+
defaults = defaults.merge(rules[workflow]["defaults"] || {}) if rules[workflow]
|
9
12
|
|
10
13
|
job_rules = IndiferentHash.setup(defaults.dup)
|
11
14
|
|
@@ -50,47 +53,88 @@ module HPC
|
|
50
53
|
job_rules
|
51
54
|
end
|
52
55
|
|
53
|
-
def self.get_job_dependencies(job, job_rules)
|
56
|
+
def self.get_job_dependencies(job, job_rules = nil)
|
54
57
|
deps = job.dependencies || []
|
55
58
|
deps += job.input_dependencies || []
|
56
59
|
deps
|
57
60
|
end
|
58
61
|
|
59
|
-
def self.
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
options.delete "recursive_clean"
|
65
|
-
options.delete "clean_task"
|
66
|
-
options.delete "clean"
|
67
|
-
options.delete "tail"
|
68
|
-
options.delete "printfile"
|
69
|
-
options.delete "detach"
|
62
|
+
def self.get_recursive_job_dependencies(job)
|
63
|
+
deps = get_job_dependencies(job)
|
64
|
+
(deps + deps.collect{|dep| get_recursive_job_dependencies(dep) }).flatten
|
65
|
+
end
|
70
66
|
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
def self.piggyback(job, job_rules, job_deps)
|
68
|
+
return false unless job_rules["skip"]
|
69
|
+
final_deps = job_deps - job_deps.collect{|dep| get_recursive_job_dependencies(dep)}.flatten.uniq
|
70
|
+
return final_deps.first if final_deps.length == 1
|
71
|
+
return false
|
72
|
+
end
|
74
73
|
|
74
|
+
def self.get_chains(job, rules, chains = {})
|
75
75
|
job_rules = self.job_rules(rules, job)
|
76
|
+
job_deps = get_job_dependencies(job)
|
77
|
+
|
78
|
+
input_deps = []
|
79
|
+
job.rec_dependencies.each do |dep|
|
80
|
+
input_deps.concat dep.input_dependencies
|
81
|
+
end
|
76
82
|
|
77
|
-
|
83
|
+
job_deps.each do |dep|
|
84
|
+
input_deps.concat dep.input_dependencies
|
85
|
+
get_chains(dep, rules, chains)
|
86
|
+
end
|
78
87
|
|
79
|
-
|
80
|
-
|
88
|
+
job_deps.select do |dep|
|
89
|
+
chained = job_rules["chain_tasks"] &&
|
81
90
|
job_rules["chain_tasks"][job.workflow.to_s] && job_rules["chain_tasks"][job.workflow.to_s].include?(job.task_name.to_s) &&
|
82
91
|
job_rules["chain_tasks"][dep.workflow.to_s] && job_rules["chain_tasks"][dep.workflow.to_s].include?(dep.task_name.to_s)
|
83
92
|
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
dep_skip = dep.done? && ! input_deps.include?(dep) && self.job_rules(rules, dep)["skip"]
|
94
|
+
chained || dep_skip
|
95
|
+
end.each do |dep|
|
96
|
+
chains[job] ||= []
|
97
|
+
chains[job] << dep
|
98
|
+
chains[job].concat chains[dep] if chains[dep]
|
99
|
+
end
|
100
|
+
|
101
|
+
chains
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.workload(job, rules, chains, options, seen = nil)
|
105
|
+
return [] if job.done?
|
106
|
+
if seen.nil?
|
107
|
+
seen = {}
|
108
|
+
target_job = true
|
109
|
+
end
|
110
|
+
|
111
|
+
job_rules = self.job_rules(rules, job)
|
112
|
+
job_deps = get_job_dependencies(job)
|
113
|
+
|
114
|
+
|
115
|
+
chain = chains[job]
|
116
|
+
chain -= seen.keys if chain
|
117
|
+
piggyback = piggyback(job, job_rules, job_deps)
|
118
|
+
dep_ids = job_deps.collect do |dep|
|
119
|
+
seen[dep] = nil if chain && chain.include?(dep) #&& ! job.input_dependencies.include?(dep)
|
120
|
+
next_options = IndiferentHash.setup(options.dup)
|
121
|
+
if piggyback and piggyback == dep
|
122
|
+
next_options[:piggyback] ||= []
|
123
|
+
next_options[:piggyback].push job
|
124
|
+
ids = workload(dep, rules, chains, next_options, seen)
|
87
125
|
else
|
88
|
-
|
126
|
+
next_options.delete :piggyback
|
127
|
+
ids = workload(dep, rules, chains, next_options, seen)
|
89
128
|
end
|
90
|
-
end.flatten.compact.uniq
|
91
129
|
|
92
|
-
|
93
|
-
|
130
|
+
ids = [ids].flatten.compact.collect{|id| ['canfail', id] * ":"} if job.canfail_paths.include? dep.path
|
131
|
+
|
132
|
+
seen[dep] = ids
|
133
|
+
ids
|
134
|
+
end.compact.flatten.uniq
|
135
|
+
|
136
|
+
return seen[job] || dep_ids if seen.include?(job)
|
137
|
+
return seen[piggyback] if piggyback
|
94
138
|
|
95
139
|
job_rules.delete :chain_tasks
|
96
140
|
job_rules.delete :tasks
|
@@ -105,7 +149,48 @@ module HPC
|
|
105
149
|
job_options[:config_keys] = job_options[:config_keys] ? config_keys + "," + job_options[:config_keys] : config_keys
|
106
150
|
end
|
107
151
|
|
108
|
-
|
152
|
+
|
153
|
+
if options[:piggyback]
|
154
|
+
manifest = options[:piggyback].uniq
|
155
|
+
manifest += [job]
|
156
|
+
manifest.concat chain if chain
|
157
|
+
job = options[:piggyback].first
|
158
|
+
job_options.delete :piggyback
|
159
|
+
else
|
160
|
+
manifest = [job]
|
161
|
+
manifest.concat chain if chain
|
162
|
+
end
|
163
|
+
|
164
|
+
manifest.uniq!
|
165
|
+
|
166
|
+
job_options[:manifest] = manifest.collect{|j| j.workflow_short_path }
|
167
|
+
|
168
|
+
if options[:dry_run]
|
169
|
+
puts Log.color(:magenta, "Manifest: ") + Log.color(:blue, job_options[:manifest] * ", ") + " - tasks: #{job_options[:task_cpus] || 1} - time: #{job_options[:time]} - config: #{job_options[:config_keys]}"
|
170
|
+
puts Log.color(:yellow, "Deps: ") + Log.color(:blue, job_options[:slurm_dependencies]*", ")
|
171
|
+
job_options[:manifest].first
|
172
|
+
else
|
173
|
+
run_job(job, job_options)
|
174
|
+
end
|
109
175
|
end
|
176
|
+
|
177
|
+
|
178
|
+
def self.orchestrate_job(job, options)
|
179
|
+
options.delete "recursive_clean"
|
180
|
+
options.delete "clean_task"
|
181
|
+
options.delete "clean"
|
182
|
+
options.delete "tail"
|
183
|
+
options.delete "printfile"
|
184
|
+
options.delete "detach"
|
185
|
+
|
186
|
+
rules = YAML.load(Open.read(options[:orchestration_rules])) if options[:orchestration_rules]
|
187
|
+
rules ||= {}
|
188
|
+
IndiferentHash.setup(rules)
|
189
|
+
|
190
|
+
chains = get_chains(job, rules)
|
191
|
+
|
192
|
+
workload(job, rules, chains, options)
|
193
|
+
end
|
194
|
+
|
110
195
|
end
|
111
196
|
end
|
data/lib/rbbt/hpc/slurm.rb
CHANGED
@@ -21,10 +21,12 @@ module HPC
|
|
21
21
|
exclusive = options.delete :exclusive
|
22
22
|
highmem = options.delete :highmem
|
23
23
|
|
24
|
-
|
24
|
+
manifest = options.delete :manifest
|
25
|
+
|
26
|
+
queue = options.delete(:queue) || Rbbt::Config.get('queue', :slurm_queue, :slurm, :SLURM, :default => 'bsc_ls')
|
25
27
|
task_cpus = options.delete(:task_cpus) || 1
|
26
28
|
nodes = options.delete(:nodes) || 1
|
27
|
-
time = options.delete(:time) || "0:00
|
29
|
+
time = options.delete(:time) || "0:02:00"
|
28
30
|
|
29
31
|
inputs_dir = options.delete :inputs_dir
|
30
32
|
config_keys = options.delete :config_keys
|
@@ -258,6 +260,10 @@ EOF
|
|
258
260
|
workflow write_info --recursive --force=false --check_pid "$step_path" slurm_job $SLURM_JOB_ID
|
259
261
|
EOF
|
260
262
|
|
263
|
+
header +=<<-EOF if manifest
|
264
|
+
#MANIFEST: #{manifest * ", "}
|
265
|
+
EOF
|
266
|
+
|
261
267
|
header +=<<-EOF
|
262
268
|
#CMD: #{rbbt_cmd}
|
263
269
|
EOF
|
@@ -301,11 +307,15 @@ EOF
|
|
301
307
|
coda +=<<-EOF
|
302
308
|
|
303
309
|
# Sync data to target location
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
310
|
+
if [ $exit_status == '0' ]; then
|
311
|
+
mkdir -p "$(dirname '#{target}')"
|
312
|
+
rsync -avztAXHP --copy-unsafe-links "#{source}/" "#{target}/" &>> #{fsync}
|
313
|
+
sync_es="$?"
|
314
|
+
echo $sync_es > #{fsyncexit}
|
315
|
+
find '#{target}' -type l -ls | awk '$13 ~ /^#{target.gsub('/','\/')}/ { sub("#{source}", "#{target}", $13); print $11, $13 }' | while read A B; do rm $A; ln -s $B $A; done
|
316
|
+
else
|
317
|
+
sync_es="$exit_status"
|
318
|
+
fi
|
309
319
|
EOF
|
310
320
|
|
311
321
|
if contain && (wipe_container == "post" || wipe_container == "both")
|
@@ -331,11 +341,11 @@ EOF
|
|
331
341
|
else
|
332
342
|
coda +=<<-EOF
|
333
343
|
##{exec_cmd} system clean
|
334
|
-
if [ $exit_status == '0' -a $sync_es == '0' ]; then
|
344
|
+
#if [ $exit_status == '0' -a $sync_es == '0' ]; then
|
335
345
|
rm -Rfv #{contain} &>> #{fsync}
|
336
|
-
else
|
337
|
-
echo "ERROR: Process failed or results could not sync correctly. Contain directory not purged" &>> #{fsync}
|
338
|
-
fi
|
346
|
+
#else
|
347
|
+
# echo "ERROR: Process failed or results could not sync correctly. Contain directory not purged" &>> #{fsync}
|
348
|
+
#fi
|
339
349
|
EOF
|
340
350
|
|
341
351
|
end
|
data/lib/rbbt/persist.rb
CHANGED
data/lib/rbbt/resource/path.rb
CHANGED
@@ -218,7 +218,14 @@ module Path
|
|
218
218
|
|
219
219
|
else
|
220
220
|
where = where.to_sym
|
221
|
-
|
221
|
+
|
222
|
+
if paths.include? where
|
223
|
+
path = paths[where]
|
224
|
+
elsif where.to_s.include?("/")
|
225
|
+
path = where.to_s
|
226
|
+
else
|
227
|
+
raise "Did not recognize the 'where' tag: #{where}. Options: #{paths.keys}" unless paths.include? where
|
228
|
+
end
|
222
229
|
|
223
230
|
if where == :lib
|
224
231
|
libdir = @libdir || Path.caller_lib_dir(caller_lib) || "NOLIBDIR"
|
@@ -227,7 +234,6 @@ module Path
|
|
227
234
|
end
|
228
235
|
|
229
236
|
pwd = FileUtils.pwd
|
230
|
-
path = paths[where]
|
231
237
|
path = File.join(path, "{PATH}") unless path.include? "PATH}" or path.include? "{BASENAME}"
|
232
238
|
path = path.
|
233
239
|
sub('{PKGDIR}', pkgdir).
|
data/lib/rbbt/tsv/manipulate.rb
CHANGED
data/lib/rbbt/util/open.rb
CHANGED
@@ -748,7 +748,9 @@ module Open
|
|
748
748
|
if (dir_sub_path = find_repo_dir(path))
|
749
749
|
writable_repo?(*dir_sub_path)
|
750
750
|
else
|
751
|
-
if File.
|
751
|
+
if File.symlink?(path)
|
752
|
+
File.writable?(File.dirname(path))
|
753
|
+
elsif File.exist?(path)
|
752
754
|
File.writable?(path)
|
753
755
|
else
|
754
756
|
File.writable?(File.dirname(File.expand_path(path)))
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -190,7 +190,7 @@ module Workflow
|
|
190
190
|
return Misc.string2const Misc.camel_case(wf_name)
|
191
191
|
end
|
192
192
|
|
193
|
-
Log.
|
193
|
+
Log.high{"Loading workflow #{wf_name}"}
|
194
194
|
require_local_workflow(wf_name) or
|
195
195
|
(Workflow.autoinstall and `rbbt workflow install #{Misc.snake_case(wf_name)} || rbbt workflow install #{wf_name}` and require_local_workflow(wf_name)) or raise("Workflow not found or could not be loaded: #{ wf_name }")
|
196
196
|
begin
|
@@ -379,7 +379,7 @@ module Workflow
|
|
379
379
|
has_overriden_inputs = false
|
380
380
|
|
381
381
|
inputs.each do |k,v|
|
382
|
-
has_overriden_inputs = true if String === k and k.include? "#"
|
382
|
+
#has_overriden_inputs = true if String === k and k.include? "#"
|
383
383
|
next unless (task_inputs.include?(k.to_sym) or task_inputs.include?(k.to_s))
|
384
384
|
default = all_defaults[k]
|
385
385
|
next if default == v
|
@@ -382,8 +382,8 @@ module Workflow
|
|
382
382
|
else
|
383
383
|
task_info = d[:workflow].task_info(d[:task])
|
384
384
|
|
385
|
-
|
386
|
-
d[:workflow]._job(d[:task], d[:jobname],
|
385
|
+
_inputs = assign_dep_inputs({}, options.merge(d[:inputs] || {}), real_dependencies, task_info)
|
386
|
+
d[:workflow]._job(d[:task], d[:jobname], _inputs)
|
387
387
|
end
|
388
388
|
end
|
389
389
|
ComputeDependency.setup(d, compute) if compute
|
@@ -400,8 +400,8 @@ module Workflow
|
|
400
400
|
setup_override_dependency(value, dep[:workflow], dep[:task])
|
401
401
|
else
|
402
402
|
task_info = (dep[:task] && dep[:workflow]) ? dep[:workflow].task_info(dep[:task]) : nil
|
403
|
-
|
404
|
-
dep = dep[:workflow]._job(dep[:task], dep[:jobname],
|
403
|
+
_inputs = assign_dep_inputs({}, dep[:inputs], real_dependencies, task_info)
|
404
|
+
dep = dep[:workflow]._job(dep[:task], dep[:jobname], _inputs)
|
405
405
|
end
|
406
406
|
end
|
407
407
|
end
|
@@ -53,7 +53,11 @@ module Workflow
|
|
53
53
|
if file =~ /\.yaml/
|
54
54
|
inputs[input.to_sym] = YAML.load(Open.read(file))
|
55
55
|
else
|
56
|
-
|
56
|
+
if File.symlink?(file)
|
57
|
+
inputs[input.to_sym] = File.readlink(file)
|
58
|
+
else
|
59
|
+
inputs[input.to_sym] = Open.realpath(file)
|
60
|
+
end
|
57
61
|
end
|
58
62
|
when :text
|
59
63
|
Log.debug "Reading #{ input } from #{file}"
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -114,9 +114,13 @@ class Step
|
|
114
114
|
|
115
115
|
def copy_files_dir
|
116
116
|
if File.symlink?(self.files_dir)
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
begin
|
118
|
+
realpath = Open.realpath(self.files_dir)
|
119
|
+
Open.rm self.files_dir
|
120
|
+
Open.cp realpath, self.files_dir
|
121
|
+
rescue
|
122
|
+
Log.warn "Copy files_dir for #{self.workflow_short_path}: " + $!.message
|
123
|
+
end
|
120
124
|
end
|
121
125
|
end
|
122
126
|
|
@@ -465,7 +469,10 @@ class Step
|
|
465
469
|
self
|
466
470
|
end
|
467
471
|
|
468
|
-
|
472
|
+
#connected = true means that dependency searching ends when a result is done
|
473
|
+
#but dependencies are absent, meanining that the file could have been dropped
|
474
|
+
#in
|
475
|
+
def rec_dependencies(connected = false, seen = [])
|
469
476
|
|
470
477
|
# A step result with no info_file means that it was manually
|
471
478
|
# placed. In that case, do not consider its dependencies
|
@@ -480,9 +487,9 @@ class Step
|
|
480
487
|
#next if self.done? && Open.exists?(info_file) && info[:dependencies] && info[:dependencies].select{|task,name,path| path == step.path }.empty?
|
481
488
|
next if archived_deps.include? step.path
|
482
489
|
next if seen.include? step.path
|
483
|
-
next if self.done? &&
|
490
|
+
next if self.done? && connected && ! updatable?
|
484
491
|
|
485
|
-
r = step.rec_dependencies(
|
492
|
+
r = step.rec_dependencies(connected, new_dependencies.collect{|d| d.path})
|
486
493
|
new_dependencies.concat r
|
487
494
|
new_dependencies << step
|
488
495
|
}
|
@@ -154,14 +154,24 @@ class Step
|
|
154
154
|
@name ||= path.sub(/.*\/#{Regexp.quote task_name.to_s}\/(.*)/, '\1')
|
155
155
|
end
|
156
156
|
|
157
|
+
|
157
158
|
def short_path
|
158
159
|
[task_name, name] * "/"
|
159
160
|
end
|
160
161
|
|
162
|
+
def workflow_short_path
|
163
|
+
return short_path unless workflow
|
164
|
+
workflow.to_s + "#" + short_path
|
165
|
+
end
|
166
|
+
|
161
167
|
def task_name
|
162
168
|
@task_name ||= task.name
|
163
169
|
end
|
164
170
|
|
171
|
+
def task_signature
|
172
|
+
[workflow.to_s, task_name] * "#"
|
173
|
+
end
|
174
|
+
|
165
175
|
# {{{ INFO
|
166
176
|
|
167
177
|
def info_file
|
@@ -456,7 +466,7 @@ class Step
|
|
456
466
|
end
|
457
467
|
|
458
468
|
def dirty_files
|
459
|
-
rec_dependencies = self.rec_dependencies
|
469
|
+
rec_dependencies = self.rec_dependencies(true)
|
460
470
|
return [] if rec_dependencies.empty?
|
461
471
|
canfail_paths = self.canfail_paths
|
462
472
|
|
@@ -112,23 +112,27 @@ class Step
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def updatable?
|
115
|
-
|
115
|
+
return true if ENV["RBBT_UPDATE_ALL_JOBS"] == 'true'
|
116
|
+
return false unless ENV["RBBT_UPDATE"] == "true"
|
117
|
+
return false unless Open.exists?(info_file)
|
118
|
+
return true if status != :noinfo && ! (relocated? && done?)
|
119
|
+
false
|
116
120
|
end
|
117
121
|
|
118
122
|
def dependency_checks
|
119
123
|
return [] if ENV["RBBT_UPDATE"] != "true"
|
120
124
|
|
121
|
-
rec_dependencies.
|
125
|
+
rec_dependencies(true).
|
122
126
|
reject{|dependency| (defined?(WorkflowRemoteClient) && WorkflowRemoteClient::RemoteStep === dependency) || Open.remote?(dependency.path) }.
|
123
127
|
reject{|dependency| dependency.error? }.
|
124
128
|
#select{|dependency| Open.exists?(dependency.path) || ((Open.exists?(dependency.info_file) && (dependency.status == :cleaned) || dependency.status == :waiting)) }.
|
125
|
-
select{|dependency| dependency.updatable? }.
|
129
|
+
#select{|dependency| dependency.updatable? }.
|
126
130
|
collect{|dependency| Workflow.relocate_dependency(self, dependency)}
|
127
131
|
end
|
128
132
|
|
129
133
|
def input_checks
|
130
|
-
(inputs.select{|i| Step === i } + inputs.select{|i| Path === i && Step === i.resource}.collect{|i| i.resource})
|
131
|
-
select{|dependency| dependency.updatable? }
|
134
|
+
(inputs.select{|i| Step === i } + inputs.select{|i| Path === i && Step === i.resource}.collect{|i| i.resource})
|
135
|
+
#select{|dependency| dependency.updatable? }
|
132
136
|
end
|
133
137
|
|
134
138
|
def checks
|
@@ -153,7 +157,8 @@ class Step
|
|
153
157
|
canfail_paths = self.canfail_paths
|
154
158
|
this_mtime = Open.mtime(self.path) if Open.exists?(self.path)
|
155
159
|
|
156
|
-
outdated_time = checks.select{|dep| dep.updatable? && dep.done? && Persist.newer?(path, dep.path) }
|
160
|
+
#outdated_time = checks.select{|dep| dep.updatable? && dep.done? && Persist.newer?(path, dep.path) }
|
161
|
+
outdated_time = checks.select{|dep| dep.done? && Persist.newer?(path, dep.path) }
|
157
162
|
outdated_dep = checks.reject{|dep| dep.done? || (dep.error? && ! dep.recoverable_error? && canfail_paths.include?(dep.path)) }
|
158
163
|
|
159
164
|
#checks.each do |dep|
|
data/lib/rbbt/workflow/usage.rb
CHANGED
@@ -21,7 +21,7 @@ module Task
|
|
21
21
|
|
22
22
|
selects = []
|
23
23
|
if inputs.any?
|
24
|
-
inputs.zip(input_types.values_at(*inputs)).select{|i,t| t.to_sym == :select
|
24
|
+
inputs.zip(input_types.values_at(*inputs)).select{|i,t| t.to_sym == :select && input_options[i] && input_options[i][:select_options] }.each{|i,t| selects << [i, input_options[i][:select_options]] }
|
25
25
|
puts SOPT.input_doc(inputs, input_types, input_descriptions, input_defaults, true)
|
26
26
|
puts
|
27
27
|
end
|
@@ -78,6 +78,7 @@ class Step
|
|
78
78
|
name = info[:name] || File.basename(path)
|
79
79
|
status = :unsync if status == :done and not Open.exist?(path)
|
80
80
|
status = :notfound if status == :noinfo and not Open.exist?(path)
|
81
|
+
|
81
82
|
str = " " * offset
|
82
83
|
str << prov_report_msg(status, name, path, info)
|
83
84
|
step.dependencies.reverse.each do |dep|
|
@@ -90,7 +91,14 @@ class Step
|
|
90
91
|
if expand_repeats
|
91
92
|
str << Log.color(:green, Log.uncolor(prov_report(dep, offset+1, task)))
|
92
93
|
else
|
93
|
-
|
94
|
+
info = dep.info || {}
|
95
|
+
status = info[:status] || :missing
|
96
|
+
status = "remote" if Open.remote?(path) || Open.ssh?(path)
|
97
|
+
name = info[:name] || File.basename(path)
|
98
|
+
status = :unsync if status == :done and not Open.exist?(path)
|
99
|
+
status = :notfound if status == :noinfo and not Open.exist?(path)
|
100
|
+
|
101
|
+
str << Log.color(status == :notfound ? :blue : :green, " " * (offset + 1) + Log.uncolor(prov_report_msg(status, name, path, info)))
|
94
102
|
end
|
95
103
|
end
|
96
104
|
end if step.dependencies
|
data/share/Rlib/util.R
CHANGED
@@ -484,7 +484,7 @@ rbbt.model.inpute <- function(data, formula, ...){
|
|
484
484
|
data
|
485
485
|
}
|
486
486
|
|
487
|
-
rbbt.tsv.melt <- function(tsv, variable = NULL, value = NULL, key.field = NULL){
|
487
|
+
rbbt.tsv.melt <- function(tsv, variable = NULL, value = NULL, key.field = NULL, ...){
|
488
488
|
rbbt.require('reshape2')
|
489
489
|
if (is.null(key.field)){ key.field = attributes(data)$key.field;}
|
490
490
|
if (is.null(key.field)){ key.field = "ID" }
|
@@ -494,7 +494,7 @@ rbbt.tsv.melt <- function(tsv, variable = NULL, value = NULL, key.field = NULL){
|
|
494
494
|
|
495
495
|
tsv[key.field] = rownames(tsv)
|
496
496
|
|
497
|
-
m <- melt(tsv)
|
497
|
+
m <- melt(tsv, id.vars=c(key.field), ...)
|
498
498
|
|
499
499
|
names(m) <- c(key.field, variable, value)
|
500
500
|
|
@@ -784,6 +784,12 @@ rbbt.plot.venn <- function(data, a=NULL, category=NULL, fill=NULL, ...) {
|
|
784
784
|
return(out)
|
785
785
|
}
|
786
786
|
|
787
|
+
rbbt.plot.upset <- function(data, variable = NULL, ...){
|
788
|
+
rbbt.require('UpSetR')
|
789
|
+
data[data == TRUE] = 1
|
790
|
+
return(upset(data, ...))
|
791
|
+
}
|
792
|
+
|
787
793
|
rbbt.plot.pca <- function(data, center = TRUE, scale. = TRUE, ...) {
|
788
794
|
rbbt.require('vqv/ggbiplot')
|
789
795
|
data <- rbbt.impute(data)
|
data/share/rbbt_commands/migrate
CHANGED
@@ -70,6 +70,13 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
70
70
|
cmd = nil
|
71
71
|
end
|
72
72
|
|
73
|
+
if m = Open.read(fcmd).match(/#MANIFEST: (.*)/)
|
74
|
+
manifest = m[1]
|
75
|
+
else
|
76
|
+
manifest = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
|
73
80
|
if m = Open.read(fcmd).match(/# Run command\n(.*?)\n/im)
|
74
81
|
exe = m[1].sub('step_path=$(','')
|
75
82
|
else
|
@@ -98,15 +105,24 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
98
105
|
if File.exists?(fstatus = File.join(dir, 'job.status'))
|
99
106
|
nodes = Open.read(fstatus).split("\n").last.split(/\s+/).last.split(",")
|
100
107
|
elsif job_nodes[id]
|
101
|
-
nodes = job_nodes[id]
|
108
|
+
nodes = job_nodes[id].reject{|n| n.include? "("}
|
102
109
|
else
|
103
110
|
nodes = []
|
104
111
|
end
|
105
112
|
|
113
|
+
if File.exists?(File.join(dir, 'exit.status'))
|
114
|
+
now = File.ctime(File.join(dir, 'exit.status'))
|
115
|
+
else
|
116
|
+
now = Time.now
|
117
|
+
end
|
118
|
+
|
106
119
|
if File.exists?(File.join(dir, 'std.out'))
|
120
|
+
cerrt = File.ctime File.join(dir, 'std.err')
|
121
|
+
coutt = File.ctime File.join(dir, 'std.out')
|
107
122
|
outt = File.mtime File.join(dir, 'std.out')
|
108
123
|
errt = File.mtime File.join(dir, 'std.err')
|
109
|
-
time_diff =
|
124
|
+
time_diff = now - [outt, errt].max
|
125
|
+
time_elapsed = now - [cerrt, coutt].min
|
110
126
|
end
|
111
127
|
|
112
128
|
fdep = File.join(dir, 'dependencies.list')
|
@@ -115,14 +131,19 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
115
131
|
fcadep = File.join(dir, 'canfail_dependencies.list')
|
116
132
|
cadeps = Open.read(fcadep).split("\n") if File.exists?(fcadep)
|
117
133
|
|
118
|
-
if done || error || aborted || running || queued || jobid
|
134
|
+
if done || error || aborted || running || queued || jobid
|
119
135
|
select = false
|
120
136
|
select = true if done && exit_status == 0
|
121
137
|
select = true if error && exit_status && exit_status != 0
|
122
138
|
select = true if aborted && (exit_status.nil? && ! running_jobs.include?(id))
|
123
|
-
|
124
|
-
select = true if
|
139
|
+
is_running = exit_status.nil? && running_jobs.include?(id) && (!deps || (running_jobs & deps).empty?)
|
140
|
+
select = true if queued && deps && (running_jobs & deps).any? || queued && is_running && nodes.empty?
|
141
|
+
select = true if running && nodes.any? && (exit_status.nil? && running_jobs.include?(id)) && (!deps || (running_jobs & deps).empty?)
|
125
142
|
select = true if jobid && jobid.split(",").include?(id)
|
143
|
+
select = select && cmd.match(/#{search}/) if search
|
144
|
+
next unless select
|
145
|
+
elsif search
|
146
|
+
select = false
|
126
147
|
select = true if search && cmd.match(/#{search}/)
|
127
148
|
next unless select
|
128
149
|
end
|
@@ -130,6 +151,8 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
130
151
|
|
131
152
|
puts Log.color :blue, dir
|
132
153
|
puts Log.color(:magenta, "Creation: ") << File.mtime(File.join(dir, 'command.slurm')).to_s
|
154
|
+
puts Log.color(:magenta, "Started: ") << File.ctime(File.join(dir, 'std.err')).to_s if File.exist?(File.join(dir, 'std.err'))
|
155
|
+
puts Log.color(:magenta, "Manifest: ") << Log.color(:yellow, manifest)
|
133
156
|
puts Log.color(:magenta, "Done: ") << File.mtime(File.join(dir, 'exit.status')).to_s if File.exist?(File.join(dir, 'exit.status'))
|
134
157
|
puts Log.color(:magenta, "Exec: ") << (exe || "Missing")
|
135
158
|
puts Log.color(:magenta, "CMD: ") << (Log.color(:yellow, cmd) || "Missing")
|
@@ -138,7 +161,8 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
138
161
|
puts Log.color(:magenta, "Dependencies: ") << deps * ", " if deps
|
139
162
|
puts Log.color(:magenta, "Dependencies (can fail): ") << cadeps * ", " if cadeps
|
140
163
|
puts Log.color(:magenta, "Nodes: ") << nodes * ", "
|
141
|
-
puts Log.color(:magenta, "
|
164
|
+
puts Log.color(:magenta, "Time elapsed: ") << Misc.format_seconds(time_elapsed) if time_elapsed
|
165
|
+
puts Log.color(:magenta, "Output: ") << File.exists?(File.join(dir, 'std.out')).to_s << (id.nil? || File.exists?(File.join(dir, 'exit.status')) ? "" : " (last update " + Misc.format_seconds(time_diff) + " ago)")
|
142
166
|
|
143
167
|
if options[:sbatch_parameters]
|
144
168
|
puts Log.color(:magenta, "SBATCH parameters: ")
|
@@ -180,8 +204,11 @@ workdir.glob("**/command.slurm").sort_by{|f| File.mtime(f)}.each do |fcmd|
|
|
180
204
|
start = rss_average.keys.sort.first
|
181
205
|
eend = rss_average.keys.sort.last
|
182
206
|
time_elapsed = eend - start
|
207
|
+
ticks = 1 if ticks == 0
|
208
|
+
time_elapsed = 1 if time_elapsed == 0
|
183
209
|
puts Log.color(:yellow, "CPU average: ") + "%.2f" % ( ticks / clock_ticks / time_elapsed * 100).to_s
|
184
210
|
puts Log.color(:yellow, "RSS average: ") + "%.2f GB" % Misc.mean(rss_average.collect{|t,l| Misc.sum(l) / (1024 * 1024 * 1024)}).to_s
|
211
|
+
puts Log.color(:yellow, "Time: ") + Misc.format_seconds((eend - start))
|
185
212
|
|
186
213
|
end
|
187
214
|
|
@@ -16,6 +16,7 @@ $ rbbt workflow server [options] <Workflow>
|
|
16
16
|
-B--Bind* Bind IP
|
17
17
|
-p--port* TCP port
|
18
18
|
-s--server* Server type: thin, webrick, unicorn, etc
|
19
|
+
-so--server_options* Additional options for server (e.g. option1=value1;option2=value2)
|
19
20
|
-f--finder Start server with finder functionality
|
20
21
|
-RS--Rserve_session* Rserve session to use, otherwise start new one
|
21
22
|
-wd--workdir* Change the working directory of the workflow
|
@@ -32,7 +33,6 @@ $ rbbt workflow server [options] <Workflow>
|
|
32
33
|
--export_synchronous* Export workflow tasks as synchronous
|
33
34
|
--export_exec* Export workflow tasks as exec
|
34
35
|
--export_stream* Export workflow tasks as stream
|
35
|
-
--options* Additional options for server (e.g. option1=value1;option2=value2)
|
36
36
|
EOF
|
37
37
|
|
38
38
|
if options[:help]
|
@@ -108,20 +108,24 @@ TmpFile.with_file do |app_dir|
|
|
108
108
|
config_ru_file = File.exist?('./workflow_config.ru') ? './workflow_config.ru' : Rbbt.share['workflow_config.ru'].find
|
109
109
|
|
110
110
|
|
111
|
-
if options[:
|
112
|
-
options[:
|
111
|
+
if options[:server_options]
|
112
|
+
options[:server_options].split(";").each do |pair|
|
113
113
|
name, _sep, value = pair.partition("=")
|
114
114
|
name = name[1..-1].to_sym if name[0] == ':'
|
115
|
+
value = value.to_i if value =~ /^\d+$/
|
116
|
+
value = true if value == "true"
|
117
|
+
value = false if value == "false"
|
115
118
|
options[name] = value
|
116
119
|
end
|
120
|
+
options.delete :server_options
|
117
121
|
end
|
118
122
|
|
119
123
|
case server
|
120
124
|
when 'unicorn'
|
121
125
|
`env RBBT_LOG=#{Log.severity.to_s} unicorn -c #{ Rbbt.share['unicorn.rb'].find } '#{config_ru_file}' -p #{options[:Port] || "2887"}`
|
122
|
-
when '
|
126
|
+
when 'puma_production'
|
123
127
|
#`puma '#{config_ru_file}' -p #{options[:Port] || "2887"} -w 3 -t 8:32 --preload`
|
124
|
-
|
128
|
+
CMD.cmd_log("env RBBT_LOG=#{Log.severity.to_s} puma '#{config_ru_file}' -p #{options[:Port] || "2887"} -w 20 -t 10:160 --preload")
|
125
129
|
else
|
126
130
|
options[:config] = config_ru_file
|
127
131
|
|
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.30.
|
4
|
+
version: 5.30.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|