aspera-cli 4.4.0 → 4.5.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1042 -787
  3. data/bin/ascli +1 -1
  4. data/bin/asession +3 -5
  5. data/docs/Makefile +4 -7
  6. data/docs/README.erb.md +988 -740
  7. data/examples/faspex4.rb +4 -6
  8. data/examples/transfer.rb +2 -2
  9. data/lib/aspera/aoc.rb +139 -118
  10. data/lib/aspera/cli/listener/progress_multi.rb +5 -5
  11. data/lib/aspera/cli/main.rb +64 -34
  12. data/lib/aspera/cli/manager.rb +19 -20
  13. data/lib/aspera/cli/plugin.rb +9 -1
  14. data/lib/aspera/cli/plugins/aoc.rb +156 -143
  15. data/lib/aspera/cli/plugins/ats.rb +11 -10
  16. data/lib/aspera/cli/plugins/bss.rb +2 -2
  17. data/lib/aspera/cli/plugins/config.rb +236 -112
  18. data/lib/aspera/cli/plugins/faspex.rb +29 -7
  19. data/lib/aspera/cli/plugins/faspex5.rb +21 -8
  20. data/lib/aspera/cli/plugins/node.rb +21 -9
  21. data/lib/aspera/cli/plugins/orchestrator.rb +5 -3
  22. data/lib/aspera/cli/plugins/preview.rb +2 -2
  23. data/lib/aspera/cli/plugins/server.rb +3 -3
  24. data/lib/aspera/cli/plugins/shares.rb +17 -0
  25. data/lib/aspera/cli/transfer_agent.rb +47 -85
  26. data/lib/aspera/cli/version.rb +1 -1
  27. data/lib/aspera/environment.rb +4 -4
  28. data/lib/aspera/fasp/{manager.rb → agent_base.rb} +7 -6
  29. data/lib/aspera/fasp/{connect.rb → agent_connect.rb} +46 -39
  30. data/lib/aspera/fasp/{local.rb → agent_direct.rb} +14 -17
  31. data/lib/aspera/fasp/{http_gw.rb → agent_httpgw.rb} +4 -4
  32. data/lib/aspera/fasp/{node.rb → agent_node.rb} +25 -8
  33. data/lib/aspera/fasp/agent_trsdk.rb +106 -0
  34. data/lib/aspera/fasp/default.rb +17 -0
  35. data/lib/aspera/fasp/installation.rb +64 -48
  36. data/lib/aspera/fasp/parameters.rb +7 -3
  37. data/lib/aspera/faspex_gw.rb +6 -6
  38. data/lib/aspera/keychain/encrypted_hash.rb +120 -0
  39. data/lib/aspera/keychain/macos_security.rb +94 -0
  40. data/lib/aspera/log.rb +45 -32
  41. data/lib/aspera/node.rb +3 -6
  42. data/lib/aspera/rest.rb +65 -49
  43. metadata +68 -27
  44. data/lib/aspera/api_detector.rb +0 -60
  45. data/lib/aspera/secrets.rb +0 -20
@@ -32,6 +32,20 @@ module Aspera
32
32
  private_constant :KEY_NODE,:KEY_PATH,:VAL_ALL,:PACKAGE_MATCH_FIELD,:ATOM_MAILBOXES,
33
33
  :ATOM_PARAMS,:ATOM_EXT_PARAMS,:PUB_LINK_EXTERNAL_MATCH
34
34
 
35
+ class << self
36
+ def detect(base_url)
37
+ api=Rest.new({:base_url=>base_url})
38
+ result=api.call({:operation=>'POST',:subpath=>'aspera/faspex',:headers=>{'Accept'=>'application/xrds+xml'},:text_body_params=>''})
39
+ # 4.x
40
+ if result[:http].body.start_with?('<?xml')
41
+ res_s=XmlSimple.xml_in(result[:http].body, {'ForceArray' => false})
42
+ version=res_s['XRD']['application']['version']
43
+ return {version: version}
44
+ end
45
+ return nil
46
+ end
47
+ end
48
+
35
49
  def initialize(env)
36
50
  @api_v3=nil
37
51
  @api_v4=nil
@@ -136,6 +150,8 @@ module Aspera
136
150
  mailbox_query.delete(MAX_PAGES)
