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
@@ -6,6 +6,8 @@ require 'aspera/preview/file_types'
6
6
  require 'aspera/persistency_action_once'
7
7
  require 'aspera/node'
8
8
  require 'aspera/hash_ext'
9
+ require 'aspera/timer_limiter'
10
+ require 'aspera/id_generator'
9
11
  require 'date'
10
12
  require 'securerandom'
11
13
 
@@ -24,7 +26,8 @@ module Aspera
24
26
  DEFAULT_PREVIEWS_FOLDER='previews'
25
27
  AK_MARKER_FILE='.aspera_access_key'
26
28
  LOCAL_STORAGE_PCVL='file:///'
27
- private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER, :LOCAL_STORAGE_PCVL, :AK_MARKER_FILE
29
+ LOG_LIMITER_SEC=30.0
30
+ private_constant :PREV_GEN_TAG, :PREVIEW_FOLDER_SUFFIX, :PREVIEW_BASENAME, :TMP_DIR_PREFIX, :DEFAULT_PREVIEWS_FOLDER, :LOCAL_STORAGE_PCVL, :AK_MARKER_FILE, :LOG_LIMITER_SEC
28
31
 
29
32
  # option_skip_format has special accessors
30
33
  attr_accessor :option_previews_folder
@@ -40,6 +43,8 @@ module Aspera
40
43
  @preview_formats_to_generate=Aspera::Preview::Generator::PREVIEW_FORMATS.clone
41
44
  # options for generation
42
45
  @gen_options=Aspera::Preview::Options.new
46
+ # used to trigger periodic processing
47
+ @periodic=TimerLimiter.new(LOG_LIMITER_SEC)
43
48
  # link CLI options to gen_info attributes
44
49
  self.options.set_obj_attr(:skip_format,self,:option_skip_format,[]) # no skip
45
50
  self.options.set_obj_attr(:folder_reset_cache,self,:option_folder_reset_cache,:no)
@@ -113,13 +118,14 @@ module Aspera
113
118
  end
114
119
 
115
120
  # old version based on folders
116
- def process_trevents(iteration_token)
121
+ # @param iteration_persistency can be nil
122
+ def process_trevents(iteration_persistency)
117
123
  events_filter={
118
124
  'access_key'=>@access_key_self['id'],
119
125
  'type'=>'download.ended'
120
126
  }
121
- # optionally by iteration token
122
- events_filter['iteration_token']=iteration_token unless iteration_token.nil?
127
+ # optionally add iteration token from persistency
128
+ events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
123
129
  begin
124
130
  events=@api_node.read('events',events_filter)[:data]
125
131
  rescue RestCallError => e
@@ -132,47 +138,63 @@ module Aspera
132
138
  end
133
139
  return if events.empty?
134
140
  events.each do |event|
135
- next unless event['data']['direction'].eql?('receive')
136
- next unless event['data']['status'].eql?('completed')
137
- next unless event['data']['error_code'].eql?(0)
138
- next unless event['data'].dig('tags','aspera',PREV_GEN_TAG).nil?
139
- folder_id=event.dig('data','tags','aspera','node','file_id')
140
- folder_id||=event.dig('data','file_id')
141
- next if folder_id.nil?
142
- folder_entry=@api_node.read("files/#{folder_id}")[:data] rescue nil
143
- next if folder_entry.nil?
144
- scan_folder_files(folder_entry)
141
+ if event['data']['direction'].eql?('receive') and
142
+ event['data']['status'].eql?('completed') and
143
+ event['data']['error_code'].eql?(0) and
144
+ event['data'].dig('tags','aspera',PREV_GEN_TAG).nil?
145
+ folder_id=event.dig('data','tags','aspera','node','file_id')
146
+ folder_id||=event.dig('data','file_id')
147
+ if !folder_id.nil?
148
+ folder_entry=@api_node.read("files/#{folder_id}")[:data] rescue nil
149
+ scan_folder_files(folder_entry) unless folder_entry.nil?
150
+ end
151
+ end
152
+ if @periodic.trigger? or event.equal?(events.last)
153
+ Log.log.info("Processed event #{event['id']}")
154
+ # save checkpoint to avoid losing processing in case of error
155
+ if !iteration_persistency.nil?
156
+ iteration_persistency.data[0]=event['id'].to_s
157
+ iteration_persistency.save
158
+ end
159
+ end
145
160
  end
