rbbt-util 5.26.157 → 5.26.158

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: 4a7cb4caf03456a936ef3dab72d6ab957a2f3dce03324291fb10c0f4aabe527f
4
- data.tar.gz: 8690112157a89e64313dc7fcec69c7192db507c210bac57cfd529271294b7174
3
+ metadata.gz: d372a2cf6d424504011197b2b99f84319e79522a5817d3e54d13f61370622b5a
4
+ data.tar.gz: e8914f26719c718b2885394034a688ec4280aaf59a2dca2bad7e4dabfa744b85
5
5
  SHA512:
6
- metadata.gz: 68a347659e479c2cd5fdb09c15bbe09be216586c403f984689c6b72a360cb63f3c4815edabfb675362e4137440e67e5d364da836e4cca33c5ce200a5a7d19539
7
- data.tar.gz: f77fa581d1b0cd8fd6d34f4ac63101a40162f0d7012e2ff9917c7f5b0de729a56f5c81a011528f676aac4cb59a30ebc09acdf47892bfadf1662d41a22ec2959c
6
+ metadata.gz: 4b5bbe62b81fdc7fb024b724d4c6c020839b54cb290d9ffe296f0d9841a233d951c44e0687faefc744cc04884672c10ea5d14ad0b32de9bdacfa152db54e4fbe
7
+ data.tar.gz: 99019bcc2bcedcf1205228277f41324d37731806f608f508a3f8772681c9eb0820e4f860430a1c0b4721f42f199fdf8d2876c8f5e19deac6b2792eb31e234c58
@@ -415,4 +415,11 @@ def self.add_libdir(dir=nil)
415
415
  CMD.cmd("unzip '#{zip_file}' -d '#{dir}'")
416
416
  end
417
417
  end
418
+
419
+
420
+ def self.ssh_run(server, script)
421
+ Log.debug "Run ssh script in #{server}:\n#{script}"
422
+ CMD.cmd("ssh '#{server}' 'shopt -s expand_aliases; bash -l -c \"ruby\"' ", :in => script, :log => true).read
423
+ end
424
+
418
425
  end
