rbbt-util 5.35.4 → 5.37.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: b3a214b340c407e61c0cc227b8872c4b0c71463affcf6bf84f9d4ed66486435c
4
- data.tar.gz: d62735c060afe4507cc69363280d76e24c88ed89063531e526fe5a03ecfd6386
3
+ metadata.gz: 281f115df5158fada081c65b87e292e69e4f378b8353d6e2542b3c45db0c03f1
4
+ data.tar.gz: e9801707391c731a2a4b4ead8156cff82fdf30c4e769a8bed34d1d77185b447a
5
5
  SHA512:
6
- metadata.gz: f8fd64ecb9abc0a5bf519fa294196e8b1082a4ef358106806bc7c18dadfb361bd22b2b14aca96f3dd32c276461fa6616d3613a7de7aca87ceaddc7db74d57e23
7
- data.tar.gz: baa4182336b92c09eead8fed9a28b2bc6d84aea7e2e9e98c4597ae3373ac31ff27feb62e1dbd73817ac69f7986a00ceb2bf404d38fb739919882fea684053bfa
6
+ metadata.gz: 3da2876b28c245b27f32b6dbd00188bdeea498fb118ccab048fb5d72925548e5e89926dea6b6316573e9d95a570c07983109e0e09de92360ec89ab2aec088439
7
+ data.tar.gz: 0166d47c3198bfc4c636a1f47ac9be1d2a64b8d97a2836166b02d1ac17d46c2b835ad210953da586f15a98f8593e18f6010ebc07220e95da149dcabec44b33a9
data/lib/rbbt/persist.rb CHANGED
@@ -44,6 +44,8 @@ module Persist
44
44
  end
45
45
 
46
46
  def self.is_persisted?(path, persist_options = {})
47
+ return true if Open.remote?(path)
48
+ return true if Open.ssh?(path)
47
49
  return false if not Open.exists? path
48
50
  return false if TrueClass === persist_options[:update]
49
51
 
@@ -319,7 +319,8 @@ module Path
319
319
 
320
320
 
321
321
  def open(options = {}, &block)
322
- Open.open(self.produce.find, options, &block)
322
+ file = Open.remote?(self) || Open.ssh?(self) ? self : self.produce.find
323
+ Open.open(file, options, &block)
323
324
  end
324
325
 
325
326
  def to_s
@@ -331,7 +332,16 @@ module Path
331
332
  end
332
333
 
333
334
  def tsv(*args)
334
- TSV.open(self.produce, *args)
335
+ begin
336
+ path = self.produce
337
+ rescue Resource::ResourceNotFound => e
338
+ begin
339
+ path = self.set_extension('tsv').produce
340
+ rescue Resource::ResourceNotFound
341
+ raise e
342
+ end
343
+ end
344
+ TSV.open(path, *args)
335
345
  end
336
346
 
337
347
  def tsv_options(options = {})
@@ -28,6 +28,7 @@ class RbbtProcessQueue
28
28
 
29
29
 
30
30
  def dump(obj, stream)
31
+ obj.concurrent_stream = nil if obj.respond_to?(:concurrent_stream)
31
32
  case obj
32
33
  when Annotated
33
34
  payload = @serializer.dump(obj)
@@ -65,11 +65,7 @@ class RbbtProcessQueue
65
65
  Log.high "Worker #{Process.pid} leaving"
66
66
  rescue Exception
67
67
  Log.high "Worker #{Process.pid} had exception: #{$!.message}"
68
- Log.exception $!
69
- begin
70
- @callback_queue.push($!) if @callback_queue
71
- rescue
72
- end
68
+ @callback_queue.push($!) if @callback_queue
73
69
  Kernel.exit! -1
74
70
  ensure
75
71
  @callback_queue.close_write if @callback_queue
@@ -180,13 +180,15 @@ class RbbtProcessQueue
180
180
  rescue Exception
181
181
  Log.low "Process monitor exception [#{Process.pid}]: #{$!.message}"
182
182
  processes.each{|p| p.abort_and_join}
