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