rbbt-util 5.37.16 → 5.38.1

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rbbt/annotations.rb +2 -0
  3. data/lib/rbbt/hpc/batch.rb +15 -6
  4. data/lib/rbbt/hpc/orchestrate.rb +1 -0
  5. data/lib/rbbt/persist.rb +2 -2
  6. data/lib/rbbt/resource/path.rb +1 -0
  7. data/lib/rbbt/resource/rake.rb +1 -1
  8. data/lib/rbbt/resource.rb +7 -2
  9. data/lib/rbbt/tsv/excel.rb +5 -1
  10. data/lib/rbbt/tsv/parser.rb +2 -2
  11. data/lib/rbbt/util/misc/development.rb +6 -1
  12. data/lib/rbbt/util/misc/exceptions.rb +10 -0
  13. data/lib/rbbt/util/misc/format.rb +3 -0
  14. data/lib/rbbt/util/misc/indiferent_hash.rb +9 -6
  15. data/lib/rbbt/util/misc/inspect.rb +22 -8
  16. data/lib/rbbt/util/misc/options.rb +1 -1
  17. data/lib/rbbt/util/misc/pipes.rb +3 -3
  18. data/lib/rbbt/util/open.rb +0 -1
  19. data/lib/rbbt/util/ssh.rb +107 -14
  20. data/lib/rbbt/workflow/remote_workflow/driver/ssh.rb +75 -15
  21. data/lib/rbbt/workflow/remote_workflow/remote_step/ssh.rb +16 -7
  22. data/lib/rbbt/workflow/remote_workflow/remote_step.rb +1 -0
  23. data/lib/rbbt/workflow/step/dependencies.rb +6 -5
  24. data/lib/rbbt/workflow/step/info.rb +1 -1
  25. data/lib/rbbt/workflow/step/run.rb +1 -1
  26. data/lib/rbbt/workflow/step/save_load_inputs.rb +9 -8
  27. data/lib/rbbt/workflow/step.rb +7 -2
  28. data/lib/rbbt/workflow/usage.rb +3 -3
  29. data/lib/rbbt/workflow.rb +1 -1
  30. data/share/rbbt_commands/hpc/list +293 -293
  31. data/share/rbbt_commands/lsf/list +293 -293
  32. data/share/rbbt_commands/resource/find +2 -1
  33. data/share/rbbt_commands/resource/glob +1 -1
  34. data/share/rbbt_commands/slurm/list +293 -293
  35. data/share/rbbt_commands/workflow/task +63 -21
  36. data/test/rbbt/test_resource.rb +7 -2
  37. data/test/rbbt/tsv/test_accessor.rb +21 -4
  38. data/test/rbbt/tsv/test_parser.rb +28 -0
  39. data/test/rbbt/util/misc/test_indiferent_hash.rb +14 -0
  40. data/test/rbbt/util/test_cmd.rb +7 -1
  41. data/test/rbbt/util/test_open.rb +3 -3
  42. data/test/rbbt/util/test_ssh.rb +10 -0
  43. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 565fc431e80f7167a95a8ad2c3499207c1422dacfe7ce1cd4cd2c1a4f0bad5c4
4
- data.tar.gz: 34b393012cd4d58bcb9917cb2c45f8e0f23cefca73139647454b6affcb80cf57
3
+ metadata.gz: 1121593dec25c29d7986047727d555ed0e75df6b158f9f1332764ad4886462c5
4
+ data.tar.gz: c8e39ef65e23445cf5d0c8817af961a9206016c5e3665d87e042f0fadec2b8df
5
5
  SHA512:
