t2-server 0.6.1 → 0.9.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.
Files changed (52) hide show
  1. data/.rvmrc +1 -0
  2. data/CHANGES.rdoc +48 -0
  3. data/LICENCE.rdoc +2 -2
  4. data/README.rdoc +245 -10
  5. data/Rakefile +108 -0
  6. data/bin/t2-delete-runs +21 -34
  7. data/bin/t2-get-output +134 -0
  8. data/bin/t2-run-workflow +121 -109
  9. data/bin/t2-server-admin +128 -0
  10. data/bin/t2-server-info +25 -38
  11. data/lib/t2-server-cli.rb +116 -0
  12. data/lib/t2-server.rb +16 -27
  13. data/lib/t2-server/admin.rb +147 -0
  14. data/lib/t2-server/connection-parameters.rb +144 -0
  15. data/lib/t2-server/connection.rb +352 -0
  16. data/lib/t2-server/credentials.rb +84 -0
  17. data/lib/t2-server/exceptions.rb +42 -21
  18. data/lib/t2-server/port.rb +472 -0
  19. data/lib/t2-server/run.rb +822 -227
  20. data/lib/t2-server/server.rb +313 -317
  21. data/lib/t2-server/util.rb +71 -0
  22. data/lib/t2-server/xml/libxml.rb +87 -0
  23. data/lib/t2-server/xml/nokogiri.rb +85 -0
  24. data/lib/t2-server/xml/rexml.rb +85 -0
  25. data/lib/t2-server/xml/xml.rb +111 -0
  26. data/lib/t2server.rb +4 -1
  27. data/t2-server.gemspec +112 -0
  28. data/test/tc_admin.rb +63 -0
  29. data/test/{tc_paths.rb → tc_params.rb} +11 -25
  30. data/test/tc_perms.rb +132 -0
  31. data/test/tc_run.rb +200 -67
  32. data/test/tc_secure.rb +191 -0
  33. data/test/tc_server.rb +25 -23
  34. data/test/tc_util.rb +74 -0
  35. data/test/ts_t2server.rb +57 -12
  36. data/test/workflows/always_fail.t2flow +69 -0
  37. data/test/workflows/list_and_value.t2flow +12 -0
  38. data/test/workflows/list_with_errors.t2flow +107 -0
  39. data/test/workflows/secure/basic-http.t2flow +74 -0
  40. data/test/workflows/secure/basic-https.t2flow +74 -0
  41. data/test/workflows/secure/client-https.t2flow +162 -0
  42. data/test/workflows/secure/digest-http.t2flow +129 -0
  43. data/test/workflows/secure/digest-https.t2flow +107 -0
  44. data/test/workflows/secure/heater-pk.pem +20 -0
  45. data/test/workflows/secure/user-cert.p12 +0 -0
  46. data/test/workflows/secure/ws-http.t2flow +180 -0
  47. data/test/workflows/secure/ws-https.t2flow +180 -0
  48. data/test/workflows/strings.txt +10 -0
  49. data/test/workflows/xml_xpath.t2flow +136 -136
  50. data/version.yml +4 -0
  51. metadata +132 -34
  52. data/lib/t2-server/xml.rb +0 -86
data/lib/t2-server/run.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2010, 2011 The University of Manchester, UK.
1
+ # Copyright (c) 2010-2012 The University of Manchester, UK.
2
2
  #
3
3
  # All rights reserved.
4
4
  #
@@ -14,7 +14,7 @@
14
14
  #
15
15
  # * Neither the names of The University of Manchester nor the names of its
16
16
  # contributors may be used to endorse or promote products derived from this
17
- # software without specific prior written permission.
17
+ # software without specific prior written permission.
18
18
  #
19
19
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
20
  # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -30,9 +30,10 @@
30
30
  #
31
31
  # Author: Robert Haines
32
32
 
33
- require 'rubygems'
34
- require 'libxml'
33
+ require 'base64'
35
34
  require 'time'
35
+ require 'rubygems'
36
+ require 'taverna-baclava'
36
37
 
37
38
  module T2Server
38
39
 
@@ -40,267 +41,378 @@ module T2Server
40
41
  # setup and configuration required.
41
42
  #
42
43
  # A run can be in one of three states:
43
- # * Initialized: The run has been accepted by the server. It may not yet be
44
+ # * :initialized - The run has been accepted by the server. It may not yet be
44
45
  # ready to run though as its input port may not have been set.
45
- # * Running: The run is being run by the server.
46
- # * Finished: The run has finished running and its outputs are available for
47
- # download.
46
+ # * :running - The run is being run by the server.
47
+ # * :finished - The run has finished running and its outputs are available
48
+ # for download.
48
49
  class Run
49
- include LibXML
50
+ include XML::Methods
50
51
 
51
52
  private_class_method :new
52
53
 
53
- # The identifier of this run on the server. It is currently a UUID
54
- # (version 4).
55
- attr_reader :uuid
54
+ # The identifier of this run on the server.
55
+ attr_reader :identifier
56
+ alias :id :identifier
57
+
58
+ # The server instance that this run is hosted on.
59
+ attr_reader :server
60
+
61
+ # The owner (username) of this run
62
+ attr_reader :owner
56
63
 
57
64
  # :stopdoc:
