aspera-cli 4.2.0 → 4.4.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +749 -353
  3. data/docs/Makefile +4 -4
  4. data/docs/README.erb.md +743 -283
  5. data/docs/doc_tools.rb +58 -0
  6. data/docs/test_env.conf +9 -1
  7. data/examples/aoc.rb +14 -3
  8. data/examples/faspex4.rb +89 -0
  9. data/lib/aspera/aoc.rb +24 -22
  10. data/lib/aspera/cli/main.rb +48 -20
  11. data/lib/aspera/cli/plugin.rb +13 -6
  12. data/lib/aspera/cli/plugins/aoc.rb +117 -78
  13. data/lib/aspera/cli/plugins/config.rb +127 -80
  14. data/lib/aspera/cli/plugins/faspex.rb +112 -63
  15. data/lib/aspera/cli/plugins/faspex5.rb +29 -25
  16. data/lib/aspera/cli/plugins/node.rb +54 -25
  17. data/lib/aspera/cli/plugins/preview.rb +94 -68
  18. data/lib/aspera/cli/plugins/server.rb +16 -5
  19. data/lib/aspera/cli/transfer_agent.rb +92 -72
  20. data/lib/aspera/cli/version.rb +1 -1
  21. data/lib/aspera/command_line_builder.rb +48 -31
  22. data/lib/aspera/cos_node.rb +4 -3
  23. data/lib/aspera/fasp/http_gw.rb +47 -26
  24. data/lib/aspera/fasp/local.rb +31 -24
  25. data/lib/aspera/fasp/manager.rb +3 -0
  26. data/lib/aspera/fasp/node.rb +23 -1
  27. data/lib/aspera/fasp/parameters.rb +72 -89
  28. data/lib/aspera/fasp/parameters.yaml +531 -0
  29. data/lib/aspera/fasp/uri.rb +1 -1
  30. data/lib/aspera/faspex_gw.rb +10 -9
  31. data/lib/aspera/id_generator.rb +22 -0
  32. data/lib/aspera/node.rb +11 -3
  33. data/lib/aspera/oauth.rb +131 -135
  34. data/lib/aspera/persistency_action_once.rb +11 -7
  35. data/lib/aspera/persistency_folder.rb +6 -26
  36. data/lib/aspera/rest.rb +1 -1
  37. data/lib/aspera/sync.rb +40 -35
  38. data/lib/aspera/timer_limiter.rb +22 -0
  39. data/lib/aspera/web_auth.rb +105 -0
  40. metadata +22 -4
  41. data/docs/transfer_spec.html +0 -99
  42. data/lib/aspera/fasp/aoc.rb +0 -24
@@ -7,8 +7,10 @@ require 'aspera/persistency_action_once'
7
7
  require 'aspera/open_application'
8
8
  require 'aspera/fasp/uri'
9
9
  require 'aspera/nagios'
10
+ require 'aspera/id_generator'
10
11
  require 'xmlsimple'
11
12
  require 'json'
13
+ require 'cgi'
12
14
 
13
15
  module Aspera
14
16
  module Cli
@@ -17,7 +19,19 @@ module Aspera
17
19
  KEY_NODE='node'
18
20
  KEY_PATH='path'
19
21
  VAL_ALL='ALL'
20
- private_constant :KEY_NODE,:KEY_PATH,:VAL_ALL
22
+ # added field in result that identifies the package
23
+ PACKAGE_MATCH_FIELD='package_id'
24
+ # list of supported atoms
25
+ ATOM_MAILBOXES=[:inbox, :archive, :sent]
26
+ # allowed parameters for inbox.atom
27
+ ATOM_PARAMS=['page', 'count', 'startIndex']
28
+ # with special parameters (from Plugin class)
29
+ ATOM_EXT_PARAMS=ATOM_PARAMS+[MAX_ITEMS, MAX_PAGES]
30
+ # sub path in url for public link delivery
31
+ PUB_LINK_EXTERNAL_MATCH='external_deliveries/'
32
+ private_constant :KEY_NODE,:KEY_PATH,:VAL_ALL,:PACKAGE_MATCH_FIELD,:ATOM_MAILBOXES,
33
+ :ATOM_PARAMS,:ATOM_EXT_PARAMS,:PUB_LINK_EXTERNAL_MATCH
34
+
21
35
  def initialize(env)