137
151
  end
138
152
  loop do
153
+ # get a batch of package information
154
+ # order: first batch is latest packages, and then in a batch ids are increasing
139
155
  atom_xml=api_v3.call({operation: 'GET',subpath: "#{mailbox}.atom",headers: {'Accept'=>'application/xml'},url_params: mailbox_query})[:http].body
140
156
  box_data=XmlSimple.xml_in(atom_xml, {'ForceArray' => true})
141
157
  Log.dump(:box_data,box_data)
@@ -143,7 +159,8 @@ module Aspera
143
159
  Log.log.debug("new items: #{items.count}")
144
160
  # it is the end if page is empty
145
161
  break if items.empty?
146
- items.each do |package|
162
+ # results will be sorted in reverse id
163
+ items.reverse.each do |package|
147
164
  package[PACKAGE_MATCH_FIELD]=case mailbox
148
165
  when :inbox,:archive
149
166
  recipient=package['to'].select{|i|i['name'].first.eql?(recipient_name)}.first
@@ -151,12 +168,16 @@ module Aspera
151
168
  else # :sent
152
169
  package['delivery_id'].first
153
170
  end
154
- # keep only those for the specified recipient
171
+ # keep only those for the specified recipient,
155
172
  result.push(package) unless package[PACKAGE_MATCH_FIELD].nil?
156
173
  end
174
+ #result.push({PACKAGE_MATCH_FIELD=>'======'})
157
175
  Log.log.debug("total items: #{result.count}")
158
176
  # reach the limit ?
159
- break if !max_items.nil? and result.count > max_items
177
+ if !max_items.nil? and result.count >= max_items
178
+ result=result.slice(0,max_items) if result.count > max_items
179
+ break
180
+ end
160
181
  link=box_data['link'].select{|i|i['rel'].eql?('next')}.first
161
182
  Log.log.debug("link: #{link}")
162
183
  # no next link
@@ -267,10 +288,11 @@ module Aspera
267
288
  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
289
  end
269
290
  # get command line parameters
270
- delivid=self.options.get_option(:id,:mandatory)
291
+ delivid=self.instance_identifier()
292
+ raise "empty id" if delivid.empty?
271
293
  if delivid.eql?(VAL_ALL)
272
294
  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
295
+ # TODO : remove ids from skip not present in inbox to avoid growing too big
274
296
  # skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
275
297
  pkg_id_uri.select!{|i|!skip_ids_data.include?(i[:id])}
276
298
  else
@@ -294,7 +316,7 @@ module Aspera
294
316
  if !link_data[:subpath].start_with?(PUB_LINK_EXTERNAL_MATCH)
295
317
  raise CliBadArgument,"Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
296
318
  end
297
- # Note: unauthenticated API (autorization is in url params)
319
+ # Note: unauthenticated API (authorization is in url params)
298
320
  api_public_link=Rest.new({:base_url=>link_data[:base_url]})
299
321
  pkgdatares=api_public_link.call({:operation=>'GET',:subpath=>link_data[:subpath],:url_params=>{:passcode=>link_data[:query]['passcode']},:headers=>{'Accept'=>'application/xml'}})
300
322
  if !pkgdatares[:http].body.start_with?('<?xml ')
@@ -400,7 +422,7 @@ module Aspera
400
422
  return self.entity_action(api_v4,'metadata_profiles',nil,:id)
401
423
  when :package
402
424
  pkg_box_type=self.options.get_next_command([:users])
403
- pkg_box_id=self.options.get_option(:id,:mandatory)
425
+ pkg_box_id=self.instance_identifier()
404
426
  return self.entity_action(api_v4,"#{pkg_box_type}/#{pkg_box_id}/packages",nil,:id)
405
427
  end
406
428
  when :address_book
@@ -7,14 +7,27 @@ module Aspera
7
7
  module Cli
8
8
  module Plugins
9
9
  class Faspex5 < BasicAuthPlugin
10
+ class << self
11
+ def detect(base_url)
12
+ api=Rest.new({:base_url=>base_url})
13
+ result=api.read('api/v5/configuration/ping')
14
+ if result[:http].code.start_with?('2') and result[:http].body.strip.empty?
15
+ return {version: '5'}
16
+ end
17
+ return nil
18
+ end
19
+ end
20
+
10
21
  VAL_ALL='ALL'