58
- STATE = {
59
- :initialized => "Initialized",
60
- :running => "Operating",
61
- :finished => "Finished",
62
- :stopped => "Stopped"
65
+ XPaths = {
66
+ # Run XPath queries
67
+ :run_desc => XML::Methods.xpath_compile("/nsr:runDescription"),
68
+ :dir => XML::Methods.xpath_compile("//nss:dir"),
69
+ :file => XML::Methods.xpath_compile("//nss:file"),
70
+ :expiry => XML::Methods.xpath_compile("//nsr:expiry"),
71
+ :workflow => XML::Methods.xpath_compile("//nsr:creationWorkflow"),
72
+ :status => XML::Methods.xpath_compile("//nsr:status"),
73
+ :createtime => XML::Methods.xpath_compile("//nsr:createTime"),
74
+ :starttime => XML::Methods.xpath_compile("//nsr:startTime"),
75
+ :finishtime => XML::Methods.xpath_compile("//nsr:finishTime"),
76
+ :wdir => XML::Methods.xpath_compile("//nsr:workingDirectory"),
77
+ :inputs => XML::Methods.xpath_compile("//nsr:inputs"),
78
+ :output => XML::Methods.xpath_compile("//nsr:output"),
79
+ :securectx => XML::Methods.xpath_compile("//nsr:securityContext"),
80
+ :listeners => XML::Methods.xpath_compile("//nsr:listeners"),
81
+ :baclava => XML::Methods.xpath_compile("//nsr:baclava"),
82
+ :inputexp => XML::Methods.xpath_compile("//nsr:expected"),
83
+
84
+ # Port descriptions XPath queries
85
+ :port_in => XML::Methods.xpath_compile("//port:input"),
86
+ :port_out => XML::Methods.xpath_compile("//port:output"),
87
+
88
+ # Run security XPath queries
89
+ :sec_creds => XML::Methods.xpath_compile("//nsr:credentials"),
90
+ :sec_perms => XML::Methods.xpath_compile("//nsr:permissions"),
91
+ :sec_trusts => XML::Methods.xpath_compile("//nsr:trusts"),
92
+ :sec_perm =>
93
+ XML::Methods.xpath_compile("/nsr:permissionsDescriptor/nsr:permission"),
94
+ :sec_uname => XML::Methods.xpath_compile("nsr:userName"),
95
+ :sec_uperm => XML::Methods.xpath_compile("nsr:permission"),
96
+ :sec_cred => XML::Methods.xpath_compile("/nsr:credential"),
97
+ :sec_suri => XML::Methods.xpath_compile("nss:serviceURI"),
98
+ :sec_trust =>
99
+ XML::Methods.xpath_compile("/nsr:trustedIdentities/nsr:trust")
63
100
  }
64
101
 
102
+ # The name to be used internally for retrieving results via baclava
103
+ BACLAVA_FILE = "out.xml"
104
+
65
105
  # New is private but rdoc does not get it right! Hence :stopdoc: section.
66
- def initialize(server, uuid)
106
+ def initialize(server, id, credentials = nil)
67
107
  @server = server
68
- @uuid = uuid
108
+ @identifier = id
69
109
  @workflow = ""
70
110
  @baclava_in = false
71
- @baclava_out = ""
72
-
73
- @links = get_attributes(@server.get_run_attribute(uuid, ""))
74
- #@links.each {|key, val| puts "#{key}: #{val}"}
111
+ @baclava_out = false
112
+
113
+ @credentials = credentials
114
+
115
+ run_desc = xml_document(@server.get_run_attribute(@identifier, "",
116
+ "application/xml", @credentials))
117
+ @owner = xpath_attr(run_desc, XPaths[:run_desc], "owner")
118
+ @links = get_attributes(run_desc)
119
+
120
+ # initialize ports lists to nil as an empty list means no inputs/outputs
121
+ @input_ports = nil
122
+ @output_ports = nil
75
123
  end
76
124
  # :startdoc:
77
125
 
78
126
  # :call-seq:
79
127
  # Run.create(server, workflow) -> run
128
+ # Run.create(server, workflow, connection_parameters) -> run
129
+ # Run.create(server, workflow, user_credentials) -> run
130
+ # Run.create(server, workflow, ...) {|run| ...}
80
131
  #
81
- # Create a new run in the +Initialized+ state. The run will be created on
132
+ # Create a new run in the :initialized state. The run will be created on
82
133
  # the server with address supplied by _server_. This can either be a
83
134
  # String of the form <tt>http://example.com:8888/blah</tt> or an already
84
135
  # created instance of T2Server::Server. The _workflow_ must also be
85
- # supplied as a string in t2flow or scufl format.
86
- def Run.create(server, workflow, uuid="")
87
- if server.class == String
88
- server = Server.connect(server)
136
+ # supplied as a string in t2flow or scufl format. User credentials and
137
+ # connection parameters can be supplied if required but are both optional.
138
+ # If _server_ is an instance of T2Server::Server then
139
+ # _connection_parameters_ will be ignored.
140
+ #
141
+ # This method will _yield_ the newly created Run if a block is given.
142
+ def Run.create(server, workflow, *rest)
143
+ credentials = nil
144
+ id = nil
145
+ conn_params = nil
146
+
147
+ rest.each do |param|
148
+ case param
149
+ when String
150
+ id = param
151
+ when ConnectionParameters
152
+ conn_params = param
153
+ when HttpCredentials
154
+ credentials = param
155
+ end
89
156
  end
90
- if uuid == ""
91
- new(server, server.initialize_run(workflow))
92
- else
93
- new(server, uuid)
157
+
158
+ if server.class != Server
159
+ server = Server.new(server, conn_params)
160
+ end
161
+
162
+ if id.nil?
163
+ id = server.initialize_run(workflow, credentials)
94
164
  end
165
+
166
+ run = new(server, id, credentials)
167
+ yield(run) if block_given?
168
+ run
95
169
  end
96
170
 
171
+ # :stopdoc:
172
+ def uuid
173
+ warn "[DEPRECATION] 'uuid' is deprecated and will be removed in 1.0. " +
174
+ "Please use Run#id or Run#identifier instead."
175
+ @identifier
176
+ end
177
+ # :startdoc:
178
+
97
179
  # :call-seq:
98
- # run.delete
180
+ # delete
99
181
  #
100
182
  # Delete this run from the server.
101
183
  def delete
102
- @server.delete_run uuid
184
+ @server.delete_run(@identifier, @credentials)
103
185
  end
104
186
 
105
- # :call-seq:
106
- # run.inputs -> string
107
- #
108
- # Return the path to the input ports of this run on the server.
187
+ # :stopdoc:
109
188
  def inputs
189
+ warn "[DEPRECATION] 'inputs' is deprecated and will be removed in 1.0."
110
190
  @links[:inputs]
111
191
  end
112
192
 
193
+ def set_input(input, value)
194
+ warn "[DEPRECATION] 'Run#set_input' is deprecated and will be removed " +
195
+ "in 1.0. Input ports are set directly instead. The most direct " +
196
+ "replacement for this method is: 'Run#input_port(input).value = value'"
197
+
198
+ input_port(input).value = value
199
+ end
200
+
201
+ def set_input_file(input, filename)
202
+ warn "[DEPRECATION] 'Run#set_input_file' is deprecated and will be " +
203
+ "removed in 1.0. Input ports are set directly instead. The most " +
204
+ "direct replacement for this method is: " +
205
+ "'Run#input_port(input).remote_file = filename'"
206
+
207
+ input_port(input).remote_file = filename
208
+ end
209
+ # :startdoc:
210
+
113
211
  # :call-seq:
114
- # run.set_input(input, value) -> bool
212
+ # input_ports -> Hash
115
213
  #
116
- # Set the workflow input port _input_ to _value_.
117
- #
118
- # Raises RunStateError if the run is not in the +Initialized+ state.
119
- def set_input(input, value)
120
- state = status
121
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
214
+ # Return a hash (name, port) of all the input ports this run expects.
215
+ def input_ports
216
+ @input_ports = _get_input_port_info if @input_ports.nil?
122
217
 
123
- @server.set_run_input(self, input, value)
218
+ @input_ports
124
219
  end
125
220
 
126
221
  # :call-seq:
127
- # run.set_input_file(input, filename) -> bool
222
+ # input_port(port) -> Port
128
223
  #
129
- # Set the workflow input port _input_ to use the file at _filename_ as its
130
- # input data.
224
+ # Get _port_.
225
+ def input_port(port)
226
+ input_ports[port]
227
+ end
228
+
229
+ # :call-seq:
230
+ # output_ports -> Hash
131
231
  #
132
- # Raises RunStateError if the run is not in the +Initialized+ state.
133
- def set_input_file(input, filename)
134
- state = status
135
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
232
+ # Return a hash (name, port) of all the output ports this run has. Until
233
+ # the run is finished this method will return _nil_.
234
+ def output_ports
235
+ if finished? and @output_ports.nil?
236
+ @output_ports = _get_output_port_info
237
+ end
136
238
 
137
- @server.set_run_input_file(self, input, filename)
239
+ @output_ports
138
240
  end
139
241
 
140
242
  # :call-seq:
141
- # run.get_output_ports -> list
243
+ # output_port(port) -> Port
142
244
  #
143
- # Return a list of all the output ports
245
+ # Get output port _port_.
246
+ def output_port(port)
247
+ output_ports[port] if finished?
248
+ end
249
+
250
+ # :stopdoc:
144
251
  def get_output_ports
252
+ warn "[DEPRECATION] 'get_output_ports' is deprecated and will be " +
253
+ "removed in 1.0. Please use 'Run#output_ports' instead."
145
254
  lists, items = _ls_ports("out")
146
255
  items + lists
147
256
  end
148
257
 
149
- # :call-seq:
150
- # run.get_output(output, refs=false) -> string or list
151
- #
152
- # Return the values of the workflow output port _output_. These are
153
- # returned as a list of strings or, if the output port represents a
154
- # singleton value, then a string returned. By default this method returns
155
- # the actual data from the output port but if _refs_ is set to true then
156
- # it will instead return URIs to the actual data in the same list format.
157
- # See also Run#get_output_refs.
158
258
  def get_output(output, refs=false)
259
+ warn "[DEPRECATION] 'get_output' is deprecated and will be removed " +
260
+ "in 1.0. Please use 'Run#output_port(port).values' instead."
159
261
  _get_output(output, refs)
160
262
  end
161
263
 
162
- # :call-seq:
163
- # run.get_output_refs(output) -> string or list
164
- #
165
- # Return references (URIs) to the values of the workflow output port
166
- # _output_. These are returned as a list of URIs or, if the output port
167
- # represents a singleton value, then a single URI is returned. The URIs
168
- # are returned as strings.
169
264
  def get_output_refs(output)
265
+ warn "[DEPRECATION] 'get_output_refs' is deprecated and will be " +
266
+ "removed in 1.0. Please use 'Run#output_port(port).data' instead."
170
267
  _get_output(output, true)
171
268
  end
269
+ # :startdoc:
172
270
 
173
271
  # :call-seq:
174
- # run.expiry -> string
272
+ # expiry -> string
175
273
  #
176
274
  # Return the expiry time of this run as an instance of class Time.
177
275
  def expiry
178
- Time.parse(@server.get_run_attribute(@uuid, @links[:expiry]))
276
+ Time.parse(@server.get_run_attribute(@identifier, @links[:expiry],
277
+ "text/plain", @credentials))
179
278
  end
180
279
 
181
280
  # :call-seq:
182
- # run.expiry=(time) -> bool
281
+ # expiry=(time) -> bool
183
282
  #
184
- # Set the expiry time of this run to _time_. The format of _time_ should
185
- # be something that the Ruby Time class can parse. If the value given does
186
- # not specify a date then today's date will be assumed. If a time/date in
187
- # the past is specified, the expiry time will not be changed.
283
+ # Set the expiry time of this run to _time_. _time_ should either be a Time
284
+ # object or something that the Time class can parse. If the value given
285
+ # does not specify a date then today's date will be assumed. If a time/date
286
+ # in the past is specified, the expiry time will not be changed.
188
287
  def expiry=(time)
288
+ unless time.instance_of? Time
289
+ time = Time.parse(time)
290
+ end
291
+
189
292
  # need to massage the xmlschema format slightly as the server cannot
190
293
  # parse timezone offsets with a colon (eg +00:00)
191
- date_str = Time.parse(time).xmlschema(2)
294
+ date_str = time.xmlschema(2)
192
295
  date_str = date_str[0..-4] + date_str[-2..-1]
193
- @server.set_run_attribute(@uuid, @links[:expiry], date_str)
296
+ @server.set_run_attribute(@identifier, @links[:expiry], date_str,
297
+ "text/plain", @credentials)
194
298
  end
195
299
 
196
300
  # :call-seq:
197
- # run.workflow -> string
301
+ # workflow -> string
198
302
  #
199
303
  # Get the workflow that this run represents.
200
304
  def workflow
201
305
  if @workflow == ""
202
- @workflow = @server.get_run_attribute(@uuid, @links[:workflow])
306
+ @workflow = @server.get_run_attribute(@identifier, @links[:workflow],
307
+ "application/xml", @credentials)
203
308
  end
204
309
  @workflow
205
310
  end
206
311
 
207
312
  # :call-seq:
208
- # run.status -> string
313
+ # status -> string
209
314
  #
210
- # Get the status of this run.
315
+ # Get the status of this run. Status can be one of :initialized,
316
+ # :running or :finished.
211
317
  def status
212
- @server.get_run_attribute(@uuid, @links[:status])
318
+ text_to_state(@server.get_run_attribute(@identifier, @links[:status],
319
+ "text/plain", @credentials))
213
320
  end
214
321
 
215
322
  # :call-seq:
216
- # run.start
323
+ # start
217
324
  #
218
325
  # Start this run on the server.
219
326
  #
220
- # Raises RunStateError if the run is not in the +Initialized+ state.
327
+ # Raises RunStateError if the run is not in the :initialized state.
221
328
  def start
222
329
  state = status
223
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
330
+ raise RunStateError.new(state, :initialized) if state != :initialized
224
331
 
225
- @server.set_run_attribute(@uuid, @links[:status], STATE[:running])
332
+ # set all the inputs
333
+ _check_and_set_inputs unless baclava_input?
334
+
335
+ @server.set_run_attribute(@identifier, @links[:status],
336
+ state_to_text(:running), "text/plain", @credentials)
226
337
  end
227
338
 
228
339
  # :call-seq:
229
- # run.wait(params={})
340
+ # wait(check_interval = 1)
230
341
  #
231
- # Wait (block) for this run to finish. Possible values that can be passed
232
- # in via _params_ are:
233
- # * :interval - How often (in seconds) to test for run completion.
234
- # Default +1+.
235
- # * :progress - Print a dot (.) each interval to show that something is
236
- # actually happening. Default +false+.
342
+ # Wait (block) for this run to finish. How often (in seconds) the run is
343
+ # tested for completion can be specified with check_interval.
237
344
  #
238
- # Raises RunStateError if the run is not in the +Running+ state.
239
- def wait(params={})
345
+ # Raises RunStateError if the run is still in the :initialised state.
346
+ def wait(*params)
240
347
  state = status
241
- raise RunStateError.new(state, STATE[:running]) if state != STATE[:running]
348
+ raise RunStateError.new(state, :running) if state == :initialized
349
+
350
+ interval = 1
351
+ params.each do |param|
352
+ case param
353
+ when Hash
354
+ warn "[DEPRECATION] 'Run#wait(params={})' is deprecated and will " +
355
+ "be removed in 1.0. Please use Run#wait(check_interval) instead."
356
+ interval = param[:interval] || 1
357
+ when Integer
358
+ interval = param
359
+ end
360
+ end
242
361
 
243
- interval = params[:interval] || 1
244
- progress = params[:progress] || false
245
- keepalive = params[:keepalive] || false ### TODO maybe move out of params
246
-
247
362
  # wait
248
363
  until finished?
249
364
  sleep(interval)
250
- if progress
251
- print "."
252
- STDOUT.flush
253
- end
254
365
  end
255
-
256
- # tidy up output if there is any
257
- puts if progress
258
366
  end
259
367
 
260
368
  # :call-seq:
261
- # run.exitcode -> integer
369
+ # exitcode -> integer
262
370
  #
263
371
  # Get the return code of the run. Zero indicates success.
264
372
  def exitcode
265
- @server.get_run_attribute(@uuid, @links[:exitcode]).to_i
373
+ @server.get_run_attribute(@identifier, @links[:exitcode], "text/plain",
374
+ @credentials).to_i
266
375
  end
267
376
 
268
377
  # :call-seq:
269
- # run.stdout -> string
378
+ # stdout -> string
270
379
  #
271
380
  # Get anything that the run printed to the standard out stream.
272
381
  def stdout
273
- @server.get_run_attribute(@uuid, @links[:stdout])
382
+ @server.get_run_attribute(@identifier, @links[:stdout], "text/plain",
383
+ @credentials)
274
384
  end
275
385
 
276
386
  # :call-seq:
277
- # run.stderr -> string
387
+ # stderr -> string
278
388
  #
279
389
  # Get anything that the run printed to the standard error stream.
280
390
  def stderr
281
- @server.get_run_attribute(@uuid, @links[:stderr])
391
+ @server.get_run_attribute(@identifier, @links[:stderr], "text/plain",
392
+ @credentials)
282
393
  end
283
394
 
284
395
  # :call-seq:
285
- # run.mkdir(dir) -> bool
396
+ # mkdir(dir) -> bool
286
397
  #
287
398
  # Create a directory in the run's working directory on the server. This
288
399
  # could be used to store input data.
289
400
  def mkdir(dir)
290
- dir.strip_path!
401
+ dir = Util.strip_path_slashes(dir)
291
402
  if dir.include? ?/
292
403
  # if a path is given then separate the leaf from the
293
404
  # end and add the rest of the path to the wdir link
294
405
  leaf = dir.split("/")[-1]
295
406
  path = dir[0...-(leaf.length + 1)]
296
- @server.make_run_dir(@uuid, "#{@links[:wdir]}/#{path}", leaf)
407
+ @server.create_dir(@identifier, "#{@links[:wdir]}/#{path}", leaf,
408
+ @credentials)
297
409
  else
298
- @server.make_run_dir(@uuid, @links[:wdir], dir)
410
+ @server.create_dir(@identifier, @links[:wdir], dir, @credentials)
299
411
  end
300
412
  end
301
413
 
302
414
  # :call-seq:
303
- # run.upload_file(filename, params={}) -> string
415
+ # upload_file(filename, params={}) -> string
304
416
  #
305
417
  # Upload a file, with name _filename_, to the server. Possible values that
306
418
  # can be passed in via _params_ are:
@@ -313,160 +425,578 @@ module T2Server
313
425
  location = params[:dir] || ""
314
426
  location = "#{@links[:wdir]}/#{location}"
315
427
  rename = params[:rename] || ""
316
- @server.upload_run_file(@uuid, filename, location, rename)
428
+ @server.upload_file(@identifier, filename, location, rename,
429
+ @credentials)
317
430
  end
318
431
 
319
432
  # :call-seq:
320
- # run.upload_input_file(input, filename, params={}) -> string
321
- #
322
- # Upload a file, with name _filename_, to the server and set it as the
323
- # input data for input port _input_. Possible values that can be passed
324
- # in via _params_ are:
325
- # * :dir - The directory to upload to. If this is not left blank the
326
- # corresponding directory will need to have been created by Run#mkdir.
327
- # * :rename - Save the file on the server with a different name.
328
- #
329
- # The name of the file on the server is returned.
433
+ # upload_data(data, remote_name, remote_directory = "") -> bool
330
434
  #
331
- # Raises RunStateError if the run is not in the +Initialized+ state.
435
+ # Upload data to the server and store it in <tt>remote_file</tt>. The
436
+ # remote directory to put this file in can also be specified, but if it is
437
+ # it must first have been created by a call to Run#mkdir.
438
+ def upload_data(data, remote_name, remote_directory = "")
439
+ location = "#{@links[:wdir]}/#{remote_directory}"
440
+ @server.upload_data(@identifier, data, remote_name, location,
441
+ @credentials)
442
+ end
443
+
444
+ # :stopdoc:
332
445
  def upload_input_file(input, filename, params={})
333
- state = status
334
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
446
+ warn "[DEPRECATION] 'Run#upload_input_file' is deprecated and will be " +
447
+ "removed in 1.0. Input ports are set directly instead. The most " +
448
+ "direct replacement for this method is: " +
449
+ "'Run#input_port(input).file = filename'"
335
450
 
336
- file = upload_file(filename, params)
337
- set_input_file(input, file)
451
+ input_port(input).file = filename
338
452
  end
453
+ # :startdoc:
339
454
 
340
455
  # :call-seq:
341
- # run.upload_baclava_file(filename) -> bool
456
+ # baclava_input=(filename) -> bool
342
457
  #
343
- # Upload a baclava file to be used for the workflow inputs.
344
- def upload_baclava_file(filename)
458
+ # Use a baclava file for the workflow inputs.
459
+ def baclava_input=(filename)
345
460
  state = status
346
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
461
+ raise RunStateError.new(state, :initialized) if state != :initialized
347
462
 
348
- @baclava_in = true
349
463
  rename = upload_file(filename)
350
- @server.set_run_attribute(@uuid, @links[:baclava], rename)
464
+ result = @server.set_run_attribute(@identifier, @links[:baclava], rename,
465
+ "text/plain", @credentials)
466
+
467
+ @baclava_in = true if result
468
+
469
+ result
351
470
  end
352
471
 
472
+ # :stopdoc:
473
+ def upload_baclava_input(filename)
474
+ warn "[DEPRECATION] 'upload_baclava_input' is deprecated and will be " +
475
+ "removed in 1.0. Please use 'Run#baclava_input=' instead."
476
+ self.baclava_input = filename
477
+ end
478
+
479
+ def upload_baclava_file(filename)
480
+ warn "[DEPRECATION] 'upload_baclava_file' is deprecated and will be " +
481
+ "removed in 1.0. Please use 'Run#baclava_input=' instead."
482
+ self.baclava_input = filename
483
+ end
484
+ # :startdoc:
485
+
353
486
  # :call-seq:
354
- # run.set_baclava_output(name="out.xml") -> bool
487
+ # request_baclava_output -> bool
355
488
  #
356
- # Set the server to save the outputs of this run in baclava format. The
357
- # filename can be specified with the _name_ parameter otherwise a default
358
- # of 'out.xml' is used. This must be done before the run is started.
359
- def set_baclava_output(name="out.xml")
489
+ # Set the server to save the outputs of this run in baclava format. This
490
+ # must be done before the run is started.
491
+ def request_baclava_output
492
+ return if @baclava_out
360
493
  state = status
361
- raise RunStateError.new(state, STATE[:initialized]) if state != STATE[:initialized]
362
-
363
- @baclava_out = name.strip_path
364
- @server.set_run_attribute(@uuid, @links[:output], @baclava_out)
494
+ raise RunStateError.new(state, :initialized) if state != :initialized
495
+
496
+ @baclava_out = @server.set_run_attribute(@identifier, @links[:output],
497
+ BACLAVA_FILE, "text/plain", @credentials)
498
+ end
499
+
500
+ # :stopdoc:
501
+ def set_baclava_output(name="")
502
+ warn "[DEPRECATION] 'set_baclava_output' is deprecated and will be " +
503
+ "removed in 1.0. Please use 'Run#request_baclava_output' instead."
504
+ self.request_baclava_output
365
505
  end
506
+ # :startdoc:
366
507
 
367
508
  # :call-seq:
368
- # run.get_baclava_output -> string
509
+ # baclava_input? -> bool
510
+ #
511
+ # Have the inputs to this run been set by a baclava document?
512
+ def baclava_input?
513
+ @baclava_in
514
+ end
515
+
516
+ # :call-seq:
517
+ # baclava_output? -> bool
518
+ #
519
+ # Has this run been set to return results in baclava format?
520
+ def baclava_output?
521
+ @baclava_out
522
+ end
523
+
524
+ # :call-seq:
525
+ # baclava_output -> string
369
526
  #
370
527
  # Get the outputs of this run in baclava format. This can only be done if
371
528
  # the output has been requested in baclava format by #set_baclava_output
372
529
  # before starting the run.
530
+ def baclava_output
531
+ state = status
532
+ raise RunStateError.new(state, :finished) if state != :finished
533
+
534
+ raise AccessForbiddenError.new("baclava output") if !@baclava_out
535
+ @server.get_run_attribute(@identifier,
536
+ "#{@links[:wdir]}/#{BACLAVA_FILE}", "*/*", @credentials)
537
+ end
538
+
539
+ # :stopdoc:
373
540
  def get_baclava_output
541
+ warn "[DEPRECATION] 'get_baclava_output' is deprecated and will be " +
542
+ "removed in 1.0. Please use 'Run#baclava_output' instead."
543
+ baclava_output
544
+ end
545
+ # :startdoc:
546
+
547
+ # :call-seq:
548
+ # zip_output -> binary blob
549
+ #
550
+ # Get the working directory of this run directly from the server in zip
551
+ # format.
552
+ def zip_output
374
553
  state = status
375
- raise RunStateError.new(state, STATE[:finished]) if state != STATE[:finished]
376
-
377
- raise AttributeNotFoundError.new("#{@links[:wdir]}/#{@baclava_out}") if @baclava_out == ""
378
- @server.get_run_attribute(@uuid, "#{@links[:wdir]}/#{@baclava_out}")
554
+ raise RunStateError.new(state, :finished) if state != :finished
555
+
556
+ @server.get_run_attribute(@identifier, "#{@links[:wdir]}/out",
557
+ "application/zip", @credentials)
379
558
  end
380
559
 
381
560
  # :call-seq:
382
- # run.initialized? -> bool
561
+ # initialized? -> bool
383
562
  #
384
- # Is this run in the +Initialized+ state?
563
+ # Is this run in the :initialized state?
385
564
  def initialized?
386
- status == STATE[:initialized]
565
+ status == :initialized
387
566
  end
388
567
 
389
568
  # :call-seq:
390
- # run.running? -> bool
569
+ # running? -> bool
391
570
  #
392
- # Is this run in the +Running+ state?
571
+ # Is this run in the :running state?
393
572
  def running?
394
- status == STATE[:running]
573
+ status == :running
395
574
  end
396
575
 
397
576
  # :call-seq:
398
- # run.finished? -> bool
577
+ # finished? -> bool
399
578
  #
400
- # Is this run in the +Finished+ state?
579
+ # Is this run in the :finished state?
401
580
  def finished?
402
- status == STATE[:finished]
581
+ status == :finished
403
582
  end
404
583
 
405
584
  # :call-seq:
406
- # run.create_time -> string
585
+ # create_time -> string
407
586
  #
408
587
  # Get the creation time of this run as an instance of class Time.
409
588
  def create_time
410
- Time.parse(@server.get_run_attribute(@uuid, @links[:createtime]))
589
+ Time.parse(@server.get_run_attribute(@identifier, @links[:createtime],
590
+ "text/plain", @credentials))
411
591
  end
412
592
 
413
593
  # :call-seq:
414
- # run.start_time -> string
594
+ # start_time -> string
415
595
  #
416
596
  # Get the start time of this run as an instance of class Time.
417
597
  def start_time
418
- Time.parse(@server.get_run_attribute(@uuid, @links[:starttime]))
598
+ Time.parse(@server.get_run_attribute(@identifier, @links[:starttime],
599
+ "text/plain", @credentials))
419
600
  end
420
601
 
421
602
  # :call-seq:
422
- # run.finish_time -> string
603
+ # finish_time -> string
423
604
  #
424
605
  # Get the finish time of this run as an instance of class Time.
425
606
  def finish_time
426
- Time.parse(@server.get_run_attribute(@uuid, @links[:finishtime]))
607
+ Time.parse(@server.get_run_attribute(@identifier, @links[:finishtime],
608
+ "text/plain", @credentials))
609
+ end
610
+
611
+ # :call-seq:
612
+ # owner? -> bool
613
+ #
614
+ # Are the credentials being used to access this run those of the owner?
615
+ # The owner of the run can give other users certain access rights to their
616
+ # runs but only the owner can change these rights - or even see what they
617
+ # are. Sometimes it is useful to know if the user accessing the run is
618
+ # actually the owner of it or not.
619
+ def owner?
620
+ @credentials.username == @owner
621
+ end
622
+
623
+ # :call-seq:
624
+ # grant_permission(username, permission) -> username
625
+ #
626
+ # Grant the user the stated permission. A permission can be one of
627
+ # <tt>:none</tt>, <tt>:read</tt>, <tt>:update</tt> or <tt>:destroy</tt>.
628
+ # Only the owner of a run may grant permissions on it. +nil+ is returned
629
+ # if a user other than the owner uses this method.
630
+ def grant_permission(username, permission)
631
+ return unless owner?
632
+
633
+ value = XML::Fragments::PERM_UPDATE % [username, permission.to_s]
634
+ @server.create_run_attribute(@identifier, @links[:sec_perms], value,
635
+ "application/xml", @credentials)
636
+ end
637
+
638
+ # :call-seq:
639
+ # permissions -> hash
640
+ #
641
+ # Return a hash (username => permission) of all the permissions set for
642
+ # this run. Only the owner of a run may query its permissions. +nil+ is
643
+ # returned if a user other than the owner uses this method.
644
+ def permissions
645
+ return unless owner?
646
+
647
+ perms = {}
648
+ doc = xml_document(@server.get_run_attribute(@identifier,
649
+ @links[:sec_perms], "application/xml", @credentials))
650
+
651
+ xpath_find(doc, XPaths[:sec_perm]).each do |p|
652
+ user = xml_node_content(xpath_first(p, XPaths[:sec_uname]))
653
+ perm = xml_node_content(xpath_first(p, XPaths[:sec_uperm])).to_sym
654
+ perms[user] = perm
655
+ end
656
+
657
+ perms
658
+ end
659
+
660
+ # :call-seq:
661
+ # permission(username) -> permission
662
+ #
663
+ # Return the permission granted to the supplied username, if any. Only the
664
+ # owner of a run may query its permissions. +nil+ is returned if a user
665
+ # other than the owner uses this method.
666
+ def permission(username)
667
+ return unless owner?
668
+
669
+ permissions[username]
670
+ end
671
+
672
+ # :call-seq:
673
+ # revoke_permission(username) -> bool
674
+ #
675
+ # Revoke whatever permissions that have been granted to the user. Only the
676
+ # owner of a run may revoke permissions on it. +nil+ is returned if a user
677
+ # other than the owner uses this method.
678
+ def revoke_permission(username)
679
+ return unless owner?
680
+
681
+ path = "#{@links[:sec_perms]}/#{username}"
682
+ @server.delete_run_attribute(@identifier, path, @credentials)
683
+ end
684
+
685
+ # :call-seq:
686
+ # add_password_credential(service_uri, username, password) -> String
687
+ #
688
+ # Provide a username and password credential for the secure service at the
689
+ # specified URI. The id of the credential on the server is returned. Only
690
+ # the owner of a run may supply credentials for it. +nil+ is returned if a
691
+ # user other than the owner uses this method.
692
+ def add_password_credential(uri, username, password)
693
+ return unless owner?
694
+
695
+ # Is this a new credential, or an update?
696
+ id = credential(uri)
697
+
698
+ # basic uri checks
699
+ uri = _check_cred_uri(uri)
700
+
701
+ cred = XML::Fragments::USERPASS_CRED % [uri, username, password]
702
+ value = XML::Fragments::CREDENTIAL % cred
703
+
704
+ if id.nil?
705
+ @server.create_run_attribute(@identifier, @links[:sec_creds], value,
706
+ "application/xml", @credentials)
707
+ else
708
+ path = "#{@links[:sec_creds]}/#{id}"
709
+ @server.set_run_attribute(@identifier, path, value, "application/xml",
710
+ @credentials)
711
+ end
712
+ end
713
+
714
+ # :call-seq:
715
+ # add_keypair_credential(service_uri, filename, password,
716
+ # alias = "Imported Certificate", type = :pkcs12) -> String
717
+ #
718
+ # Provide a client certificate credential for the secure service at the
719
+ # specified URI. You will need to provide the password to unlock the
720
+ # private key. You will also need to provide the 'alias' or 'friendlyName'
721
+ # of the key you wish to use if it differs from the default. The id of the
722
+ # credential on the server is returned. Only the owner of a run may supply
723
+ # credentials for it. +nil+ is returned if a user other than the owner uses
724
+ # this method.
725
+ def add_keypair_credential(uri, filename, password,
726
+ name = "Imported Certificate", type = :pkcs12)
727
+ return unless owner?
728
+
729
+ type = type.to_s.upcase
730
+ contents = Base64.encode64(IO.read(filename))
731
+
732
+ # basic uri checks
733
+ uri = _check_cred_uri(uri)
734
+
735
+ cred = XML::Fragments::KEYPAIR_CRED % [uri, name, contents,
736
+ type, password]
737
+ value = XML::Fragments::CREDENTIAL % cred
738
+
739
+ @server.create_run_attribute(@identifier, @links[:sec_creds], value,
740
+ "application/xml", @credentials)
427
741
  end
428
742
 
743
+ # :call-seq:
744
+ # credentials -> Hash
745
+ #
746
+ # Return a hash (service_uri => identifier) of all the credentials provided
747
+ # for this run. Only the owner of a run may query its credentials. +nil+ is
748
+ # returned if a user other than the owner uses this method.
749
+ def credentials
750
+ return unless owner?
751
+
752
+ creds = {}
753
+ doc = xml_document(@server.get_run_attribute(@identifier,
754
+ @links[:sec_creds], "application/xml", @credentials))
755
+
756
+ xpath_find(doc, XPaths[:sec_cred]).each do |c|
757
+ uri = xml_node_content(xpath_first(c, XPaths[:sec_suri]))
758
+ id = xml_node_attribute(c, "href").split('/')[-1]
759
+ creds[uri] = id
760
+ end
761
+
762
+ creds
763
+ end
764
+
765
+ # :call-seq:
766
+ # credential(service_uri) -> String
767
+ #
768
+ # Return the identifier of the credential set for the supplied service, if
769
+ # any. Only the owner of a run may query its credentials. +nil+ is
770
+ # returned if a user other than the owner uses this method.
771
+ def credential(uri)
772
+ return unless owner?
773
+
774
+ credentials[uri]
775
+ end
776
+
777
+ # :call-seq:
778
+ # delete_credential(service_uri) -> bool
779
+ #
780
+ # Delete the credential that has been provided for the specified service.
781
+ # Only the owner of a run may delete its credentials. +nil+ is returned if
782
+ # a user other than the owner uses this method.
783
+ def delete_credential(uri)
784
+ return unless owner?
785
+
786
+ path = "#{@links[:sec_creds]}/#{credentials[uri]}"
787
+ @server.delete_run_attribute(@identifier, path, @credentials)
788
+ end
789
+
790
+ # :call-seq:
791
+ # delete_all_credentials -> bool
792
+ #
793
+ # Delete all credentials associated with this workflow run. Only the owner
794
+ # of a run may delete its credentials. +nil+ is returned if a user other
795
+ # than the owner uses this method.
796
+ def delete_all_credentials
797
+ return unless owner?
798
+
799
+ @server.delete_run_attribute(@identifier, @links[:sec_creds],
800
+ @credentials)
801
+ end
802
+
803
+ # :call-seq:
804
+ # add_trust(filename, type = :x509) -> String
805
+ #
806
+ # Add a trusted identity (server public key) to verify peers when using
807
+ # https connections to Web Services. The id of the trust on the server is
808
+ # returned. Only the owner of a run may add a trust. +nil+ is returned if
809
+ # a user other than the owner uses this method.
810
+ def add_trust(filename, type = :x509)
811
+ return unless owner?
812
+
813
+ type = type.to_s.upcase
814
+
815
+ contents = Base64.encode64(IO.read(filename))
816
+
817
+ value = XML::Fragments::TRUST % [contents, type]
818
+ @server.create_run_attribute(@identifier, @links[:sec_trusts], value,
819
+ "application/xml", @credentials)
820
+ end
821
+
822
+ # :call-seq:
823
+ # trusts -> Array
824
+ #
825
+ # Return a list of all the ids of trusts that have been registered for this
826
+ # run. At present there is no way to differentiate between trusts without
827
+ # noting the id returned when originally uploaded. Only the owner of a run
828
+ # may query its trusts. +nil+ is returned if a user other than the owner
829
+ # uses this method.
830
+ def trusts
831
+ return unless owner?
832
+
833
+ t_ids = []
834
+ doc = xml_document(@server.get_run_attribute(@identifier,
835
+ @links[:sec_trusts], "application/xml", @credentials))
836
+
837
+ xpath_find(doc, XPaths[:sec_trust]). each do |t|
838
+ t_ids << xml_node_attribute(t, "href").split('/')[-1]
839
+ end
840
+
841
+ t_ids
842
+ end
843
+
844
+ # :call-seq:
845
+ # delete_trust(id) -> bool
846
+ #
847
+ # Delete the trust with the provided id. Only the owner of a run may
848
+ # delete its trusts. +nil+ is returned if a user other than the owner uses
849
+ # this method.
850
+ def delete_trust(id)
851
+ return unless owner?
852
+
853
+ path = "#{@links[:sec_trusts]}/#{id}"
854
+ @server.delete_run_attribute(@identifier, path, @credentials)
855
+ end
856
+
857
+ # :call-seq:
858
+ # delete_all_trusts -> bool
859
+ #
860
+ # Delete all trusted identities associated with this workflow run. Only
861
+ # the owner of a run may delete its trusts. +nil+ is returned if a user
862
+ # other than the owner uses this method.
863
+ def delete_all_trusts
864
+ return unless owner?
865
+
866
+ @server.delete_run_attribute(@identifier, @links[:sec_trusts],
867
+ @credentials)
868
+ end
869
+
870
+ # :stopdoc:
871
+ # Outputs are represented as a directory structure with the eventual list
872
+ # items (leaves) as files. This method (not part of the public API)
873
+ # downloads a file from the run's working directory.
874
+ def download_output_data(path, range = nil)
875
+ @server.download_run_file(@identifier, "#{@links[:wdir]}/out/#{path}",
876
+ range, @credentials)
877
+ end
878
+ # :startdoc:
879
+
429
880
  private
430
881
 
882
+ # Check each input to see if it requires a list input and call the
883
+ # requisite upload method for the entire set of inputs.
884
+ def _check_and_set_inputs
885
+ lists = false
886
+ input_ports.each_value do |port|
887
+ if port.depth > 0
888
+ lists = true
889
+ break
890
+ end
891
+ end
892
+
893
+ lists ? _fake_lists : _set_all_inputs
894
+ end
895
+
896
+ # Set all the inputs on the server. The inputs must have been set prior to
897
+ # this call using the InputPort API.
898
+ def _set_all_inputs
899
+ input_ports.each_value do |port|
900
+ next unless port.set?
901
+
902
+ if port.file?
903
+ # If we're using a local file upload it first then set the port to
904
+ # use a remote file.
905
+ unless port.remote_file?
906
+ file = upload_file(port.file)
907
+ port.remote_file = file
908
+ end
909
+
910
+ xml_value = xml_text_node(port.file)
911
+ path = "#{@links[:inputs]}/input/#{port.name}"
912
+ @server.set_run_attribute(self, path,
913
+ XML::Fragments::RUNINPUTFILE % xml_value, "application/xml",
914
+ @credentials)
915
+ else
916
+ xml_value = xml_text_node(port.value)
917
+ path = "#{@links[:inputs]}/input/#{port.name}"
918
+ @server.set_run_attribute(self, path,
919
+ XML::Fragments::RUNINPUTVALUE % xml_value, "application/xml",
920
+ @credentials)
921
+ end
922
+ end
923
+ end
924
+
925
+ # Fake being able to handle lists as inputs by converting everything into
926
+ # one big baclava document and uploading that. This has to be done for all
927
+ # inputs or none at all. The inputs must have been set prior to this call
928
+ # using the InputPort API.
929
+ def _fake_lists
930
+ data_map = {}
931
+
932
+ input_ports.each_value do |port|
933
+ next unless port.set?
934
+
935
+ if port.file?
936
+ unless port.remote_file?
937
+ file = File.read(port.file)
938
+ data_map[port.name] = Taverna::Baclava::Node.new(file)
939
+ end
940
+ else
941
+ data_map[port.name] = Taverna::Baclava::Node.new(port.value)
942
+ end
943
+ end
944
+
945
+ # Create and upload the baclava data.
946
+ baclava = Taverna::Baclava::Writer.write(data_map)
947
+ upload_data(baclava, "in.baclava")
948
+ @server.set_run_attribute(@identifier, @links[:baclava], "in.baclava",
949
+ "text/plain", @credentials)
950
+ end
951
+
952
+ # Check that the uri passed in is suitable for credential use:
953
+ # * rserve uris must not have a path.
954
+ # * http(s) uris must have at least "/" as their path.
955
+ def _check_cred_uri(uri)
956
+ u = URI(uri)
957
+
958
+ case u.scheme
959
+ when "rserve"
960
+ u.path = ""
961
+ when /https?/
962
+ u.path = "/" if u.path == ""
963
+ end
964
+
965
+ u.to_s
966
+ end
967
+
431
968
  # List a directory in the run's workspace on the server. If dir is left
432
969
  # blank then / is listed. As there is no concept of changing into a
433
970
  # directory (cd) in Taverna Server then all paths passed into _ls_ports
434
971
  # should be full paths starting at "root". The contents of a directory are
435
972
  # returned as a list of two lists, "lists" and "values" respectively.
436
973
  def _ls_ports(dir="", top=true)
437
- dir.strip_path!
438
- dir_list = @server.get_run_attribute(@uuid, "#{@links[:wdir]}/#{dir}")
974
+ dir = Util.strip_path_slashes(dir)
975
+ dir_list = @server.get_run_attribute(@identifier,
976
+ "#{@links[:wdir]}/#{dir}", "*/*", @credentials)
439
977
 
440
978
  # compile a list of directory entries stripping the
441
979
  # directory name from the front of each filename
442
980
  lists = []
443
981
  values = []
444
982
 
445
- begin
446
- doc = XML::Document.string(dir_list)
983
+ doc = xml_document(dir_list)
447
984
 
448
- doc.find(XPaths::DIR, Namespaces::MAP).each do |e|
449
- if top
450
- lists << e.content.split('/')[-1]
451
- else
452
- index = (e.attributes['name'].to_i - 1)
453
- lists[index] = e.content.split('/')[-1]
454
- end
985
+ xpath_find(doc, XPaths[:dir]).each do |e|
986
+ if top
987
+ lists << xml_node_content(e).split('/')[-1]
988
+ else
989
+ index = (xml_node_attribute(e, 'name').to_i - 1)
990
+ lists[index] = xml_node_content(e).split('/')[-1]
455
991
  end
992
+ end
456
993
 
457
- doc.find(XPaths::FILE, Namespaces::MAP).each do |e|
458
- if top
459
- values << e.content.split('/')[-1]
460
- else
461
- index = (e.attributes['name'].to_i - 1)
462
- values[index] = e.content.split('/')[-1]
463
- end
464
- end
465
- rescue XML::Error => xmle
466
- # We expect to get a DOCUMENT_EMPTY error in some cases. All others
467
- # should be re-raised.
468
- if xmle.code != XML::Error::DOCUMENT_EMPTY
469
- raise xmle
994
+ xpath_find(doc, XPaths[:file]).each do |e|
995
+ if top
996
+ values << xml_node_content(e).split('/')[-1]
997
+ else
998
+ index = (xml_node_attribute(e, 'name').to_i - 1)
999
+ values[index] = xml_node_content(e).split('/')[-1]
470
1000
  end
471
1001
  end
472
1002
 
@@ -474,7 +1004,7 @@ module T2Server
474
1004
  end
475
1005
 
476
1006
  def _get_output(output, refs=false, top=true)
477
- output.strip_path!
1007
+ output = Util.strip_path_slashes(output)
478
1008
 
479
1009
  # if at the top level we need to check if the port represents a list
480
1010
  # or a singleton value
@@ -482,9 +1012,12 @@ module T2Server
482
1012
  lists, items = _ls_ports("out")
483
1013
  if items.include? output
484
1014
  if refs
485
- return "#{@server.uri}/rest/runs/#{@uuid}/#{@links[:wdir]}/out/#{output}"
1015
+ return "#{@server.uri}/rest/runs/#{@identifier}/" +
1016
+ "#{@links[:wdir]}/out/#{output}"
486
1017
  else
487
- return @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}")
1018
+ return @server.get_run_attribute(@identifier,
1019
+ "#{@links[:wdir]}/out/#{output}", "application/octet-stream",
1020
+ @credentials)
488
1021
  end