146
- return events.last['id'].to_s
147
161
  end
148
162
 
149
163
  # requests recent events on node api and process newly modified folders
150
- def process_events(iteration_token)
164
+ def process_events(iteration_persistency)
151
165
  # get new file creation by access key (TODO: what if file already existed?)
152
166
  events_filter={
153
167
  'access_key'=>@access_key_self['id'],
154
168
  'type'=>'file.*'
155
169
  }
156
- # and optionally by iteration token
157
- events_filter['iteration_token']=iteration_token unless iteration_token.nil?
170
+ # optionally add iteration token from persistency
171
+ events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
158
172
  events=@api_node.read('events',events_filter)[:data]
159
173
  return if events.empty?
160
174
  events.each do |event|
161
175
  # process only files
162
- next unless event.dig('data','type').eql?('file')
163
- file_entry=@api_node.read("files/#{event['data']['id']}")[:data] rescue nil
164
- next if file_entry.nil?
165
- next unless @option_skip_folders.select{|d|file_entry['path'].start_with?(d)}.empty?
166
- file_entry['parent_file_id']=event['data']['parent_file_id']
167
- if event['types'].include?('file.deleted')
168
- Log.log.error('TODO'.red)
176
+ if event.dig('data','type').eql?('file')
177
+ file_entry=@api_node.read("files/#{event['data']['id']}")[:data] rescue nil
178
+ if !file_entry.nil? and
179
+ @option_skip_folders.select{|d|file_entry['path'].start_with?(d)}.empty?
180
+ file_entry['parent_file_id']=event['data']['parent_file_id']
181
+ if event['types'].include?('file.deleted')
182
+ Log.log.error('TODO'.red)
183
+ end
184
+ if event['types'].include?('file.deleted')
185
+ generate_preview(file_entry)
186
+ end
187
+ end
169
188
  end
170
- if event['types'].include?('file.deleted')
171
- generate_preview(file_entry)
189
+ if @periodic.trigger? or event.equal?(events.last)
190
+ Log.log.info("Processing event #{event['id']}")
191
+ # save checkpoint to avoid losing processing in case of error
192
+ if !iteration_persistency.nil?
193
+ iteration_persistency.data[0]=event['id'].to_s
194
+ iteration_persistency.save
195
+ end
172
196
  end
173
197
  end
174
- # write new iteration file
175
- return events.last['id'].to_s
176
198
  end
177
199
 
178
200
  def do_transfer(direction,folder_id,source_filename,destination='/')
@@ -180,15 +202,17 @@ module Aspera
180
202
  if @default_transfer_spec.nil?
181
203
  # make a dummy call to get some default transfer parameters
182
204
  res=@api_node.create('files/upload_setup',{'transfer_requests'=>[{'transfer_request'=>{'paths'=>[{}],'destination_root'=>'/'}}]})
183
- sample_transfer_spec=res[:data]['transfer_specs'].first['transfer_spec']
205
+ template_ts=res[:data]['transfer_specs'].first['transfer_spec']
184
206
  # get ports, anyway that should be 33001 for both. add remote_user ?
185
- @default_transfer_spec=['ssh_port','fasp_port'].inject({}){|h,e|h[e]=sample_transfer_spec[e];h}
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)
209
+ Log.log.warn("remote_user shall be xfer")
210
+ @default_transfer_spec['remote_user']=Aspera::Node::ACCESS_KEY_TRANSFER_USER
211
+ end
212
+ Aspera::Node::set_ak_basic_token(@default_transfer_spec,@access_key_self['id'],self.options.get_option(:password,:mandatory))
186
213
  # note: we use the same address for ascp than for node api instead of the one from upload_setup