22
+ private_constant :VAL_ALL
23
+
11
24
  def initialize(env)
12
25
  super(env)
13
- options.add_opt_simple(:client_id,'API client identifier in application')
14
- options.add_opt_simple(:client_secret,'API client secret in application')
15
- options.add_opt_simple(:redirect_uri,'API client redirect URI')
16
- options.add_opt_list(:auth,Oauth.auth_types.clone.push(:boot),'type of Oauth authentication')
17
- options.add_opt_simple(:private_key,'RSA private key PEM value for JWT (prefix file path with @val:@file:)')
26
+ options.add_opt_simple(:client_id,'OAuth client identifier')
27
+ options.add_opt_simple(:client_secret,'OAuth client secret')
28
+ options.add_opt_simple(:redirect_uri,'OAuth redirect URI')
29
+ options.add_opt_list(:auth,Oauth.auth_types.clone.push(:boot),'OAuth type of authentication')
30
+ options.add_opt_simple(:private_key,'Oauth RSA private key PEM value for JWT (prefix file path with @val:@file:)')
18
31
  options.set_option(:auth,:jwt)
19
32
  options.parse_options!
20
33
  end
@@ -43,7 +56,7 @@ module Aspera
43
56
  :redirect_uri => options.get_option(:redirect_uri,:mandatory),
44
57
  }})
45
58
  when :jwt
46
- # currently Faspex 5 beta 3 only supports non-user based apis (e.g. jobs)
59
+ # currently Faspex 5 beta 4 only supports non-user based apis (e.g. jobs)
47
60
  app_client_id=options.get_option(:client_id,:mandatory)