489
1022
  end
490
1023
  end
@@ -496,55 +1029,117 @@ module T2Server
496
1029
  result = []
497
1030
 
498
1031
  # for each list recurse into it and add the items to the result
499
- lists.each {|list| result << _get_output("#{output}/#{list}", refs, false)}
1032
+ lists.each do |list|
1033
+ result << _get_output("#{output}/#{list}", refs, false)
1034
+ end
500
1035
 
501
1036
  # for each item, add it to the output list
502
1037
  items.each do |item|
503
1038
  if refs
504
- result << "#{@server.uri}/rest/runs/#{@uuid}/#{@links[:wdir]}/out/#{output}/#{item}"
1039
+ result << "#{@server.uri}/rest/runs/#{@identifier}/" +
1040
+ "#{@links[:wdir]}/out/#{output}/#{item}"
505
1041
  else
506
- result << @server.get_run_attribute(@uuid, "#{@links[:wdir]}/out/#{output}/#{item}")
1042
+ result << @server.get_run_attribute(@identifier,
1043
+ "#{@links[:wdir]}/out/#{output}/#{item}",
1044
+ "application/octet-stream", @credentials)
507
1045
  end
508
1046
  end
509
1047
 
510
1048
  result
511
1049
  end
512
1050
 
513
- def get_attributes(desc)
1051
+ def _get_input_port_info
1052
+ ports = {}
1053
+ port_desc = @server.get_run_attribute(@identifier, @links[:inputexp],
1054
+ "application/xml", @credentials)
1055
+
1056
+ doc = xml_document(port_desc)
1057
+
1058
+ xpath_find(doc, XPaths[:port_in]).each do |inp|
1059
+ port = InputPort.new(self, inp)
1060
+ ports[port.name] = port
1061
+ end
1062
+
1063
+ ports
1064
+ end
1065
+
1066
+ def _get_output_port_info
1067
+ ports = {}
1068
+ port_desc = @server.get_run_attribute(@identifier, @links[:output],
1069
+ "application/xml", @credentials)
1070
+
1071
+ doc = xml_document(port_desc)
1072
+
1073
+ xpath_find(doc, XPaths[:port_out]).each do |out|
1074
+ port = OutputPort.new(self, out)
1075
+ ports[port.name] = port
1076
+ end
1077
+
1078
+ ports
1079
+ end
1080
+
1081
+ def get_attributes(doc)
514
1082
  # first parse out the basic stuff