187
- @default_transfer_spec.merge!({
188
- 'token' => "Basic #{Base64.strict_encode64("#{@access_key_self['id']}:#{self.options.get_option(:password,:mandatory)}")}",
189
- 'remote_host' => @transfer_server_address,
190
- 'remote_user' => Fasp::ACCESS_KEY_TRANSFER_USER
191
- })
214
+ # TODO: configurable ? useful ?
215
+ @default_transfer_spec['remote_host']=@transfer_server_address
192
216
  end
193
217
  tspec=@default_transfer_spec.merge({
194
218
  'direction' => direction,
@@ -338,40 +362,45 @@ module Aspera
338
362
  entry=entries_to_process.shift
339
363
  # process this entry only if it is within the scan_start
340
364
  entry_path_with_slash=entry['path']
365
+ Log.log.info("processing entry #{entry_path_with_slash}") if @periodic.trigger?
341
366
  entry_path_with_slash="#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
342
367
  if !scan_start.nil? and !scan_start.start_with?(entry_path_with_slash) and !entry_path_with_slash.start_with?(scan_start)
343
368
  Log.log.debug("#{entry['path']} folder (skip start)".bg_red)
344
369
  next
345
370
  end
346
371
  Log.log.debug("item:#{entry}")
347
- case entry['type']
348
- when 'file'
349
- if filter_block.call(entry)
350
- generate_preview(entry)
351
- else
352
- Log.log.debug('skip by filter')
353
- end
354
- when 'link'
355
- Log.log.debug('Ignoring link.')
356
- when 'folder'
357
- if @option_skip_folders.include?(entry['path'])
358
- Log.log.debug("#{entry['path']} folder (skip list)".bg_red)
359
- else
360
- Log.log.debug("#{entry['path']} folder".green)
361
- # get folder content
362
- folder_entries=get_folder_entries(entry['id'])
363
- # process all items in current folder
364
- folder_entries.each do |folder_entry|
365
- # add path for older versions of ES
366
- if !folder_entry.has_key?('path')
367
- folder_entry['path']=entry_path_with_slash+folder_entry['name']
372
+ begin
373
+ case entry['type']
374
+ when 'file'
375
+ if filter_block.call(entry)
376
+ generate_preview(entry)
377
+ else
378
+ Log.log.debug('skip by filter')
379
+ end
380
+ when 'link'
381
+ Log.log.debug('Ignoring link.')
382
+ when 'folder'
383
+ if @option_skip_folders.include?(entry['path'])
384
+ Log.log.debug("#{entry['path']} folder (skip list)".bg_red)
385
+ else
386
+ Log.log.debug("#{entry['path']} folder".green)
387
+ # get folder content
388
+ folder_entries=get_folder_entries(entry['id'])
389
+ # process all items in current folder
390
+ folder_entries.each do |folder_entry|
391
+ # add path for older versions of ES
392
+ if !folder_entry.has_key?('path')
393
+ folder_entry['path']=entry_path_with_slash+folder_entry['name']
394
+ end
395
+ folder_entry['parent_file_id']=entry['id']
396
+ entries_to_process.push(folder_entry)
368
397
  end
369
- folder_entry['parent_file_id']=entry['id']
370
- entries_to_process.push(folder_entry)
371
398
  end
399
+ else
400
+ Log.log.warn("unknown entry type: #{entry['type']}")
372
401
  end
373
- else
374
- Log.log.warn("unknown entry type: #{entry['type']}")
402
+ rescue => e
403
+ Log.log.warn("An error occured: #{e}, ignoring")
375
404
  end
376
405
  end
377
406
  end
@@ -437,18 +466,15 @@ module Aspera
437
466
  scan_folder_files(folder_info,scan_path)
438
467
  return Main.result_status('scan finished')
439
468
  when :events,:trevents
440
- iteration_data=[]
441
469
  iteration_persistency=nil
442
470
  if self.options.get_option(:once_only,:mandatory)
443
471
  iteration_persistency=PersistencyActionOnce.new(
444
472
  manager: @agents[:persistency],
445
- data: iteration_data,
446
- ids: ["preview_iteration_#{command}",self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)])
473
+ data: [],
474
+ id: IdGenerator.from_list(['preview_iteration',command.to_s,self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)]))
447
475
  end