22
36
  @api_v3=nil
23
37
  @api_v4=nil
@@ -26,7 +40,8 @@ module Aspera
26
40
  self.options.add_opt_simple(:delivery_info,'package delivery information (extended value)')
27
41
  self.options.add_opt_simple(:source_name,'create package from remote source (by name)')
28
42
  self.options.add_opt_simple(:storage,'Faspex local storage definition')
29
- self.options.add_opt_list(:box,[:inbox,:sent,:archive],'package box')
43
+ self.options.add_opt_simple(:recipient,'use if recipient is a dropbox (with *)')
44
+ self.options.add_opt_list(:box,ATOM_MAILBOXES,'package box')
30
45
  self.options.set_option(:box,:inbox)
31
46
  self.options.parse_options!
32
47
  end
@@ -101,29 +116,57 @@ module Aspera
101
116
  return @api_v4
102
117
  end
103
118
 
104
- ACTIONS=[ :health,:package, :source, :me, :dropbox, :v4, :address_book, :login_methods ]
105
-
106
- # we match recv command on atom feed on this field
107
- PACKAGE_MATCH_FIELD='package_id'
108
-
119
+ # query supports : {"startIndex":10,"count":1,"page":109}
109
120
  def mailbox_all_entries
110
- my_user_name=self.options.get_option(:username,:mandatory)
121
+ recipient_name=self.options.get_option(:recipient,:optional) || self.options.get_option(:username,:mandatory)
122
+ # mailbox is in ATOM_MAILBOXES
111
123
  mailbox=self.options.get_option(:box,:mandatory)
112
- all_inbox_xml=api_v3.call({:operation=>'GET',:subpath=>"#{mailbox}.atom",:headers=>{'Accept'=>'application/xml'}})[:http].body
113
- all_inbox_data=XmlSimple.xml_in(all_inbox_xml, {'ForceArray' => true})
114
- Log.dump(:all_inbox_data,all_inbox_data)
115
- result=all_inbox_data.has_key?('entry') ? all_inbox_data['entry'] : []
116
- result.each do |e|
117
- case mailbox
118
- when :inbox,:archive
119
- recipient=e['to'].select{|i|i['name'].first.eql?(my_user_name)}.first
120
- e[PACKAGE_MATCH_FIELD]=recipient.nil? ? 'n/a' : recipient['recipient_delivery_id'].first
121
- when :sent
122
- e[PACKAGE_MATCH_FIELD]=e['delivery_id'].first
124
+ # parameters
125
+ mailbox_query=self.options.get_option(:query,:optional)
126
+ max_items=nil
127
+ max_pages=nil
128
+ result=[]
129
+ if !mailbox_query.nil?
130
+ raise "query: must be Hash or nil" unless mailbox_query.is_a?(Hash)
131
+ raise "query: supported params: #{ATOM_EXT_PARAMS}" unless (mailbox_query.keys-ATOM_EXT_PARAMS).empty?
132
+ raise "query: startIndex and page are exclusive" if mailbox_query.has_key?('startIndex') and mailbox_query.has_key?('page')
133
+ max_items=mailbox_query[MAX_ITEMS]
134
+ mailbox_query.delete(MAX_ITEMS)
135
+ max_pages=mailbox_query[MAX_PAGES]
136
+ mailbox_query.delete(MAX_PAGES)
137
+ end
138
+ loop do
139
+ atom_xml=api_v3.call({operation: 'GET',subpath: "#{mailbox}.atom",headers: {'Accept'=>'application/xml'},url_params: mailbox_query})[:http].body
140
+ box_data=XmlSimple.xml_in(atom_xml, {'ForceArray' => true})
141
+ Log.dump(:box_data,box_data)
142
+ items=box_data.has_key?('entry') ? box_data['entry'] : []
143
+ Log.log.debug("new items: #{items.count}")
144
+ # it is the end if page is empty
145
+ break if items.empty?
146
+ items.each do |package|
147
+ package[PACKAGE_MATCH_FIELD]=case mailbox
148
+ when :inbox,:archive
149
+ recipient=package['to'].select{|i|i['name'].first.eql?(recipient_name)}.first
150
+ recipient.nil? ? nil : recipient['recipient_delivery_id'].first
151
+ else # :sent
152
+ package['delivery_id'].first
153
+ end
154
+ # keep only those for the specified recipient
155
+ result.push(package) unless package[PACKAGE_MATCH_FIELD].nil?
123
156
  end
