rbbt-util 5.28.14 → 5.30.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rbbt/hpc.rb +1 -551
- data/lib/rbbt/hpc/orchestrate.rb +111 -0
- data/lib/rbbt/hpc/slurm.rb +603 -0
- data/lib/rbbt/persist.rb +4 -0
- data/lib/rbbt/persist/tsv/adapter.rb +48 -13
- data/lib/rbbt/util/cmd.rb +6 -1
- data/lib/rbbt/util/misc/inspect.rb +13 -3
- data/lib/rbbt/util/misc/options.rb +0 -42
- data/lib/rbbt/util/procpath.rb +49 -0
- data/lib/rbbt/workflow.rb +2 -1
- data/lib/rbbt/workflow/accessor.rb +7 -1
- data/lib/rbbt/workflow/examples.rb +2 -2
- data/lib/rbbt/workflow/step.rb +8 -5
- data/lib/rbbt/workflow/step/accessor.rb +28 -19
- data/lib/rbbt/workflow/step/dependencies.rb +1 -2
- data/lib/rbbt/workflow/step/run.rb +2 -5
- data/lib/rbbt/workflow/usage.rb +1 -1
- data/lib/rbbt/workflow/util/orchestrator.rb +14 -9
- data/lib/rbbt/workflow/util/provenance.rb +5 -2
- data/share/rbbt_commands/slurm/clean +165 -0
- data/share/rbbt_commands/slurm/list +220 -0
- data/share/rbbt_commands/slurm/orchestrate +48 -0
- data/share/rbbt_commands/{workflow/slurm → slurm/task} +11 -3
- data/share/rbbt_commands/tsv/slice +3 -3
- data/share/rbbt_commands/workflow/info +1 -1
- data/share/rbbt_commands/workflow/task +17 -7
- data/share/rbbt_commands/workflow/write_info +52 -0
- data/test/rbbt/test_workflow.rb +7 -7
- data/test/rbbt/util/test_procpath.rb +23 -0
- metadata +12 -3
data/lib/rbbt/persist.rb
CHANGED
@@ -110,6 +110,8 @@ module Persist
|
|
110
110
|
def self.load_file(path, type)
|
111
111
|
begin
|
112
112
|
case (type || :marshal).to_sym
|
113
|
+
when :path
|
114
|
+
path
|
113
115
|
when :nil
|
114
116
|
nil
|
115
117
|
when :boolean
|
@@ -167,6 +169,8 @@ module Persist
|
|
167
169
|
end
|
168
170
|
|
169
171
|
case (type || :marshal).to_sym
|
172
|
+
when :path
|
173
|
+
nil
|
170
174
|
when :nil
|
171
175
|
nil
|
172
176
|
when :boolean
|
@@ -104,9 +104,6 @@ module Persist
|
|
104
104
|
write(true) if closed? || ! write?
|
105
105
|
res = begin
|
106
106
|
yield
|
107
|
-
rescue Exception
|
108
|
-
Log.exception $!
|
109
|
-
raise $!
|
110
107
|
ensure
|
111
108
|
close
|
112
109
|
end
|
@@ -115,7 +112,6 @@ module Persist
|
|
115
112
|
end
|
116
113
|
|
117
114
|
def read_and_close
|
118
|
-
#return yield if @locked
|
119
115
|
if read? || write?
|
120
116
|
begin
|
121
117
|
return yield
|
@@ -134,6 +130,45 @@ module Persist
|
|
134
130
|
end
|
135
131
|
end
|
136
132
|
|
133
|
+
def read_lock
|
134
|
+
read if closed?
|
135
|
+
if read?
|
136
|
+
begin
|
137
|
+
return yield
|
138
|
+
ensure
|
139
|
+
close
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
lock do
|
144
|
+
close
|
145
|
+
read true
|
146
|
+
begin
|
147
|
+
yield
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def write_lock
|
153
|
+
write if closed?
|
154
|
+
if write?
|
155
|
+
begin
|
156
|
+
return yield
|
157
|
+
ensure
|
158
|
+
close
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
lock do
|
163
|
+
close
|
164
|
+
write true
|
165
|
+
begin
|
166
|
+
yield
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
137
172
|
def merge!(hash)
|
138
173
|
hash.each do |key,values|
|
139
174
|
self[key] = values
|
@@ -141,38 +176,38 @@ module Persist
|
|
141
176
|
end
|
142
177
|
|
143
178
|
def range(*args)
|
144
|
-
self.
|
179
|
+
self.read_lock do
|
145
180
|
super(*args)
|
146
181
|
end
|
147
182
|
end
|
148
183
|
|
149
184
|
def include?(*args)
|
150
|
-
self.
|
185
|
+
self.read_lock do
|
151
186
|
super(*args) #- TSV::ENTRY_KEYS.to_a
|
152
187
|
end
|
153
188
|
end
|
154
189
|
|
155
190
|
def [](*args)
|
156
|
-
self.
|
191
|
+
self.read_lock do
|
157
192
|
super(*args) #- TSV::ENTRY_KEYS.to_a
|
158
193
|
end
|
159
194
|
end
|
160
195
|
|
161
196
|
def []=(*args)
|
162
|
-
self.
|
197
|
+
self.write_lock do
|
163
198
|
super(*args) #- TSV::ENTRY_KEYS.to_a
|
164
199
|
end
|
165
200
|
end
|
166
201
|
|
167
202
|
def keys(*args)
|
168
|
-
self.
|
203
|
+
self.read_lock do
|
169
204
|
super(*args)
|
170
205
|
end
|
171
206
|
end
|
172
207
|
|
173
208
|
|
174
209
|
def prefix(key)
|
175
|
-
self.
|
210
|
+
self.read_lock do
|
176
211
|
range(key, 1, key + MAX_CHAR, 1)
|
177
212
|
end
|
178
213
|
end
|
@@ -184,13 +219,13 @@ module Persist
|
|
184
219
|
|
185
220
|
|
186
221
|
def size(*args)
|
187
|
-
self.
|
222
|
+
self.read_lock do
|
188
223
|
super(*args)
|
189
224
|
end
|
190
225
|
end
|
191
226
|
|
192
227
|
def each(*args, &block)
|
193
|
-
self.
|
228
|
+
self.read_lock do
|
194
229
|
super(*args, &block)
|
195
230
|
end
|
196
231
|
end
|
@@ -208,7 +243,7 @@ module Persist
|
|
208
243
|
end
|
209
244
|
|
210
245
|
def values_at(*keys)
|
211
|
-
self.
|
246
|
+
self.read_lock do
|
212
247
|
keys.collect do |k|
|
213
248
|
self[k]
|
214
249
|
end
|
data/lib/rbbt/util/cmd.rb
CHANGED
@@ -217,7 +217,7 @@ module CMD
|
|
217
217
|
end
|
218
218
|
end
|
219
219
|
|
220
|
-
def self.
|
220
|
+
def self.cmd_pid(*args)
|
221
221
|
all_args = *args
|
222
222
|
|
223
223
|
all_args << {} unless Hash === all_args.last
|
@@ -248,4 +248,9 @@ module CMD
|
|
248
248
|
nil
|
249
249
|
end
|
250
250
|
|
251
|
+
def self.cmd_log(*args)
|
252
|
+
cmd_pid(*args)
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
|
251
256
|
end
|
@@ -287,7 +287,9 @@ module Misc
|
|
287
287
|
when Symbol
|
288
288
|
obj.to_s
|
289
289
|
when (defined?(Path) and Path)
|
290
|
-
if Step
|
290
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj))
|
291
|
+
obj2str(Workflow.load_step(obj))
|
292
|
+
elsif defined?(Step) && Step === obj.resource
|
291
293
|
"Step file: " + obj
|
292
294
|
else
|
293
295
|
if obj.exists?
|
@@ -322,7 +324,11 @@ module Misc
|
|
322
324
|
remove_long_items(obj)
|
323
325
|
when File
|
324
326
|
if obj.respond_to? :filename and obj.filename
|
325
|
-
|
327
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj.filename))
|
328
|
+
obj2str(Workflow.load_step(obj.filename))
|
329
|
+
else
|
330
|
+
"<IO:" << obj.filename << "--" << mtime_str(obj.filename) << ">"
|
331
|
+
end
|
326
332
|
else
|
327
333
|
"<IO:" << obj.path << "--" << mtime_str(obj.path) << ">"
|
328
334
|
end
|
@@ -330,7 +336,11 @@ module Misc
|
|
330
336
|
"<IO:" << obj.short_path << ">"
|
331
337
|
when IO
|
332
338
|
if obj.respond_to? :filename and obj.filename
|
333
|
-
|
339
|
+
if defined?(Step) && Open.exists?(Step.info_file(obj.filename))
|
340
|
+
obj2str(Workflow.load_step(obj.filename))
|
341
|
+
else
|
342
|
+
"<IO:" << obj.filename << "--" << mtime_str(obj.filename) << ">"
|
343
|
+
end
|
334
344
|
else
|
335
345
|
|
336
346
|
if obj.respond_to? :obj2str
|
@@ -242,48 +242,6 @@ module Misc
|
|
242
242
|
|
243
243
|
return options
|
244
244
|
|
245
|
-
options = {}
|
246
|
-
string.split(/#/).each do |str|
|
247
|
-
if str.match(/(.*)=(.*)/)
|
248
|
-
option, value = $1, $2
|
249
|
-
else
|
250
|
-
option, value = str, true
|
251
|
-
end
|
252
|
-
|
253
|
-
option = option.sub(":",'').to_sym if option.chars.first == ':'
|
254
|
-
value = value.sub(":",'').to_sym if String === value and value.chars.first == ':'
|
255
|
-
|
256
|
-
if value == true
|
257
|
-
options[option] = option.to_s.chars.first != '!'
|
258
|
-
else
|
259
|
-
options[option] = Thread.start do
|
260
|
-
$SAFE = 0;
|
261
|
-
case
|
262
|
-
when value =~ /^(?:true|T)$/i
|
263
|
-
true
|
264
|
-
when value =~ /^(?:false|F)$/i
|
265
|
-
false
|
266
|
-
when Symbol === value
|
267
|
-
value
|
268
|
-
when (String === value and value =~ /^\/(.*)\/$/)
|
269
|
-
Regexp.new /#{$1}/
|
270
|
-
else
|
271
|
-
begin
|
272
|
-
Kernel.const_get value
|
273
|
-
rescue
|
274
|
-
begin
|
275
|
-
raise if value =~ /[a-z]/ and defined? value
|
276
|
-
eval(value)
|
277
|
-
rescue Exception
|
278
|
-
value
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end.value
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
options
|
287
245
|
end
|
288
246
|
|
289
247
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rbbt/util/cmd'
|
2
|
+
module ProcPath
|
3
|
+
CMD.tool :procpath do
|
4
|
+
'pip install procpath'
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.record(pid, path, options = {})
|
8
|
+
IndiferentHash.setup(options)
|
9
|
+
options = Misc.add_defaults options, "interval" => 30
|
10
|
+
|
11
|
+
cmd_options = %w(interval recnum reevalnum).inject({}){|acc,k| acc[k] = options[k]; acc}
|
12
|
+
|
13
|
+
Log.debug "ProcPath recording #{pid} in #{path} (#{Misc.fingerprint options})"
|
14
|
+
procpath_thread = Thread.new do
|
15
|
+
begin
|
16
|
+
procpath_pid = CMD.cmd_pid(:procpath, "record --database-file '#{path}' '$..children[?(@.stat.pid == #{pid})]'", cmd_options.merge(:nofail => true, :add_option_dashes => true))
|
17
|
+
rescue Exception
|
18
|
+
Log.exceptions $!
|
19
|
+
Process.kill "INT", procpath_pid
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
procpath_thread.report_on_exception = false
|
24
|
+
|
25
|
+
Process.wait pid.to_i
|
26
|
+
procpath_thread.raise Interrupt
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.plot(path, output, options = {})
|
30
|
+
IndiferentHash.setup(options)
|
31
|
+
options = Misc.add_defaults options, "query-name" => 'rss', 'epsilon' => 0.5, "moving-average-window" => 10
|
32
|
+
|
33
|
+
cmd_options = %w(query-name epsilon monitor-average-window title logarithmic after before custom-query-file custom-value-expr).inject({}){|acc,k| acc[k] = options[k]; acc}
|
34
|
+
CMD.cmd_log(:procpath, "plot --database-file '#{path}' --plot-file '#{output}' ", cmd_options.merge(:nofail => true, :add_option_dashes => true))
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.monitor(pid, path)
|
38
|
+
database, options_str = path.split("#")
|
39
|
+
options = options_str.nil? ? {} : Misc.string2hash(options_str)
|
40
|
+
|
41
|
+
database = File.expand_path database
|
42
|
+
Log.low "ProcPath monitor #{pid} in #{database} (#{Misc.fingerprint options})"
|
43
|
+
|
44
|
+
ProcPath.record(pid, database + '.sqlite3', options)
|
45
|
+
ProcPath.plot(database + '.sqlite3', database + '.cpu.svg', options.merge("query-name" => 'cpu'))
|
46
|
+
ProcPath.plot(database + '.sqlite3', database + '.rss.svg', options.merge("query-name" => 'rss'))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
data/lib/rbbt/workflow.rb
CHANGED
@@ -385,7 +385,7 @@ module Workflow
|
|
385
385
|
next if default == v
|
386
386
|
next if (String === default and Symbol === v and v.to_s == default)
|
387
387
|
next if (Symbol === default and String === v and v == default.to_s)
|
388
|
-
real_inputs[k] = v
|
388
|
+
real_inputs[k.to_sym] = v
|
389
389
|
end
|
390
390
|
|
391
391
|
jobname_input_value = inputs[jobname_input] || all_defaults[jobname_input]
|
@@ -410,6 +410,7 @@ module Workflow
|
|
410
410
|
job.workflow = self
|
411
411
|
job.clean_name = jobname
|
412
412
|
job.overriden = overriden
|
413
|
+
job.real_inputs = real_inputs.keys
|
413
414
|
job
|
414
415
|
end
|
415
416
|
|
@@ -16,6 +16,10 @@ end
|
|
16
16
|
|
17
17
|
module Workflow
|
18
18
|
|
19
|
+
def self.job_path?(path)
|
20
|
+
path.split("/")[-4] == "jobs"
|
21
|
+
end
|
22
|
+
|
19
23
|
def log(status, message = nil, &block)
|
20
24
|
Step.log(status, message, nil, &block)
|
21
25
|
end
|
@@ -301,7 +305,9 @@ module Workflow
|
|
301
305
|
|
302
306
|
def setup_override_dependency(dep, workflow, task_name)
|
303
307
|
dep = Step === dep ? dep : Workflow.load_step(dep)
|
308
|
+
dep.workflow = workflow
|
304
309
|
dep.info[:name] = dep.name
|
310
|
+
dep.original_task_name ||= dep.task_name if dep.workflow
|
305
311
|
begin
|
306
312
|
workflow = Kernel.const_get workflow if String === workflow
|
307
313
|
dep.task = workflow.tasks[task_name] if dep.task.nil? && workflow.tasks.include?(task_name)
|
@@ -309,7 +315,7 @@ module Workflow
|
|
309
315
|
Log.exception $!
|
310
316
|
end
|
311
317
|
dep.task_name = task_name
|
312
|
-
dep.overriden =
|
318
|
+
dep.overriden = dep.original_task_name.to_sym
|
313
319
|
dep
|
314
320
|
end
|
315
321
|
|
@@ -50,8 +50,8 @@ module Workflow
|
|
50
50
|
case input_types[input]
|
51
51
|
when :file
|
52
52
|
Log.debug "Pointing #{ input } to #{file}"
|
53
|
-
if file =~ /\.
|
54
|
-
inputs[input.to_sym] = Open.read(file)
|
53
|
+
if file =~ /\.yaml/
|
54
|
+
inputs[input.to_sym] = YAML.load(Open.read(file))
|
55
55
|
else
|
56
56
|
inputs[input.to_sym] = Open.realpath(file)
|
57
57
|
end
|
data/lib/rbbt/workflow/step.rb
CHANGED
@@ -12,6 +12,7 @@ class Step
|
|
12
12
|
attr_accessor :exec
|
13
13
|
attr_accessor :relocated
|
14
14
|
attr_accessor :result, :mutex, :seen
|
15
|
+
attr_accessor :real_inputs, :original_task_name
|
15
16
|
|
16
17
|
RBBT_DEBUG_CLEAN = ENV["RBBT_DEBUG_CLEAN"] == 'true'
|
17
18
|
|
@@ -145,11 +146,13 @@ class Step
|
|
145
146
|
seen = []
|
146
147
|
while path = deps.pop
|
147
148
|
dep_info = archived_info[path]
|
148
|
-
dep_info
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
149
|
+
if dep_info
|
150
|
+
dep_info[:inputs].each do |k,v|
|
151
|
+
all_inputs[k] = v unless all_inputs.include?(k)
|
152
|
+
end if dep_info[:inputs]
|
153
|
+
deps.concat(dep_info[:dependencies].collect{|p| p.last } - seen) if dep_info[:dependencies]
|
154
|
+
deps.concat(dep_info[:archived_dependencies].collect{|p| p.last } - seen) if dep_info[:archived_dependencies]
|
155
|
+
end
|
153
156
|
seen << path
|
154
157
|
end
|
155
158
|
|
@@ -93,18 +93,22 @@ class Step
|
|
93
93
|
|
94
94
|
Log.debug "Saving job input #{name} (#{type}) into #{path}"
|
95
95
|
case
|
96
|
+
when Step === value
|
97
|
+
Open.ln_s(value.path, path)
|
98
|
+
when type.to_s == "file"
|
99
|
+
if String === value && File.exists?(value)
|
100
|
+
Open.ln_s(value, path)
|
101
|
+
else
|
102
|
+
Open.write(path + '.yaml', value.to_yaml)
|
103
|
+
end
|
96
104
|
when Array === value
|
97
|
-
Open.write(path, value * "\n")
|
105
|
+
Open.write(path, value.collect{|v| Step === v ? v.path : v.to_s} * "\n")
|
98
106
|
when IO === value
|
99
|
-
|
100
|
-
|
101
|
-
if String === value && File.exists?(value)
|
102
|
-
Open.link(value, path)
|
107
|
+
if value.filename && String === value.filename && File.exists?(value.filename)
|
108
|
+
Open.ln_s(value.filename, path)
|
103
109
|
else
|
104
|
-
Open.write(path
|
110
|
+
Open.write(path, value)
|
105
111
|
end
|
106
|
-
when Step === value
|
107
|
-
value = value.produce.load
|
108
112
|
else
|
109
113
|
Open.write(path, value.to_s)
|
110
114
|
end
|
@@ -114,18 +118,24 @@ class Step
|
|
114
118
|
def self.save_job_inputs(job, dir, options = nil)
|
115
119
|
options = IndiferentHash.setup options.dup if options
|
116
120
|
|
117
|
-
task_name = job.task_name
|
121
|
+
task_name = Symbol === job.overriden ? job.overriden : job.task_name
|
118
122
|
workflow = job.workflow
|
119
123
|
workflow = Kernel.const_get workflow if String === workflow
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
if workflow
|
125
|
+
task_info = workflow.task_info(task_name)
|
126
|
+
input_types = task_info[:input_types]
|
127
|
+
task_inputs = task_info[:inputs]
|
128
|
+
input_defaults = task_info[:input_defaults]
|
129
|
+
else
|
130
|
+
task_info = input_types = task_inputs = input_defaults = {}
|
131
|
+
end
|
124
132
|
|
125
133
|
inputs = {}
|
134
|
+
real_inputs = job.real_inputs || job.info[:real_inputs]
|
126
135
|
job.recursive_inputs.zip(job.recursive_inputs.fields).each do |value,name|
|
127
136
|
next unless task_inputs.include? name.to_sym
|
128
|
-
next
|
137
|
+
next unless real_inputs.include? name.to_sym
|
138
|
+
next if options && ! options.include?(name)
|
129
139
|
next if value.nil?
|
130
140
|
next if input_defaults[name] == value
|
131
141
|
inputs[name] = value
|
@@ -222,7 +232,7 @@ class Step
|
|
222
232
|
def init_info(force = false)
|
223
233
|
return nil if @exec || info_file.nil? || (Open.exists?(info_file) && ! force)
|
224
234
|
Open.lock(info_file, :lock => info_lock) do
|
225
|
-
i = {:status => :waiting, :pid => Process.pid, :path => path}
|
235
|
+
i = {:status => :waiting, :pid => Process.pid, :path => path, :real_inputs => real_inputs}
|
226
236
|
i[:dependencies] = dependencies.collect{|dep| [dep.task_name, dep.name, dep.path]} if dependencies
|
227
237
|
Misc.sensiblewrite(info_file, Step.serialize_info(i), :force => true, :lock => false)
|
228
238
|
@info_cache = IndiferentHash.setup(i)
|
@@ -495,8 +505,8 @@ class Step
|
|
495
505
|
|
496
506
|
def running?
|
497
507
|
return false if ! (started? || status == :ending)
|
498
|
-
|
499
|
-
|
508
|
+
return nil unless Open.exist?(self.pid_file)
|
509
|
+
pid = Open.read(self.pid_file).to_i
|
500
510
|
|
501
511
|
return false if done? or error? or aborted?
|
502
512
|
|
@@ -520,8 +530,7 @@ class Step
|
|
520
530
|
end
|
521
531
|
|
522
532
|
def nopid?
|
523
|
-
|
524
|
-
! pid && ! (status.nil? || status == :aborted || status == :done || status == :error || status == :cleaned)
|
533
|
+
! Open.exists?(pid_file) && ! (status.nil? || status == :aborted || status == :done || status == :error || status == :cleaned)
|
525
534
|
end
|
526
535
|
|
527
536
|
def aborted?
|