rbbt-util 5.37.16 → 5.38.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: 565fc431e80f7167a95a8ad2c3499207c1422dacfe7ce1cd4cd2c1a4f0bad5c4
4
- data.tar.gz: 34b393012cd4d58bcb9917cb2c45f8e0f23cefca73139647454b6affcb80cf57
3
+ metadata.gz: df9e238ba28f6bc0e16d1a22d8c9c34f7b57334f795ad9c4104ee1e689bc8fd5
4
+ data.tar.gz: 61cc2c91be510161edde982567750f13abb2388cf624e15ca1e4c72633382a3b
5
5
  SHA512:
6
- metadata.gz: 854c7a84a92a654a987f5d1274dabd6d7564108d8a200eeb377321aa02361264d979f42f8e91350be2ed0074bd0f153b6dd66d7ea5f4597edca5b68edf2d8c0a
7
- data.tar.gz: 34c765a22fed51dfaf873a0abcd0338f2a9ab15ed09bca0ac445075261ea040002e042312e62951c951bfc48d08162175cf697f9890e5b1a0c3ac58f2b0a03d0
6
+ metadata.gz: a3528b56bfc0faebf62c701fcc637a43af8ffe0639a795859f56dca57749b60a3cd821941b7e7bf079cca27a9a8aa8d9d69f71235b41c1ff48e73b4292a31683
7
+ data.tar.gz: dd71f1e7b233fb46d5e1210466bd6f64b0dd9162633d475f7e38bc8a057c853411bf27c35ffb3ce935f10b6e8d0055efdd7f5822adf09ff8f95a0bfa0efe8971
@@ -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
 
@@ -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
@@ -423,13 +423,20 @@ 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
+ Log.debug "Run ssh script in #{server}:\n#{script}"
436
+
437
+ SSHLine.ruby(server, script)
438
+ end
439
+
433
440
  def self.ssh_connection(server, reset = false)
434
441
  @@ssh_connections ||= {}
435
442
  @@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)
data/lib/rbbt/util/ssh.rb CHANGED
@@ -1,25 +1,76 @@
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 cmd(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
+ @ch.send_data(cmd)
55
+ @ch.send_data("echo DONECMD: $?\n")
56
+ @ssh.loop{ !@complete_output }
57
+ if @exit_status.to_i == 0
58
+ return @output
59
+ else
60
+ raise SSHProcessFailed.new @host, "Ruby script:\n#{script}"
61
+ end
62
+ end
25
63
 
64
+ @connections = {}
65
+ def self.open(host, user = nil)
66
+ @connections[[host, user]] ||= SSHLine.new host, user
67
+ end
68
+
69
+ def self.run(server, cmd)
70
+ open(server).cmd(cmd)
71
+ end
72
+
73
+ def self.ruby(server, script)
74
+ open(server).ruby(script)
75
+ end
76
+ end
@@ -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,40 @@ 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
227
+ job_list = [job] 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
245
+ iif all_deps
246
+
247
+ missing_deps = []
248
+ all_deps.each do |dep,jobs|
249
+ next if dep.done?
250
+ Log.medium "Producing #{dep.workflow}:#{dep.short_path} dependency for #{Misc.fingerprint jobs}"
251
+ dep.run(true)
252
+ missing_deps << dep
233
253
  end
254
+ Step.wait_for_jobs missing_deps
234
255
 
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 }"
256
+ migrate_dependencies = all_deps.keys.select{|d| d.done? }.collect{|d| d.path }
257
+ Log.medium "Migrating #{migrate_dependencies.length} dependencies from #{Misc.fingerprint job_list} to #{ server }"
238
258
  Step.migrate(migrate_dependencies, search_path, :target => server) if migrate_dependencies.any?
239
259
  end
240
260
 
@@ -242,7 +262,6 @@ job.clean
242
262
  inputs = job.inputs.to_hash.slice(*job.real_inputs.map{|i| i.to_s})
243
263
  job.dependencies.each do |dep|
244
264
  next if dep.done?
245
- iif [dep, dep.inputs, dep.real_inputs]
246
265
  inputs = dep.inputs.to_hash.slice(*dep.real_inputs.map{|i| i.to_s}).merge(inputs)
247
266
  inputs = missing_dep_inputs(dep).merge(inputs)
248
267
  end
@@ -279,6 +298,47 @@ job.clean
279
298
  rjob
280
299
  end
281
300
 