6
- metadata.gz: 854c7a84a92a654a987f5d1274dabd6d7564108d8a200eeb377321aa02361264d979f42f8e91350be2ed0074bd0f153b6dd66d7ea5f4597edca5b68edf2d8c0a
7
- data.tar.gz: 34c765a22fed51dfaf873a0abcd0338f2a9ab15ed09bca0ac445075261ea040002e042312e62951c951bfc48d08162175cf697f9890e5b1a0c3ac58f2b0a03d0
6
+ metadata.gz: 22c87766544a52c9c6f0d6da369422a452f553d1a61b82a54674f676a3867a6c1d29028c2efd031563f982cdadbce56d36950c8e100bd34ac2a7e95b39af8a58
7
+ data.tar.gz: 0a79edc89dad388bca9b94dfeccba1df64c4294e84c4f19743608038e55f8800181911d787980adae3ec1c715a993430ccbf2bc07d6212485b89843ada93de74
@@ -154,6 +154,8 @@ module Annotated
154
154
 
155
155
  def self.purge(object)
156
156
  case object
157
+ when Path
158
+ object
157
159
  when String
158
160
  object.respond_to?(:clean_annotations) ?
159
161
  object.clean_annotations :
@@ -556,23 +556,32 @@ env > #{batch_options[:fenv]}
556
556
  fcmd
557
557
  end
558
558
 
559
+ def batch_dir_for_id(batch_base_dir, id)
560
+ job_id_file = Dir.glob(File.join(batch_base_dir, '*/job.id')).select{|f| Open.read(f).strip == id.to_s }.first
561
+ job_id_file ? File.dirname(job_id_file) : nil
562
+ end
559
563
 
560
564
  def run_job(job, options = {})
561
565
  system = self.to_s.split("::").last
562
566
 
567
+ batch_base_dir, clean_batch_job, remove_batch_dir, procpath, tail, batch_dependencies, dry_run = Misc.process_options options,
568
+ :batch_base_dir, :clean_batch_job, :remove_batch_dir, :batch_procpath, :tail, :batch_dependencies, :dry_run,
569
+ :batch_base_dir => File.expand_path(File.join('~/rbbt-batch'))
570
+
563
571
  if (batch_job = job.info[:batch_job]) && job_queued(batch_job)
564
572
  Log.info "Job #{job.short_path} already queued in #{batch_job}"
565
- return batch_job
573
+ return batch_job, batch_dir_for_id(batch_base_dir, batch_job)
566
574
  end
567
575
 
568
576
  if job.running?
569
577
  Log.info "Job #{job.short_path} already running in #{job.info[:pid]}"
570
- return job.info[:batch_job]
571
- end
572
578
 
573
- batch_base_dir, clean_batch_job, remove_batch_dir, procpath, tail, batch_dependencies, dry_run = Misc.process_options options,
574
- :batch_base_dir, :clean_batch_job, :remove_batch_dir, :batch_procpath, :tail, :batch_dependencies, :dry_run,
575
- :batch_base_dir => File.expand_path(File.join('~/rbbt-batch'))
579
+ if job.info[:batch_job]
580
+ return job.info[:batch_job], batch_dir_for_id(batch_base_dir, batch_job)
581
+ else
582
+ return
583
+ end
584
+ end
576
585
 
577
586
  workflow = job.original_workflow ||job.workflow
578
587
  task_name = job.original_task_name || job.task_name
@@ -103,6 +103,7 @@ module HPC
103
103
  last_dir = dir
104
104
  end
105
105
  end
106
+
106
107
  [last_id, last_dir]
107
108
  end
108
109
 
data/lib/rbbt/persist.rb CHANGED
@@ -115,7 +115,7 @@ module Persist
115
115
  begin
116
116
  case (type || :marshal).to_sym
117
117
  when :path
118
- path
118
+ Path.setup(Open.read(path).strip)
119
119
  when :nil
120
120
  nil
121
121
  when :boolean
@@ -172,7 +172,7 @@ module Persist
172
172
 
173
173
  case (type || :marshal).to_sym
174
174
  when :path
175
- nil
175
+ Open.write(path, content)
176
176
  when :nil
177
177
  nil
178
178
  when :boolean
@@ -479,4 +479,5 @@ module Path
479
479
  def clean_annotations
480
480
  "" << self.to_s
481
481
  end
482
+
482
483
  end
@@ -53,7 +53,7 @@ module Rake
53
53
  Rake::Task.clear
54
54
  Rake::FileTask.clear_files
55
55
  end