183
- Log.low "Processes aborted #{Process.pid}"
183
+ Log.low "Processes aborted for monitor #{Process.pid}"
184
184
  processes.clear
185
185
 
186
+ @manager_thread.report_on_exception = false
186
187
  @manager_thread.raise $! if @manager_thread.alive?
187
188
  raise Aborted, "Aborted monitor thread with exception"
188
189
  end
189
190
  end
191
+ @monitor_thread.report_on_exception = false
190
192
 
191
193
  RbbtSemaphore.post_semaphore(@sem)
192
194
 
@@ -215,33 +217,36 @@ class RbbtProcessQueue
215
217
  init_master
216
218
 
217
219
  RbbtSemaphore.synchronize(@sem) do
218
- @callback_thread = Thread.new do
219
- begin
220
- loop do
221
- p = @callback_queue.pop unless @callback_queue.cleaned
220
+ @callback_thread = Thread.new do
221
+ begin
222
+ loop do
223
+ p = @callback_queue.pop unless @callback_queue.cleaned
222
224
 
223
- if Exception === p or (Array === p and Exception === p.first)
224
- e = Array === p ? p.first : p
225
- Log.low "Callback recieved exception from worker: #{e.message}" unless Aborted === e or ClosedStream === e
226
- raise e
227
- end
225
+ if Exception === p or (Array === p and Exception === p.first)
226
+ e = Array === p ? p.first : p
227
+ Log.low "Callback recieved exception from worker: #{e.message}" unless Aborted === e or ClosedStream === e
228
+ raise e
229
+ end
228
230
 
229
- if @callback.arity == 0
230
- @callback.call
231
- else
232
- @callback.call p
231
+ if @callback.arity == 0
232
+ @callback.call
233
+ else
234
+ @callback.call p
235
+ end
233
236
  end
237
+ rescue ClosedStream
238
+ Log.low "Callback thread closing"
239
+ rescue Aborted
240
+ Log.low "Callback thread aborted"
241
+ raise $!
242
+ rescue Exception
243
+ Log.low "Exception captured in callback: #{$!.message}"
244
+ raise $!
234
245
  end
235
- rescue ClosedStream
236
- Log.low "Callback thread closing"
237
- rescue Aborted
238
- Log.low "Callback thread aborted"
239
- raise $!
240
- rescue Exception
241
- Log.low "Exception captured in callback: #{$!.message}"
242
- raise $!
243
- end
244
- end if @callback_queue
246
+ end if @callback_queue
247
+
248
+ @callback_thread.report_on_exception = false
249
+
245
250
  end
246
251
 
247
252
  end
@@ -343,9 +343,9 @@ module Misc
343
343
  end
344
344
  when Array
345
345
  if obj.length > HASH2MD5_MAX_ARRAY_LENGTH
346
- "[" << sample_large_obj(obj, HASH2MD5_MAX_ARRAY_LENGTH).collect{|v| obj2str(v)} * "," << "]"
346
+ "[" << sample_large_obj(obj, HASH2MD5_MAX_ARRAY_LENGTH).collect{|v| obj2str(v.nil? ? "" : v) } * "," << "]"
347
347
  else
348
- "[" << obj.collect{|v| obj2str(v) } * "," << "]"
348
+ "[" << obj.collect{|v| obj2str(v.nil? ? "" : v) } * "," << "]"
349
349
  end
350
350
  when TSV::Parser
351
351
  remove_long_items(obj)
@@ -59,12 +59,12 @@ module RbbtPython
59
59
  end
60
60
 
61
61
  def self.get_class(module_name, class_name)
62
- save_module_name = module_name.gsub(".", "_")
62
+ save_module_name = module_name.to_s.gsub(".", "_")
63
63
  RbbtPython.pyimport(module_name, as: save_module_name)
64
64
  RbbtPython.send(save_module_name).send(class_name)
65
65
  end
66
66
 
67
- def self.class_new_obj(module_name, class_name, args)
67
+ def self.class_new_obj(module_name, class_name, args={})
68
68
  RbbtPython.get_class(module_name, class_name).new(**args)
69
69
  end
70
70
 
@@ -175,6 +175,8 @@ module Workflow
175
175
 
