rbbt-util 5.26.157 → 5.26.158

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