56
- rescue Exception
56
+ rescue
57
57
  Log.error "Error in rake: #{$!.message}"
58
58
  Log.exception $!
59
59
  Kernel.exit! -1
data/lib/rbbt/resource.rb CHANGED
@@ -350,6 +350,7 @@ url='#{url}'
350
350
  end
351
351
 
352
352
  def identify(path)
353
+ return path unless path.start_with?("/")
353
354
  path = File.expand_path(path)
354
355
  path += "/" if File.directory?(path)
355
356
  resource ||= Rbbt
@@ -357,6 +358,7 @@ url='#{url}'
357
358
  locations -= [:current, "current"]
358
359
  locations << :current
359
360
  search_paths = IndiferentHash.setup(resource.search_paths)
361
+ choices = []
360
362
  locations.uniq.each do |name|
361
363
  pattern = search_paths[name]
362
364
  pattern = resource.search_paths[pattern] while Symbol === pattern
@@ -370,12 +372,15 @@ url='#{url}'
370
372
  "(?:/(?<REST>.*))?/?$"
371
373
  if m = path.match(regexp)
372
374
  if ! m.named_captures.include?("PKGDIR") || m["PKGDIR"] == resource.pkgdir
373
- return self[m["TOPLEVEL"]][m["SUBPATH"]][m["REST"]]
375
+ unlocated = ([m["TOPLEVEL"],m["SUBPATH"],m["REST"]] * "/")
376
+ unlocated.gsub!(/\/+/,'/')
377
+ unlocated[self.subdir] = "" if self.subdir
378
+ choices << self.annotate(unlocated)
374
379
  end
375
380
  end
376
381
  end
377
382
  end
378
- nil
383
+ choices.sort_by{|s| s.length }.first
379
384
  end
380
385
  end
381
386
 
@@ -154,7 +154,11 @@ module TSV
154
154
  sheet1 = book.create_worksheet
155
155
  sheet1.name = sheet if sheet
156
156
 
157
- sheet1.row(0).concat fields
157
+ if fields
158
+ sheet1.row(0).concat fields if fields
159
+ else
160
+ sheet1.row(0).concat ["No field info"]
161
+ end
158
162
 
159
163
  rows.each_with_index do |cells,i|
160
164
  sheet1.row(i+1).concat cells
@@ -130,12 +130,12 @@ module TSV
130
130
  keys = parts[key_position].split(@sep2, -1)
131
131
  values = case
132
132
  when field_positions.nil?
133
- parts.tap{|o| o.delete_at key_position}
133
+ parts.tap{|o| o.delete_at key_position}
134
134
  when field_positions.empty?
135
135
  []
136
136
  else
137
137
  parts.values_at *field_positions
138
- end.collect{|value| (value.nil? || value.empty?) ? [] : value.split(@sep2, -1) }
138
+ end.collect{|value| (value.nil? || value.empty?) ? [""] : value.split(@sep2, -1) }
139
139
  [keys, values]
140
140
  end
141
141
 
@@ -423,13 +423,18 @@ def self.add_libdir(dir=nil)
423
423
  end
424
424
  end
425
425
 
426
- def self.ssh_run(server, script = nil)
426
+ def self.ssh_run_old(server, script = nil)
427
427
  Log.debug "Run ssh script in #{server}:\n#{script}"
428
428
 
429
429
  #CMD.cmd("ssh '#{server}' 'shopt -s expand_aliases; bash -l -c \"ruby\"' ", :in => script, :log => true).read
430
430
  CMD.cmd("ssh '#{server}' ruby", :in => script, :log => true).read
431
431
  end
432
432
 
433
+ def self.ssh_run(server, script = nil)
434
+ require 'rbbt/util/ssh'
435
+ SSHLine.ruby(server, script)
436
+ end
437
+
433
438
  def self.ssh_connection(server, reset = false)
434
439
  @@ssh_connections ||= {}
435
440
  @@ssh_connections.delete server if reset
@@ -28,6 +28,16 @@ class ProcessFailed < StandardError;
28
28
  end
29
29
  end
30
30
 
