aspera-cli 4.1.0 → 4.3.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 +455 -229
- data/docs/Makefile +4 -4
- data/docs/README.erb.md +457 -126
- data/docs/test_env.conf +19 -2
- data/examples/aoc.rb +14 -3
- data/examples/faspex4.rb +89 -0
- data/lib/aspera/aoc.rb +38 -40
- data/lib/aspera/cli/main.rb +65 -33
- data/lib/aspera/cli/plugins/aoc.rb +54 -65
- data/lib/aspera/cli/plugins/ats.rb +2 -2
- data/lib/aspera/cli/plugins/config.rb +158 -137
- data/lib/aspera/cli/plugins/faspex.rb +111 -64
- data/lib/aspera/cli/plugins/faspex5.rb +35 -48
- data/lib/aspera/cli/plugins/node.rb +3 -2
- data/lib/aspera/cli/plugins/preview.rb +88 -55
- data/lib/aspera/cli/transfer_agent.rb +98 -62
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/command_line_builder.rb +48 -31
- data/lib/aspera/cos_node.rb +34 -28
- data/lib/aspera/environment.rb +2 -2
- data/lib/aspera/fasp/aoc.rb +1 -1
- data/lib/aspera/fasp/installation.rb +68 -45
- data/lib/aspera/fasp/local.rb +89 -45
- data/lib/aspera/fasp/manager.rb +3 -0
- data/lib/aspera/fasp/node.rb +23 -1
- data/lib/aspera/fasp/parameters.rb +57 -86
- data/lib/aspera/fasp/parameters.yaml +531 -0
- data/lib/aspera/fasp/resume_policy.rb +13 -12
- data/lib/aspera/fasp/uri.rb +1 -1
- data/lib/aspera/id_generator.rb +22 -0
- data/lib/aspera/node.rb +14 -3
- data/lib/aspera/oauth.rb +135 -129
- data/lib/aspera/persistency_action_once.rb +11 -7
- data/lib/aspera/persistency_folder.rb +6 -26
- data/lib/aspera/rest.rb +3 -12
- data/lib/aspera/secrets.rb +20 -0
- 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 -3
- data/docs/transfer_spec.html +0 -99
|
@@ -4,7 +4,10 @@ require 'aspera/preview/options'
|
|
|
4
4
|
require 'aspera/preview/utils'
|
|
5
5
|
require 'aspera/preview/file_types'
|
|
6
6
|
require 'aspera/persistency_action_once'
|
|
7
|
+
require 'aspera/node'
|
|
7
8
|
require 'aspera/hash_ext'
|
|
9
|
+
require 'aspera/timer_limiter'
|
|
10
|
+
require 'aspera/id_generator'
|
|
8
11
|
require 'date'
|
|
9
12
|
require 'securerandom'
|
|
10
13
|
|
|
@@ -23,7 +26,8 @@ module Aspera
|
|
|
23
26
|
DEFAULT_PREVIEWS_FOLDER='previews'
|
|
24
27
|
AK_MARKER_FILE='.aspera_access_key'
|
|
25
28
|
LOCAL_STORAGE_PCVL='file:///'
|
|
26
|
-
|
|
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
|
|
27
31
|
|
|
28
32
|
# option_skip_format has special accessors
|
|
29
33
|
attr_accessor :option_previews_folder
|
|
@@ -39,6 +43,8 @@ module Aspera
|
|
|
39
43
|
@preview_formats_to_generate=Aspera::Preview::Generator::PREVIEW_FORMATS.clone
|
|
40
44
|
# options for generation
|
|
41
45
|
@gen_options=Aspera::Preview::Options.new
|
|
46
|
+
# used to trigger periodic processing
|
|
47
|
+
@periodic=TimerLimiter.new(LOG_LIMITER_SEC)
|
|
42
48
|
# link CLI options to gen_info attributes
|
|
43
49
|
self.options.set_obj_attr(:skip_format,self,:option_skip_format,[]) # no skip
|
|
44
50
|
self.options.set_obj_attr(:folder_reset_cache,self,:option_folder_reset_cache,:no)
|
|
@@ -112,13 +118,14 @@ module Aspera
|
|
|
112
118
|
end
|
|
113
119
|
|
|
114
120
|
# old version based on folders
|
|
115
|
-
|
|
121
|
+
# @param iteration_persistency can be nil
|
|
122
|
+
def process_trevents(iteration_persistency)
|
|
116
123
|
events_filter={
|
|
117
124
|
'access_key'=>@access_key_self['id'],
|
|
118
125
|
'type'=>'download.ended'
|
|
119
126
|
}
|
|
120
|
-
# optionally
|
|
121
|
-
events_filter['iteration_token']=
|
|
127
|
+
# optionally add iteration token from persistency
|
|
128
|
+
events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
|
|
122
129
|
begin
|
|
123
130
|
events=@api_node.read('events',events_filter)[:data]
|
|
124
131
|
rescue RestCallError => e
|
|
@@ -131,47 +138,63 @@ module Aspera
|
|
|
131
138
|
end
|
|
132
139
|
return if events.empty?
|
|
133
140
|
events.each do |event|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
144
160
|
end
|
|
145
|
-
return events.last['id'].to_s
|
|
146
161
|
end
|
|
147
162
|
|
|
148
163
|
# requests recent events on node api and process newly modified folders
|
|
149
|
-
def process_events(
|
|
164
|
+
def process_events(iteration_persistency)
|
|
150
165
|
# get new file creation by access key (TODO: what if file already existed?)
|
|
151
166
|
events_filter={
|
|
152
167
|
'access_key'=>@access_key_self['id'],
|
|
153
168
|
'type'=>'file.*'
|
|
154
169
|
}
|
|
155
|
-
#
|
|
156
|
-
events_filter['iteration_token']=
|
|
170
|
+
# optionally add iteration token from persistency
|
|
171
|
+
events_filter['iteration_token']=iteration_persistency.data.first unless iteration_persistency.nil?
|
|
157
172
|
events=@api_node.read('events',events_filter)[:data]
|
|
158
173
|
return if events.empty?
|
|
159
174
|
events.each do |event|
|
|
160
175
|
# process only files
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
|
168
188
|
end
|
|
169
|
-
if event
|
|
170
|
-
|
|
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
|
|
171
196
|
end
|
|
172
197
|
end
|
|
173
|
-
# write new iteration file
|
|
174
|
-
return events.last['id'].to_s
|
|
175
198
|
end
|
|
176
199
|
|
|
177
200
|
def do_transfer(direction,folder_id,source_filename,destination='/')
|
|
@@ -322,12 +345,14 @@ module Aspera
|
|
|
322
345
|
end # generate_preview
|
|
323
346
|
|
|
324
347
|
# scan all files in provided folder entry
|
|
348
|
+
# @param scan_start subpath to start folder scan inside
|
|
325
349
|
def scan_folder_files(top_entry,scan_start=nil)
|
|
326
350
|
if !scan_start.nil?
|
|
327
351
|
# canonical path: start with / and ends with /
|
|
328
352
|
scan_start='/'+scan_start.split('/').select{|i|!i.empty?}.join('/')
|
|
329
353
|
scan_start="#{scan_start}/" #unless scan_start.end_with?('/')
|
|
330
354
|
end
|
|
355
|
+
filter_block=Aspera::Node.file_matcher(options.get_option(:value,:optional))
|
|
331
356
|
Log.log.debug("scan: #{top_entry} : #{scan_start}".green)
|
|
332
357
|
# don't use recursive call, use list instead
|
|
333
358
|
entries_to_process=[top_entry]
|
|
@@ -335,36 +360,45 @@ module Aspera
|
|
|
335
360
|
entry=entries_to_process.shift
|
|
336
361
|
# process this entry only if it is within the scan_start
|
|
337
362
|
entry_path_with_slash=entry['path']
|
|
363
|
+
Log.log.info("processing entry #{entry_path_with_slash}") if @periodic.trigger?
|
|
338
364
|
entry_path_with_slash="#{entry_path_with_slash}/" unless entry_path_with_slash.end_with?('/')
|
|
339
365
|
if !scan_start.nil? and !scan_start.start_with?(entry_path_with_slash) and !entry_path_with_slash.start_with?(scan_start)
|
|
340
366
|
Log.log.debug("#{entry['path']} folder (skip start)".bg_red)
|
|
341
367
|
next
|
|
342
368
|
end
|
|
343
369
|
Log.log.debug("item:#{entry}")
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
Log.log.debug(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
#
|
|
359
|
-
|
|
360
|
-
|
|
370
|
+
begin
|
|
371
|
+
case entry['type']
|
|
372
|
+
when 'file'
|
|
373
|
+
if filter_block.call(entry)
|
|
374
|
+
generate_preview(entry)
|
|
375
|
+
else
|
|
376
|
+
Log.log.debug('skip by filter')
|
|
377
|
+
end
|
|
378
|
+
when 'link'
|
|
379
|
+
Log.log.debug('Ignoring link.')
|
|
380
|
+
when 'folder'
|
|
381
|
+
if @option_skip_folders.include?(entry['path'])
|
|
382
|
+
Log.log.debug("#{entry['path']} folder (skip list)".bg_red)
|
|
383
|
+
else
|
|
384
|
+
Log.log.debug("#{entry['path']} folder".green)
|
|
385
|
+
# get folder content
|
|
386
|
+
folder_entries=get_folder_entries(entry['id'])
|
|
387
|
+
# process all items in current folder
|
|
388
|
+
folder_entries.each do |folder_entry|
|
|
389
|
+
# add path for older versions of ES
|
|
390
|
+
if !folder_entry.has_key?('path')
|
|
391
|
+
folder_entry['path']=entry_path_with_slash+folder_entry['name']
|
|
392
|
+
end
|
|
393
|
+
folder_entry['parent_file_id']=entry['id']
|
|
394
|
+
entries_to_process.push(folder_entry)
|
|
361
395
|
end
|
|
362
|
-
folder_entry['parent_file_id']=entry['id']
|
|
363
|
-
entries_to_process.push(folder_entry)
|
|
364
396
|
end
|
|
397
|
+
else
|
|
398
|
+
Log.log.warn("unknown entry type: #{entry['type']}")
|
|
365
399
|
end
|
|
366
|
-
|
|
367
|
-
Log.log.warn("
|
|
400
|
+
rescue => e
|
|
401
|
+
Log.log.warn("An error occured: #{e}, ignoring")
|
|
368
402
|
end
|
|
369
403
|
end
|
|
370
404
|
end
|
|
@@ -430,16 +464,15 @@ module Aspera
|
|
|
430
464
|
scan_folder_files(folder_info,scan_path)
|
|
431
465
|
return Main.result_status('scan finished')
|
|
432
466
|
when :events,:trevents
|
|
433
|
-
iteration_data=[]
|
|
434
467
|
iteration_persistency=nil
|
|
435
468
|
if self.options.get_option(:once_only,:mandatory)
|
|
436
469
|
iteration_persistency=PersistencyActionOnce.new(
|
|
437
470
|
manager: @agents[:persistency],
|
|
438
|
-
data:
|
|
439
|
-
|
|
471
|
+
data: [],
|
|
472
|
+
id: IdGenerator.from_list(['preview_iteration',command.to_s,self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)]))
|
|
440
473
|
end
|
|
441
|
-
|
|
442
|
-
iteration_persistency
|
|
474
|
+
# call processing method specified by command line command
|
|
475
|
+
send("process_#{command}",iteration_persistency)
|
|
443
476
|
return Main.result_status("#{command} finished")
|
|
444
477
|
when :check
|
|
445
478
|
Aspera::Preview::Utils.check_tools(@skip_types)
|
|
@@ -10,41 +10,56 @@ module Aspera
|
|
|
10
10
|
module Cli
|
|
11
11
|
# The Transfer agent is a common interface to start a transfer using
|
|
12
12
|
# one of the supported transfer agents
|
|
13
|
-
# provides CLI options to select one of the transfer agents (
|
|
13
|
+
# provides CLI options to select one of the transfer agents (FASP/ascp client)
|
|
14
14
|
class TransferAgent
|
|
15
15
|
# special value for --sources : read file list from arguments
|
|
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,"additional information for transfer client")
|
|
47
|
+
options.add_opt_list(:src_type,[:list,:pair],"type of file list")
|
|
48
|
+
options.add_opt_list(:transfer,[:direct,:httpgw,:connect,:node,:aoc],"type of transfer")
|
|
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
|
|
|
62
|
+
# multiple option are merged
|
|
48
63
|
def option_transfer_spec=(value); @transfer_spec_cmdline.merge!(value); end
|
|
49
64
|
|
|
50
65
|
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
|
@@ -53,8 +68,8 @@ module Aspera
|
|
|
53
68
|
@agent=instance
|
|
54
69
|
@agent.add_listener(Listener::Logger.new)
|
|
55
70
|
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
|
56
|
-
if
|
|
57
|
-
(
|
|
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))
|
|
58
73
|
@agent.add_listener(@progress_listener)
|
|
59
74
|
end
|
|
60
75
|
end
|
|
@@ -62,51 +77,59 @@ module Aspera
|
|
|
62
77
|
# analyze options and create new agent if not already created or set
|
|
63
78
|
def set_agent_by_options
|
|
64
79
|
return nil unless @agent.nil?
|
|
65
|
-
agent_type
|
|
80
|
+
agent_type=options.get_option(:transfer,:mandatory)
|
|
66
81
|
case agent_type
|
|
67
82
|
when :direct
|
|
68
|
-
agent_options
|
|
83
|
+
agent_options=options.get_option(:transfer_info,:optional)
|
|
69
84
|
agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
|
70
85
|
new_agent=Fasp::Local.new(agent_options)
|
|
71
|
-
new_agent.quiet=false if
|
|
86
|
+
new_agent.quiet=false if options.get_option(:progress,:mandatory).eql?(:native)
|
|
72
87
|
when :httpgw
|
|
73
|
-
httpgw_config
|
|
88
|
+
httpgw_config=options.get_option(:transfer_info,:mandatory)
|
|
74
89
|
new_agent=Fasp::HttpGW.new(httpgw_config)
|
|
75
90
|
when :connect
|
|
76
91
|
new_agent=Fasp::Connect.new
|
|
77
92
|
when :node
|
|
78
|
-
# way for code to setup alternate node api in
|
|
93
|
+
# way for code to setup alternate node api in advance
|
|
79
94
|
# support: @preset:<name>
|
|
80
95
|
# support extended values
|
|
81
|
-
node_config
|
|
96
|
+
node_config=options.get_option(:transfer_info,:optional)
|
|
82
97
|
# if not specified: use default node
|
|
83
98
|
if node_config.nil?
|
|
84
|
-
param_set_name
|
|
99
|
+
param_set_name=config.get_plugin_default_config_name(:node)
|
|
85
100
|
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
|
|
86
|
-
node_config
|
|
101
|
+
node_config=config.preset_by_name(param_set_name)
|
|
87
102
|
end
|
|
88
103
|
Log.log.debug("node=#{node_config}")
|
|
89
|
-
raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>"
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
+
}})
|
|
95
123
|
end
|
|
96
|
-
node_api=Rest.new({
|
|
97
|
-
:base_url => sym_config[:url],
|
|
98
|
-
:auth => {
|
|
99
|
-
:type =>:basic,
|
|
100
|
-
:username => sym_config[:username],
|
|
101
|
-
:password => sym_config[:password]
|
|
102
|
-
}})
|
|
103
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)
|
|
104
127
|
when :aoc
|
|
105
|
-
aoc_config
|
|
128
|
+
aoc_config=options.get_option(:transfer_info,:optional)
|
|
106
129
|
if aoc_config.nil?
|
|
107
|
-
param_set_name
|
|
130
|
+
param_set_name=config.get_plugin_default_config_name(:aspera)
|
|
108
131
|
raise CliBadArgument,"No default AoC configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
|
|
109
|
-
aoc_config
|
|
132
|
+
aoc_config=config.preset_by_name(param_set_name)
|
|
110
133
|
end
|
|
111
134
|
Log.log.debug("aoc=#{aoc_config}")
|
|
112
135
|
raise CliBadArgument,"the aoc configuration shall be Hash, not #{aoc_config.class} (#{aoc_config}), refer to manual" if !aoc_config.is_a?(Hash)
|
|
@@ -118,7 +141,7 @@ module Aspera
|
|
|
118
141
|
aoc_config[:private_key]=ExtendedValue.instance.evaluate(aoc_config[:private_key])
|
|
119
142
|
new_agent=Fasp::Aoc.new(aoc_config)
|
|
120
143
|
else
|
|
121
|
-
raise "
|
|
144
|
+
raise "Unexpected transfer agent type: #{agent_type}"
|
|
122
145
|
end
|
|
123
146
|
set_agent_instance(new_agent)
|
|
124
147
|
return nil
|
|
@@ -128,7 +151,7 @@ module Aspera
|
|
|
128
151
|
# sets default if needed
|
|
129
152
|
# param: 'send' or 'receive'
|
|
130
153
|
def destination_folder(direction)
|
|
131
|
-
dest_folder
|
|
154
|
+
dest_folder=options.get_option(:to_folder,:optional)
|
|
132
155
|
return dest_folder unless dest_folder.nil?
|
|
133
156
|
dest_folder=@transfer_spec_cmdline['destination_root']
|
|
134
157
|
return dest_folder unless dest_folder.nil?
|
|
@@ -151,12 +174,12 @@ module Aspera
|
|
|
151
174
|
# start with lower priority : get paths from transfer spec on command line
|
|
152
175
|
@transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
|
|
153
176
|
# is there a source list option ?
|
|
154
|
-
file_list
|
|
177
|
+
file_list=options.get_option(:sources,:optional)
|
|
155
178
|
case file_list
|
|
156
179
|
when nil,FILE_LIST_FROM_ARGS
|
|
157
180
|
Log.log.debug("getting file list as parameters")
|
|
158
181
|
# get remaining arguments
|
|
159
|
-
file_list
|
|
182
|
+
file_list=options.get_next_argument("source file list",:multiple)
|
|
160
183
|
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?
|
|
161
184
|
when FILE_LIST_FROM_TRANSFER_SPEC
|
|
162
185
|
Log.log.debug("assume list provided in transfer spec")
|
|
@@ -173,14 +196,14 @@ module Aspera
|
|
|
173
196
|
if !@transfer_paths.nil?
|
|
174
197
|
Log.log.warn("--sources overrides paths from --ts")
|
|
175
198
|
end
|
|
176
|
-
case
|
|
199
|
+
case options.get_option(:src_type,:mandatory)
|
|
177
200
|
when :list
|
|
178
201
|
# when providing a list, just specify source
|
|
179
202
|
@transfer_paths=file_list.map{|i|{'source'=>i}}
|
|
180
203
|
when :pair
|
|
181
|
-
raise CliBadArgument,"
|
|
204
|
+
raise CliBadArgument,"When using pair, provide an even number of paths: #{file_list.length}" unless file_list.length.even?
|
|
182
205
|
@transfer_paths=file_list.each_slice(2).to_a.map{|s,d|{'source'=>s,'destination'=>d}}
|
|
183
|
-
else raise "
|
|
206
|
+
else raise "Unsupported src_type"
|
|
184
207
|
end
|
|
185
208
|
Log.log.debug("paths=#{@transfer_paths}")
|
|
186
209
|
return @transfer_paths
|
|
@@ -188,20 +211,20 @@ module Aspera
|
|
|
188
211
|
|
|
189
212
|
# start a transfer and wait for completion, plugins shall use this method
|
|
190
213
|
# @param transfer_spec
|
|
191
|
-
# @param
|
|
192
|
-
#
|
|
214
|
+
# @param tr_opts specific options for the transfer_agent
|
|
215
|
+
# tr_opts[:src] specifies how destination_root is set (how transfer spec was generated)
|
|
193
216
|
# other options are carried to specific agent
|
|
194
|
-
def start(transfer_spec,
|
|
217
|
+
def start(transfer_spec,tr_opts)
|
|
195
218
|
# check parameters
|
|
196
219
|
raise "transfer_spec must be hash" unless transfer_spec.is_a?(Hash)
|
|
197
|
-
raise "
|
|
220
|
+
raise "tr_opts must be hash" unless tr_opts.is_a?(Hash)
|
|
198
221
|
# process :src option
|
|
199
222
|
case transfer_spec['direction']
|
|
200
223
|
when 'receive'
|
|
201
224
|
# init default if required in any case
|
|
202
225
|
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
|
203
226
|
when 'send'
|
|
204
|
-
case
|
|
227
|
+
case tr_opts[:src]
|
|
205
228
|
when :direct
|
|
206
229
|
# init default if required
|
|
207
230
|
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
|
@@ -212,12 +235,12 @@ module Aspera
|
|
|
212
235
|
when :node_gen4
|
|
213
236
|
@transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
|
|
214
237
|
else
|
|
215
|
-
raise StandardError,"InternalError: unsupported value: #{
|
|
238
|
+
raise StandardError,"InternalError: unsupported value: #{tr_opts[:src]}"
|
|
216
239
|
end
|
|
217
240
|
end
|
|
218
241
|
|
|
219
242
|
# only used here
|
|
220
|
-
|
|
243
|
+
tr_opts.delete(:src)
|
|
221
244
|
|
|
222
245
|
# update command line paths, unless destination already has one
|
|
223
246
|
@transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
|
|
@@ -226,13 +249,26 @@ module Aspera
|
|
|
226
249
|
# create transfer agent
|
|
227
250
|
self.set_agent_by_options
|
|
228
251
|
Log.log.debug("transfer agent is a #{@agent.class}")
|
|
229
|
-
@agent.start_transfer(transfer_spec,
|
|
252
|
+
@agent.start_transfer(transfer_spec,tr_opts)
|
|
230
253
|
result=@agent.wait_for_transfers_completion
|
|
231
254
|
@progress_listener.reset
|
|
232
255
|
Fasp::Manager.validate_status_list(result)
|
|
256
|
+
send_email_transfer_notification(transfer_spec,result)
|
|
233
257
|
return result
|
|
234
258
|
end
|
|
235
259
|
|
|
260
|
+
def send_email_transfer_notification(transfer_spec,statuses)
|
|
261
|
+
return if options.get_option(:notif_to,:optional).nil?
|
|
262
|
+
global_status=self.class.session_status(statuses)
|
|
263
|
+
email_vars={
|
|
264
|
+
global_transfer_status: global_status,
|
|
265
|
+
subject: "ascli transfer: #{global_status}",
|
|
266
|
+
body: "Transfer is: #{global_status}",
|
|
267
|
+
ts: transfer_spec
|
|
268
|
+
}
|
|
269
|
+
@env[:config].send_email_template(email_vars,DEFAULT_TRANSFER_NOTIF_TMPL)
|
|
270
|
+
end
|
|
271
|
+
|
|
236
272
|
# @return :success if all sessions statuses returned by "start" are success
|
|
237
273
|
# else return the first error exception object
|
|
238
274
|
def self.session_status(statuses)
|
data/lib/aspera/cli/version.rb
CHANGED