157
+ Log.log.debug("total items: #{result.count}")
158
+ # reach the limit ?
159
+ break if !max_items.nil? and result.count > max_items
160
+ link=box_data['link'].select{|i|i['rel'].eql?('next')}.first
161
+ Log.log.debug("link: #{link}")
162
+ # no next link
163
+ break if link.nil?
164
+ # replace parameters with the ones from next link
165
+ params=CGI.parse(URI.parse(link['href']).query)
166
+ mailbox_query=params.keys.inject({}){|m,i|;m[i]=params[i].first;m}
167
+ Log.log.debug("query: #{mailbox_query}")
168
+ break if !max_pages.nil? and mailbox_query['page'].to_i > max_pages
124
169
  end
125
- # remove dropbox packages
126
- result.select!{|p|p['metadata'].first['field'].select{|j|j['name'].eql?('_dropbox_name')}.empty? rescue false}
127
170
  return result
128
171
  end
129
172
 
@@ -159,6 +202,8 @@ module Aspera
159
202
  return pkgdatares.first
160
203
  end
161
204
 
205
+ ACTIONS=[ :health,:package, :source, :me, :dropbox, :v4, :address_book, :login_methods ]
206
+
162
207
  def execute_action
163
208
  command=self.options.get_next_command(ACTIONS)
164
209
  case command
@@ -208,52 +253,59 @@ module Aspera
208
253
  #Log.dump('transfer_spec',transfer_spec)
209
254
  return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
210
255
  when :recv
211
- public_link_url=self.options.get_option(:link,:optional)
212
- if !public_link_url.nil?
213
- link_data=self.class.get_link_data(public_link_url)
214
- if !link_data[:subpath].match(%r{external_deliveries/})
215
- raise CliBadArgument,"pub link is #{link_data[:subpath]}, expecting external_deliveries/"
256
+ link_url=self.options.get_option(:link,:optional)
257
+ # list of faspex ID/URI to download
258
+ pkg_id_uri=nil
259
+ skip_ids_data=[]
260
+ skip_ids_persistency=nil
261
+ case link_url
262
+ when nil # usual case: no link
263
+ if self.options.get_option(:once_only,:mandatory)
264
+ skip_ids_persistency=PersistencyActionOnce.new(
265
+ manager: @agents[:persistency],
266
+ data: skip_ids_data,
267
+ id: IdGenerator.from_list(['faspex_recv',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),self.options.get_option(:box,:mandatory).to_s]))
268
+ end
269
+ # get command line parameters
270
+ delivid=self.options.get_option(:id,:mandatory)
271
+ if delivid.eql?(VAL_ALL)
272
+ pkg_id_uri=mailbox_all_entries.map{|i|{:id=>i[PACKAGE_MATCH_FIELD],:uri=>self.class.get_fasp_uri_from_entry(i)}}
273
+ # TODO : remove ids from skip not present in inbox
274
+ # skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
275
+ pkg_id_uri.select!{|i|!skip_ids_data.include?(i[:id])}
276
+ else
277
+ recipient=options.get_option(:recipient,:optional)
278
+ if !recipient.nil? and recipient.start_with?('*')
279
+ raise "Dropbox and Workgroup packages should use link option with faspe:"
280
+ end
281
+ # TODO: delivery id is the right one if package was receive by workgroup
282
+ endpoint=case self.options.get_option(:box,:mandatory)
283
+ when :inbox,:archive;'received'
284
+ when :sent; 'sent'
285
+ end
286
+ entry_xml=api_v3.call({:operation=>'GET',:subpath=>"#{endpoint}/#{delivid}",:headers=>{'Accept'=>'application/xml'}})[:http].body
287
+ package_entry=XmlSimple.xml_in(entry_xml, {'ForceArray' => true})
288
+ pkg_id_uri=[{:id=>delivid,:uri=>self.class.get_fasp_uri_from_entry(package_entry)}]
289
+ end
290
+ when /^faspe:/
291
+ pkg_id_uri=[{:id=>'package',:uri=>link_url}]
292
+ else
293
+ link_data=self.class.get_link_data(link_url)
294
+ if !link_data[:subpath].start_with?(PUB_LINK_EXTERNAL_MATCH)
295
+ raise CliBadArgument,"Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
216
296
  end