31
+ class SSHProcessFailed < StandardError
32
+ attr_accessor :host, :cmd
33
+ def initialize(host, cmd)
34
+ @host = host
35
+ @cmd = cmd
36
+ message = "SSH server #{host} failed cmd '#{cmd}'"
37
+ super(message)
38
+ end
39
+ end
40
+
31
41
  class ConcurrentStreamProcessFailed < ProcessFailed
32
42
  attr_accessor :concurrent_stream
33
43
  def initialize(pid = Process.pid, msg = nil, concurrent_stream = nil)
@@ -27,6 +27,9 @@ module Misc
27
27
 
28
28
  def self.format_paragraph(text, size = 80, indent = 0, offset = 0)
29
29
  i = 0
30
+ offset = 0 if offset.nil?
31
+ indent = 0 if indent.nil?
32
+ size = 80 if size.nil?
30
33
  size = size + offset + indent
31
34
  re = /((?:\n\s*\n\s*)|(?:\n\s*(?=\*)))/
32
35
  text.split(re).collect do |paragraph|
@@ -22,18 +22,21 @@ module IndiferentHash
22
22
  @_default ||= self.default or self.default_proc
23
23
  end
24
24
 
25
- def [](key)
26
- res = super(key)
25
+ def [](key, *args)
26
+ res = super(key, *args)
27
+ res = IndiferentHash.setup(res) if Hash === res
27
28
  return res unless res.nil? or (_default? and not keys.include? key)
28
29
 
29
30
  case key
30
31
  when Symbol, Module
31
- super(key.to_s)
32
+ res = super(key.to_s, *args)
32
33
  when String
33
- super(key.to_sym)
34
- else
35
- res
34
+ res = super(key.to_sym, *args)
36
35
  end
36
+
37
+ res = IndiferentHash.setup(res) if Hash === res
38
+
39
+ res
37
40
  end
38
41
 
39
42
  def values_at(*key_list)
@@ -275,7 +275,6 @@ module Misc
275
275
 
276
276
 
277
277
  def self.step_file?(path)
278
- return true if defined?(Step) && Step === path.resource
279
278
  return false unless path =~ /\.files(?:\/|$)/
280
279
  parts = path.split("/")
281
280
  job = parts.select{|p| p =~ /\.files$/}.first
@@ -283,9 +282,19 @@ module Misc
283
282
  i = parts.index job
284
283
  begin
285
284
  workflow, task = parts.values_at i - 2, i - 1
286
- Workflow.require_workflow workflow
285
+ _loaded = false
286
+ begin
287
+ Kernel.const_get(workflow)
288
+ rescue
289
+ if ! _loaded
290
+ Workflow.require_workflow workflow
291
+ _loaded = true
292
+ retry
293
+ end
294
+ raise $!
295
+ end
287
296
  #return Kernel.const_get(workflow).tasks.include? task.to_sym
288
- return true
297
+ return parts[i-2..-1] * "/"
289
298
  rescue
290
299
  Log.exception $!
291
300
  end
@@ -293,6 +302,8 @@ module Misc
293
302
  false
294
303
  end
295
304
 
305
+ RBBT_IDENTIFY_PATH = ENV["RBBT_IDENTIFY_PATH"] != 'false'
306
+
296
307
  def self.obj2str(obj)
297
308
  _obj = obj
298
309
  obj = Annotated.purge(obj) if Annotated === obj
@@ -310,18 +321,21 @@ module Misc
310
321
  'false'
311
322
  when Hash
312
323
  "{"<< obj.collect{|k,v| obj2str(k) + '=>' << obj2str(v)}*"," << "}"
313
- when (defined?(Path) and Path)
324
+ when (defined?(Path) && Path)
314
325
  if defined?(Step) && Open.exists?(Step.info_file(obj))
315
326
  obj2str(Workflow.load_step(obj))
316
- elsif step_file?(obj)
317
- "Step file: " + obj
327
+ elsif step_file_path = step_file?(obj)
328
+ "Step file: " + step_file_path
318
329
  else
319
330
  if obj.exists?