48
61
  @api_v5=Rest.new({
49
62
  :base_url => faxpex5_api_v5_url,
@@ -85,7 +98,7 @@ module Aspera
85
98
  parameters=options.get_option(:value,:optional)
86
99
  return {:type => :object_list, :data=>@api_v5.read('packages',parameters)[:data]['packages']}
87
100
  when :show
88
- id=options.get_option(:id,:mandatory)
101
+ id=instance_identifier()
89
102
  return {:type => :single_object, :data=>@api_v5.read("packages/#{id}")[:data]}
90
103
  when :send
91
104
  parameters=options.get_option(:value,:mandatory)
@@ -96,7 +109,7 @@ module Aspera
96
109
  return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
97
110
  when :receive
98
111
  pkg_type='received'
99
- pack_id=options.get_option(:id,:mandatory)
112
+ pack_id=instance_identifier()
100
113
  package_ids=[pack_id]
101
114
  skip_ids_data=[]
102
115
  skip_ids_persistency=nil
@@ -2,6 +2,7 @@ require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/nagios'
3
3
  require 'aspera/hash_ext'
4
4
  require 'aspera/id_generator'
5
+ require 'aspera/node'
5
6
  require 'base64'
6
7
  require 'zlib'
7
8
 
@@ -9,8 +10,19 @@ module Aspera
9
10
  module Cli
10
11
  module Plugins
11
12
  class Node < BasicAuthPlugin
13
+ class << self
14
+ def detect(base_url)
15
+ api=Rest.new({:base_url=>base_url})
16
+ result=api.call({:operation=>'GET',:subpath=>'ping'})
17
+ if result[:http].body.eql?('')
18
+ return {:product=>:node,:version=>'unknown'}
19
+ end
20
+ return nil
21
+ end
22
+ end
12
23
  SAMPLE_SOAP_CALL='<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types"><soapenv:Header></soapenv:Header><soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body></soapenv:Envelope>'
13
24
  private_constant :SAMPLE_SOAP_CALL
25
+
14
26
  def initialize(env)
15
27
  super(env)
16
28
  # this is added to some requests , for instance to add tags (COS)
@@ -165,7 +177,7 @@ module Aspera
165
177
  when :mkdir
166
178
  path_list=get_next_arg_add_prefix(prefix_path,"folder path or ext.val. list")
167
179
  path_list=[path_list] unless path_list.is_a?(Array)
168
- #TODO
180
+ #TODO: a command for that ?
169
181
  #resp=@api_node.create('space',{ "paths" => path_list.map {|i| {:type=>:directory,:path=>i} } } )
170
182
  resp=@api_node.create('files/create',{ "paths" => [{ :type => :directory, :path => path_list } ] } )
171
183
  return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
@@ -224,9 +236,9 @@ module Aspera
224
236
  raise "shall be basic auth" unless @api_node.params[:auth][:type].eql?(:basic)
225
237
  transfer_spec={
226
238
  '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
239
+ 'remote_user'=>Aspera::Fasp::Default::ACCESS_KEY_TRANSFER_USER,
240
+ 'ssh_port' =>Aspera::Fasp::Default::SSH_PORT,
241
+ 'direction' =>case command;when :upload;'send';when :download;'recv';else raise "Error";end
230
242
  }
231
243
  else raise "ERROR: token_type #{tt}"
232
244
  end
@@ -244,7 +256,7 @@ module Aspera
244
256
  unless command.eql?(:list)
245
257
  asyncname=self.options.get_option(:name,:optional)
246
258
  if asyncname.nil?
247
- asyncid=self.options.get_option(:id,:mandatory)
259
+ asyncid=self.instance_identifier()
248
260
  if asyncid.eql?('ALL') and [:show,:delete].include?(command)
249
261
  asyncids=@api_node.read('async/list')[:data]['sync_ids']
250
262
  else
@@ -327,7 +339,7 @@ module Aspera
327
339
  case command
328
340
  when :list
329
341
  resp=@api_node.read('ops/transfers',self.options.get_option(:value,:optional))
330
- return { :type => :object_list, :data => resp[:data], :fields=>['id','status'] } # TODO
342
+ return { :type => :object_list, :data => resp[:data], :fields=>['id','status'] } # TODO: useful?
331
343
  when :create
332
344
  resp=@api_node.create('streams',self.options.get_option(:value,:mandatory))
333
345
  return { :type => :single_object, :data => resp[:data] }
@@ -350,7 +362,7 @@ module Aspera
350
362
  command=self.options.get_next_command([ :list, :cancel, :show ])
351
363
  res_class_path='ops/transfers'
352
364
  if [:cancel, :show].include?(command)
353
- one_res_id=self.options.get_option(:id,:mandatory)
365
+ one_res_id=self.instance_identifier()
354
366
  one_res_path="#{res_class_path}/#{one_res_id}"
355
367
  end
356
368
  case command
@@ -372,7 +384,7 @@ module Aspera
372
384
  when :service
373
385
  command=self.options.get_next_command([ :list, :create, :delete])
374
386
  if [:delete].include?(command)
375
- svcid=self.options.get_option(:id,:mandatory)
387
+ svcid=self.instance_identifier()
376
388
  end
377
389
  case command
378
390
  when :list
@@ -392,7 +404,7 @@ module Aspera
392
404
  #return entity_action(@api_node,'v3/watchfolders',nil,:id)
393
405
  command=self.options.get_next_command([ :create, :list, :show, :modify, :delete, :state])
394
406
  if [:show,:modify,:delete,:state].include?(command)
395
- one_res_id=self.options.get_option(:id,:mandatory)
407
+ one_res_id=self.instance_identifier()
396
408
  one_res_path="#{res_class_path}/#{one_res_id}"
397
409
  end
398
410
  # hum, to avoid: Unable to convert 2016_09_14 configuration
@@ -110,12 +110,14 @@ module Aspera
110
110
  return {:type=>:object_list,:data=>result['Plugin']}
111
111
  when :workflow
112
112
  command=self.options.get_next_command([:list, :status, :inputs, :details, :start, :export])
113
- unless [:list, :status].include?(command)
114
- wf_id=self.options.get_option(:id,:mandatory)
113
+ unless [:list].include?(command)
114
+ wf_id=self.instance_identifier()
115
115
  end
116
116
  case command
117
117
  when :status
118
- result=call_API('workflows_status')[:data]
118
+ options={}
119
+ options[:id]=wf_id unless wf_id.eql?('ALL')
120
+ result=call_API('workflows_status',options)[:data]
119
121
  return {:type=>:object_list,:data=>result['workflows']['workflow']}
120
122
  when :list
121
123
  result=call_API('workflows_list',id: 0)[:data]
@@ -205,9 +205,9 @@ module Aspera
205
205
  template_ts=res[:data]['transfer_specs'].first['transfer_spec']
206
206
  # get ports, anyway that should be 33001 for both. add remote_user ?
207
207
  @default_transfer_spec=['ssh_port','fasp_port'].inject({}){|h,e|h[e]=template_ts[e];h}
208
- if ! @default_transfer_spec['remote_user'].eql?(Aspera::Node::ACCESS_KEY_TRANSFER_USER)
208
+ if ! @default_transfer_spec['remote_user'].eql?(Aspera::Fasp::Default::ACCESS_KEY_TRANSFER_USER)
209
209
  Log.log.warn("remote_user shall be xfer")
210
- @default_transfer_spec['remote_user']=Aspera::Node::ACCESS_KEY_TRANSFER_USER
210
+ @default_transfer_spec['remote_user']=Aspera::Fasp::Default::ACCESS_KEY_TRANSFER_USER
211
211
  end
212
212
  Aspera::Node::set_ak_basic_token(@default_transfer_spec,@access_key_self['id'],self.options.get_option(:password,:mandatory))
213
213
  # note: we use the same address for ascp than for node api instead of the one from upload_setup
@@ -1,6 +1,6 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/ascmd'
3
- require 'aspera/node'
3
+ require 'aspera/fasp/default'
4
4
  require 'aspera/ssh'
5
5
  require 'aspera/nagios'
6
6
  require 'tempfile'
@@ -73,8 +73,8 @@ module Aspera
73
73
  case server_uri.scheme
74
74
  when 'ssh'
75
75
  if self.options.get_option(:username,:optional).nil?
76
- self.options.set_option(:username,Aspera::Node::ACCESS_KEY_TRANSFER_USER)
77
- Log.log.info("Using default transfer user: #{Aspera::Node::ACCESS_KEY_TRANSFER_USER}")
76
+ self.options.set_option(:username,Aspera::Fasp::Default::ACCESS_KEY_TRANSFER_USER)
77
+ Log.log.info("Using default transfer user: #{Aspera::Fasp::Default::ACCESS_KEY_TRANSFER_USER}")
78
78
  end
79
79
  server_transfer_spec['remote_user']=self.options.get_option(:username,:mandatory)
80
80
  ssh_options=self.options.get_option(:ssh_options,:optional)
@@ -4,6 +4,23 @@ module Aspera
4
4
  module Cli
5
5
  module Plugins
6
6
  class Shares < BasicAuthPlugin
7
+ class << self
8
+ def detect(base_url)
9
+ api=Rest.new({:base_url=>base_url})
10
+ # Shares
11
+ begin
12
+ # shall fail: shares requires auth, but we check error message
13
+ api.read('node_api/app')
14
+ rescue RestCallError => e
15
+ if e.response.code.to_s.eql?('401') and e.response.body.eql?('{"error":{"user_message":"API user authentication failed"}}')
16
+ return {:version=>'unknown'}
17
+ end
18
+ rescue
19
+ end
20
+ nil
21
+ end
22
+ end
23
+
7
24
  def initialize(env)
8
25
  super(env)
9
26
  #self.options.parse_options!
@@ -1,8 +1,4 @@
1
- require 'aspera/fasp/local'
2
1
  require 'aspera/fasp/parameters'
3
- require 'aspera/fasp/connect'
4
- require 'aspera/fasp/node'
5
- require 'aspera/fasp/http_gw'
6
2
  require 'aspera/cli/listener/logger'
7
3
  require 'aspera/cli/listener/progress_multi'
8
4
 
@@ -25,12 +21,14 @@ Transfer is: <%=global_transfer_status%>
25
21
 
26
22
  <%=ts.to_yaml%>
27
23
  END_OF_TEMPLATE
28
- #%
24
+ #% (formating bug in eclipse)
29
25
  private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:DEFAULT_TRANSFER_NOTIF_TMPL
26
+ TRANSFER_AGENTS=[:direct,:node,:connect,:httpgw,:trsdk]
27
+
30
28
  # @param env external objects: option manager, config file manager
31
- def initialize(env)
32
- # same as plugin environment
33
- @env=env
29
+ def initialize(opt_mgr,config)
30
+ @opt_mgr=opt_mgr
31
+ @config=config
34
32
  # command line can override transfer spec
35
33
  @transfer_spec_cmdline={'create_dir'=>true}
36
34
  # the currently selected transfer agent
@@ -38,25 +36,21 @@ END_OF_TEMPLATE
38
36
  @progress_listener=Listener::ProgressMulti.new
39
37
  # source/destination pair, like "paths" of transfer spec
40
38
  @transfer_paths=nil
41
- options.set_obj_attr(:ts,self,:option_transfer_spec)
42
- options.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{options.get_option(:ts,:optional)}")
43
- options.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{options.get_option(:local_resume,:optional)}")
44
- options.add_opt_simple(:to_folder,"destination folder for downloaded files")
45
- options.add_opt_simple(:sources,"list of source files (see doc)")
46
- options.add_opt_simple(:transfer_info,"parameters for transfer agent")
47
- options.add_opt_list(:src_type,[:list,:pair],"type of file list")
48
- options.add_opt_list(:transfer,[:direct,:httpgw,:connect,:node],"type of transfer agent")
49
- options.add_opt_list(:progress,[:none,:native,:multi],"type of progress bar")
50
- options.set_option(:transfer,:direct)
51
- options.set_option(:src_type,:list)
52
- options.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
53
- options.parse_options!
39
+ @opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
40
+ @opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts,:optional)}")
41
+ @opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume,:optional)}")
42
+ @opt_mgr.add_opt_simple(:to_folder,"destination folder for downloaded files")
43
+ @opt_mgr.add_opt_simple(:sources,"list of source files (see doc)")
44
+ @opt_mgr.add_opt_simple(:transfer_info,"parameters for transfer agent")
45
+ @opt_mgr.add_opt_list(:src_type,[:list,:pair],"type of file list")
46
+ @opt_mgr.add_opt_list(:transfer,TRANSFER_AGENTS,"type of transfer agent")
47
+ @opt_mgr.add_opt_list(:progress,[:none,:native,:multi],"type of progress bar")
48
+ @opt_mgr.set_option(:transfer,:direct)
49
+ @opt_mgr.set_option(:src_type,:list)
50
+ @opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
51
+ @opt_mgr.parse_options!
54
52
  end