448
-
449
- # call method specified
450
- iteration_data[0]=send("process_#{command}",iteration_data[0])
451
- iteration_persistency.save unless iteration_persistency.nil?
476
+ # call processing method specified by command line command
477
+ send("process_#{command}",iteration_persistency)
452
478
  return Main.result_status("#{command} finished")
453
479
  when :check
454
480
  Aspera::Preview::Utils.check_tools(@skip_types)
@@ -1,5 +1,6 @@
1
1
  require 'aspera/cli/basic_auth_plugin'
2
2
  require 'aspera/ascmd'
3
+ require 'aspera/node'
3
4
  require 'aspera/ssh'
4
5
  require 'aspera/nagios'
5
6
  require 'tempfile'
@@ -67,13 +68,15 @@ module Aspera
67
68
  def execute_action
68
69
  server_uri=URI.parse(self.options.get_option(:url,:mandatory))
69
70
  Log.log.debug("URI : #{server_uri}, port=#{server_uri.port}, scheme:#{server_uri.scheme}")
71
+ server_transfer_spec={'remote_host'=>server_uri.hostname}
70
72
  shell_executor=nil
71
73
  case server_uri.scheme
72
74
  when 'ssh'
73
- server_transfer_spec={
74
- 'remote_host'=>server_uri.hostname,
75
- 'remote_user'=>self.options.get_option(:username,:mandatory),
76
- }
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}")
78
+ end
79
+ server_transfer_spec['remote_user']=self.options.get_option(:username,:mandatory)
77
80
  ssh_options=self.options.get_option(:ssh_options,:optional)
78
81
  raise 'expecting a Hash for ssh_options' unless ssh_options.is_a?(Hash)
79
82
  if !server_uri.port.nil?
@@ -102,8 +105,16 @@ module Aspera
102
105
  cred_set=true
103
106
  end
104
107
  end
105
- raise 'either password or key must be provided' if !cred_set
108
+ # if user provided transfer spec has a token, we will use by pass keys
109
+ cred_set=true if self.transfer.option_transfer_spec['token'].is_a?(String)
110
+ raise 'either password, key , or transfer spec token must be provided' if !cred_set
106
111
  shell_executor=Ssh.new(server_transfer_spec['remote_host'],server_transfer_spec['remote_user'],ssh_options)
112
+ when 'https'
113
+ raise "ERROR: transfer spec with token required" unless self.transfer.option_transfer_spec['token'].is_a?(String)
114
+ server_transfer_spec.merge!({
115
+ 'wss_enabled'=>true,
116
+ 'wss_port' =>server_uri.port
117
+ })
107
118
  when 'local'
108
119
  shell_executor=LocalExecutor.new
109
120
  else
@@ -1,7 +1,7 @@
1
1
  require 'aspera/fasp/local'
2
+ require 'aspera/fasp/parameters'
2
3
  require 'aspera/fasp/connect'
3
4
  require 'aspera/fasp/node'
4
- require 'aspera/fasp/aoc'
5
5
  require 'aspera/fasp/http_gw'
6
6
  require 'aspera/cli/listener/logger'
7
7
  require 'aspera/cli/listener/progress_multi'
@@ -16,33 +16,47 @@ module Aspera
16
16
  FILE_LIST_FROM_ARGS='@args'
17
17
  # special value for --sources : read file list from transfer spec (--ts)
18
18
  FILE_LIST_FROM_TRANSFER_SPEC='@ts'