515
- links = parse_description(desc)
516
-
1083
+ links = {}
1084
+
1085
+ [:expiry, :workflow, :status, :createtime, :starttime, :finishtime,
1086
+ :wdir, :inputs, :output, :securectx, :listeners].each do |key|
1087
+ links[key] = xpath_attr(doc, XPaths[key], "href").split('/')[-1]
1088
+ end
1089
+
517
1090
  # get inputs
518
- inputs = @server.get_run_attribute(@uuid, links[:inputs])
519
- doc = XML::Document.string(inputs)
520
- nsmap = Namespaces::MAP
521
- links[:baclava] = "#{links[:inputs]}/" + doc.find_first(XPaths::BACLAVA, nsmap).attributes["href"].split('/')[-1]
1091
+ inputs = @server.get_run_attribute(@identifier, links[:inputs],
1092
+ "application/xml",@credentials)
1093
+ doc = xml_document(inputs)
1094
+
1095
+ links[:baclava] = "#{links[:inputs]}/" +
1096
+ xpath_attr(doc, XPaths[:baclava], "href").split('/')[-1]
1097
+ links[:inputexp] = "#{links[:inputs]}/" +
1098
+ xpath_attr(doc, XPaths[:inputexp], "href").split('/')[-1]
522
1099
 
523
1100
  # set io properties