331
+ obj = (obj.resource || Rbbt).identify obj if RBBT_IDENTIFY_PATH
320
332
  if obj.directory?
321
333
  files = obj.glob("**/*")
322
- "directory: #{Misc.fingerprint(files)}"
323
- else
334
+ "directory: #{Misc.fingerprint(files.collect{|f| Misc.path_relative_to(obj.find, f)}.sort)}"
335
+ elsif obj.located?
324
336
  "file: " << Open.realpath(obj) << "--" << mtime_str(obj)
337
+ else
338
+ "path: " << obj
325
339
  end
326
340
  else
327
341
  obj + " (file missing)"
@@ -111,7 +111,7 @@ module Misc
111
111
  html = if content.nil?
112
112
  "<#{ tag }#{attr_str}/>"
113
113
  else
114
- "<#{ tag }#{attr_str}>#{ content }</#{ tag }>"
114
+ "<#{ tag }#{attr_str}>#{ content.to_s }</#{ tag }>"
115
115
  end
116
116
 
117
117
  html
@@ -318,9 +318,9 @@ module Misc
318
318
  Open.mkdir dir unless Open.exists?(dir)
319
319
  into_path, into = into, Open.open(into, :mode => 'w')
320
320
  end
321
- into.sync = true if IO === into
321
+ #into.sync = true if IO === into
322
322
  into_close = false unless into.respond_to? :close
323
- io.sync = true
323
+ #io.sync = true
324
324
 
325
325
  begin
326
326
  while c = io.readpartial(BLOCK_SIZE)
@@ -402,7 +402,7 @@ module Misc
402
402
  when (IO === content or StringIO === content or File === content)
403
403
 
404
404
  Open.write(tmp_path) do |f|
405
- f.sync = true
405
+ #f.sync = true
406
406
  while block = content.read(BLOCK_SIZE)
407
407
  f.write block
408
408
  end
@@ -240,7 +240,6 @@ module Open
240
240
  end
241
241
 
242
242
  def self.find_repo_dir(file)
243
- return nil
244
243
  self.repository_dirs.each do |dir|
245
244
  dir = dir + '/' unless dir.chars[-1] == "/"
246
245
 
data/lib/rbbt/util/ssh.rb CHANGED
@@ -1,25 +1,118 @@
1
- module RbbtSSH
1
+ require 'net/ssh'
2
2
 