176
176
  tasks[name] = task
177
177
  task_dependencies[name] = consume_dependencies
178
+
179
+ task
178
180
  end
179
181
 
180
182
  def unexport(*names)
@@ -72,6 +72,7 @@ module Workflow
72
72
  def setup_override_dependency(dep, workflow, task_name)
73
73
  return [] if dep == :skip || dep == 'skip'
74
74
 
75
+ unlocated = unlocated_override?(dep)
75
76
  dep = Workflow.load_step(dep) if not Step === dep
76
77
 
77
78
  dep.original_workflow ||= dep.workflow if dep.workflow
@@ -91,7 +92,7 @@ module Workflow
91
92
  end
92
93
 
93
94
  dep.task_name = task_name
94
- dep.overriden = dep.original_task_name.to_sym if dep.original_task_name
95
+ dep.overriden = dep.original_task_name.to_sym if dep.original_task_name && dep.original_task_name.to_s != task_name.to_s || ! unlocated
95
96
 
96
97
  dep.extend step_module
97
98
 
@@ -169,6 +170,16 @@ module Workflow
169
170
  else
170
171
 
171
172
  compute = options[:compute] if options
173
+ if options && options[:canfail]
174
+ compute = case compute
175
+ when nil
176
+ :canfail
177
+ when Array
178
+ compute + [:canfail]
179
+ else
180
+ [compute, :canfail]
181
+ end
182
+ end
172
183
 
173
184
  all_d = (real_dependencies + real_dependencies.flatten.collect{|d| d.rec_dependencies} ).flatten.compact.uniq
174
185
 
@@ -207,6 +218,16 @@ module Workflow
207
218
 
208
219
  options = {} if options.nil?
209
220
  compute = options[:compute]
221
+ if options[:canfail]
222
+ compute = case compute
223
+ when nil
224
+ :canfail
225
+ when Array
226
+ compute + [:canfail]
227
+ else
228
+ [compute, :canfail]
229
+ end
230
+ end
210
231
 
211
232
  options = IndiferentHash.setup(options.dup)
212
233
  dep = dependency.call jobname, _inputs.merge(options), real_dependencies
@@ -228,7 +249,8 @@ module Workflow
228
249
  task_info = d[:workflow].task_info(d[:task])
229
250
 
230
251
  _inputs = assign_dep_inputs({}, options.merge(d[:inputs] || {}), real_dependencies, task_info)
231
- job = d[:workflow]._job(d[:task], d[:jobname], _inputs)
252
+ _jobname = d.include?(:jobname) ? d[:jobname] : jobname
253
+ job = d[:workflow]._job(d[:task], _jobname, _inputs)
232
254
  overriden = true if TrueClass === job.overriden && (d.nil? || ! d[:not_overriden])
233
255
  job
234
256
  end
@@ -250,8 +272,8 @@ module Workflow
250
272
  else
251
273
  task_info = (dep[:task] && dep[:workflow]) ? dep[:workflow].task_info(dep[:task]) : nil
252
274
  _inputs = assign_dep_inputs({}, dep[:inputs], real_dependencies, task_info)
253
- job = dep[:workflow]._job(dep[:task], dep[:jobname], _inputs)
254
- job = d[:workflow]._job(d[:task], d[:jobname], _inputs)
275
+ _jobname = dep.include?(:jobname) ? dep[:jobname] : jobname
276
+ job = dep[:workflow]._job(dep[:task], _jobname, _inputs)
255
277
  overriden = true if TrueClass === job.overriden && (d.nil? || ! d[:not_overriden])
256
278
  job
257
279
  end
@@ -37,6 +37,7 @@ module Workflow
37
37
  def documentation_markdown
38
38
  return "" if @libdir.nil?
39
39
  file = @libdir['workflow.md'].find
40
+ file = @libdir['README.md'].find unless file.exists?
40
41
  if file.exists?
41
42
  file.read
42
43
  else
@@ -1,4 +1,6 @@
1
1
  class RemoteWorkflow
2
+ RBBT_DEBUG_REMOTE_JSON = ENV["RBBT_DEBUG_REMOTE_JSON"] == 'true'
3
+
2
4
  module SSH