217
- # Note: unauthenticated API
297
+ # Note: unauthenticated API (autorization is in url params)
218
298
  api_public_link=Rest.new({:base_url=>link_data[:base_url]})
219
299
  pkgdatares=api_public_link.call({:operation=>'GET',:subpath=>link_data[:subpath],:url_params=>{:passcode=>link_data[:query]['passcode']},:headers=>{'Accept'=>'application/xml'}})
220
300
  if !pkgdatares[:http].body.start_with?('<?xml ')
221
- OpenApplication.instance.uri(public_link_url)
301
+ OpenApplication.instance.uri(link_url)
222
302
  raise CliError, 'no such package'
223
303
  end
224
304
  package_entry=XmlSimple.xml_in(pkgdatares[:http].body, {'ForceArray' => false})
305
+ Log.dump(:package_entry,package_entry)
225
306
  transfer_uri=self.class.get_fasp_uri_from_entry(package_entry)
226
- transfer_spec=Fasp::Uri.new(transfer_uri).transfer_spec
227
- transfer_spec['direction']='receive'
228
- return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
307
+ pkg_id_uri=[{:id=>package_entry['id'],:uri=>transfer_uri}]
229
308
  end # public link
230
- # get command line parameters
231
- delivid=self.options.get_option(:id,:mandatory)
232
- # list of faspex ID/URI to download
233
- pkg_id_uri=nil
234
- skip_ids_data=[]
235
- skip_ids_persistency=nil
236
- if self.options.get_option(:once_only,:mandatory)
237
- skip_ids_persistency=PersistencyActionOnce.new(
238
- manager: @agents[:persistency],
239
- data: skip_ids_data,
240
- ids: ['faspex_recv',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),self.options.get_option(:box,:mandatory).to_s])
241
- end
242
- if delivid.eql?(VAL_ALL)
243
- pkg_id_uri=mailbox_all_entries.map{|i|{:id=>i[PACKAGE_MATCH_FIELD],:uri=>self.class.get_fasp_uri_from_entry(i)}}
244
- # TODO : remove ids from skip not present in inbox
245
- # skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
246
- pkg_id_uri.select!{|i|!skip_ids_data.include?(i[:id])}
247
- else
248
- # TODO: delivery id is the right one if package was receive by group
249
- endpoint=case self.options.get_option(:box,:mandatory)
250
- when :inbox,:archive;'received'
251
- when :sent; 'sent'
252
- end
253
- entry_xml=api_v3.call({:operation=>'GET',:subpath=>"#{endpoint}/#{delivid}",:headers=>{'Accept'=>'application/xml'}})[:http].body
254
- package_entry=XmlSimple.xml_in(entry_xml, {'ForceArray' => true})
255
- pkg_id_uri=[{:id=>delivid,:uri=>self.class.get_fasp_uri_from_entry(package_entry)}]
256
- end
257
309
  Log.dump(:pkg_id_uri,pkg_id_uri)
258
310
  return Main.result_status('no package') if pkg_id_uri.empty?
259
311
  result_transfer=[]
@@ -262,20 +314,17 @@ module Aspera
262
314
  # NOTE: only external users have token in faspe: link !
263
315
  if !transfer_spec.has_key?('token')
264
316
  sanitized=id_uri[:uri].gsub('&','&amp;')