55
53
 
56
- def options; @env[:options];end
57
-
58
- def config; @env[:config];end
59
-
60
54
  def option_transfer_spec; @transfer_spec_cmdline; end
61
55
 
62
56
  # multiple option are merged
@@ -68,8 +62,8 @@ END_OF_TEMPLATE
68
62
  @agent=instance
69
63
  @agent.add_listener(Listener::Logger.new)
70
64
  # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
71
- if options.get_option(:progress,:mandatory).eql?(:multi) or
72
- (options.get_option(:progress,:mandatory).eql?(:native) and !options.get_option(:transfer,:mandatory).eql?(:direct))
65
+ if @opt_mgr.get_option(:progress,:mandatory).eql?(:multi) or
66
+ (@opt_mgr.get_option(:progress,:mandatory).eql?(:native) and ! instance.class.to_s.eql?('Aspera::Fasp::AgentDirect'))
73
67
  @agent.add_listener(@progress_listener)
74
68
  end
75
69
  end
@@ -77,56 +71,24 @@ END_OF_TEMPLATE
77
71
  # analyze options and create new agent if not already created or set
78
72
  def set_agent_by_options
79
73
  return nil unless @agent.nil?
80
- agent_type=options.get_option(:transfer,:mandatory)
81
- case agent_type
82
- when :direct
83
- agent_options=options.get_option(:transfer_info,:optional)
84
- agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
85
- new_agent=Fasp::Local.new(agent_options)
86
- new_agent.quiet=false if options.get_option(:progress,:mandatory).eql?(:native)
87
- when :httpgw
88
- httpgw_config=options.get_option(:transfer_info,:mandatory)
89
- new_agent=Fasp::HttpGW.new(httpgw_config)
90
- when :connect
91
- new_agent=Fasp::Connect.new
92
- when :node
93
- # way for code to setup alternate node api in advance
94
- # support: @preset:<name>
95
- # support extended values
96
- node_config=options.get_option(:transfer_info,:optional)
97
- # if not specified: use default node
98
- if node_config.nil?
99
- param_set_name=config.get_plugin_default_config_name(:node)
100
- raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
101
- node_config=config.preset_by_name(param_set_name)
102
- end
103
- Log.log.debug("node=#{node_config}")
104
- raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>" unless node_config.is_a?(Hash)
105
- # here, node_config is a Hash
106
- node_config=node_config.symbolize_keys
107
- # Check mandatory params
108
- [:url,:username,:password].each { |k| raise CliBadArgument,"missing parameter [#{k}] in node specification: #{node_config}" unless node_config.has_key?(k) }
109
- if node_config[:password].match(/^Bearer /)
110
- node_api=Rest.new({
111
- base_url: node_config[:url],
112
- headers: {
113
- 'X-Aspera-AccessKey'=>node_config[:username],
114
- 'Authorization' =>node_config[:password]}})
115
- else
116
- node_api=Rest.new({
117
- base_url: node_config[:url],
118
- auth: {
119
- type: :basic,
120
- username: node_config[:username],
121
- password: node_config[:password]
122
- }})
123
- end
124
- new_agent=Fasp::Node.new(node_api)
125
- # add root id if it's an access key
126
- new_agent.options={root_id: node_config[:root_id]} if node_config.has_key?(:root_id)
127
- else
128
- raise "Unexpected transfer agent type: #{agent_type}"
74
+ agent_type=@opt_mgr.get_option(:transfer,:mandatory)
75
+ require "aspera/fasp/agent_#{agent_type}"
76
+ agent_options=@opt_mgr.get_option(:transfer_info,:optional)
77
+ raise CliBadArgument,"the transfer agent configuration shall be Hash, not #{agent_options.class} (#{agent_options}), use either @json:<json> or @preset:<parameter set name>" unless [Hash,NilClass].include?(agent_options.class)
78
+ # special case
79
+ if agent_type.eql?(:node) and agent_options.nil?
80
+ param_set_name=@config.get_plugin_default_config_name(:node)
81
+ raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
82
+ agent_options=@config.preset_by_name(param_set_name)
129
83
  end