3
5
  #def self.run(server, script)
4
6
  # Log.debug "Run ssh script in #{server}:\n#{script}"
@@ -21,9 +23,16 @@ class RemoteWorkflow
21
23
 
22
24
  workflow, task, job, *rest = path.split("/")
23
25
 
26
+ workflow_name = begin
27
+ wf = Kernel.const_get(workflow) if String === workflow && ! workflow.empty?
28
+ wf.respond_to?(:complete_name) ? (wf.complete_name || workflow) : workflow
29
+ rescue
30
+ workflow
31
+ end
32
+
24
33
  script =<<-EOF
25
34
  require 'rbbt/workflow'
26
- wf = Workflow.require_workflow "#{workflow}"
35
+ wf = Workflow.require_workflow "#{workflow_name}"
27
36
  EOF
28
37
 
29
38
  case task
@@ -86,7 +95,7 @@ STDOUT.write res.to_json
86
95
  EOF
87
96
 
88
97
  json = Misc.ssh_run(server, script)
89
- Log.debug "JSON (#{ url }): #{json}"
98
+ Log.debug "JSON (#{ url }): #{json}" if RBBT_DEBUG_REMOTE_JSON
90
99
  JSON.parse(json)
91
100
  end
92
101
 
@@ -172,11 +181,18 @@ job.clean
172
181
  def self.upload_inputs(server, inputs, input_types, input_id)
173
182
  TmpFile.with_file do |dir|
174
183
  if Step.save_inputs(inputs, input_types, dir)
175
- Dir.glob(File.join(dir, "*.as_step")).each do |file|
176
- path = Open.read(file).strip
177
- new = Step.migrate(path, :user, :target => server)
178
- Open.write(file, new)
179
- end
184
+ # Dir.glob(File.join(dir, "*.as_step")).each do |file|
185
+ # Log.medium "Migrating Step input #{file} #{ server }"
186
+ # path = Open.read(file).strip
187
+ # new = Step.migrate(path, :user, :target => server)
188
+ # Open.write(file, new)
189
+ # end
190
+
191
+ files = Dir.glob(File.join(dir, "*.as_step"))
192
+ paths = files.collect{|f| Open.read(f).strip }
193
+ new = Step.migrate(paths, :user, :target => server)
194
+ files.zip(new).each{|file,new| Open.write(file, new) }
195
+
180
196
  CMD.cmd_log("ssh '#{server}' mkdir -p .rbbt/tmp/tmp-ssh_job_inputs/; scp -r '#{dir}' #{server}:.rbbt/tmp/tmp-ssh_job_inputs/#{input_id}")
181
197
  end
182
198
  end
@@ -204,31 +220,57 @@ job.clean
204
220
  # rjob.run
205
221
  #end
206
222
 
223
+ def self.upload_dependencies(job, server, search_path = 'user', produce_dependencies = false)
224
+ server, path = parse_url(server) if server =~ /^ssh:\/\//
225
+ job.dependencies.each do |dep|
226
+ Log.medium "Producing #{dep.workflow}:#{dep.short_path} dependency for #{job.workflow}:#{job.short_path}"
227
+ dep.produce
228
+ end if produce_dependencies
229
+
230
+ job.input_dependencies.each do |dep|
231
+ Log.medium "Producing #{dep.workflow}:#{dep.short_path} dependency for #{job.workflow}:#{job.short_path}"
232
+ dep.produce
233
+ end
234
+
235
+ migrate_dependencies = job.rec_dependencies.select{|d| d.done? }.collect{|d| d.path }
236
+ migrate_dependencies += job.input_dependencies.select{|d| d.done? }.collect{|d| d.path }
237
+ Log.medium "Migrating #{migrate_dependencies.length} dependencies from #{job.path} to #{ server }"
238
+ Step.migrate(migrate_dependencies, search_path, :target => server) if migrate_dependencies.any?
239
+ end
240
+
241
+ def self.missing_dep_inputs(job)
242
+ inputs = job.inputs.to_hash.slice(*job.real_inputs.map{|i| i.to_s})
243
+ job.dependencies.each do |dep|
244
+ next if dep.done?
245
+ iif [dep, dep.inputs, dep.real_inputs]
246
+ inputs = dep.inputs.to_hash.slice(*dep.real_inputs.map{|i| i.to_s}).merge(inputs)
247
+ inputs = missing_dep_inputs(dep).merge(inputs)
248
+ end
249
+ inputs
250
+ end
251
+
207
252
  def self.relay_job(job, server, options = {})
