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 +4 -4
- data/lib/rbbt/hpc/batch.rb +15 -6
- data/lib/rbbt/hpc/orchestrate.rb +1 -0
- data/lib/rbbt/resource/path.rb +1 -0
- data/lib/rbbt/resource/rake.rb +1 -1
- data/lib/rbbt/util/misc/development.rb +8 -1
- data/lib/rbbt/util/misc/exceptions.rb +10 -0
- data/lib/rbbt/util/ssh.rb +65 -14
- data/lib/rbbt/workflow/remote_workflow/driver/ssh.rb +73 -13
- data/lib/rbbt/workflow/remote_workflow/remote_step/ssh.rb +12 -4
- data/lib/rbbt/workflow/step/run.rb +1 -1
- data/lib/rbbt/workflow/step/save_load_inputs.rb +5 -3
- data/lib/rbbt/workflow/step.rb +1 -1
- data/lib/rbbt/workflow/usage.rb +3 -3
- data/share/rbbt_commands/hpc/list +293 -293
- data/share/rbbt_commands/lsf/list +293 -293
- data/share/rbbt_commands/resource/find +1 -0
- data/share/rbbt_commands/slurm/list +293 -293
- data/share/rbbt_commands/workflow/task +9 -14
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df9e238ba28f6bc0e16d1a22d8c9c34f7b57334f795ad9c4104ee1e689bc8fd5
|
4
|
+
data.tar.gz: 61cc2c91be510161edde982567750f13abb2388cf624e15ca1e4c72633382a3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3528b56bfc0faebf62c701fcc637a43af8ffe0639a795859f56dca57749b60a3cd821941b7e7bf079cca27a9a8aa8d9d69f71235b41c1ff48e73b4292a31683
|
7
|
+
data.tar.gz: dd71f1e7b233fb46d5e1210466bd6f64b0dd9162633d475f7e38bc8a057c853411bf27c35ffb3ce935f10b6e8d0055efdd7f5822adf09ff8f95a0bfa0efe8971
|
data/lib/rbbt/hpc/batch.rb
CHANGED
@@ -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
|
-
|
574
|
-
|
575
|
-
|
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
|
data/lib/rbbt/hpc/orchestrate.rb
CHANGED
data/lib/rbbt/resource/path.rb
CHANGED
data/lib/rbbt/resource/rake.rb
CHANGED
@@ -423,13 +423,20 @@ def self.add_libdir(dir=nil)
|
|
423
423
|
end
|
424
424
|
end
|
425
425
|
|
426
|
-
def self.
|
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
|
-
|
1
|
+
require 'net/ssh'
|
2
2
|
|
3
|
-
|
4
|
-
server = server.sub(%r(^ssh:(//)?), '')
|
3
|
+
class SSHLine
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(host, user = nil)
|
6
|
+
@host = host
|
7
|
+
@user = user
|
8
8
|
|
9
|
-
|
9
|
+
@ssh = Net::SSH.start(@host, @user)
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
@ch = @ssh.open_channel do |ch|
|
12
|
+
ch.exec 'bash'
|
13
|
+
end
|
14
14
|
|
15
|
-
|
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
|
-
|
25
|
+
@ch.on_extended_data do |_,c,err|
|
26
|
+
STDERR.write err
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
|
-
def
|
21
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
231
|
-
|
232
|
-
|
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 =
|
236
|
-
migrate_dependencies
|
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
|
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(
|
91
|
-
|
92
|
-
|
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.
|
71
|
-
|
72
|
-
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'
|
data/lib/rbbt/workflow/step.rb
CHANGED
data/lib/rbbt/workflow/usage.rb
CHANGED
@@ -128,7 +128,7 @@ module Workflow
|
|
128
128
|
break if child
|
129
129
|
|
130
130
|
if child
|
131
|
-
description << "->
|
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,
|
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, "->
|
207
|
+
puts Misc.format_paragraph Log.color(:blue, "->" + prov_string) if prov_string && ! prov_string.empty?
|
208
208
|
end
|
209
209
|
|
210
210
|
else
|