524
1101
  links[:io] = "#{links[:listeners]}/io"
525
1102
  links[:stdout] = "#{links[:io]}/properties/stdout"
526
1103
  links[:stderr] = "#{links[:io]}/properties/stderr"
527
1104
  links[:exitcode] = "#{links[:io]}/properties/exitcode"
528
-
1105
+
1106
+ # security properties - only available to the owner of a run
1107
+ if owner?
1108
+ securectx = @server.get_run_attribute(@identifier, links[:securectx],
1109
+ "application/xml", @credentials)
1110
+ doc = xml_document(securectx)
1111
+
1112
+ [:sec_creds, :sec_perms, :sec_trusts].each do |key|
1113
+ links[key] = "#{links[:securectx]}/" + xpath_attr(doc, XPaths[key],
1114
+ "href").split('/')[-1]
1115
+ end
1116
+ end
1117
+
529
1118
  links
530
1119
  end
531
1120
 
532
- def parse_description(desc)
533
- doc = XML::Document.string(desc)
534
- nsmap = Namespaces::MAP
535
- {
536
- :expiry => doc.find_first(XPaths::EXPIRY, nsmap).attributes["href"].split('/')[-1],
537
- :workflow => doc.find_first(XPaths::WORKFLOW, nsmap).attributes["href"].split('/')[-1],
538
- :status => doc.find_first(XPaths::STATUS, nsmap).attributes["href"].split('/')[-1],
539
- :createtime => doc.find_first(XPaths::CREATETIME, nsmap).attributes["href"].split('/')[-1],
540
- :starttime => doc.find_first(XPaths::STARTTIME, nsmap).attributes["href"].split('/')[-1],
541
- :finishtime => doc.find_first(XPaths::FINISHTIME, nsmap).attributes["href"].split('/')[-1],
542
- :wdir => doc.find_first(XPaths::WDIR, nsmap).attributes["href"].split('/')[-1],
543
- :inputs => doc.find_first(XPaths::INPUTS, nsmap).attributes["href"].split('/')[-1],
544
- :output => doc.find_first(XPaths::OUTPUT, nsmap).attributes["href"].split('/')[-1],
545
- :securectx => doc.find_first(XPaths::SECURECTX, nsmap).attributes["href"].split('/')[-1],
546
- :listeners => doc.find_first(XPaths::LISTENERS, nsmap).attributes["href"].split('/')[-1]
547
- }
1121
+ # :stopdoc:
1122
+ STATE2TEXT = {
1123
+ :initialized => "Initialized",
1124
+ :running => "Operating",
1125
+ :finished => "Finished",
1126
+ :stopped => "Stopped"
1127
+ }
1128
+
1129
+ TEXT2STATE = {
1130
+ "Initialized" => :initialized,
1131
+ "Operating" => :running,
1132
+ "Finished" => :finished,
1133
+ "Stopped" => :stopped
1134
+ }
1135
+ # :startdoc:
1136
+
1137
+ def state_to_text(state)
1138
+ STATE2TEXT[state.to_sym]
1139
+ end
1140
+
1141
+ def text_to_state(text)
1142
+ TEXT2STATE[text]
548
1143
  end
549
1144
  end
550
1145
  end