208
- migrate, produce, produce_dependencies, search_path = Misc.process_options options.dup,
209
- :migrate, :produce, :produce_dependencies, :search_path
253
+ migrate, produce, produce_dependencies, search_path, run_type, slurm_options = Misc.process_options options.dup,
254
+ :migrate, :produce, :produce_dependencies, :search_path, :run_type, :slurm_options
210
255
 
211
256
  search_path ||= 'user'
212
257
 
213
258
  produce = true if migrate
214
259
 
215
260
  workflow_name = job.workflow.to_s
216
- inputs = job.recursive_inputs.to_hash
217
-
218
- job.dependencies.each do |dep|
219
- dep.produce
220
- end if options[:produce_dependencies]
221
-
222
- job.rec_dependencies.each do |dep|
223
- Step.migrate(dep.path, search_path, :target => server) if dep.done?
224
- end
261
+ remote_workflow = RemoteWorkflow.new("ssh://#{server}:#{workflow_name}", "#{workflow_name}")
262
+ inputs = job.recursive_inputs.to_hash.slice(*job.real_inputs.map{|i| i.to_s})
263
+ Log.medium "Relaying dependency #{job.workflow}:#{job.short_path} to #{server} (#{inputs.keys * ", "})"
225
264
 
226
- remote_workflow = RemoteWorkflow.new("ssh://#{server}:#{job.workflow.to_s}", "#{job.workflow.to_s}")
265
+ upload_dependencies(job, server, search_path, options[:produce_dependencies])
227
266
  rjob = remote_workflow.job(job.task_name.to_s, job.clean_name, inputs)
228
267
 
229
268
  override_dependencies = job.rec_dependencies.select{|dep| dep.done? }.collect{|dep| [dep.workflow.to_s, dep.task_name.to_s] * "#" << "=" << Rbbt.identify(dep.path)}
230
269
  rjob.override_dependencies = override_dependencies
231
270
 
271
+ rjob.run_type = run_type
272
+ rjob.slurm_options = slurm_options || {}
273
+
232
274
  if options[:migrate]
233
275
  rjob.produce
234
276
  Step.migrate(Rbbt.identify(job.path), 'user', :source => server)
@@ -255,7 +297,7 @@ job.clean
255
297
  @task_info ||= IndiferentHash.setup({})
256
298
 
257
299
  if @task_info[task].nil?
258
- task_info = RemoteWorkflow::SSH.get_json(File.join(@base_url, task.to_s))
300
+ task_info = RemoteWorkflow::SSH.get_json(File.join(@base_url || @url, task.to_s))
259
301
  task_info = RemoteWorkflow::SSH.fix_hash(task_info)
260
302
 
261
303
  task_info[:result_type] = task_info[:result_type].to_sym if task_info[:result_type]
@@ -1,6 +1,6 @@
1
1
  class RemoteStep
2
2
  module SSH
3
- attr_accessor :override_dependencies
3
+ attr_accessor :override_dependencies, :run_type, :slurm_options
4
4
 
5
5
  def init_job(cache_type = nil, other_params = {})
6
6
  return self if @url
@@ -52,15 +52,31 @@ class RemoteStep
52
52
  end
53
53
 
54
54
  def _run
55
+ RemoteWorkflow::SSH.upload_dependencies(self, @server)
55
56
  RemoteWorkflow::SSH.run_job(File.join(base_url, task.to_s), @input_id, @base_name)
56
57
  end
57
58
 