265
- # TODO: file jira
266
- #XXsanitized.gsub!(/%3D%3D$/,'==')
267
- #XXsanitized.gsub!(/%3D$/,'=')
268
317
  xmlpayload='<?xml version="1.0" encoding="UTF-8"?><url-list xmlns="http://schemas.asperasoft.com/xml/url-list"><url href="'+sanitized+'"/></url-list>'
269
318
  transfer_spec['token']=api_v3.call({:operation=>'POST',:subpath=>'issue-token?direction=down',:headers=>{'Accept'=>'text/plain','Content-Type'=>'application/vnd.aspera.url-list+xml'},:text_body_params=>xmlpayload})[:http].body
270
319
  end
271
320
  transfer_spec['direction']='receive'
272
321
  statuses=self.transfer.start(transfer_spec,{:src=>:node_gen3})
273
- result_transfer.push({'package'=>id_uri[:id],'status'=>statuses.map{|i|i.to_s}.join(',')})
322
+ result_transfer.push({'package'=>id_uri[:id],Main::STATUS_FIELD=>statuses})
274
323
  # skip only if all sessions completed
275
324
  skip_ids_data.push(id_uri[:id]) if TransferAgent.session_status(statuses).eql?(:success)
276
325
  end
277
326
  skip_ids_persistency.save unless skip_ids_persistency.nil?
278
- return {:type=>:object_list,:data=>result_transfer}
327
+ return Main.result_transfer_multiple(result_transfer)
279
328
  end
280
329
  when :source
281
330
  command_source=self.options.get_next_command([ :list, :id, :name ])
@@ -1,5 +1,6 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/persistency_action_once'
3
+ require 'aspera/id_generator'
3
4
  require 'securerandom'
4
5
 
5
6
  module Aspera
@@ -17,14 +18,20 @@ module Aspera
17
18
  options.set_option(:auth,:jwt)
18
19
  options.parse_options!
19
20
  end
20
- ACTIONS=[ :node, :package, :auth_client ]
21
21
 
22
22
  def set_api
23
- faxpex5_api_base_url=options.get_option(:url,:mandatory)
24
- faxpex5_api_v5_url="#{faxpex5_api_base_url}/api/v5"
25
- faxpex5_api_auth_url="#{faxpex5_api_base_url}/auth"
23
+ @faxpex5_api_base_url=options.get_option(:url,:mandatory)
24
+ faxpex5_api_v5_url="#{@faxpex5_api_base_url}/api/v5"
25
+ faxpex5_api_auth_url="#{@faxpex5_api_base_url}/auth"
26
26
  case options.get_option(:auth,:mandatory)
27
+ when :boot
28
+ # the password here is the token copied directly from browser in developer mode
29
+ @api_v5=Rest.new({
30
+ :base_url => faxpex5_api_v5_url,
31
+ :headers => {'Authorization'=>options.get_option(:password,:mandatory)},
32
+ })
27
33
  when :web
34
+ # opens a browser and ask user to auth using web
28
35
  @api_v5=Rest.new({
29
36
  :base_url => faxpex5_api_v5_url,
30
37
  :auth => {
@@ -34,18 +41,9 @@ module Aspera
34
41
  :state => SecureRandom.uuid,
35
42
  :client_id => options.get_option(:client_id,:mandatory),
36
43
  :redirect_uri => options.get_option(:redirect_uri,:mandatory),
37
- #:token_field =>'auth_token',
38
- #:path_token => 'token',
39
- #:path_authorize => 'authorize',
40
- #:userpass_body => {name: faxpex5_username,password: faxpex5_password}
41
44
  }})
42
- when :boot
43
- @api_v5=Rest.new({
44
- :base_url => faxpex5_api_v5_url,
45
- :headers => {'Authorization'=>options.get_option(:password,:mandatory)},
46
- })
47
45
  when :jwt
48
- #raise "JWT to be implemented"
46
+ # currently Faspex 5 beta 3 only supports non-user based apis (e.g. jobs)
49
47
  app_client_id=options.get_option(:client_id,:mandatory)
