rbbt-util 5.26.77 → 5.26.78

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.
@@ -0,0 +1,305 @@
1
+ module WorkflowRESTClient
2
+ def self.encode(url)
3
+ begin
4
+ URI.encode(url)
5
+ rescue
6
+ Log.warn $!.message
7
+ url
8
+ end
9
+ end
10
+
11
+ def self.fix_hash(hash, fix_values = false)
12
+ fixed = {}
13
+ hash.each do |key, value|
14
+ fixed[key.to_sym] = case value
15
+ when TrueClass
16
+ value
17
+ when FalseClass
18
+ value
19
+ when Hash
20
+ fix_hash(value)
21
+ when (fix_values and String )
22
+ value.to_sym
23
+ when IO
24
+ value.read
25
+ when TSV::Dumper
26
+ value.stream
27
+ when Step
28
+ stream = get_stream(value)
29
+ stream || value.load
30
+ else
31
+ value
32
+ end
33
+ end
34
+ fixed
35
+ end
36
+
37
+ def self.parse_exception(text)
38
+ klass, message = text.split " => "
39
+ begin
40
+ klass = Kernel.const_get klass
41
+ return klass.new message
42
+ rescue
43
+ message
44
+ end
45
+ end
46
+
47
+ def self.capture_exception
48
+ begin
49
+ yield
50
+ rescue Exception => e
51
+ raise e unless e.respond_to? :response
52
+ begin
53
+ ne = parse_exception e.response.to_s
54
+ case ne
55
+ when String
56
+ raise e.class, ne
57
+ when Exception
58
+ raise ne
59
+ else
60
+ raise
61
+ end
62
+ rescue
63
+ raise e
64
+ end
65
+ raise $!
66
+ end
67
+ end
68
+
69
+ def self.fix_params(params)
70
+ new_params = {}
71
+ params.each do |k,v|
72
+ if Array === v and v.empty?
73
+ new_params[k] = "EMPTY_ARRAY"
74
+ else
75
+ new_params[k] = v
76
+ end
77
+ end
78
+ new_params
79
+ end
80
+
81
+ def self.clean_url(url, params = {})
82
+ params = params.merge({ :_format => 'json', :update => 'clean' })
83
+ params = fix_params params
84
+ res = capture_exception do
85
+ Misc.insist(2, 0.5) do
86
+ Log.debug{ "RestClient clean: #{ url } - #{Misc.fingerprint params}" }
87
+ res = begin
88
+ RestClient.get(self.encode(url), :params => params)
89
+ rescue RestClient::NotFound
90
+ return nil
91
+ end
92
+ raise TryAgain if res.code == 202
93
+ res
94
+ end
95
+ end
96
+ res
97
+ end
98
+
99
+ def self.get_raw(url, params = {})
100
+ params = params.merge({ :_format => 'raw' })
101
+ params = fix_params params
102
+ res = capture_exception do
103
+ Misc.insist(2, 0.5) do
104
+ raise "No url" if url.nil?
105
+ Log.debug{ "RestClient get_raw: #{ url } - #{Misc.fingerprint params}" }
106
+ res = RestClient.get(self.encode(url), :params => params)
107
+ raise TryAgain if res.code == 202
108
+ res.to_s
109
+ end
110
+ end
111
+ res
112
+ end
113
+
114
+ def self.get_json(url, params = {})
115
+ Log.debug{ "RestClient get_json: #{ url } - #{Misc.fingerprint params }" }
116
+ params = params.merge({ :_format => 'json' })
117
+ params = fix_params params
118
+
119
+ res = capture_exception do
120
+ Misc.insist(2, 0.5) do
121
+ RestClient.get(self.encode(url), :params => params)
122
+ end
123
+ end
124
+
125
+ begin
126
+ JSON.parse(res)
127
+ rescue
128
+ res
129
+ end
130
+ end
131
+
132
+ def self.post_jobname(url, params = {})
133
+ Log.debug{ "RestClient post_jobname: #{ url } - #{Misc.fingerprint params}" }
134
+ params = params.merge({ :_format => 'jobname' })
135
+ params = fix_params params
136
+
137
+ WorkflowRESTClient.__prepare_inputs_for_restclient(params)
138
+ name = capture_exception do
139
+ RestClient.post(self.encode(url), params)
140
+ end
141
+
142
+ Log.debug{ "RestClient jobname returned for #{ url } - #{Misc.fingerprint params}: #{name}" }
143
+
144
+ name
145
+ end
146
+
147
+ def self.post_json(url, params = {})
148
+ if url =~ /_cache_type=:exec/
149
+ JSON.parse(Open.open(url, :nocache => true))
150
+ else
151
+ params = params.merge({ :_format => 'json' })
152
+ params = fix_params params
153
+
154
+ res = capture_exception do
155
+ RestClient.post(self.encode(url), params)
156
+ end
157
+
158
+ begin
159
+ JSON.parse(res)
160
+ rescue
161
+ res
162
+ end
163
+ end
164
+ end
165
+
166
+ def get
167
+ params ||= {}
168
+ params = params.merge(:_format => [:string, :boolean, :tsv, :annotations,:array].include?(result_type.to_sym) ? :raw : :json )
169
+ Misc.insist 3, rand(2) + 1 do
170
+ begin
171
+ init_job if url.nil?
172
+ @adaptor.get_raw(url, params)
173
+ rescue
174
+ Log.exception $!
175
+ raise $!
176
+ end
177
+ end
178
+ end
179
+
180
+ def load
181
+ params = {}
182
+ join unless done? or streaming?
183
+ raise get_exception if error? or aborted?
184
+ load_res get
185
+ end
186
+
187
+ def exec_job
188
+ res = _run_job(:exec)
189
+ load_res res, result_type == :array ? :json : result_type
190
+ end
191
+
192
+ def abort
193
+ return self if status == :done
194
+ @adaptor.get_json(@url + '?_update=abort') if @url and @name
195
+ self
196
+ end
197
+
198
+ def init_job(cache_type = nil, other_params = {})
199
+ cache_type = :asynchronous if cache_type.nil? and not @is_exec
200
+ cache_type = :exec if cache_type.nil?
201
+ @last_info_time = nil
202
+ @done = false
203
+ get_streams
204
+ @name ||= Persist.memory("RemoteSteps", :workflow => self, :task => task, :jobname => @name, :inputs => inputs, :cache_type => cache_type) do
205
+ Misc.insist do
206
+ @adaptor.post_jobname(File.join(base_url, task.to_s), inputs.merge(other_params).merge(:jobname => @name||@base_name, :_cache_type => cache_type))
207
+ end
208
+ end
209
+ if Open.remote? @name
210
+ @url = @name
211
+ @name = File.basename(@name)
212
+ else
213
+ @url = File.join(base_url, task.to_s, @name)
214
+ end
215
+ self
216
+ end
217
+
218
+ def recursive_clean
219
+ Log.warn "Not doing recursive cleans"
220
+ return
221
+ begin
222
+ _restart
223
+ params = {:_update => :recursive_clean}
224
+ @adaptor.get_raw(url, params)
225
+ rescue Exception
226
+ Log.exception $!
227
+ end
228
+ self
229
+ end
230
+
231
+ def _clean
232
+ begin
233
+ _restart
234
+ params = {:_update => :clean}
235
+ @adaptor.clean_url(url, params) if @url
236
+ rescue Exception
237
+ Log.exception $!
238
+ end
239
+ end
240
+
241
+ def clean
242
+ init_job
243
+ _clean
244
+ self
245
+ end
246
+
247
+ def stream_job(task_url, task_params, stream_input, cache_type = :exec)
248
+ require 'rbbt/util/misc/multipart_payload'
249
+ WorkflowRESTClient.capture_exception do
250
+ @streaming = true
251
+
252
+ Log.debug{ "RestClient stream #{Process.pid}: #{ task_url } #{stream_input} #{cache_type} - #{Misc.fingerprint task_params}" }
253
+ res = RbbtMutiplartPayload.issue task_url, task_params, stream_input, nil, nil, true
254
+ type = res.gets
255
+
256
+ out = case type.strip
257
+ when "LOCATION"
258
+ @url = res.gets
259
+ @url.sub!(/\?.*/,'')
260
+ join
261
+ WorkflowRESTClient.get_raw(@url)
262
+ @done = true
263
+ @streaming = false
264
+ when /STREAM: (.*)/
265
+ @url = $1.strip
266
+ res.callback = Proc.new do
267
+ Log.medium "Done streaming result from #{@url}"
268
+ @done = true
269
+ @streaming = false
270
+ end
271
+ res
272
+ when "BULK"
273
+ begin
274
+ res.read
275
+ ensure
276
+ @done = true
277
+ @streaming = false
278
+ end
279
+ else
280
+ raise "What? " + type
281
+ end
282
+
283
+ ConcurrentStream.setup(out, :filename => @url)
284
+
285
+ out
286
+ end
287
+ end
288
+
289
+ def _run_job(cache_type = :async)
290
+ get_streams
291
+
292
+ task_url = URI.encode(File.join(base_url, task.to_s))
293
+ @adaptor.__prepare_inputs_for_restclient(inputs)
294
+ task_params = inputs.merge(:_cache_type => cache_type, :jobname => base_name, :_format => [:string, :boolean, :tsv, :annotations].include?(result_type) ? :raw : :json)
295
+
296
+ if cache_type == :stream or cache_type == :exec and stream_input and inputs[stream_input]
297
+ io = self.stream_job(task_url, task_params, stream_input, cache_type)
298
+ return io
299
+ else
300
+ @adaptor.execute_job(base_url, task, task_params, cache_type)
301
+ end
302
+
303
+ end
304
+
305
+ end
@@ -0,0 +1,78 @@
1
+ module WorkflowSSHClient
2
+ def self.__prepare_inputs_for_restclient(inputs)
3
+ inputs.each do |k,v|
4
+ if v.respond_to? :path and not v.respond_to? :original_filename
5
+ class << v
6
+ def original_filename
7
+ File.expand_path(path)
8
+ end
9
+ end
10
+ end
11
+
12
+ if Array === v and v.empty?
13
+ inputs[k] = "EMPTY_ARRAY"
14
+ end
15
+ end
16
+ end
17
+
18
+ def workflow_description
19
+ WorkflowSSHClient.get_raw(File.join(url, 'description'))
20
+ end
21
+
22
+ def documentation
23
+ @documention ||= IndiferentHash.setup(WorkflowSSHClient.get_json(File.join(url, "documentation")))
24
+ @documention
25
+ end
26
+
27
+ def task_info(task)
28
+ @task_info ||= IndiferentHash.setup({})
29
+ @task_info[task]
30
+
31
+ if @task_info[task].nil?
32
+ task_info = WorkflowSSHClient.get_json(File.join(url, task.to_s, 'info'))
33
+ task_info = WorkflowSSHClient.fix_hash(task_info)
34
+
35
+ task_info[:result_type] = task_info[:result_type].to_sym
36
+ task_info[:export] = task_info[:export].to_sym
37
+ task_info[:input_types] = WorkflowSSHClient.fix_hash(task_info[:input_types], true)
38
+ task_info[:inputs] = task_info[:inputs].collect{|input| input.to_sym }
39
+
40
+ @task_info[task] = task_info
41
+ end
42
+ @task_info[task]
43
+ end
44
+
45
+ def tasks
46
+ @tasks ||= Hash.new do |hash,task_name|
47
+ info = @task_info[task_name]
48
+ task = Task.setup info do |*args|
49
+ raise "This is a remote task"
50
+ end
51
+ task.name = task_name.to_sym
52
+ hash[task_name] = task
53
+ end
54
+ end
55
+
56
+ def load_tasks
57
+ @task_info.keys.each{|name| tasks[name]}
58
+ end
59
+
60
+ def task_dependencies
61
+ @task_dependencies ||= Hash.new do |hash,task|
62
+ hash[task] = if exported_tasks.include? task
63
+ WorkflowSSHClient.get_json(File.join(url, task.to_s, 'dependencies'))
64
+ else
65
+ []
66
+ end
67
+ end
68
+ end
69
+
70
+ def init_remote_tasks
71
+ @task_info = IndiferentHash.setup(WorkflowSSHClient.get_json(url))
72
+ @exec_exports = @stream_exports = @synchronous_exports = []
73
+ @asynchronous_exports = @task_info.keys
74
+ end
75
+
76
+ def self.execute_job(base_url, task, task_params, cache_type)
77
+ end
78
+ end
@@ -0,0 +1,281 @@
1
+ module SSHClient
2
+ def self.run(server, script)
3
+ Log.debug "Run ssh script in #{server}:\n#{script}"
4
+ CMD.cmd("ssh '#{server}' ruby ", :in => script).read.strip
5
+ end
6
+
7
+ def self.run_log(server, script)
8
+ Log.debug "Run and monitor ssh script in #{server}:\n#{script}"
9
+ CMD.cmd_log("ssh '#{server}' ruby ", :in => script)
10
+ end
11
+
12
+ def self.parse_url(url)
13
+ m = url.match(/ssh:\/\/([^:]+):(.*)/)
14
+ server = m.captures[0]
15
+ path = m.captures[1]
16
+ [server, path]
17
+ end
18
+
19
+ def self.path_script(path)
20
+
21
+ workflow, task, job, *rest = path.split("/")
22
+
23
+ script =<<-EOF
24
+ require 'rbbt/workflow'
25
+ wf = Workflow.require_workflow "#{workflow}"
26
+ EOF
27
+ case task
28
+ when nil
29
+ script +=<<-EOF
30
+ task_info = {}
31
+ wf.tasks.keys.each do |task|
32
+ task_info[task] = wf.task_info(task)
33
+ end
34
+ res = task_info
35
+ EOF
36
+ when 'documentation'
37
+ script +=<<-EOF
38
+ res = documentation = wf.documentation
39
+ EOF
40
+ else
41
+ if job.nil?
42
+ script +=<<-EOF
43
+ task = '#{task}'
44
+ res = task_info = wf.task_info(task)
45
+ EOF
46
+ else
47
+ case rest.first
48
+ when nil
49
+ script +=<<-EOF
50
+ task = '#{task}'
51
+ jobname = '#{job}'
52
+ res = job = wf.fast_load_id(File.join(task, jobname))
53
+ EOF
54
+ when "info"
55
+ script +=<<-EOF
56
+ task = '#{task}'
57
+ jobname = '#{job}'
58
+ job = wf.fast_load_id(File.join(task, jobname))
59
+ res = job_info = job.info
60
+ EOF
61
+ else
62
+ raise "Unkown path: #{[path, rest].inspect}"
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.get_json(url, params)
69
+ server, path = parse_url(url)
70
+ script = path_script(path)
71
+
72
+ script +=<<-EOF
73
+ puts res.to_json
74
+ EOF
75
+
76
+ JSON.parse(self.run(server, script))
77
+ end
78
+
79
+ def self.get_raw(url, params)
80
+ server, path = parse_url(url)
81
+ script = path_script(path)
82
+
83
+ script +=<<-EOF
84
+ puts res
85
+ EOF
86
+
87
+ self.run(server, script)
88
+ end
89
+
90
+ def self.post_job(url, inputs, input_types, jobname = nil)
91
+ server, path = parse_url(url)
92
+ script = path_script(path)
93
+
94
+ id = "inputs-" << rand(100000).to_s
95
+ CMD.cmd("ssh '#{server}' mkdir -p .rbbt/tmp/tmp-ssh_job_inputs/ ", :in => script).read
96
+ TmpFile.with_file do |dir|
97
+ if Step.save_inputs(inputs, input_types, dir)
98
+ CMD.cmd("scp -r '#{dir}' .rbbt/tmp/tmp-ssh_job_inputs/#{id}")
99
+ end
100
+ end
101
+
102
+ script +=<<-EOF
103
+ jobname = #{jobname.nil? ? 'nil' : "'#{jobname}'"}
104
+ path = '.rbbt/tmp/tmp-ssh_job_inputs/#{id}'
105
+ job_inputs = Workflow.load_inputs(path, task_info[:inputs], task_info[:input_types])
106
+ job = wf.job(task, jobname, job_inputs)
107
+ Log.severity = 10
108
+ job.fork
109
+ puts job.name
110
+ EOF
111
+ self.run(server, script)
112
+ end
113
+
114
+ def self.run_job(url, inputs, input_types, jobname = nil)
115
+ server, path = parse_url(url)
116
+ script = path_script(path)
117
+
118
+ id = "inputs-" << rand(100000).to_s
119
+ CMD.cmd("ssh '#{server}' mkdir -p .rbbt/tmp/tmp-ssh_job_inputs/ ", :in => script).read
120
+ TmpFile.with_file do |dir|
121
+ if Step.save_inputs(inputs, input_types, dir)
122
+ CMD.cmd("scp -r '#{dir}' .rbbt/tmp/tmp-ssh_job_inputs/#{id}")
123
+ end
124
+ end
125
+
126
+ script +=<<-EOF
127
+ jobname = #{jobname.nil? ? 'nil' : "'#{jobname}'"}
128
+ path = '.rbbt/tmp/tmp-ssh_job_inputs/#{id}'
129
+ job_inputs = Workflow.load_inputs(path, task_info[:inputs], task_info[:input_types])
130
+ job = wf.job(task, jobname, job_inputs)
131
+ job.run
132
+ EOF
133
+ self.run_log(server, script)
134
+ end
135
+
136
+ def self.clean(url)
137
+ server, path = parse_url(url)
138
+ script = path_script(path)
139
+
140
+ script +=<<-EOF
141
+ job.clean
142
+ EOF
143
+ self.run(server, script)
144
+ end
145
+
146
+ end
147
+
148
+ module WorkflowSSHClient
149
+ def self.fix_hash(hash, fix_values = false)
150
+ fixed = {}
151
+ hash.each do |key, value|
152
+ fixed[key.to_sym] = case value
153
+ when TrueClass
154
+ value
155
+ when FalseClass
156
+ value
157
+ when Hash
158
+ fix_hash(value)
159
+ when (fix_values and String )
160
+ value.to_sym
161
+ when IO
162
+ value.read
163
+ when TSV::Dumper
164
+ value.stream
165
+ when Step
166
+ stream = get_stream(value)
167
+ stream || value.load
168
+ else
169
+ value
170
+ end
171
+ end
172
+ fixed
173
+ end
174
+
175
+ def self.parse_exception(text)
176
+ klass, message = text.split " => "
177
+ begin
178
+ klass = Kernel.const_get klass
179
+ return klass.new message
180
+ rescue
181
+ message
182
+ end
183
+ end
184
+
185
+ def self.capture_exception
186
+ begin
187
+ yield
188
+ rescue Exception => e
189
+ raise e unless e.respond_to? :response
190
+ begin
191
+ ne = parse_exception e.response.to_s
192
+ case ne
193
+ when String
194
+ raise e.class, ne
195
+ when Exception
196
+ raise ne
197
+ else
198
+ raise
199
+ end
200
+ rescue
201
+ raise e
202
+ end
203
+ raise $!
204
+ end
205
+ end
206
+
207
+ def self.fix_params(params)
208
+ new_params = {}
209
+ params.each do |k,v|
210
+ if Array === v and v.empty?
211
+ new_params[k] = "EMPTY_ARRAY"
212
+ else
213
+ new_params[k] = v
214
+ end
215
+ end
216
+ new_params
217
+ end
218
+
219
+ def self.get_json(url, params = {})
220
+ Log.debug{ "SSHClient get_json: #{ url } - #{Misc.fingerprint params }" }
221
+ params = params.merge({ :_format => 'json' })
222
+ params = fix_params params
223
+
224
+ res = capture_exception do
225
+ Misc.insist(2, 0.5) do
226
+ SSHClient.get_json(url, :params => params)
227
+ end
228
+ end
229
+
230
+ begin
231
+ JSON.parse(res)
232
+ rescue
233
+ res
234
+ end
235
+ end
236
+
237
+
238
+ def self.post_jobname(url, inputs, input_types)
239
+ SSHClient.post_job(url, inputs, input_types)
240
+ end
241
+
242
+ def init_job(cache_type = nil, other_params = {})
243
+ cache_type = :asynchronous if cache_type.nil? and not @is_exec
244
+ cache_type = :exec if cache_type.nil?
245
+ @last_info_time = nil
246
+ @done = false
247
+ get_streams
248
+ @name ||= Persist.memory("RemoteSteps", :workflow => self, :task => task, :jobname => @name, :inputs => inputs, :cache_type => cache_type) do
249
+ Misc.insist do
250
+ #@adaptor.post_jobname(File.join(base_url, task.to_s), inputs.merge(other_params).merge(:jobname => @name||@base_name, :_cache_type => cache_type))
251
+ input_types = {}
252
+ @adaptor.post_jobname(File.join(base_url, task.to_s), inputs, input_types)
253
+ end
254
+ end
255
+ if Open.remote? @name
256
+ @url = @name
257
+ @name = File.basename(@name)
258
+ else
259
+ @url = File.join(base_url, task.to_s, @name)
260
+ end
261
+ self
262
+ end
263
+
264
+ def path
265
+ server, path = SSHClient.parse_url(url)
266
+ "ssh://" + server + ":" + info[:path]
267
+ end
268
+
269
+ def run(*args)
270
+ input_types = {}
271
+ SSHClient.run_job(File.join(base_url, task.to_s), inputs, input_types)
272
+ end
273
+
274
+
275
+ def clean
276
+ init_job
277
+ SSHClient.clean(@url) if done?
278
+ _restart
279
+ end
280
+
281
+ end