rbbt-util 5.37.16 → 5.38.0

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.
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