aspera-cli 4.10.0 → 4.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/BUGS.md +19 -0
- data/CHANGELOG.md +528 -0
- data/CONTRIBUTING.md +143 -0
- data/README.md +977 -589
- data/bin/ascli +4 -4
- data/bin/asession +12 -12
- data/docs/test_env.conf +29 -19
- data/examples/aoc.rb +6 -6
- data/examples/dascli +18 -16
- data/examples/faspex4.rb +15 -15
- data/examples/node.rb +12 -12
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +12 -12
- data/lib/aspera/aoc.rb +344 -272
- data/lib/aspera/ascmd.rb +56 -54
- data/lib/aspera/ats_api.rb +4 -4
- data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
- data/lib/aspera/cli/extended_value.rb +9 -9
- data/lib/aspera/cli/{formater.rb → formatter.rb} +69 -69
- data/lib/aspera/cli/listener/line_dump.rb +1 -1
- data/lib/aspera/cli/listener/logger.rb +1 -1
- data/lib/aspera/cli/listener/progress.rb +5 -6
- data/lib/aspera/cli/listener/progress_multi.rb +16 -21
- data/lib/aspera/cli/main.rb +72 -73
- data/lib/aspera/cli/manager.rb +112 -112
- data/lib/aspera/cli/plugin.rb +68 -48
- data/lib/aspera/cli/plugins/alee.rb +4 -4
- data/lib/aspera/cli/plugins/aoc.rb +322 -720
- data/lib/aspera/cli/plugins/ats.rb +50 -52
- data/lib/aspera/cli/plugins/bss.rb +10 -10
- data/lib/aspera/cli/plugins/config.rb +514 -410
- data/lib/aspera/cli/plugins/console.rb +12 -12
- data/lib/aspera/cli/plugins/cos.rb +18 -20
- data/lib/aspera/cli/plugins/faspex.rb +134 -136
- data/lib/aspera/cli/plugins/faspex5.rb +235 -70
- data/lib/aspera/cli/plugins/node.rb +378 -309
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -49
- data/lib/aspera/cli/plugins/preview.rb +129 -120
- data/lib/aspera/cli/plugins/server.rb +137 -83
- data/lib/aspera/cli/plugins/shares.rb +77 -52
- data/lib/aspera/cli/plugins/sync.rb +13 -33
- data/lib/aspera/cli/transfer_agent.rb +61 -61
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +78 -74
- data/lib/aspera/cos_node.rb +31 -29
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +30 -28
- data/lib/aspera/fasp/agent_base.rb +17 -15
- data/lib/aspera/fasp/agent_connect.rb +34 -32
- data/lib/aspera/fasp/agent_direct.rb +70 -73
- data/lib/aspera/fasp/agent_httpgw.rb +79 -74
- data/lib/aspera/fasp/agent_node.rb +26 -26
- data/lib/aspera/fasp/agent_trsdk.rb +20 -20
- data/lib/aspera/fasp/error.rb +3 -2
- data/lib/aspera/fasp/error_info.rb +11 -8
- data/lib/aspera/fasp/installation.rb +80 -80
- data/lib/aspera/fasp/listener.rb +2 -2
- data/lib/aspera/fasp/parameters.rb +103 -92
- data/lib/aspera/fasp/parameters.yaml +313 -214
- data/lib/aspera/fasp/resume_policy.rb +10 -10
- data/lib/aspera/fasp/transfer_spec.rb +22 -2
- data/lib/aspera/fasp/uri.rb +7 -7
- data/lib/aspera/faspex_gw.rb +80 -159
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/hash_ext.rb +3 -3
- data/lib/aspera/id_generator.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +23 -28
- data/lib/aspera/keychain/macos_security.rb +21 -20
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/nagios.rb +24 -23
- data/lib/aspera/node.rb +217 -38
- data/lib/aspera/oauth.rb +78 -74
- data/lib/aspera/open_application.rb +19 -11
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +13 -13
- data/lib/aspera/preview/file_types.rb +8 -8
- data/lib/aspera/preview/generator.rb +67 -67
- data/lib/aspera/preview/utils.rb +27 -27
- data/lib/aspera/proxy_auto_config.js +63 -63
- data/lib/aspera/proxy_auto_config.rb +19 -19
- data/lib/aspera/rest.rb +65 -67
- data/lib/aspera/rest_call_error.rb +2 -1
- data/lib/aspera/rest_error_analyzer.rb +22 -21
- data/lib/aspera/rest_errors_aspera.rb +16 -16
- data/lib/aspera/secret_hider.rb +17 -14
- data/lib/aspera/ssh.rb +15 -14
- data/lib/aspera/sync.rb +177 -62
- data/lib/aspera/temp_file_manager.rb +2 -2
- data/lib/aspera/uri_reader.rb +4 -4
- data/lib/aspera/web_auth.rb +13 -64
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +11 -6
- metadata.gz.sig +0 -0
data/lib/aspera/environment.rb
CHANGED
@@ -10,12 +10,16 @@ module Aspera
|
|
10
10
|
OS_X = :osx
|
11
11
|
OS_LINUX = :linux
|
12
12
|
OS_AIX = :aix
|
13
|
-
OS_LIST = [OS_WINDOWS,OS_X,OS_LINUX,OS_AIX].freeze
|
13
|
+
OS_LIST = [OS_WINDOWS, OS_X, OS_LINUX, OS_AIX].freeze
|
14
14
|
CPU_X86_64 = :x86_64
|
15
15
|
CPU_PPC64 = :ppc64
|
16
16
|
CPU_PPC64LE = :ppc64le
|
17
17
|
CPU_S390 = :s390
|
18
|
-
CPU_LIST = [CPU_X86_64,CPU_PPC64,CPU_PPC64LE,CPU_S390].freeze
|
18
|
+
CPU_LIST = [CPU_X86_64, CPU_PPC64, CPU_PPC64LE, CPU_S390].freeze
|
19
|
+
|
20
|
+
BITS_PER_BYTE = 8
|
21
|
+
MEBI = 1024 * 1024
|
22
|
+
BYTES_PER_MEBIBIT = MEBI / BITS_PER_BYTE
|
19
23
|
|
20
24
|
class << self
|
21
25
|
def ruby_version
|
@@ -24,9 +28,9 @@ module Aspera
|
|
24
28
|
|
25
29
|
def os
|
26
30
|
case RbConfig::CONFIG['host_os']
|
27
|
-
when /mswin
|
31
|
+
when /mswin/, /msys/, /mingw/, /cygwin/, /bccwin/, /wince/, /emc/
|
28
32
|
return OS_WINDOWS
|
29
|
-
when /darwin
|
33
|
+
when /darwin/, /mac os/
|
30
34
|
return OS_X
|
31
35
|
when /linux/
|
32
36
|
return OS_LINUX
|
@@ -39,9 +43,9 @@ module Aspera
|
|
39
43
|
|
40
44
|
def cpu
|
41
45
|
case RbConfig::CONFIG['host_cpu']
|
42
|
-
when /x86_64
|
46
|
+
when /x86_64/, /x64/
|
43
47
|
return CPU_X86_64
|
44
|
-
when /powerpc
|
48
|
+
when /powerpc/, /ppc64/
|
45
49
|
return CPU_PPC64LE if os.eql?(OS_LINUX)
|
46
50
|
return CPU_PPC64
|
47
51
|
when /s390/
|
@@ -65,9 +69,9 @@ module Aspera
|
|
65
69
|
# on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH%
|
66
70
|
# so, tell Ruby the right way
|
67
71
|
def fix_home
|
68
|
-
return unless os.eql?(OS_WINDOWS) && ENV.
|
72
|
+
return unless os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV['USERPROFILE'])
|
69
73
|
ENV['HOME'] = ENV['USERPROFILE']
|
70
|
-
Log.log.debug
|
74
|
+
Log.log.debug{"Windows: set home to USERPROFILE: #{ENV['HOME']}"}
|
71
75
|
end
|
72
76
|
|
73
77
|
def empty_binding
|
@@ -76,37 +80,35 @@ module Aspera
|
|
76
80
|
|
77
81
|
# secure execution of Ruby code
|
78
82
|
def secure_eval(code)
|
79
|
-
Kernel.send('lave'.reverse,code,empty_binding, __FILE__, __LINE__)
|
83
|
+
Kernel.send('lave'.reverse, code, empty_binding, __FILE__, __LINE__)
|
80
84
|
end
|
81
85
|
|
82
86
|
# value is provided in block
|
83
|
-
def write_file_restricted(path,force: false)
|
87
|
+
def write_file_restricted(path, force: false)
|
84
88
|
raise 'coding error, missing content block' unless block_given?
|
85
89
|
if force || !File.exist?(path)
|
86
90
|
File.unlink(path) rescue nil # Windows may give error
|
87
|
-
File.write(path,yield)
|
91
|
+
File.write(path, yield)
|
88
92
|
restrict_file_access(path)
|
89
93
|
end
|
90
94
|
return path
|
91
95
|
end
|
92
96
|
|
93
|
-
def restrict_file_access(path,mode: nil)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
Log.log.debug("No restriction can be set for #{path}");
|
103
|
-
end
|
97
|
+
def restrict_file_access(path, mode: nil)
|
98
|
+
if mode.nil?
|
99
|
+
# or FileUtils ?
|
100
|
+
if File.file?(path)
|
101
|
+
mode = 0o600
|
102
|
+
elsif File.directory?(path)
|
103
|
+
mode = 0o700
|
104
|
+
else
|
105
|
+
Log.log.debug{"No restriction can be set for #{path}"}
|
104
106
|
end
|
105
|
-
File.chmod(mode,path) unless mode.nil?
|
106
|
-
rescue => e
|
107
|
-
Log.log.warn(e.message)
|
108
107
|
end
|
108
|
+
File.chmod(mode, path) unless mode.nil?
|
109
|
+
rescue => e
|
110
|
+
Log.log.warn(e.message)
|
109
111
|
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
112
|
+
end # self
|
113
|
+
end # Environment
|
114
|
+
end # Aspera
|
@@ -6,6 +6,7 @@ module Aspera
|
|
6
6
|
# sub classes shall implement start_transfer and shutdown
|
7
7
|
class AgentBase
|
8
8
|
# fields description for JSON generation
|
9
|
+
# spellchecker: disable
|
9
10
|
INTEGER_FIELDS = %w[Bytescont FaspFileArgIndex StartByte Rate MinRate Port Priority RateCap MinRateCap TCPPort CreatePolicy TimePolicy
|
10
11
|
DatagramSize XoptFlags VLinkVersion PeerVLinkVersion DSPipelineDepth PeerDSPipelineDepth ReadBlockSize WriteBlockSize
|
11
12
|
ClusterNumNodes ClusterNodeId Size Written Loss FileBytes PreTransferBytes TransferBytes PMTU Elapsedusec ArgScansAttempted
|
@@ -13,7 +14,8 @@ module Aspera
|
|
13
14
|
BOOLEAN_FIELDS = %w[Encryption Remote RateLock MinRateLock PolicyLock FilesEncrypt FilesDecrypt VLinkLocalEnabled VLinkRemoteEnabled
|
14
15
|
MoveRange Keepalive TestLogin UseProxy Precalc RTTAutocorrect].freeze
|
15
16
|
EXPECTED_METHODS = %i[text struct enhanced].freeze
|
16
|
-
private_constant :INTEGER_FIELDS
|
17
|
+
private_constant :INTEGER_FIELDS, :BOOLEAN_FIELDS, :EXPECTED_METHODS
|
18
|
+
# spellchecker: enable
|
17
19
|
|
18
20
|
class << self
|
19
21
|
# This checks the validity of the value returned by wait_for_transfers_completion
|
@@ -28,13 +30,13 @@ module Aspera
|
|
28
30
|
|
29
31
|
# translates legacy event into enhanced (JSON) event
|
30
32
|
def enhanced_event_format(event)
|
31
|
-
return event.keys.each_with_object({}) do |e,h|
|
33
|
+
return event.keys.each_with_object({}) do |e, h|
|
32
34
|
# capital_to_snake_case
|
33
|
-
new_name = e
|
34
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
35
|
-
gsub(/([a-z\d])(usec)$/,'\1_\2')
|
36
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
37
|
-
downcase
|
35
|
+
new_name = e
|
36
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
37
|
+
.gsub(/([a-z\d])(usec)$/, '\1_\2')
|
38
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
39
|
+
.downcase
|
38
40
|
value = event[e]
|
39
41
|
value = value.to_i if INTEGER_FIELDS.include?(e)
|
40
42
|
value = value.eql?('Yes') if BOOLEAN_FIELDS.include?(e)
|
@@ -46,7 +48,7 @@ module Aspera
|
|
46
48
|
@listeners = []
|
47
49
|
end
|
48
50
|
|
49
|
-
def notify_listeners(current_event_text,current_event_data)
|
51
|
+
def notify_listeners(current_event_text, current_event_data)
|
50
52
|
Log.log.debug('send event to listeners')
|
51
53
|
enhanced_event = nil
|
52
54
|
@listeners.each do |listener|
|
@@ -59,16 +61,16 @@ module Aspera
|
|
59
61
|
end
|
60
62
|
end # notify_listeners
|
61
63
|
|
62
|
-
def notify_begin(id,size)
|
63
|
-
notify_listeners('emulated',{LISTENER_SESSION_ID_B => id,'Type' => 'NOTIFICATION','PreTransferBytes' => size})
|
64
|
+
def notify_begin(id, size)
|
65
|
+
notify_listeners('emulated', {LISTENER_SESSION_ID_B => id, 'Type' => 'NOTIFICATION', 'PreTransferBytes' => size})
|
64
66
|
end
|
65
67
|
|
66
|
-
def notify_progress(id,size)
|
67
|
-
notify_listeners('emulated',{LISTENER_SESSION_ID_B => id,'Type' => 'STATS','Bytescont' => size})
|
68
|
+
def notify_progress(id, size)
|
69
|
+
notify_listeners('emulated', {LISTENER_SESSION_ID_B => id, 'Type' => 'STATS', 'Bytescont' => size})
|
68
70
|
end
|
69
71
|
|
70
72
|
def notify_end(id)
|
71
|
-
notify_listeners('emulated',{LISTENER_SESSION_ID_B => id,'Type' => 'DONE'})
|
73
|
+
notify_listeners('emulated', {LISTENER_SESSION_ID_B => id, 'Type' => 'DONE'})
|
72
74
|
end
|
73
75
|
|
74
76
|
public
|
@@ -78,13 +80,13 @@ module Aspera
|
|
78
80
|
|
79
81
|
# listener receives events
|
80
82
|
def add_listener(listener)
|
81
|
-
raise "expect one of #{EXPECTED_METHODS}" if EXPECTED_METHODS.inject(0){|m,e|m
|
83
|
+
raise "expect one of #{EXPECTED_METHODS}" if EXPECTED_METHODS.inject(0){|m, e|m + (listener.respond_to?("event_#{e}") ? 1 : 0)}.eql?(0)
|
82
84
|
@listeners.push(listener)
|
83
85
|
self
|
84
86
|
end
|
85
87
|
|
86
88
|
# the following methods must be implemented by subclass:
|
87
|
-
# start_transfer(transfer_spec,
|
89
|
+
# start_transfer(transfer_spec, token_regenerator: nil) : start transfer
|
88
90
|
# wait_for_transfers_completion : wait for termination of all transfers, @return list of : :success or error message
|
89
91
|
# optional: shutdown
|
90
92
|
end
|
@@ -8,52 +8,54 @@ require 'tty-spinner'
|
|
8
8
|
|
9
9
|
module Aspera
|
10
10
|
module Fasp
|
11
|
-
class AgentConnect < AgentBase
|
12
|
-
|
13
|
-
SLEEP_SEC_BETWEEN_RETRY =
|
14
|
-
private_constant :
|
11
|
+
class AgentConnect < Aspera::Fasp::AgentBase
|
12
|
+
CONNECT_START_URIS = ['fasp://initialize', 'fasp://initialize', 'aspera-drive://initialize', 'https://test-connect.ibmaspera.com/']
|
13
|
+
SLEEP_SEC_BETWEEN_RETRY = 3
|
14
|
+
private_constant :CONNECT_START_URIS, :SLEEP_SEC_BETWEEN_RETRY
|
15
15
|
def initialize(_options)
|
16
16
|
super()
|
17
17
|
@connect_settings = {
|
18
18
|
'app_id' => SecureRandom.uuid
|
19
19
|
}
|
20
20
|
raise 'Using connect requires a graphical environment' if !OpenApplication.default_gui_mode.eql?(:graphical)
|
21
|
-
|
21
|
+
method_index = 0
|
22
22
|
begin
|
23
23
|
connect_url = Installation.instance.connect_uri
|
24
|
-
Log.log.debug
|
25
|
-
@connect_api = Rest.new({base_url: "#{connect_url}/v5/connect",headers: {'Origin' => Rest.user_agent}}) # could use v6 also now
|
26
|
-
|
27
|
-
Log.
|
24
|
+
Log.log.debug{"found: #{connect_url}"}
|
25
|
+
@connect_api = Rest.new({base_url: "#{connect_url}/v5/connect", headers: {'Origin' => Rest.user_agent}}) # could use v6 also now
|
26
|
+
connect_info = @connect_api.read('info/version')[:data]
|
27
|
+
Log.log.info('Connect was reached') if method_index > 0
|
28
|
+
Log.dump(:connect_version, connect_info)
|
28
29
|
rescue StandardError => e # Errno::ECONNREFUSED
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
start_url = CONNECT_START_URIS[method_index]
|
31
|
+
method_index += 1
|
32
|
+
raise StandardError, "Unable to start connect #{method_index} times" if start_url.nil?
|
33
|
+
Log.log.warn{"Aspera Connect is not started (#{e}). Trying to start it ##{method_index}..."}
|
34
|
+
if !OpenApplication.uri_graphical(start_url)
|
33
35
|
OpenApplication.uri_graphical('https://downloads.asperasoft.com/connect2/')
|
34
|
-
raise StandardError,'Connect is not installed'
|
36
|
+
raise StandardError, 'Connect is not installed'
|
35
37
|
end
|
36
38
|
sleep(SLEEP_SEC_BETWEEN_RETRY)
|
37
39
|
retry
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
def start_transfer(transfer_spec,
|
43
|
+
def start_transfer(transfer_spec, token_regenerator: nil)
|
42
44
|
if transfer_spec['direction'] == 'send'
|
43
|
-
Log.log.warn
|
45
|
+
Log.log.warn{"Connect requires upload selection using GUI, ignoring #{transfer_spec['paths']}".red}
|
44
46
|
transfer_spec.delete('paths')
|
45
|
-
|
47
|
+
selection = @connect_api.create('windows/select-open-file-dialog/', {
|
46
48
|
'aspera_connect_settings' => @connect_settings,
|
47
49
|
'title' => 'Select Files',
|
48
50
|
'suggestedName' => '',
|
49
51
|
'allowMultipleSelection' => true,
|
50
52
|
'allowedFileTypes' => ''})[:data]
|
51
|
-
transfer_spec['paths'] =
|
53
|
+
transfer_spec['paths'] = selection['dataTransfer']['files'].map { |i| {'source' => i['name']}}
|
52
54
|
end
|
53
55
|
@request_id = SecureRandom.uuid
|
54
56
|
# if there is a token, we ask connect client to use well known ssh private keys
|
55
57
|
# instead of asking password
|
56
|
-
transfer_spec['authentication'] = 'token' if transfer_spec.
|
58
|
+
transfer_spec['authentication'] = 'token' if transfer_spec.key?('token')
|
57
59
|
connect_transfer_args = {
|
58
60
|
'aspera_connect_settings' => @connect_settings.merge({
|
59
61
|
'request_id' => @request_id,
|
@@ -63,7 +65,7 @@ module Aspera
|
|
63
65
|
'transfer_spec' => transfer_spec
|
64
66
|
}]}
|
65
67
|
# asynchronous anyway
|
66
|
-
res = @connect_api.create('transfers/start',connect_transfer_args)[:data]
|
68
|
+
res = @connect_api.create('transfers/start', connect_transfer_args)[:data]
|
67
69
|
@xfer_id = res['transfer_specs'].first['transfer_spec']['tags']['aspera']['xfer_id']
|
68
70
|
end
|
69
71
|
|
@@ -73,39 +75,39 @@ module Aspera
|
|
73
75
|
spinner = nil
|
74
76
|
begin
|
75
77
|
loop do
|
76
|
-
tr_info = @connect_api.create("transfers/info/#{@xfer_id}",connect_activity_args)[:data]
|
78
|
+
tr_info = @connect_api.create("transfers/info/#{@xfer_id}", connect_activity_args)[:data]
|
77
79
|
if tr_info['transfer_info'].is_a?(Hash)
|
78
|
-
|
79
|
-
if
|
80
|
+
transfer = tr_info['transfer_info']
|
81
|
+
if transfer.nil?
|
80
82
|
Log.log.warn('no session in Connect')
|
81
83
|
break
|
82
84
|
end
|
83
85
|
# TODO: get session id
|
84
|
-
case
|
86
|
+
case transfer['status']
|
85
87
|
when 'completed'
|
86
88
|
notify_end(@connect_settings['app_id'])
|
87
89
|
break
|
88
|
-
when 'initiating','queued'
|
90
|
+
when 'initiating', 'queued'
|
89
91
|
if spinner.nil?
|
90
92
|
spinner = TTY::Spinner.new('[:spinner] :title', format: :classic)
|
91
93
|
spinner.start
|
92
94
|
end
|
93
|
-
spinner.update(title:
|
95
|
+
spinner.update(title: transfer['status'])
|
94
96
|
spinner.spin
|
95
97
|
when 'running'
|
96
|
-
#puts "running: sessions:#{
|
97
|
-
if !started && (
|
98
|
+
# puts "running: sessions:#{transfer['sessions'].length}, #{transfer['sessions'].map{|i| i['bytes_transferred']}.join(',')}"
|
99
|
+
if !started && (transfer['bytes_expected'] != 0)
|
98
100
|
spinner&.success
|
99
|
-
notify_begin(@connect_settings['app_id'],
|
101
|
+
notify_begin(@connect_settings['app_id'], transfer['bytes_expected'])
|
100
102
|
started = true
|
101
103
|
else
|
102
|
-
notify_progress(@connect_settings['app_id'],
|
104
|
+
notify_progress(@connect_settings['app_id'], transfer['bytes_written'])
|
103
105
|
end
|
104
106
|
when 'failed'
|
105
107
|
spinner&.error
|
106
|
-
raise Fasp::Error,
|
108
|
+
raise Fasp::Error, transfer['error_desc']
|
107
109
|
else
|
108
|
-
raise Fasp::Error, "unknown status: #{
|
110
|
+
raise Fasp::Error, "unknown status: #{transfer['status']}: #{transfer['error_desc']}"
|
109
111
|
end
|
110
112
|
end
|
111
113
|
sleep(1)
|