19
- private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC
20
- # @param cli_objects external objects: option manager, config file manager
21
- def initialize(cli_objects)
22
- @opt_mgr=cli_objects[:options]
23
- @config=cli_objects[:config]
24
- # transfer spec overrides provided on command line
25
- @transfer_spec_cmdline={"create_dir"=>true}
19
+ DEFAULT_TRANSFER_NOTIF_TMPL=<<END_OF_TEMPLATE
20
+ From: <%=from_name%> <<%=from_email%>>
21
+ To: <<%=to%>>
22
+ Subject: <%=subject%>
23
+
24
+ Transfer is: <%=global_transfer_status%>
25
+
26
+ <%=ts.to_yaml%>
27
+ END_OF_TEMPLATE
28
+ #%
29
+ private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC,:DEFAULT_TRANSFER_NOTIF_TMPL
30
+ # @param env external objects: option manager, config file manager
31
+ def initialize(env)
32
+ # same as plugin environment
33
+ @env=env
34
+ # command line can override transfer spec
35
+ @transfer_spec_cmdline={'create_dir'=>true}
26
36
  # the currently selected transfer agent
27
37
  @agent=nil
28
38
  @progress_listener=Listener::ProgressMulti.new
29
39
  # source/destination pair, like "paths" of transfer spec
30
40
  @transfer_paths=nil
31
- @opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
32
- @opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts,:optional)}")
33
- @opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume,:optional)}")
34
- @opt_mgr.add_opt_simple(:to_folder,"destination folder for downloaded files")
35
- @opt_mgr.add_opt_simple(:sources,"list of source files (see doc)")
36
- @opt_mgr.add_opt_simple(:transfer_info,"additional information for transfer client")
37
- @opt_mgr.add_opt_list(:src_type,[:list,:pair],"type of file list")
38
- @opt_mgr.add_opt_list(:transfer,[:direct,:httpgw,:connect,:node,:aoc],"type of transfer")
39
- @opt_mgr.add_opt_list(:progress,[:none,:native,:multi],"type of progress bar")
40
- @opt_mgr.set_option(:transfer,:direct)
41
- @opt_mgr.set_option(:src_type,:list)
42
- @opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
43
- @opt_mgr.parse_options!
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!
44
54
  end
45
55
 
56
+ def options; @env[:options];end
57
+
58
+ def config; @env[:config];end
59
+
46
60
  def option_transfer_spec; @transfer_spec_cmdline; end
47
61
 
48
62
  # multiple option are merged
@@ -54,8 +68,8 @@ module Aspera
54
68
  @agent=instance
55
69
  @agent.add_listener(Listener::Logger.new)
56
70
  # use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
57
- if @opt_mgr.get_option(:progress,:mandatory).eql?(:multi) or
58
- (@opt_mgr.get_option(:progress,:mandatory).eql?(:native) and !@opt_mgr.get_option(:transfer,:mandatory).eql?(:direct))
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))
59
73
  @agent.add_listener(@progress_listener)
60
74
  end
61
75
  end
@@ -63,15 +77,15 @@ module Aspera
63
77
  # analyze options and create new agent if not already created or set
64
78
  def set_agent_by_options
65
79
  return nil unless @agent.nil?
66
- agent_type=@opt_mgr.get_option(:transfer,:mandatory)
80
+ agent_type=options.get_option(:transfer,:mandatory)
67
81
  case agent_type
68
82
  when :direct
69
- agent_options=@opt_mgr.get_option(:transfer_info,:optional)
83
+ agent_options=options.get_option(:transfer_info,:optional)
70
84
  agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
71
85
  new_agent=Fasp::Local.new(agent_options)
72
- new_agent.quiet=false if @opt_mgr.get_option(:progress,:mandatory).eql?(:native)
86
+ new_agent.quiet=false if options.get_option(:progress,:mandatory).eql?(:native)
73
87
  when :httpgw
74
- httpgw_config=@opt_mgr.get_option(:transfer_info,:mandatory)
88
+ httpgw_config=options.get_option(:transfer_info,:mandatory)
75
89
  new_agent=Fasp::HttpGW.new(httpgw_config)
76
90
  when :connect
77
91
  new_agent=Fasp::Connect.new
@@ -79,45 +93,37 @@ module Aspera
79
93
  # way for code to setup alternate node api in advance