84
+ # special case
85
+ if agent_type.eql?(:direct) and @opt_mgr.get_option(:progress,:mandatory).eql?(:native)
86
+ agent_options={} if agent_options.nil?
87
+ agent_options[:quiet]=false
88
+ end
89
+ agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
90
+ # get agent instance
91
+ new_agent=Kernel.const_get("Aspera::Fasp::Agent#{agent_type.capitalize}").new(agent_options)
130
92
  set_agent_instance(new_agent)
131
93
  return nil
132
94
  end
@@ -135,8 +97,8 @@ END_OF_TEMPLATE
135
97
  # sets default if needed
136
98
  # param: 'send' or 'receive'
137
99
  def destination_folder(direction)
138
- dest_folder=options.get_option(:to_folder,:optional)
139
- return dest_folder unless dest_folder.nil?
100
+ dest_folder=@opt_mgr.get_option(:to_folder,:optional)
101
+ return File.expand_path(dest_folder) unless dest_folder.nil?
140
102
  dest_folder=@transfer_spec_cmdline['destination_root']
141
103
  return dest_folder unless dest_folder.nil?
142
104
  # default: / on remote, . on local
@@ -158,16 +120,16 @@ END_OF_TEMPLATE
158
120
  # start with lower priority : get paths from transfer spec on command line