50
48
  @api_v5=Rest.new({
51
49
  :base_url => faxpex5_api_v5_url,
@@ -53,28 +51,33 @@ module Aspera
53
51
  :type => :oauth2,
54
52
  :base_url => faxpex5_api_auth_url,
55
53
  :grant => :jwt,
54
+ :f5_username => options.get_option(:username,:mandatory),
55
+ :f5_password => options.get_option(:password,:mandatory),
56
56
  :client_id => app_client_id,
57
57
  :client_secret => options.get_option(:client_secret,:mandatory),
58
- #:redirect_uri => options.get_option(:redirect_uri,:mandatory),
59
58
  :jwt_subject => "client:#{app_client_id}", # TODO Mmmm
60
59
  :jwt_audience => app_client_id, # TODO Mmmm
61
60
  :jwt_private_key_obj => OpenSSL::PKey::RSA.new(options.get_option(:private_key,:mandatory)),
62
- :jwt_is_f5 => true,
63
61
  :jwt_headers => {typ: 'JWT'}
64
62
  }})
65
63
  end
66
64
  end
67
65
 
66
+ ACTIONS=[ :node, :package, :auth_client, :jobs ]
67
+
68
68
  #
69
69
  def execute_action
70
70
  set_api
71
71
  command=options.get_next_command(ACTIONS)
72
72
  case command
73
73
  when :auth_client
74
- api_auth=Rest.new(@api_v5.params.merge({base_url: @api_v5.params[:base_url].gsub(/api\/v5$/,'auth')}))
74
+ api_auth=Rest.new(@api_v5.params.merge({base_url: "#{@faxpex5_api_base_url}/auth"}))
75
75
  return self.entity_action(api_auth,'oauth_clients',nil,:id,nil,true)
76
76
  when :node
77
77
  return self.entity_action(@api_v5,'nodes',nil,:id,nil,true)
78
+ when :jobs
79
+ # to test JWT
80
+ return self.entity_action(@api_v5,'jobs',nil,:id,nil,true)
78
81
  when :package
79
82
  command=options.get_next_command([:list,:show,:send,:receive])
80
83
  case command
@@ -86,7 +89,7 @@ module Aspera
86
89
  return {:type => :single_object, :data=>@api_v5.read("packages/#{id}")[:data]}
87
90
  when :send
88
91
  parameters=options.get_option(:value,:mandatory)
89
- raise CliBadArgument,'package value must be hash, refer to API' unless parameters.is_a?(Hash)
92
+ raise CliBadArgument,'value must be hash, refer to API' unless parameters.is_a?(Hash)
90
93
  package=@api_v5.create('packages',parameters)[:data]
91
94
  transfer_spec=@api_v5.create("packages/#{package['id']}/transfer_spec/upload",{transfer_type: 'Connect'})[:data]
92
95
  transfer_spec.delete('authentication')
@@ -98,16 +101,17 @@ module Aspera
98
101
  skip_ids_data=[]
99
102
  skip_ids_persistency=nil
100
103
  if options.get_option(:once_only,:mandatory)
104
+ # read ids from persistency
101
105
  skip_ids_persistency=PersistencyActionOnce.new(
102
106
  manager: @agents[:persistency],
103
- data: skip_ids_data,
104
- ids: ['faspex_recv',options.get_option(:url,:mandatory),options.get_option(:username,:mandatory),pkg_type])
107
+ data: skip_ids_data,
108
+ id: IdGenerator.from_list(['faspex_recv',options.get_option(:url,:mandatory),options.get_option(:username,:mandatory),pkg_type]))
105
109
  end
106
110
  if pack_id.eql?(VAL_ALL)
107
- # todo: if packages have same name, they will overwrite
111
+ # TODO: if packages have same name, they will overwrite
108
112
  parameters=options.get_option(:value,:optional)
109
- parameters||={"type"=>"received","subtype"=>"mypackages","limit"=>1000}
110
- raise CliBadArgument,'value filter must be hash (API GET)' unless parameters.is_a?(Hash)
113
+ parameters||={'type'=>'received','subtype'=>'mypackages','limit'=>1000}
114
+ raise CliBadArgument,'value filter must be Hash (API GET)' unless parameters.is_a?(Hash)
111
115
  package_ids=@api_v5.read('packages',parameters)[:data]['packages'].map{|p|p['id']}