80
94
  # support: @preset:<name>
81
95
  # support extended values
82
- node_config=@opt_mgr.get_option(:transfer_info,:optional)
96
+ node_config=options.get_option(:transfer_info,:optional)
83
97
  # if not specified: use default node
84
98
  if node_config.nil?
85
- param_set_name=@config.get_plugin_default_config_name(:node)
99
+ param_set_name=config.get_plugin_default_config_name(:node)
86
100
  raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
87
- node_config=@config.preset_by_name(param_set_name)
101
+ node_config=config.preset_by_name(param_set_name)
88
102
  end
89
103
  Log.log.debug("node=#{node_config}")
90
- raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>" if !node_config.is_a?(Hash)
91
- # now check there are required parameters
92
- sym_config=[:url,:username,:password].inject({}) do |h,param|
93
- raise CliBadArgument,"missing parameter [#{param}] in node specification: #{node_config}" if !node_config.has_key?(param.to_s)
94
- h[param]=node_config[param.to_s]
95
- h
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
+ }})
96
123
  end
97
- node_api=Rest.new({
98
- :base_url => sym_config[:url],
99
- :auth => {
100
- :type =>:basic,
101
- :username => sym_config[:username],
102
- :password => sym_config[:password]
103
- }})
104
124
  new_agent=Fasp::Node.new(node_api)
105
- when :aoc
106
- aoc_config=@opt_mgr.get_option(:transfer_info,:optional)
107
- if aoc_config.nil?
108
- param_set_name=@config.get_plugin_default_config_name(:aspera)
109
- raise CliBadArgument,"No default AoC configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
110
- aoc_config=@config.preset_by_name(param_set_name)
111
- end
112
- Log.log.debug("aoc=#{aoc_config}")
113
- raise CliBadArgument,"the aoc configuration shall be Hash, not #{aoc_config.class} (#{aoc_config}), refer to manual" if !aoc_config.is_a?(Hash)
114
- # convert keys from string (config) to symbol (agent)
115
- aoc_config=aoc_config.symbolize_keys
116
- # convert auth value from string (config) to symbol (agent)
117
- aoc_config[:auth]=aoc_config[:auth].to_sym if aoc_config[:auth].is_a?(String)
118
- # private key could be @file:... in config
119
- aoc_config[:private_key]=ExtendedValue.instance.evaluate(aoc_config[:private_key])
120
- new_agent=Fasp::Aoc.new(aoc_config)
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)
121
127
  else
122
128
  raise "Unexpected transfer agent type: #{agent_type}"
123
129
  end
@@ -129,7 +135,7 @@ module Aspera
129
135
  # sets default if needed
130
136
  # param: 'send' or 'receive'
131
137
  def destination_folder(direction)
132
- dest_folder=@opt_mgr.get_option(:to_folder,:optional)
138
+ dest_folder=options.get_option(:to_folder,:optional)
133
139
  return dest_folder unless dest_folder.nil?
134
140
  dest_folder=@transfer_spec_cmdline['destination_root']
135
141
  return dest_folder unless dest_folder.nil?
@@ -152,16 +158,17 @@ module Aspera
152
158
  # start with lower priority : get paths from transfer spec on command line
153
159
  @transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
154
160
  # is there a source list option ?
155
- file_list=@opt_mgr.get_option(:sources,:optional)
161
+ file_list=options.get_option(:sources,:optional)
156
162
  case file_list
157
163
  when nil,FILE_LIST_FROM_ARGS
158
164
  Log.log.debug("getting file list as parameters")
159
165
  # get remaining arguments
160
- file_list=@opt_mgr.get_next_argument("source file list",:multiple)
166
+ file_list=options.get_next_argument("source file list",:multiple)
161
167
  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?
162
168
  when FILE_LIST_FROM_TRANSFER_SPEC
163
169
  Log.log.debug("assume list provided in transfer spec")
