rbbt-util 5.37.16 → 5.38.1

Sign up to get free protection for your applications and to get access to all the features.
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)