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.
- checksums.yaml +4 -4
- data/README.md +749 -353
- data/docs/Makefile +4 -4
- data/docs/README.erb.md +743 -283
- data/docs/doc_tools.rb +58 -0
- data/docs/test_env.conf +9 -1
- data/examples/aoc.rb +14 -3
- data/examples/faspex4.rb +89 -0
- data/lib/aspera/aoc.rb +24 -22
- data/lib/aspera/cli/main.rb +48 -20
- data/lib/aspera/cli/plugin.rb +13 -6
- data/lib/aspera/cli/plugins/aoc.rb +117 -78
- data/lib/aspera/cli/plugins/config.rb +127 -80
- data/lib/aspera/cli/plugins/faspex.rb +112 -63
- data/lib/aspera/cli/plugins/faspex5.rb +29 -25
- data/lib/aspera/cli/plugins/node.rb +54 -25
- data/lib/aspera/cli/plugins/preview.rb +94 -68
- data/lib/aspera/cli/plugins/server.rb +16 -5
- data/lib/aspera/cli/transfer_agent.rb +92 -72
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +48 -31
- data/lib/aspera/cos_node.rb +4 -3
- data/lib/aspera/fasp/http_gw.rb +47 -26
- data/lib/aspera/fasp/local.rb +31 -24
- data/lib/aspera/fasp/manager.rb +3 -0
- data/lib/aspera/fasp/node.rb +23 -1
- data/lib/aspera/fasp/parameters.rb +72 -89
- data/lib/aspera/fasp/parameters.yaml +531 -0
- data/lib/aspera/fasp/uri.rb +1 -1
- data/lib/aspera/faspex_gw.rb +10 -9
- data/lib/aspera/id_generator.rb +22 -0
- data/lib/aspera/node.rb +11 -3
- data/lib/aspera/oauth.rb +131 -135
- data/lib/aspera/persistency_action_once.rb +11 -7
- data/lib/aspera/persistency_folder.rb +6 -26
- data/lib/aspera/rest.rb +1 -1
- data/lib/aspera/sync.rb +40 -35
- data/lib/aspera/timer_limiter.rb +22 -0
- data/lib/aspera/web_auth.rb +105 -0
- metadata +22 -4
- data/docs/transfer_spec.html +0 -99
- 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
|
-
|
|
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
|
-
|
|
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
|
|
122
|
-
events_filter['iteration_token']=
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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(
|
|
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
|
-
#
|
|
157
|
-
events_filter['iteration_token']=
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
|
171
|
-
|
|
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
|
-
|
|
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]=
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
folder_entry
|
|
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
|
-
|
|
374
|
-
Log.log.warn("
|
|
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:
|
|
446
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
58
|
-
(
|
|
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
|
|
80
|
+
agent_type=options.get_option(:transfer,:mandatory)
|
|
67
81
|
case agent_type
|
|
68
82
|
when :direct
|
|
69
|
-
agent_options
|
|
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
|
|
86
|
+
new_agent.quiet=false if options.get_option(:progress,:mandatory).eql?(:native)
|
|
73
87
|
when :httpgw
|
|
74
|
-
httpgw_config
|
|
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
|
|
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
|
|
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
|
|
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>"
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
193
|
-
#
|
|
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,
|
|
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 "
|
|
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
|
|
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: #{
|
|
223
|
+
raise StandardError,"InternalError: unsupported value: #{tr_opts[:src]}"
|
|
217
224
|
end
|
|
218
225
|
end
|
|
219
226
|
|
|
220
227
|
# only used here
|
|
221
|
-
|
|
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,
|
|
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)
|
data/lib/aspera/cli/version.rb
CHANGED