164
- raise CliBadArgument,"transfer spec on command line must have sources" if @transfer_paths.nil?
170
+ special_case_direct_with_list=options.get_option(:transfer,:mandatory).eql?(:direct) and Fasp::Parameters.ts_has_file_list(@transfer_spec_cmdline)
171
+ raise CliBadArgument,"transfer spec on command line must have sources" if @transfer_paths.nil? and !special_case_direct_with_list
165
172
  # here we assume check of sources is made in transfer agent
166
173
  return @transfer_paths
167
174
  when Array
@@ -174,7 +181,7 @@ module Aspera
174
181
  if !@transfer_paths.nil?
175
182
  Log.log.warn("--sources overrides paths from --ts")
176
183
  end
177
- case @opt_mgr.get_option(:src_type,:mandatory)
184
+ case options.get_option(:src_type,:mandatory)
178
185
  when :list
179
186
  # when providing a list, just specify source
180
187
  @transfer_paths=file_list.map{|i|{'source'=>i}}
@@ -189,20 +196,20 @@ module Aspera
189
196
 
190
197
  # start a transfer and wait for completion, plugins shall use this method
191
198
  # @param transfer_spec
192
- # @param options specific options for the transfer_agent
193
- # options[:src] specifies how destination_root is set (how transfer spec was generated)
199
+ # @param tr_opts specific options for the transfer_agent
200
+ # tr_opts[:src] specifies how destination_root is set (how transfer spec was generated)
194
201
  # other options are carried to specific agent
195
- def start(transfer_spec,options)
202
+ def start(transfer_spec,tr_opts)
196
203
  # check parameters
197
204
  raise "transfer_spec must be hash" unless transfer_spec.is_a?(Hash)
198
- raise "options must be hash" unless options.is_a?(Hash)
205
+ raise "tr_opts must be hash" unless tr_opts.is_a?(Hash)
199
206
  # process :src option
200
207
  case transfer_spec['direction']
201
208
  when 'receive'
202
209
  # init default if required in any case
203
210
  @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
204
211
  when 'send'
205
- case options[:src]
212
+ case tr_opts[:src]
206
213
  when :direct
207
214
  # init default if required
208
215
  @transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
@@ -213,12 +220,12 @@ module Aspera
213
220
  when :node_gen4
214
221
  @transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
215
222
  else
216
- raise StandardError,"InternalError: unsupported value: #{options[:src]}"
223
+ raise StandardError,"InternalError: unsupported value: #{tr_opts[:src]}"
217
224
  end
218
225
  end
219
226
 
220
227
  # only used here
221
- options.delete(:src)
228
+ tr_opts.delete(:src)
222
229
 
223
230
  # update command line paths, unless destination already has one
224
231
  @transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
@@ -227,13 +234,26 @@ module Aspera
227
234
  # create transfer agent
228
235
  self.set_agent_by_options
229
236
  Log.log.debug("transfer agent is a #{@agent.class}")
230
- @agent.start_transfer(transfer_spec,options)
237
+ @agent.start_transfer(transfer_spec,tr_opts)
231
238
  result=@agent.wait_for_transfers_completion
232
239
  @progress_listener.reset
233
240
  Fasp::Manager.validate_status_list(result)
241
+ send_email_transfer_notification(transfer_spec,result)
234
242
  return result
235
243
  end
236
244
 
245
+ def send_email_transfer_notification(transfer_spec,statuses)
246
+ return if options.get_option(:notif_to,:optional).nil?
247
+ global_status=self.class.session_status(statuses)
248
+ email_vars={
249
+ global_transfer_status: global_status,
250
+ subject: "ascli transfer: #{global_status}",
251
+ body: "Transfer is: #{global_status}",
252
+ ts: transfer_spec
253
+ }
254
+ @env[:config].send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
255
+ end
256
+
237
257
  # @return :success if all sessions statuses returned by "start" are success
238
258
  # else return the first error exception object
239
259
  def self.session_status(statuses)
@@ -1,5 +1,5 @@
1
1
  module Aspera
2
2
  module Cli
3
- VERSION = "4.2.0"
3
+ VERSION = "4.4.0"
4
4
  end
5
5
  end