159
121
  @transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
160
122
  # is there a source list option ?
161
- file_list=options.get_option(:sources,:optional)
123
+ file_list=@opt_mgr.get_option(:sources,:optional)
162
124
  case file_list
163
125
  when nil,FILE_LIST_FROM_ARGS
164
126
  Log.log.debug("getting file list as parameters")
165
127
  # get remaining arguments
166
- file_list=options.get_next_argument("source file list",:multiple)
128
+ file_list=@opt_mgr.get_next_argument("source file list",:multiple)
167
129
  raise CliBadArgument,"specify at least one file on command line or use --sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) or file_list.empty?
168
130
  when FILE_LIST_FROM_TRANSFER_SPEC
169
131
  Log.log.debug("assume list provided in transfer spec")
170
- special_case_direct_with_list=options.get_option(:transfer,:mandatory).eql?(:direct) and Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
132
+ special_case_direct_with_list=@opt_mgr.get_option(:transfer,:mandatory).eql?(:direct) and Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
171
133
  raise CliBadArgument,"transfer spec on command line must have sources" if @transfer_paths.nil? and !special_case_direct_with_list
172
134
  # here we assume check of sources is made in transfer agent
173
135
  return @transfer_paths
@@ -181,7 +143,7 @@ END_OF_TEMPLATE
181
143
  if !@transfer_paths.nil?