112
116
  package_ids.select!{|i|!skip_ids_data.include?(i)}
113
117
  end
@@ -117,12 +121,12 @@ module Aspera
117
121
  transfer_spec=@api_v5.create("packages/#{id}/transfer_spec/download",{transfer_type: 'Connect', type: pkg_type})[:data]
118
122
  transfer_spec.delete('authentication')
119
123
  statuses=self.transfer.start(transfer_spec,{:src=>:node_gen3})
120
- result_transfer.push({'package'=>id,'status'=>statuses.map{|i|i.to_s}.join(',')})
124
+ result_transfer.push({'package'=>id,Main::STATUS_FIELD=>statuses})
121
125
  # skip only if all sessions completed
122
126
  skip_ids_data.push(id) if TransferAgent.session_status(statuses).eql?(:success)
123
127
  end
124
128
  skip_ids_persistency.save unless skip_ids_persistency.nil?
125
- return {:type=>:object_list,:data=>result_transfer}
129
+ return Main.result_transfer_multiple(result_transfer)
126
130
  end
127
131
  end
128
132
  end
@@ -1,6 +1,7 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/nagios'
3
3
  require 'aspera/hash_ext'
4
+ require 'aspera/id_generator'
4
5
  require 'base64'
5
6
  require 'zlib'
6
7
 
@@ -12,22 +13,34 @@ module Aspera
12
13
  private_constant :SAMPLE_SOAP_CALL
13
14
  def initialize(env)
14
15
  super(env)
15
- # this is added to some requests , for instance to add tags
16
+ # this is added to some requests , for instance to add tags (COS)
16
17
  @add_request_param = env[:add_request_param] || {}
17
18
  unless env[:skip_basic_auth_options]
18
19
  self.options.add_opt_simple(:validator,"identifier of validator (optional for central)")
19
20
  self.options.add_opt_simple(:asperabrowserurl,"URL for simple aspera web ui")
20
21
  self.options.add_opt_simple(:name,"sync name")
21
- self.options.add_opt_list(:token,[:aspera,:basic,:auto],'todo: type of token used for transfers')
22
+ self.options.add_opt_list(:token_type,[:aspera,:basic,:hybrid],'Type of token used for transfers')
22
23
  self.options.set_option(:asperabrowserurl,'https://asperabrowser.mybluemix.net')
23
- self.options.set_option(:token,:aspera)
24
+ self.options.set_option(:token_type,:aspera)
24
25
  self.options.parse_options!
25
26
  end
26
27
  return if env[:man_only]
27
28
  if env.has_key?(:node_api)
28
29
  @api_node=env[:node_api]
29
30
  else
30
- @api_node=basic_auth_api unless env[:man_only]
31
+ if self.options.get_option(:password,:mandatory).start_with?('Bearer ')
32
+ # info is provided like node_info of aoc
33
+ @api_node=Rest.new({
34
+ base_url: self.options.get_option(:url,:mandatory),
35
+ headers: {
36
+ 'Authorization' => self.options.get_option(:password,:mandatory),
37
+ 'X-Aspera-AccessKey' => self.options.get_option(:username,:mandatory),
38
+ }
39
+ })
40
+ else
41
+ # this is normal case
42
+ @api_node=basic_auth_api
43
+ end
31
44
  end
32
45
  end
33
46
 
@@ -190,22 +203,36 @@ module Aspera
190
203
  #raise "unknown type: #{send_result['self']['type']}"
191
204
  end
192
205
  return c_result_remove_prefix_path(result,'path',prefix_path)