59
+ def _run_slurm
60
+ RemoteWorkflow::SSH.run_slurm_job(File.join(base_url, task.to_s), @input_id, @base_name, @slurm_options || {})
61
+ end
62
+
63
+ def _orchestrate_slurm
64
+ RemoteWorkflow::SSH.orchestrate_slurm_job(File.join(base_url, task.to_s), @input_id, @base_name, @slurm_options || {})
65
+ end
66
+
58
67
  def produce(*args)
59
68
  input_types = {}
60
69
  init_job
61
- @remote_path = _run
70
+ @remote_path = case @run_type
71
+ when 'run', :run, nil
72
+ _run
73
+ when 'slurm', :slurm
74
+ _run_slurm
75
+ when 'orchestrate', :orchestrate
76
+ _orchestrate_slurm
77
+ end
62
78
  @started = true
63
- while ! (done? || error?)
79
+ while ! (done? || error? || aborted?)
64
80
  sleep 1
65
81
  end
66
82
  raise self.get_exception if error?
@@ -82,6 +98,18 @@ class RemoteStep
82
98
  _restart
83
99
  end
84
100
 
101
+ def abort
102
+ Log.warn "not implemented RemoteWorkflow::SSH.abort(@url, @input_id, @base_name)"
103
+ end
104
+
105
+ def input_dependencies
106
+ @input_dependencies ||= inputs.values.flatten.
107
+ select{|i| Step === i || (defined?(RemoteStep) && RemoteStep === i) } +
108
+ inputs.values.flatten.
109
+ select{|dep| Path === dep && Step === dep.resource }.
110
+ select{|dep| ! dep.resource.started? }. # Ignore input_deps already started
111
+ collect{|dep| dep.resource }
112
+ end
85
113
  end
86
114
  end
87
115
 
@@ -163,6 +163,7 @@ class RemoteStep < Step
163
163
  begin
164
164
  status = info[:status]
165
165
  @done = true if status and status.to_sym == :done
166
+ Log.low "RemoteStep status '#{status}' #{self.url}"
166
167
  status
167
168
  rescue
168
169
  Log.exception $!
@@ -1,4 +1,12 @@
1
1
  class Step
2
+ attr_accessor :clean_name, :path, :task, :workflow, :inputs, :dependencies, :bindings
3
+ attr_accessor :task_name, :overriden
4
+ attr_accessor :pid
5
+ attr_accessor :exec
6
+ attr_accessor :relocated
7
+ attr_accessor :result, :mutex, :seen
8
+ attr_accessor :real_inputs, :original_task_name, :original_workflow
9
+
2
10
 
3
11
  INFO_SERIALIZER = begin
4
12
  if ENV["RBBT_INFO_SERIALIZER"]
@@ -86,12 +94,45 @@ class Step
86
94
  end
87
95
  end
88
96
 
97
+ def task_name
98
+ @task_name ||= begin
99
+ if @task.respond_to?(:name)
100
+
101
+ @task.name
102
+ else
103
+ @path.split("/")[-2]
104
+ end
105
+ end
106
+ end
107
+
108
+ def result_type
109
+ @result_type ||= if @task.respond_to?(:result_type)
110
+ @task.result_type || info[:result_type]
111
+ else
112
+ info[:result_type]
113
+ end
114
+ end
115
+
116
+ def result_type=(type)
117
+ @result_type = type
118
+ end
119
+
120
+ def result_description
121
+ @result_description ||= if @task.respond_to?(:result_description)
122
+ @task.result_description
123
+ else
124
+ nil
125
+ end
126
+ end
127
+
128
+ def result_description=(description)
129
+ @result_description = description
130
+ end
89
131
 
90
132
  def name
91
133
  @name ||= path.sub(/.*\/#{Regexp.quote task_name.to_s}\/(.*)/, '\1')
92
134
  end
93
135
 
94
-
95
136
  def short_path
96
137
  [task_name, name] * "/"
97
138
  end
@@ -105,10 +146,6 @@ class Step
105
146
  workflow.to_s + "#" + short_path
106
147
  end
107
148
 
108
- def task_name
109
- @task_name ||= task.name
110
- end
111
-
112
149
  def task_signature
113
150
  [workflow.to_s, task_name] * "#"
114
151
  end