3
- def self.ssh(server, argv = nil, options = {})
4
- server = server.sub(%r(^ssh:(//)?), '')
3
+ class SSHLine
5
4
 
6
- argv = [] if argv.nil?
7
- argv = [argv] unless Array === argv
5
+ def initialize(host, user = nil)
6
+ @host = host
7
+ @user = user
8
8
 
9
- options = Misc.add_defaults options, :add_option_dashes => true
9
+ @ssh = Net::SSH.start(@host, @user)
10
10
 
11
- cmd_sections = [server]
12
- cmd_sections << argv * " "
13
- cmd_sections << CMD.process_cmd_options(options)
11
+ @ch = @ssh.open_channel do |ch|
12
+ ch.exec 'bash'
13
+ end
14
14
 
15
- cmd = cmd_sections.compact * " "
15
+ @ch.on_data do |_,data|
16
+ if m = data.match(/DONECMD: (\d+)\n/)
17
+ @exit_status = m[1].to_i
18
+ @output << data.sub(m[0],'')
19
+ serve_output
20
+ else
21
+ @output << data
22
+ end
23
+ end
16
24
 
17
- CMD.cmd(:ssh, cmd, :pipe => true)
25
+ @ch.on_extended_data do |_,c,err|
26
+ STDERR.write err
27
+ end
18
28
  end
19
29
 
20
- def self.command(server, command, argv = [], options = nil)
21
- ssh(server, [command] + argv, options)
30
+ def send_cmd(command)
31
+ @output = ""
32
+ @complete_output = false
33
+ @ch.send_data(command+"\necho DONECMD: $?\n")
22
34
  end
23
35
 
24
- end
36
+ def serve_output
37
+ @complete_output = true
38
+ end
39
+
40
+ def run(command)
41
+ send_cmd(command)
42
+ @ssh.loop{ ! @complete_output}
43
+ if @exit_status.to_i == 0
44
+ return @output
45
+ else
46
+ raise SSHProcessFailed.new @host, command
47
+ end
48
+ end
49
+
50
+ def ruby(script)
51
+ @output = ""
52
+ @complete_output = false
53
+ cmd = "ruby -e \"#{script.gsub('"','\\"')}\"\n"
54
+ Log.debug "Running ruby on #{@host}:\n#{ script }"
55
+ @ch.send_data(cmd)
56
+ @ch.send_data("echo DONECMD: $?\n")
57
+ @ssh.loop{ !@complete_output }
58
+ if @exit_status.to_i == 0
59
+ return @output
60
+ else
61
+ raise SSHProcessFailed.new @host, "Ruby script:\n#{script}"
62
+ end
63
+ end
64
+
65
+ def rbbt(script)
66
+ rbbt_script =<<-EOF
67
+ require 'rbbt-util'
68
+ require 'rbbt/workflow'
69
+
70
+ res = begin
71
+ old_stdout = STDOUT.dup; STDOUT.reopen(STDERR)
72
+ #{script}
73
+ ensure
74
+ STDOUT.reopen(old_stdout)
75
+ end
76
+
77
+ puts Marshal.dump(res)
78
+ EOF
79
+
80
+ m = ruby(rbbt_script)
81
+ Marshal.load m
82
+ end
83
+
84
+ def workflow(workflow, script)
85
+ preamble =<<-EOF
86
+ wf = Workflow.require_workflow('#{workflow}')
87
+ EOF
88
+
89
+ rbbt(preamble + "\n" + script)
90
+ end
91
+
92
+ @connections = {}
93
+ def self.open(host, user = nil)
94
+ @connections[[host, user]] ||= SSHLine.new host, user
95
+ end
96
+
97
+ def self.run(server, cmd, options = nil)
98
+ cmd = cmd * " " if Array === cmd
99
+ cmd += " " + CMD.process_cmd_options(options) if options
100
+ open(server).run(cmd)
101
+ end
102
+
103
+ def self.ruby(server, script)
104
+ open(server).ruby(script)
105
+ end
25
106
 
107
+ def self.rbbt(server, script)
108
+ open(server).rbbt(script)
109
+ end
110
+
111
+ def self.workflow(server, workflow, script)
112
+ open(server).workflow(workflow, script)
113
+ end
114
+
115
+ def self.command(server, command, argv = [], options = nil)
116
+ run(server, [command] + argv, options)
117
+ end
118
+ end
@@ -99,7 +99,7 @@ STDOUT.write res.to_json
99
99
  JSON.parse(json)
100
100
  end
101
101
 
102
- def self.get_raw(url, params)
102
+ def self.get_raw(url, params = {})
103
103
  server, path = parse_url(url)
104
104
  script = path_script(path)
105
105
 
@@ -193,7 +193,8 @@ job.clean
193
193
  new = Step.migrate(paths, :user, :target => server)
194
194
  files.zip(new).each{|file,new| Open.write(file, new) }
195
195
 
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}")
196
+ SSHLine.run(server, "mkdir -p .rbbt/tmp/tmp-ssh_job_inputs/")
197
+ CMD.cmd_log("scp -r '#{dir}' #{server}:.rbbt/tmp/tmp-ssh_job_inputs/#{input_id}")
197
198
  end
198
199
  end
199
200
  end
@@ -220,21 +221,39 @@ job.clean
220
221
  # rjob.run
221
222
  #end
222
223
 
223
- def self.upload_dependencies(job, server, search_path = 'user', produce_dependencies = false)
224
+ def self.upload_dependencies(job_list, server, search_path = 'user', produce_dependencies = false)
224
225
  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
226
 
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
227
+ job_list = [job_list] unless Array === job_list
228
+
229
+ all_deps = {}
230
+ if produce_dependencies
231
+ job_list.each do |job|
232
+ job.dependencies.each do |dep|
233
+ all_deps[dep] ||= []
234
+ all_deps[dep] << job
235
+ end
236
+ end
237
+ end
238
+
239
+ job_list.each do |job|
240
+ job.input_dependencies.each do |dep|
241
+ all_deps[dep] ||= []
242
+ all_deps[dep] << job
243
+ end
244
+ end
234
245
 
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 }"
246
+ missing_deps = []
247
+ all_deps.each do |dep,jobs|
248
+ next if dep.done?
249
+ Log.medium "Producing #{dep.workflow}:#{dep.short_path} dependency for #{Misc.fingerprint jobs}"
250
+ dep.run(true)
251
+ missing_deps << dep
252
+ end if produce_dependencies
253
+ Step.wait_for_jobs missing_deps
254
+
255
+ migrate_dependencies = all_deps.keys.collect{|d| [d] + d.rec_dependencies + d.input_dependencies }.flatten.select{|d| d.done? }.collect{|d| d.path }
256
+ Log.medium "Migrating #{migrate_dependencies.length} dependencies from #{Misc.fingerprint job_list} to #{ server }"
238
257
  Step.migrate(migrate_dependencies, search_path, :target => server) if migrate_dependencies.any?
239
258
  end
240
259
 
@@ -242,7 +261,6 @@ job.clean
242
261
  inputs = job.inputs.to_hash.slice(*job.real_inputs.map{|i| i.to_s})
243
262
  job.dependencies.each do |dep|
244
263
  next if dep.done?
245
- iif [dep, dep.inputs, dep.real_inputs]
246
264
  inputs = dep.inputs.to_hash.slice(*dep.real_inputs.map{|i| i.to_s}).merge(inputs)
247
265
  inputs = missing_dep_inputs(dep).merge(inputs)
248
266
  end
@@ -279,6 +297,48 @@ job.clean
279
297
  rjob
280
298
  end
281
299
 
300
+ def self.relay_job_list(job_list, server, options = {})
301
+ migrate, produce, produce_dependencies, search_path, run_type, slurm_options = Misc.process_options options.dup,
302
+ :migrate, :produce, :produce_dependencies, :search_path, :run_type, :slurm_options
303
+
304
+ search_path ||= 'user'
305
+
306
+ produce = true if migrate
307
+
308
+ upload_dependencies(job_list, server, search_path, options[:produce_dependencies])
309
+
310
+ rjobs_job = job_list.collect do |job|
311
+
312
+ workflow_name = job.workflow.to_s
313
+ remote_workflow = RemoteWorkflow.new("ssh://#{server}:#{workflow_name}", "#{workflow_name}")
314
+ inputs = IndiferentHash.setup(job.recursive_inputs.to_hash).slice(*job.real_inputs.map{|i| i.to_s})
315
+ Log.medium "Relaying dependency #{job.workflow}:#{job.short_path} to #{server} (#{inputs.keys * ", "})"
316
+
317
+ rjob = remote_workflow.job(job.task_name.to_s, job.clean_name, inputs)
318
+
319
+ override_dependencies = job.rec_dependencies.select{|dep| dep.done? }.collect{|dep| [dep.workflow.to_s, dep.task_name.to_s] * "#" << "=" << Rbbt.identify(dep.path)}
320
+ rjob.override_dependencies = override_dependencies
321
+
322
+ rjob.run_type = run_type
323
+ rjob.slurm_options = slurm_options || {}
324
+
325
+ rjob.run(true)
326
+
327
+ [rjob, job]
328
+ end
329
+
330
+ if options[:migrate]
331
+ rjobs_job.each do |rjob,job|
332
+ rjob.produce
333
+ iif [:migrate, job]
334
+ Step.migrate(Rbbt.identify(job.path), 'user', :source => server)
335
+ end
336
+ end
337
+
338
+ rjobs_job.collect{|p| p.first }
339
+ end
340
+
341
+
282
342
  def self.relay(workflow, task, jobname, inputs, server, options = {})
283
343
  job = workflow.job(task, jobname, inputs)
284
344
  relay_job(job, server, options)