@@ -0,0 +1,230 @@
1
+ require 'rest-client'
2
+ class RemoteWorkflow
3
+ module REST
4
+
5
+ def self.__prepare_inputs_for_restclient(inputs)
6
+ inputs.each do |k,v|
7
+ if v.respond_to? :path and not v.respond_to? :original_filename
8
+ class << v
9
+ def original_filename
10
+ File.expand_path(path)
11
+ end
12
+ end
13
+ end
14
+
15
+ if Array === v and v.empty?
16
+ inputs[k] = "EMPTY_ARRAY"
17
+ end
18
+ end
19
+ end
20
+
21
+ def self.encode(url)
22
+ begin
23
+ URI.encode(url)
24
+ rescue
25
+ Log.warn $!.message
26
+ url
27
+ end
28
+ end
29
+
30
+ def self.clean_url(url, params = {})
31
+ params = params.merge({ :_format => 'json', :update => 'clean' })
32
+ params = fix_params params
33
+ res = RemoteWorkflow.capture_exception do
34
+ Misc.insist(2, 0.5) do
35
+ Log.debug{ "RestClient clean: #{ url } - #{Misc.fingerprint params}" }
36
+ res = begin
37
+ RestClient.get(self.encode(url), :params => params)
38
+ rescue RestClient::NotFound
39
+ return nil
40
+ end
41
+ raise TryAgain if res.code == 202
42
+ res
43
+ end
44
+ end
45
+ res
46
+ end
47
+
48
+ def self.get_raw(url, params = {})
49
+ params = params.merge({ :_format => 'raw' })
50
+ params = fix_params params
51
+ res = RemoteWorkflow.capture_exception do
52
+ Misc.insist(2, 0.5) do
53
+ raise "No url" if url.nil?
54
+ Log.debug{ "RestClient get_raw: #{ url } - #{Misc.fingerprint params}" }
55
+ res = RestClient.get(self.encode(url), :params => params)
56
+ raise TryAgain if res.code == 202
57
+ res.to_s
58
+ end
59
+ end
60
+ res
61
+ end
62
+
63
+ def self.get_json(url, params = {})
64
+ Log.debug{ "RestClient get_json: #{ url } - #{Misc.fingerprint params }" }
65
+ params = params.merge({ :_format => 'json' })
66
+ params = RemoteWorkflow.fix_params params
67
+
68
+ res = RemoteWorkflow.capture_exception do
69
+ Misc.insist(2, 0.5) do
70
+ RestClient.get(self.encode(url), :params => params)
71
+ end
72
+ end
73
+
74
+ begin
75
+ JSON.parse(res)
76
+ rescue
77
+ res
78
+ end
79
+ end
80
+
81
+ def self.post_jobname(url, params = {})
82
+ Log.debug{ "RestClient post_jobname: #{ url } - #{Misc.fingerprint params}" }
83
+ params = params.merge({ :_format => 'jobname' })
84
+ params = RemoteWorkflow.fix_params params
85
+
86
+ WorkflowRESTClient.__prepare_inputs_for_restclient(params)
87
+ name = RemoteWorkflow.capture_exception do
88
+ RestClient.post(self.encode(url), params)
89
+ end
90
+
91
+ Log.debug{ "RestClient jobname returned for #{ url } - #{Misc.fingerprint params}: #{name}" }
92
+
93
+ name
94
+ end
95
+
96
+ def self.post_json(url, params = {})
97
+ if url =~ /_cache_type=:exec/
98
+ JSON.parse(Open.open(url, :nocache => true))
99
+ else
100
+ params = params.merge({ :_format => 'json' })
101
+ params = fix_params params
102
+
103
+ res = RemoteWorkflow.capture_exception do
104
+ RestClient.post(self.encode(url), params)
105
+ end
106
+
107
+ begin
108
+ JSON.parse(res)
109
+ rescue
110
+ res
111
+ end
112
+ end
113
+ end
114
+
115
+ def self.task_info(url, task)
116
+ @@task_info ||= {}
117
+
118
+ key = [url, task] * "#"
119
+ @@task_info[key] ||= begin
120
+ task_info = RemoteWorkflow::REST.get_json(File.join(url, task.to_s, 'info'))
121
+ task_info = RemoteWorkflow.fix_hash(task_info)
122
+
123
+ task_info[:result_type] = task_info[:result_type].to_sym
124
+ task_info[:export] = task_info[:export].to_sym
125
+ task_info[:input_types] = RemoteWorkflow.fix_hash(task_info[:input_types], true)
126
+ task_info[:inputs] = task_info[:inputs].collect{|input| input.to_sym }
127
+
128
+ task_info
129
+ end
130
+ end
131
+
132
+ def self.execute_job(base_url, task, task_params, cache_type)
133
+ RemoteWorkflow.capture_exception do
134
+ task_url = URI.encode(File.join(base_url, task.to_s))
135
+
136
+ sout, sin = Misc.pipe
137
+
138
+ post_thread = Thread.new(Thread.current) do |parent|
139
+ bl = lambda do |rok|
140
+ if Net::HTTPOK === rok
141
+ _url = rok["RBBT-STREAMING-JOB-URL"]
142
+ @url = File.join(task_url, File.basename(_url)) if _url
143
+ rok.read_body do |c,_a, _b|
144
+ sin.write c
145
+ end
146
+ sin.close
147
+ else
148
+ err = StringIO.new
149
+ rok.read_body do |c,_a, _b|
150
+ err.write c
151
+ end
152
+ text = begin
153
+ reader = Zlib::GzipReader.new(err)
154
+ reader.read
155
+ rescue
156
+ err.rewind
157
+ err.read
158
+ end
159
+ ne = @adaptor.parse_exception text
160
+ case ne
161
+ when String
162
+ parent.raise e.class, ne
163
+ when Exception
164
+ parent.raise ne
165
+ else
166
+ parent.raise "Error in RestClient: " << rok.message
167
+ end
168
+ end
169
+ end
170
+
171
+ task_params.each do |k,v|
172
+ task_params[k] = v.read if IO === v
173
+ end
174
+
175
+ Log.debug{ "RestClient execute: #{ task_url } - #{Misc.fingerprint task_params}" }
176
+ RestClient::Request.execute(:method => :post, :url => task_url, :payload => task_params, :block_response => bl)
177
+ end
178
+
179
+ # It seems like now response body are now decoded by Net::HTTP after 2.1
180
+ # https://github.com/rest-client/rest-client/blob/cf3e5a115bcdb8f3344aeac0e45b44d67fac1a42/history.md
181
+ decode = Gem.loaded_specs["rest-client"].version < Gem::Version.create('2.1')
182
+ if decode
183
+ reader = Zlib::GzipReader.new(sout)
184
+ res_io = Misc.open_pipe do |sin|
185
+ while c = reader.read(Misc::BLOCK_SIZE)
186
+ sin.write c
187
+ end
188
+ sin.close
189
+ @done = true
190
+ end
191
+ ConcurrentStream.setup(res_io, :threads => [post_thread]) do
192
+ @done = true
193
+ @streaming = false
194
+ end
195
+ else
196
+ ConcurrentStream.setup(sout, :threads => [post_thread]) do
197
+ @done = true
198
+ @streaming = false
199
+ end
200
+ end
201
+
202
+ end
203
+ end
204
+
205
+ def tasks
206
+ @tasks ||= Hash.new do |hash,task_name|
207
+ info = task_info(task_name)
208
+ task = Task.setup info do |*args|
209
+ raise "This is a remote task"
210
+ end
211
+ task.name = task_name.to_sym
212
+ hash[task_name] = task
213
+ end
214
+ end
215
+
216
+
217
+ def task_info(task)
218
+ RemoteWorkflow::REST.task_info(url, task)
219
+ end
220
+
221
+ def init_remote_tasks
222
+ task_exports = RemoteWorkflow::REST.get_json(url)
223
+ @asynchronous_exports = (task_exports["asynchronous"] || []).collect{|task| task.to_sym }
224
+ @synchronous_exports = (task_exports["synchronous"] || []).collect{|task| task.to_sym }
225
+ @exec_exports = (task_exports["exec"] || []).collect{|task| task.to_sym }
226
+ @stream_exports = (task_exports["stream"] || []).collect{|task| task.to_sym }
227
+ @can_stream = task_exports["can_stream"]
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,237 @@
1
+ class RemoteWorkflow
2
+ module SSH
3
+ #def self.run(server, script)
4
+ # Log.debug "Run ssh script in #{server}:\n#{script}"
5
+ # CMD.cmd("ssh '#{server}' 'shopt -s expand_aliases; bash -l -i -c \"ruby\"' ", :in => script, :log => true).read
6
+ #end
7
+
8
+ #def self.run_log(server, script)
9
+ # Log.debug "Run and monitor ssh script in #{server}:\n#{script}"
10
+ # CMD.cmd("ssh '#{server}' 'shopt -s expand_aliases; bash -ic \"ruby\"' ", :in => script, :log => true)
11
+ #end
12
+
13
+ def self.parse_url(url)
14
+ m = url.match(/ssh:\/\/([^:]+):(.*)/)
15
+ server = m.captures[0]
16
+ path = m.captures[1]
17
+ [server, path]
18
+ end
19
+
20
+ def self.path_script(path)
21
+
22
+ workflow, task, job, *rest = path.split("/")
23
+
24
+ script =<<-EOF
25
+ require 'rbbt/workflow'
26
+ wf = Workflow.require_workflow "#{workflow}"
27
+ EOF
28
+
29
+ case task
30
+ when nil
31
+ script +=<<-EOF
32
+ task_info = {}
33
+ wf.tasks.keys.each do |task|
34
+ task_info[task] = wf.task_info(task)
35
+ end
36
+ res = task_info
37
+ EOF
38
+ when 'documentation'
39
+ script +=<<-EOF
40
+ res = documentation = wf.documentation
41
+ EOF
42
+ else
43
+ if job.nil?
44
+ script +=<<-EOF
45
+ task = '#{task}'
46
+ res = task_info = wf.task_info(task)
47
+ EOF
48
+ else
49
+ case rest.first
50
+ when nil
51
+ script +=<<-EOF
52
+ task = '#{task}'
53
+ jobname = '#{job}'
54
+ res = job = wf.fast_load_id(File.join(task, jobname))
55
+ EOF
56
+ when "info"
57
+ script +=<<-EOF
58
+ task = '#{task}'
59
+ jobname = '#{job}'
60
+ job = wf.fast_load_id(File.join(task, jobname))
61
+ res = job_info = job.info
62
+ EOF
63
+ else
64
+ raise "Unkown path: #{[path, rest].inspect}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ def self.job_script(inputs_id, jobname = nil)
71
+ script =<<-EOF
72
+ jobname = #{jobname.nil? ? 'nil' : "'#{jobname}'"}
73
+ path = File.join(ENV["HOME"], '.rbbt/tmp/tmp-ssh_job_inputs/#{inputs_id}')
74
+ job_inputs = Workflow.load_inputs(path, task_info[:inputs], task_info[:input_types])
75
+ job = wf.job(task, jobname, job_inputs)
76
+ EOF
77
+ script
78
+ end
79
+
80
+ def self.get_json(url, params = {})
81
+ server, path = parse_url(url)
82
+ script = path_script(path)
83
+
84
+ script +=<<-EOF
85
+ STDOUT.write res.to_json
86
+ EOF
87
+
88
+ JSON.parse(Misc.ssh_run(server, script))
89
+ end
90
+
91
+ def self.get_raw(url, params)
92
+ server, path = parse_url(url)
93
+ script = path_script(path)
94
+
95
+ script +=<<-EOF
96
+ STDOUT.write res
97
+ EOF
98
+
99
+ Misc.ssh_run(server, script)
100
+ end
101
+
102
+ def self.post_job(url, inputs_id, jobname = nil)
103
+ server, path = parse_url(url)
104
+
105
+ script = path_script(path)
106
+ script += job_script(inputs_id, jobname)
107
+ script +=<<-EOF
108
+ job.init_info
109
+ STDOUT.write job.name
110
+ EOF
111
+ @name = Misc.ssh_run(server, script)
112
+ end
113
+
114
+ def self.run_job(url, input_id, jobname = nil)
115
+ server, path = parse_url(url)
116
+
117
+ script = path_script(path)
118
+ script += job_script(input_id, jobname)
119
+ script +=<<-EOF
120
+ job.produce
121
+ STDOUT.write job.path
122
+ EOF
123
+ Misc.ssh_run(server, script)
124
+ end
125
+
126
+ def self.run_slurm_job(url, input_id, jobname = nil)
127
+ server, path = parse_url(url)
128
+
129
+ script = path_script(path)
130
+ script += job_script(input_id, jobname)
131
+ script +=<<-EOF
132
+ job.produce
133
+ STDOUT.write job.path
134
+ EOF
135
+ Misc.ssh_run(server, script)
136
+ end
137
+
138
+ def self.clean(url, input_id, jobname = nil)
139
+ server, path = parse_url(url)
140
+
141
+ script = path_script(path)
142
+ script +=<<-EOF
143
+ job.clean
144
+ EOF
145
+ Misc.ssh_run(server, script)
146
+ end
147
+
148
+ def self.upload_inputs(server, inputs, input_types, input_id)
149
+ TmpFile.with_file do |dir|
150
+ if Step.save_inputs(inputs, input_types, dir)
151
+ CMD.cmd("ssh '#{server}' mkdir -p .rbbt/tmp/tmp-ssh_job_inputs/; scp -r '#{dir}' #{server}:.rbbt/tmp/tmp-ssh_job_inputs/#{input_id}")
152
+ end
153
+ end
154
+ end
155
+
156
+ def self.relay(workflow, task, jobname, inputs, server, options = {})
157
+ options = Misc.add_defaults options, :search_path => 'user'
158
+ search_path = options[:search_path]
159
+
160
+ job = workflow.job(task, jobname, inputs)
161
+
162
+ job.dependencies.each do |dep|
163
+ dep.produce
164
+ end
165
+
166
+ override_dependencies = job.dependencies.collect{|dep| [dep.workflow.to_s, dep.task_name.to_s] * "#" << "=" << Rbbt.identify(dep.path)}
167
+
168
+ job.dependencies.each do |dep|
169
+ Step.migrate(dep.path, search_path, :target => server)
170
+ end
171
+
172
+ remote = RemoteWorkflow.new("ssh://#{server}:#{workflow.to_s}", "#{workflow.to_s}")
173
+ rjob = remote.job(task, jobname, {})
174
+ rjob.override_dependencies = override_dependencies
175
+ rjob.run
176
+ end
177
+
178
+ def workflow_description
179
+ RemoteWorkflow::SSH.get_raw(File.join(url, 'description'))
180
+ end
181
+
182
+ def documentation
183
+ @documention ||= IndiferentHash.setup(RemoteWorkflow::SSH.get_json(File.join(url, "documentation")))
184
+ @documention
185
+ end
186
+
187
+ def task_info(task)
188
+ @task_info ||= IndiferentHash.setup({})
189
+
190
+ if @task_info[task].nil?
191
+ task_info = RemoteWorkflow::SSH.get_json(File.join(@base_url, task.to_s))
192
+ task_info = RemoteWorkflow::SSH.fix_hash(task_info)
193
+
194
+ task_info[:result_type] = task_info[:result_type].to_sym if task_info[:result_type]
195
+ task_info[:export] = task_info[:export].to_sym if task_info[:export]
196
+ task_info[:input_types] = RemoteWorkflow::SSH.fix_hash(task_info[:input_types], true)
197
+ task_info[:inputs] = task_info[:inputs].collect{|input| input.to_sym }
198
+
199
+ @task_info[task] = IndiferentHash.setup(task_info)
200
+ end
201
+
202
+ IndiferentHash.setup(@task_info[task])
203
+ end
204
+
205
+ def tasks
206
+ @tasks ||= Hash.new do |hash,task_name|
207
+ raise Workflow::TaskNotFoundException, "Task #{task_name} not found in workflow #{self.to_s}" unless @task_info.include?(task_name)
208
+ info = @task_info[task_name]
209
+ task = Task.setup info do |*args|
210
+ raise "This is a remote task"
211
+ end
212
+ task.name = task_name.to_sym
213
+ hash[task_name] = task
214
+ end
215
+ end
216
+
217
+ def load_tasks
218
+ @task_info.keys.each{|name| tasks[name]}
219
+ end
220
+
221
+ def task_dependencies
222
+ @task_dependencies ||= Hash.new do |hash,task|
223
+ hash[task] = if exported_tasks.include? task
224
+ RemoteWorkflow::SSH.get_json(File.join(url, task.to_s, 'dependencies'))
225
+ else
226
+ []
227
+ end
228
+ end
229
+ end
230
+
231
+ def init_remote_tasks
232
+ @task_info = IndiferentHash.setup(RemoteWorkflow::SSH.get_json(url))
233
+ @exec_exports = @stream_exports = @synchronous_exports = []
234
+ @asynchronous_exports = @task_info.keys
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,75 @@
1
+ require 'rbbt/workflow/remote_workflow/driver/rest'
2
+ require 'rbbt/workflow/remote_workflow/driver/ssh'
3
+
4
+ class RemoteWorkflow
5
+ def self.fix_hash(hash, fix_values = false)
6
+ fixed = {}
7
+ hash.each do |key, value|
8
+ fixed[key.to_sym] = case value
9
+ when TrueClass
10
+ value
11
+ when FalseClass
12
+ value
13
+ when Hash
14
+ fix_hash(value)
15
+ when (fix_values and String )
16
+ value.to_sym
17
+ when IO
18
+ value.read
19
+ when TSV::Dumper
20
+ value.stream
21
+ when Step
22
+ stream = get_stream(value)
23
+ stream || value.load
24
+ else
25
+ value
26
+ end
27
+ end
28
+ fixed
29
+ end
30
+
31
+ def self.parse_exception(text)
32
+ klass, message = text.split " => "
33
+ begin
34
+ klass = Kernel.const_get klass
35
+ return klass.new message
36
+ rescue
37
+ message
38
+ end
39
+ end
40
+
41
+ def self.capture_exception
42
+ begin
43
+ yield
44
+ rescue Exception => e
45
+ raise e unless e.respond_to? :response
46
+ begin
47
+ ne = parse_exception e.response.to_s
48
+ case ne
49
+ when String
50
+ raise e.class, ne
51
+ when Exception
52
+ raise ne
53
+ else
54
+ raise
55
+ end
56
+ rescue
57
+ raise e
58
+ end
59
+ raise $!
60
+ end
61
+ end
62
+
63
+ def self.fix_params(params)
64
+ new_params = {}
65
+ params.each do |k,v|
66
+ if Array === v and v.empty?
67
+ new_params[k] = "EMPTY_ARRAY"
68
+ else
69
+ new_params[k] = v
70
+ end
71
+ end
72
+ new_params
73
+ end
74
+
75
+ end