182
144
  Log.log.warn("--sources overrides paths from --ts")
183
145
  end
184
- case options.get_option(:src_type,:mandatory)
146
+ case @opt_mgr.get_option(:src_type,:mandatory)
185
147
  when :list
186
148
  # when providing a list, just specify source
187
149
  @transfer_paths=file_list.map{|i|{'source'=>i}}
@@ -237,13 +199,13 @@ END_OF_TEMPLATE
237
199
  @agent.start_transfer(transfer_spec,tr_opts)
238
200
  result=@agent.wait_for_transfers_completion
239
201
  @progress_listener.reset
240
- Fasp::Manager.validate_status_list(result)
202
+ Fasp::AgentBase.validate_status_list(result)
241
203
  send_email_transfer_notification(transfer_spec,result)
242
204
  return result
243
205
  end
244
206
 
245
207
  def send_email_transfer_notification(transfer_spec,statuses)
246
- return if options.get_option(:notif_to,:optional).nil?
208
+ return if @opt_mgr.get_option(:notif_to,:optional).nil?
247
209
  global_status=self.class.session_status(statuses)
248
210
  email_vars={
249
211
  global_transfer_status: global_status,
@@ -251,7 +213,7 @@ END_OF_TEMPLATE
251
213
  body: "Transfer is: #{global_status}",
252
214
  ts: transfer_spec
253
215
  }
254
- @env[:config].send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
216
+ @config.send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
255
217
  end
256
218
 
257
219
  # @return :success if all sessions statuses returned by "start" are success
@@ -1,5 +1,5 @@
1
1
  module Aspera
2
2
  module Cli
3
- VERSION = "4.4.0"
3
+ VERSION = "4.5.0"
4
4
  end
5
5
  end
@@ -33,12 +33,12 @@ module Aspera
33
33
  def self.cpu
34
34
  case RbConfig::CONFIG['host_cpu']
35
35
  when /x86_64/,/x64/
36
- return :x86_64
36
+ return CPU_X86_64
37
37
  when /powerpc/
38
- return :ppc64le if os.eql?(OS_LINUX)
39
- return :ppc64
38
+ return CPU_PPC64LE if os.eql?(OS_LINUX)
39
+ return CPU_PPC64
40
40
  when /s390/
41
- return :s390
41
+ return CPU_S390
42
42
  else # other
43
43
  raise "Unknown CPU: #{RbConfig::CONFIG['host_cpu']}"
44
44
  end
@@ -2,7 +2,7 @@ module Aspera
2
2
  module Fasp
3
3
  # Base class for FASP transfer agents
4
4
  # sub classes shall implement start_transfer and shutdown
5
- class Manager
5
+ class AgentBase
6
6
 
7
7
  private
8
8
 
@@ -68,17 +68,18 @@ module Aspera
68
68
  self
69
69
  end
70
70
 
71
- # the following methods must be implemented by subclass:
72
- # start_transfer(transfer_spec,options) : start and wait for completion
73
- # wait_for_transfers_completion : wait for termination of all transfers, @return list of : :success or error message
74
- # optional: shutdown
75
-
76
71
  # This checks the validity of the value returned by wait_for_transfers_completion
77
72
  # it must be a list of :success or exception
78
73
  def self.validate_status_list(statuses)
79
74
  raise "internal error: bad statuses type: #{statuses.class}" unless statuses.is_a?(Array)
80
75
  raise "internal error: bad statuses content: #{statuses}" unless statuses.select{|i|!i.eql?(:success) and !i.is_a?(StandardError)}.empty?
81
76
  end
77
+
78
+ # the following methods must be implemented by subclass:
79
+ # start_transfer(transfer_spec,options) : start and wait for completion
80
+ # wait_for_transfers_completion : wait for termination of all transfers, @return list of : :success or error message
81
+ # optional: shutdown
82
+
82
83
  end
83
84
  end
84
85
  end