301
+ def self.relay_job_list(job_list, server, options = {})
302
+ migrate, produce, produce_dependencies, search_path, run_type, slurm_options = Misc.process_options options.dup,
303
+ :migrate, :produce, :produce_dependencies, :search_path, :run_type, :slurm_options
304
+
305
+ search_path ||= 'user'
306
+
307
+ produce = true if migrate
308
+
309
+ upload_dependencies(job_list, server, search_path, options[:produce_dependencies])
310
+
311
+ rjobs_job = job_list.collect do |job|
312
+
313
+ workflow_name = job.workflow.to_s
314
+ remote_workflow = RemoteWorkflow.new("ssh://#{server}:#{workflow_name}", "#{workflow_name}")
315
+ inputs = job.recursive_inputs.to_hash.slice(*job.real_inputs.map{|i| i.to_s})
316
+ Log.medium "Relaying dependency #{job.workflow}:#{job.short_path} to #{server} (#{inputs.keys * ", "})"
317
+
318
+ rjob = remote_workflow.job(job.task_name.to_s, job.clean_name, inputs)
319
+
320
+ override_dependencies = job.rec_dependencies.select{|dep| dep.done? }.collect{|dep| [dep.workflow.to_s, dep.task_name.to_s] * "#" << "=" << Rbbt.identify(dep.path)}
321
+ rjob.override_dependencies = override_dependencies
322
+
323
+ rjob.run_type = run_type
324
+ rjob.slurm_options = slurm_options || {}
325
+
326
+ rjob.run(true)
327
+
328
+ [rjob, job]
329
+ end
330
+
331
+ if options[:migrate]
332
+ rjobs_job.each do |rjob,job|
333
+ rjob.produce
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)
@@ -64,7 +64,7 @@ class RemoteStep
64
64
  RemoteWorkflow::SSH.orchestrate_slurm_job(File.join(base_url, task.to_s), @input_id, @base_name, @slurm_options || {})
65
65
  end
66
66
 
67
- def produce(*args)
67
+ def issue
68
68
  input_types = {}
69
69
  init_job
70
70
  @remote_path = case @run_type
@@ -76,6 +76,10 @@ class RemoteStep
76
76
  _orchestrate_slurm
77
77
  end
78
78
  @started = true
79
+ end
80
+
81
+ def produce(*args)
82
+ issue
79
83
  while ! (done? || error? || aborted?)
80
84
  sleep 1
81
85
  end
@@ -87,9 +91,13 @@ class RemoteStep
87
91
  load_res Open.open(path)
88
92
  end
89
93
 
90
- def run(*args)
91
- produce(*args)
92
- self.load unless args.first
94
+ def run(stream = nil)
95
+ if stream
96
+ issue
97
+ else
98
+ produce
99
+ self.load unless args.first
100
+ end
93
101
  end
94
102
 
95
103
  def clean
@@ -247,7 +247,7 @@ class Step
247
247
  @exec = false
248
248
  init_info(true)
249
249
 
250
- workflow = @workflow || @task.respond_to?(:workflow) ? @task.workflow : nil
250
+ #workflow = @workflow || @task.respond_to?(:workflow) ? @task.workflow : nil
251
251
  result_type = @task.respond_to?(:result_type) ? @task.result_type : nil
252
252
  result_description = @task.respond_to?(:result_description) ? @task.result_description : nil
253
253
 
@@ -67,9 +67,9 @@ module Workflow
67
67
  inputs[input.to_sym] = steps.first
68
68
  when :step_file
69
69
  path = Open.read(file).strip
70
- path.extend Path
71
- step_path = path.match(/(.*)\.files/)[1]
72
- path.resource = Step.new step_path
70
+ step_path, relative = path.match(/(.*)\.files\/(.*)/).values_at 1, 2
71
+ step = Step.new Path.setup(step_path).find
72
+ path = step.file(relative)
73
73
  inputs[input.to_sym] = path
74
74
  when :step_file_array
75
75
  paths = Open.read(file).split("\n")
@@ -149,6 +149,8 @@ class Step
149
149
  case value
150
150
  when Path
151
151
  if Step === value.resource
152
+ step = value.resource
153
+ value = File.join('var/jobs', step.workflow.to_s, step.short_path + '.files', Misc.path_relative_to(step.files_dir, value))
152
154
  path = path + '.as_step_file'
153
155
  else
154
156
  path = path + '.as_path'
@@ -57,7 +57,7 @@ class Step
57
57
  end
58
58
 
59
59
  def workflow
60
- @workflow || info[:workflow]
60
+ @workflow || info[:workflow] || (@task && @task.respond_to?(:workflow) && @task.workflow) || path.split("/")[-3]
61
61
  end
62
62
 
63
63
 
@@ -128,7 +128,7 @@ module Workflow
128
128
  break if child
129
129
 
130
130
  if child
131
- description << "-> " << task_name.to_s
131
+ description << "->" << task_name.to_s
132
132
  elsif first
133
133
  description << "" << task_name.to_s
134
134
  else
@@ -201,10 +201,10 @@ module Workflow
201
201
  description = description.split("\n\n").first
202
202
 
203
203
  next if abridge && ! final.include?(name)
204
- puts Misc.format_definition_list_item(name.to_s, description, Log.tty_size || 80, 20, :yellow)
204
+ puts Misc.format_definition_list_item(name.to_s, description, Log.tty_size || 80, 30, :yellow)
205
205
 
206
206
  prov_string = prov_string(dep_tree(name))
207
- puts Misc.format_paragraph Log.color(:blue, "-> " + prov_string) if prov_string && ! prov_string.empty?
207
+ puts Misc.format_paragraph Log.color(:blue, "->" + prov_string) if prov_string && ! prov_string.empty?
208
208
  end
209
209
 
210
210
  else