193
- when :upload
194
- # we send only a list of one transfer request
195
- transfer_request = { :paths => [ { :destination => self.transfer.destination_folder('send') } ] }
196
- transfer_request.deep_merge!(@add_request_param)
197
- send_result=@api_node.create('files/upload_setup',{:transfer_requests => [ { :transfer_request => transfer_request } ] } )[:data]
198
- # only one request, so only one answer
199
- transfer_spec=send_result['transfer_specs'].first['transfer_spec']
200
- # delete this part, as the returned value contains only destination, and not sources
201
- transfer_spec.delete('paths')
202
- return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
203
- when :download
204
- transfer_request = {:paths => self.transfer.ts_source_paths }
205
- transfer_request.deep_merge!(@add_request_param)
206
- send_result=@api_node.create('files/download_setup',{:transfer_requests => [ { :transfer_request => transfer_request } ] } )[:data]
207
- # only one request, so only one answer
208
- transfer_spec=send_result['transfer_specs'].first['transfer_spec']
206
+ when :upload,:download
207
+ token_type=self.options.get_option(:token_type,:optional)
208
+ # nil if Shares 1.x
209
+ token_type=:aspera if token_type.nil?
210
+ case token_type
211
+ when :aspera,:hybrid
212
+ transfer_paths=case command
213
+ when :upload;[ { :destination => self.transfer.destination_folder('send') } ]
214
+ when :download;self.transfer.ts_source_paths
215
+ end
216
+ # only one request, so only one answer
217
+ transfer_spec=@api_node.create("files/#{command}_setup",{:transfer_requests => [ { transfer_request: {
218
+ paths: transfer_paths
219
+ }.deep_merge(@add_request_param) } ] } )[:data]['transfer_specs'].first['transfer_spec']
220
+ # delete this part, as the returned value contains only destination, and not sources
221
+ transfer_spec.delete('paths') if command.eql?(:upload)
222
+ when :basic
223
+ raise "shall have auth" unless @api_node.params[:auth].is_a?(Hash)
224
+ raise "shall be basic auth" unless @api_node.params[:auth][:type].eql?(:basic)
225
+ transfer_spec={
226
+ 'remote_host'=>URI.parse(@api_node.params[:base_url]).host,
227
+ 'remote_user'=>Aspera::Node::ACCESS_KEY_TRANSFER_USER,
228
+ 'ssh_port'=>Aspera::Node::SSH_PORT_DEFAULT,
229
+ 'direction'=>case command;when :upload;'send';when :download;'recv';else raise "Error";end
230
+ }
231
+ else raise "ERROR: token_type #{tt}"
232
+ end
233
+ if [:basic,:hybrid].include?(token_type)
234
+ Aspera::Node.set_ak_basic_token(transfer_spec,@api_node.params[:auth][:username],@api_node.params[:auth][:password])
235
+ end
209
236
  return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
210
237
  when :api_details
211
238
  return { :type=>:single_object, :data => @api_node.params }
@@ -271,8 +298,8 @@ module Aspera
271
298
  if self.options.get_option(:once_only,:mandatory)
272
299
  skip_ids_persistency=PersistencyActionOnce.new(
273
300
  manager: @agents[:persistency],
274
- data: iteration_data,
275
- ids: ['sync_files',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),asyncid])
301
+ data: iteration_data,
302
+ id: IdGenerator.from_list(['sync_files',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory),asyncid]))
276
303
  unless iteration_data.first.nil?
277
304
  data.select!{|l| l['fnid'].to_i>iteration_data.first}
278
305
  end
@@ -408,9 +435,11 @@ module Aspera
408
435
  command=self.options.get_next_command([ :list, :modify])
409
436
  case command
410
437
  when :list
411
- request_data.deep_merge!({"validation"=>validation}) unless validation.nil?
412
- resp=@api_node.create('services/rest/transfers/v1/files',request_data)
413
- return {:type=>:object_list,:data=>resp[:data]["file_transfer_info_result"]["file_transfer_info"],:fields=>["session_uuid","file_id","status","path"]}
438
+ request_data.deep_merge!({'validation'=>validation}) unless validation.nil?
439
+ resp=@api_node.create('services/rest/transfers/v1/files',request_data)[:data]
440
+ resp=JSON.parse(resp) if resp.is_a?(String)
441
+ Log.dump(:resp,resp)
442
+ return {:type=>:object_list,:data=>resp['file_transfer_info_result']['file_transfer_info'],:fields=>["session_uuid","file_id","status","path"]}
414
443
  when :modify
415
444
  request_data.deep_merge!(validation) unless validation.nil?
416
445
  @api_node.update('services/rest/transfers/v1/files',request_data)