aspera-cli 4.24.1 → 4.25.0.pre
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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +1064 -745
- data/CONTRIBUTING.md +43 -100
- data/README.md +1281 -720
- data/bin/ascli +20 -1
- data/bin/asession +23 -27
- data/lib/aspera/agent/base.rb +10 -21
- data/lib/aspera/agent/connect.rb +2 -3
- data/lib/aspera/agent/desktop.rb +2 -2
- data/lib/aspera/agent/direct.rb +49 -32
- data/lib/aspera/agent/factory.rb +31 -0
- data/lib/aspera/api/aoc.rb +134 -76
- data/lib/aspera/api/cos_node.rb +3 -2
- data/lib/aspera/api/faspex.rb +213 -0
- data/lib/aspera/api/node.rb +107 -94
- data/lib/aspera/ascmd.rb +1 -2
- data/lib/aspera/ascp/installation.rb +73 -58
- data/lib/aspera/ascp/management.rb +119 -23
- data/lib/aspera/assert.rb +39 -11
- data/lib/aspera/cli/error.rb +4 -2
- data/lib/aspera/cli/extended_value.rb +91 -67
- data/lib/aspera/cli/formatter.rb +62 -27
- data/lib/aspera/cli/hints.rb +8 -0
- data/lib/aspera/cli/info.rb +4 -4
- data/lib/aspera/cli/main.rb +76 -84
- data/lib/aspera/cli/manager.rb +352 -248
- data/lib/aspera/cli/plugins/alee.rb +5 -4
- data/lib/aspera/cli/plugins/aoc.rb +175 -195
- data/lib/aspera/cli/plugins/ats.rb +4 -4
- data/lib/aspera/cli/plugins/base.rb +343 -0
- data/lib/aspera/cli/plugins/basic_auth.rb +45 -0
- data/lib/aspera/cli/plugins/config.rb +283 -269
- data/lib/aspera/cli/plugins/console.rb +27 -22
- data/lib/aspera/cli/plugins/cos.rb +3 -3
- data/lib/aspera/cli/plugins/factory.rb +78 -0
- data/lib/aspera/cli/plugins/faspex.rb +49 -46
- data/lib/aspera/cli/plugins/faspex5.rb +113 -225
- data/lib/aspera/cli/plugins/faspio.rb +19 -18
- data/lib/aspera/cli/plugins/httpgw.rb +14 -13
- data/lib/aspera/cli/plugins/node.rb +162 -149
- data/lib/aspera/cli/plugins/oauth.rb +48 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +129 -45
- data/lib/aspera/cli/plugins/preview.rb +30 -50
- data/lib/aspera/cli/plugins/server.rb +21 -21
- data/lib/aspera/cli/plugins/shares.rb +45 -47
- data/lib/aspera/cli/sync_actions.rb +50 -39
- data/lib/aspera/cli/transfer_agent.rb +35 -49
- data/lib/aspera/cli/transfer_progress.rb +6 -6
- data/lib/aspera/cli/version.rb +3 -3
- data/lib/aspera/cli/wizard.rb +70 -55
- data/lib/aspera/colors.rb +6 -0
- data/lib/aspera/command_line_builder.rb +59 -61
- data/lib/aspera/command_line_converter.rb +2 -1
- data/lib/aspera/coverage.rb +2 -2
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +51 -41
- data/lib/aspera/faspex_gw.rb +7 -5
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/keychain/factory.rb +1 -2
- data/lib/aspera/keychain/macos_security.rb +1 -1
- data/lib/aspera/log.rb +37 -9
- data/lib/aspera/markdown.rb +31 -0
- data/lib/aspera/nagios.rb +7 -6
- data/lib/aspera/oauth/base.rb +25 -28
- data/lib/aspera/oauth/factory.rb +9 -9
- data/lib/aspera/oauth/url_json.rb +2 -1
- data/lib/aspera/oauth/web.rb +2 -2
- data/lib/aspera/preview/file_types.rb +23 -37
- data/lib/aspera/products/connect.rb +7 -6
- data/lib/aspera/products/desktop.rb +1 -4
- data/lib/aspera/products/other.rb +9 -1
- data/lib/aspera/products/transferd.rb +0 -1
- data/lib/aspera/rest.rb +168 -113
- data/lib/aspera/rest_error_analyzer.rb +4 -4
- data/lib/aspera/ssh.rb +7 -4
- data/lib/aspera/ssl.rb +41 -0
- data/lib/aspera/sync/args.schema.yaml +46 -3
- data/lib/aspera/sync/conf.schema.yaml +307 -123
- data/lib/aspera/sync/database.rb +2 -1
- data/lib/aspera/sync/operations.rb +135 -79
- data/lib/aspera/temp_file_manager.rb +17 -5
- data/lib/aspera/transfer/error.rb +16 -7
- data/lib/aspera/transfer/parameters.rb +35 -22
- data/lib/aspera/transfer/resumer.rb +74 -0
- data/lib/aspera/transfer/spec.rb +5 -5
- data/lib/aspera/transfer/spec.schema.yaml +170 -59
- data/lib/aspera/transfer/spec_doc.rb +49 -43
- data/lib/aspera/uri_reader.rb +2 -2
- data/lib/aspera/web_auth.rb +6 -6
- data/lib/transferd_pb.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +26 -11
- metadata.gz.sig +0 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +0 -43
- data/lib/aspera/cli/plugin.rb +0 -333
- data/lib/aspera/cli/plugin_factory.rb +0 -81
- data/lib/aspera/resumer.rb +0 -77
- data/lib/aspera/transfer/error_info.rb +0 -91
|
@@ -33,25 +33,12 @@ module Aspera
|
|
|
33
33
|
class Installation
|
|
34
34
|
include Singleton
|
|
35
35
|
|
|
36
|
-
DEFAULT_ASPERA_CONF = <<~END_OF_CONFIG_FILE
|
|
37
|
-
<?xml version='1.0' encoding='UTF-8'?>
|
|
38
|
-
<CONF version="2">
|
|
39
|
-
<default>
|
|
40
|
-
<file_system>
|
|
41
|
-
<resume_suffix>.aspera-ckpt</resume_suffix>
|
|
42
|
-
<partial_file_suffix>.partial</partial_file_suffix>
|
|
43
|
-
</file_system>
|
|
44
|
-
</default>
|
|
45
|
-
</CONF>
|
|
46
|
-
END_OF_CONFIG_FILE
|
|
47
|
-
# all executable files from SDK
|
|
48
|
-
EXE_FILES = %i[ascp ascp4 async transferd].freeze
|
|
49
|
-
SDK_FILES = %i[ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
|
|
50
|
-
TRANSFERD_ARCHIVE_LOCATION_URL = 'https://ibm.biz/sdk_location'
|
|
51
|
-
# filename for ascp with optional extension (Windows)
|
|
52
|
-
private_constant :DEFAULT_ASPERA_CONF, :SDK_FILES, :TRANSFERD_ARCHIVE_LOCATION_URL
|
|
53
36
|
# options for SSH client private key
|
|
54
37
|
CLIENT_SSH_KEY_OPTIONS = %i{dsa_rsa rsa per_client}.freeze
|
|
38
|
+
# prefix
|
|
39
|
+
USE_PRODUCT_PREFIX = 'product:'
|
|
40
|
+
# policy for product selection
|
|
41
|
+
FIRST_FOUND = 'FIRST'
|
|
55
42
|
|
|
56
43
|
# Loads YAML from cloud with locations of SDK archives for all platforms
|
|
57
44
|
# @return location structure
|
|
@@ -66,12 +53,13 @@ module Aspera
|
|
|
66
53
|
end
|
|
67
54
|
end
|
|
68
55
|
|
|
69
|
-
# set ascp executable
|
|
56
|
+
# set ascp executable "location"
|
|
70
57
|
def ascp_path=(v)
|
|
71
58
|
Aspera.assert_type(v, String)
|
|
72
|
-
Aspera.assert(!v.empty?){'ascp
|
|
73
|
-
|
|
74
|
-
@
|
|
59
|
+
Aspera.assert(!v.empty?){'ascp location cannot be empty: check your config file'}
|
|
60
|
+
@ascp_location = v
|
|
61
|
+
@ascp_path = nil
|
|
62
|
+
return
|
|
75
63
|
end
|
|
76
64
|
|
|
77
65
|
def ascp_path
|
|
@@ -88,13 +76,12 @@ module Aspera
|
|
|
88
76
|
def use_ascp_from_product(product_name)
|
|
89
77
|
if product_name.eql?(FIRST_FOUND)
|
|
90
78
|
pl = installed_products.first
|
|
91
|
-
raise "
|
|
79
|
+
raise "No Aspera transfer module or SDK found.\nRefer to the manual or install SDK with command:\nascli conf transferd install" if pl.nil?
|
|
92
80
|
else
|
|
93
81
|
pl = installed_products.find{ |i| i[:name].eql?(product_name)}
|
|
94
|
-
raise "
|
|
82
|
+
raise "No such product installed: #{product_name}" if pl.nil?
|
|
95
83
|
end
|
|
96
|
-
|
|
97
|
-
Log.log.debug{"ascp_path=#{@path_to_ascp}"}
|
|
84
|
+
@ascp_path = pl[:ascp_path]
|
|
98
85
|
end
|
|
99
86
|
|
|
100
87
|
# @return [Hash] with key = file name (String), and value = path to file
|
|
@@ -111,7 +98,9 @@ module Aspera
|
|
|
111
98
|
end
|
|
112
99
|
end
|
|
113
100
|
|
|
101
|
+
# TODO: if using another product than SDK, should use files from there
|
|
114
102
|
def check_or_create_sdk_file(filename, force: false, &block)
|
|
103
|
+
FileUtils.mkdir_p(Products::Transferd.sdk_directory)
|
|
115
104
|
return Environment.write_file_restricted(File.join(Products::Transferd.sdk_directory, filename), force: force, mode: 0o644, &block)
|
|
116
105
|
end
|
|
117
106
|
|
|
@@ -127,10 +116,18 @@ module Aspera
|
|
|
127
116
|
file = if k.eql?(:transferd)
|
|
128
117
|
Products::Transferd.transferd_path
|
|
129
118
|
else
|
|
130
|
-
#
|
|
131
|
-
|
|
119
|
+
# find ascp when needed
|
|
120
|
+
if @ascp_path.nil?
|
|
121
|
+
if @ascp_location.start_with?(USE_PRODUCT_PREFIX)
|
|
122
|
+
use_ascp_from_product(@ascp_location[USE_PRODUCT_PREFIX.length..-1])
|
|
123
|
+
else
|
|
124
|
+
@ascp_path = @ascp_location
|
|
125
|
+
end
|
|
126
|
+
Aspera.assert(File.exist?(@ascp_path)){"No such file: [#{@ascp_path}]"}
|
|
127
|
+
Log.log.debug{"ascp_path=#{@ascp_path}"}
|
|
128
|
+
end
|
|
132
129
|
# NOTE: that there might be a .exe at the end
|
|
133
|
-
@
|
|
130
|
+
@ascp_path.gsub('ascp', k.to_s)
|
|
134
131
|
end
|
|
135
132
|
when :ssh_private_dsa, :ssh_private_rsa
|
|
136
133
|
# assume last 3 letters are type
|
|
@@ -195,7 +192,9 @@ module Aspera
|
|
|
195
192
|
return exe_version
|
|
196
193
|
end
|
|
197
194
|
|
|
198
|
-
|
|
195
|
+
# Extract some stings from ascp logs
|
|
196
|
+
# Folder, PVCL, version, license information
|
|
197
|
+
def ascp_info_from_log
|
|
199
198
|
data = {}
|
|
200
199
|
# read PATHs from ascp directly, and pvcl modules as well
|
|
201
200
|
Open3.popen3(ascp_path, '-DDL-') do |_stdin, _stdout, stderr, thread|
|
|
@@ -227,8 +226,9 @@ module Aspera
|
|
|
227
226
|
return data
|
|
228
227
|
end
|
|
229
228
|
|
|
230
|
-
#
|
|
231
|
-
|
|
229
|
+
# Extract some stings from ascp binary
|
|
230
|
+
# Openssl information
|
|
231
|
+
def ascp_info_from_file
|
|
232
232
|
data = {}
|
|
233
233
|
File.binread(ascp_path).scan(/[\x20-\x7E]{10,}/) do |bin_string|
|
|
234
234
|
if (m = bin_string.match(/OPENSSLDIR.*"(.*)"/))
|
|
@@ -243,17 +243,17 @@ module Aspera
|
|
|
243
243
|
# information for `ascp info`
|
|
244
244
|
def ascp_info
|
|
245
245
|
ascp_data = file_paths
|
|
246
|
-
ascp_data.merge!(
|
|
247
|
-
ascp_data.merge!(
|
|
246
|
+
ascp_data.merge!(ascp_info_from_log)
|
|
247
|
+
ascp_data.merge!(ascp_info_from_file)
|
|
248
248
|
return ascp_data
|
|
249
249
|
end
|
|
250
250
|
|
|
251
251
|
# @return the url for download of SDK archive for the given platform and version
|
|
252
252
|
def sdk_url_for_platform(platform: nil, version: nil)
|
|
253
|
-
|
|
253
|
+
all_locations = sdk_locations
|
|
254
254
|
platform = Environment.instance.architecture if platform.nil?
|
|
255
|
-
locations =
|
|
256
|
-
raise "No SDK for platform: #{platform}" if locations.empty?
|
|
255
|
+
locations = all_locations.select{ |l| l['platform'].eql?(platform)}
|
|
256
|
+
raise "No SDK for platform: #{platform}, available: #{all_locations.map{ |i| i['platform']}.uniq}" if locations.empty?
|
|
257
257
|
version = locations.max_by{ |entry| Gem::Version.new(entry['version'])}['version'] if version.nil?
|
|
258
258
|
info = locations.select{ |entry| entry['version'].eql?(version)}
|
|
259
259
|
raise "No such version: #{version} for #{platform}" if info.empty?
|
|
@@ -271,7 +271,9 @@ module Aspera
|
|
|
271
271
|
Zip::File.open(sdk_archive_path) do |zip_file|
|
|
272
272
|
zip_file.each do |entry|
|
|
273
273
|
next if entry.name.end_with?('/')
|
|
274
|
-
|
|
274
|
+
entry.get_input_stream do |io|
|
|
275
|
+
yield(entry.name, io, nil)
|
|
276
|
+
end
|
|
275
277
|
end
|
|
276
278
|
end
|
|
277
279
|
# Other Unixes use tar.gz
|
|
@@ -296,7 +298,7 @@ module Aspera
|
|
|
296
298
|
# @param folder [String] Destination folder path
|
|
297
299
|
# @param backup [Bool] If destination folder exists, then rename
|
|
298
300
|
# @param with_exe [Bool] If false, only retrieves files, but do not generate or restrict access
|
|
299
|
-
# @param &block [Proc] A lambda that receives a file path from archive and tells
|
|
301
|
+
# @param &block [Proc] A lambda that receives a file path from archive and tells destination sub folder(end with /) or file, or nil to not extract
|
|
300
302
|
# @return ascp version (from execution)
|
|
301
303
|
def install_sdk(url: nil, version: nil, folder: nil, backup: true, with_exe: true, &block)
|
|
302
304
|
url = sdk_url_for_platform(version: version) if url.nil? || url.eql?('DEF')
|
|
@@ -308,6 +310,7 @@ module Aspera
|
|
|
308
310
|
Products::Transferd::RUNTIME_FOLDERS.any?{ |i| name.match?(%r{^[^/]*/#{i}/})} ? '/' : nil
|
|
309
311
|
end
|
|
310
312
|
end
|
|
313
|
+
FileUtils.mkdir_p(folder)
|
|
311
314
|
# rename old install
|
|
312
315
|
if backup && !Dir.empty?(folder)
|
|
313
316
|
Log.log.warn('Previous install exists, renaming folder.')
|
|
@@ -333,7 +336,7 @@ module Aspera
|
|
|
333
336
|
end
|
|
334
337
|
end
|
|
335
338
|
return unless with_exe
|
|
336
|
-
#
|
|
339
|
+
# Ensure necessary files are there, or generate them
|
|
337
340
|
SDK_FILES.each do |file_id_sym|
|
|
338
341
|
file_path = path(file_id_sym)
|
|
339
342
|
if file_path && EXE_FILES.include?(file_id_sym)
|
|
@@ -352,11 +355,27 @@ module Aspera
|
|
|
352
355
|
|
|
353
356
|
private
|
|
354
357
|
|
|
355
|
-
|
|
356
|
-
|
|
358
|
+
DEFAULT_ASPERA_CONF = <<~END_OF_CONFIG_FILE
|
|
359
|
+
<?xml version='1.0' encoding='UTF-8'?>
|
|
360
|
+
<CONF version="2">
|
|
361
|
+
<default>
|
|
362
|
+
<file_system>
|
|
363
|
+
<resume_suffix>.aspera-ckpt</resume_suffix>
|
|
364
|
+
<partial_file_suffix>.partial</partial_file_suffix>
|
|
365
|
+
</file_system>
|
|
366
|
+
</default>
|
|
367
|
+
</CONF>
|
|
368
|
+
END_OF_CONFIG_FILE
|
|
369
|
+
# all executable files from SDK
|
|
370
|
+
EXE_FILES = %i[ascp ascp4 async transferd].freeze
|
|
371
|
+
SDK_FILES = %i[ssh_private_dsa ssh_private_rsa aspera_license aspera_conf fallback_certificate fallback_private_key].unshift(*EXE_FILES).freeze
|
|
372
|
+
TRANSFERD_ARCHIVE_LOCATION_URL = 'https://ibm.biz/sdk_location'
|
|
373
|
+
# filename for ascp with optional extension (Windows)
|
|
374
|
+
private_constant :DEFAULT_ASPERA_CONF, :EXE_FILES, :SDK_FILES, :TRANSFERD_ARCHIVE_LOCATION_URL
|
|
357
375
|
|
|
358
376
|
def initialize
|
|
359
|
-
@
|
|
377
|
+
@ascp_path = nil
|
|
378
|
+
@ascp_location = nil
|
|
360
379
|
@sdk_dir = nil
|
|
361
380
|
@found_products = nil
|
|
362
381
|
@transferd_urls = TRANSFERD_ARCHIVE_LOCATION_URL
|
|
@@ -366,22 +385,18 @@ module Aspera
|
|
|
366
385
|
|
|
367
386
|
# @return the list of installed products in format of product_locations_on_current_os
|
|
368
387
|
def installed_products
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
# search installed products: with ascp
|
|
382
|
-
@found_products = Products::Other.find(scan_locations)
|
|
383
|
-
end
|
|
384
|
-
return @found_products
|
|
388
|
+
return @found_products unless @found_products.nil?
|
|
389
|
+
# :expected M app name is taken from the manifest if present, else defaults to this value
|
|
390
|
+
# :app_root M main folder for the application
|
|
391
|
+
# :log_root O location of log files (Linux uses syslog)
|
|
392
|
+
# :run_root O only for Connect Client, location of http port file
|
|
393
|
+
# :sub_bin O subfolder with executables, default : bin
|
|
394
|
+
scan_locations = Products::Transferd.locations +
|
|
395
|
+
Products::Desktop.locations +
|
|
396
|
+
Products::Connect.locations +
|
|
397
|
+
Products::Other::LOCATION_ON_THIS_OS
|
|
398
|
+
# search installed products: with ascp
|
|
399
|
+
@found_products = Products::Other.find(scan_locations)
|
|
385
400
|
end
|
|
386
401
|
end
|
|
387
402
|
end
|
|
@@ -6,6 +6,90 @@ module Aspera
|
|
|
6
6
|
module Ascp
|
|
7
7
|
# processing of ascp management port events
|
|
8
8
|
class Management
|
|
9
|
+
# from https://www.google.com/search?q=FASP+error+codes
|
|
10
|
+
# Note that the fact that an error is retry-able is not internally defined by protocol, it's client-side responsibility
|
|
11
|
+
# rubocop:disable Layout/FirstHashElementLineBreak
|
|
12
|
+
ERRORS = {
|
|
13
|
+
# id retry-able mnemo/code message additional info
|
|
14
|
+
0 => {r: false, c: 'UNKNOWN', m: 'unknown', a: 'unknown'},
|
|
15
|
+
1 => {r: false, c: 'FASP_PROTO', m: 'Generic fasp(tm) protocol error', a: 'fasp(tm) error'},
|
|
16
|
+
2 => {r: false, c: 'ASCP', m: 'Generic SCP error', a: 'ASCP error'},
|
|
17
|
+
3 => {r: false, c: 'AMBIGUOUS_TARGET', m: 'Target incorrectly specified', a: 'Ambiguous target'},
|
|
18
|
+
4 => {r: false, c: 'NO_SUCH_FILE', m: 'No such file or directory', a: 'No such file or directory'},
|
|
19
|
+
5 => {r: false, c: 'NO_PERMS', m: 'Insufficient permission to read or write', a: 'Insufficient permissions'},
|
|
20
|
+
6 => {r: false, c: 'NOT_DIR', m: 'Target is not a directory', a: 'Target must be a directory'},
|
|
21
|
+
7 => {r: false, c: 'IS_DIR', m: 'File is a directory - expected regular file', a: 'Expected regular file'},
|
|
22
|
+
8 => {r: false, c: 'USAGE', m: 'Incorrect usage of scp command', a: 'Incorrect usage of Aspera scp command'},
|
|
23
|
+
9 => {r: false, c: 'LIC_DUP', m: 'Duplicate license', a: 'Duplicate license'},
|
|
24
|
+
10 => {r: false, c: 'LIC_RATE_EXCEEDED', m: 'Rate exceeds the cap imposed by license', a: 'Rate exceeds cap imposed by license'},
|
|
25
|
+
11 => {r: false, c: 'INTERNAL_ERROR', m: 'Internal error (unexpected error)', a: 'Internal error'},
|
|
26
|
+
12 => {r: true, c: 'TRANSFER_ERROR', m: 'Error establishing control connection',
|
|
27
|
+
a: 'Error establishing SSH connection (check SSH port and firewall)'},
|
|
28
|
+
13 => {r: true, c: 'TRANSFER_TIMEOUT', m: 'Timeout establishing control connection',
|
|
29
|
+
a: 'Timeout establishing SSH connection (check SSH port and firewall)'},
|
|
30
|
+
14 => {r: true, c: 'CONNECTION_ERROR', m: 'Error establishing data connection',
|
|
31
|
+
a: 'Error establishing UDP connection (check UDP port and firewall)'},
|
|
32
|
+
15 => {r: true, c: 'CONNECTION_TIMEOUT', m: 'Timeout establishing data connection',
|
|
33
|
+
a: 'Timeout establishing UDP connection (check UDP port and firewall)'},
|
|
34
|
+
16 => {r: true, c: 'CONNECTION_LOST', m: 'Connection lost', a: 'Connection lost'},
|
|
35
|
+
17 => {r: true, c: 'RCVR_SEND_ERROR', m: 'Receiver fails to send feedback', a: 'Network failure (receiver can\'t send feedback)'},
|
|
36
|
+
18 => {r: true, c: 'RCVR_RECV_ERROR', m: 'Receiver fails to receive data packets', a: 'Network failure (receiver can\'t receive UDP data)'},
|
|
37
|
+
19 => {r: false, c: 'AUTH', m: 'Authentication failure', a: 'Authentication failure'},
|
|
38
|
+
20 => {r: false, c: 'NOTHING', m: 'Nothing to transfer', a: 'Nothing to transfer'},
|
|
39
|
+
21 => {r: false, c: 'NOT_REGULAR', m: 'Not a regular file (special file)', a: 'Not a regular file'},
|
|
40
|
+
22 => {r: false, c: 'FILE_TABLE_OVR', m: 'File table overflow', a: 'File table overflow'},
|
|
41
|
+
23 => {r: true, c: 'TOO_MANY_FILES', m: 'Too many files open', a: 'Too many files open'},
|
|
42
|
+
24 => {r: false, c: 'FILE_TOO_BIG', m: 'File too big for file system', a: 'File too big for filesystem'},
|
|
43
|
+
25 => {r: false, c: 'NO_SPACE_LEFT', m: 'No space left on disk', a: 'No space left on disk'},
|
|
44
|
+
26 => {r: false, c: 'READ_ONLY_FS', m: 'Read only file system', a: 'Read only filesystem'},
|
|
45
|
+
27 => {r: false, c: 'SOME_FILE_ERRS', m: 'Some individual files failed', a: 'One or more files failed'},
|
|
46
|
+
28 => {r: false, c: 'USER_CANCEL', m: 'Cancelled by user', a: 'Cancelled by user'},
|
|
47
|
+
29 => {r: false, c: 'LIC_NOLIC', m: 'License not found or unable to access', a: 'Unable to access license info'},
|
|
48
|
+
30 => {r: false, c: 'LIC_EXPIRED', m: 'License expired', a: 'License expired'},
|
|
49
|
+
31 => {r: false, c: 'SOCK_SETUP', m: 'Unable to setup socket (create, bind, etc ...)', a: 'Unable to set up socket'},
|
|
50
|
+
32 => {r: true, c: 'OUT_OF_MEMORY', m: 'Out of memory, unable to allocate', a: 'Out of memory'},
|
|
51
|
+
33 => {r: true, c: 'THREAD_SPAWN', m: 'Can\'t spawn thread', a: 'Unable to spawn thread'},
|
|
52
|
+
34 => {r: false, c: 'UNAUTHORIZED', m: 'Unauthorized by external auth server', a: 'Unauthorized'},
|
|
53
|
+
35 => {r: true, c: 'DISK_READ', m: 'Error reading source file from disk', a: 'Disk read error'},
|
|
54
|
+
36 => {r: true, c: 'DISK_WRITE', m: 'Error writing to disk', a: 'Disk write error'},
|
|
55
|
+
37 => {r: true, c: 'AUTHORIZATION', m: 'Used interchangeably with ERR_UNAUTHORIZED', a: 'Authorization failure'},
|
|
56
|
+
38 => {r: false, c: 'LIC_ILLEGAL', m: 'Operation not permitted by license', a: 'Operation not permitted by license'},
|
|
57
|
+
39 => {r: true, c: 'PEER_ABORTED_SESSION', m: 'Remote peer terminated session', a: 'Peer aborted session'},
|
|
58
|
+
40 => {r: true, c: 'DATA_TRANSFER_TIMEOUT', m: 'Transfer stalled, timed out', a: 'Data transfer stalled, timed out'},
|
|
59
|
+
41 => {r: false, c: 'BAD_PATH', m: 'Path violates docroot containment', a: 'File location is outside \'docroot\' hierarchy'},
|
|
60
|
+
42 => {r: false, c: 'ALREADY_EXISTS', m: 'File or directory already exists', a: 'File or directory already exists'},
|
|
61
|
+
43 => {r: false, c: 'STAT_FAILS', m: 'Cannot stat file', a: 'Cannot collect details about file or directory'},
|
|
62
|
+
44 => {r: true, c: 'PMTU_BRTT_ERROR', m: 'UDP session initiation fatal error', a: 'UDP session initiation fatal error'},
|
|
63
|
+
45 => {r: true, c: 'BWMEAS_ERROR', m: 'Bandwidth measurement fatal error', a: 'Bandwidth measurement fatal error'},
|
|
64
|
+
46 => {r: false, c: 'VLINK_ERROR', m: 'Virtual link error', a: 'Virtual link error'},
|
|
65
|
+
47 => {r: false, c: 'CONNECTION_ERROR_HTTP', m: 'Error establishing HTTP connection',
|
|
66
|
+
a: 'Error establishing HTTP connection (check HTTP port and firewall)'},
|
|
67
|
+
48 => {r: false, c: 'FILE_ENCRYPTION_ERROR', m: 'File encryption error, e.g. corrupt file',
|
|
68
|
+
a: 'File encryption/decryption error, e.g. corrupt file'},
|
|
69
|
+
49 => {r: false, c: 'FILE_DECRYPTION_PASS', m: 'File encryption/decryption error, e.g. corrupt file', a: 'File decryption error, bad passphrase'},
|
|
70
|
+
50 => {r: false, c: 'BAD_CONFIGURATION', m: 'Aspera.conf contains invalid data and was rejected', a: 'Invalid configuration'},
|
|
71
|
+
51 => {r: false, c: 'INSECURE_CONNECTION', m: 'Remote-host key check failure', a: 'Remote host is not who we expected'},
|
|
72
|
+
52 => {r: false, c: 'START_VALIDATION_FAILED', m: 'File start validation failed', a: 'File start validation failed'},
|
|
73
|
+
53 => {r: false, c: 'STOP_VALIDATION_FAILED', m: 'File stop validation failed', a: 'File stop validation failed'},
|
|
74
|
+
54 => {r: false, c: 'THRESHOLD_VALIDATION_FAILED', m: 'File threshold validation failed', a: 'File threshold validation failed'},
|
|
75
|
+
55 => {r: false, c: 'FILEPATH_TOO_LONG', m: 'File path/name too long for underlying file system', a: 'File path exceeds underlying file system limit'},
|
|
76
|
+
56 => {r: false, c: 'ILLEGAL_CHARS_IN_PATH', m: 'Windows path contains illegal characters',
|
|
77
|
+
a: 'Path being written to Windows file system contains illegal characters'},
|
|
78
|
+
57 => {r: false, c: 'CHUNK_MUST_MATCH_ALIGNMENT', m: 'Chunk size/start must be aligned with storage', a: 'Chunk size/start must be aligned with storage'},
|
|
79
|
+
58 => {r: false, c: 'VALIDATION_SESSION_ABORT', m: 'Session aborted to due to validation error', a: 'Session aborted to due validation error'},
|
|
80
|
+
59 => {r: false, c: 'REMOTE_STORAGE_ERROR', m: 'Remote storage errored', a: 'Remote storage errored'},
|
|
81
|
+
60 => {r: false, c: 'LUA_SCRIPT_ABORTED_SESSION', m: 'Session aborted due to Lua script abort', a: 'Session aborted due to Lua script abort'},
|
|
82
|
+
61 => {r: true, c: 'SSEAR_RETRYABLE', m: 'Transfer failed because of a retryable Encryption at Rest error',
|
|
83
|
+
a: 'Transfer failed because of a retryable Encryption at Rest error'},
|
|
84
|
+
62 => {r: false, c: 'SSEAR_FATAL', m: 'Transfer failed because of a fatal Encryption at Rest error',
|
|
85
|
+
a: 'Transfer failed because of a fatal Encryption at Rest error'},
|
|
86
|
+
63 => {r: false, c: 'LINK_LOOP', m: 'Path refers to a symbolic link loop', a: 'Path refers to a symbolic link loop'},
|
|
87
|
+
64 => {r: false, c: 'CANNOT_RENAME_PARTIAL_FILES', m: 'Can\'t rename a partial file', a: 'Can\'t rename a partial file.'},
|
|
88
|
+
65 => {r: false, c: 'CIPHER_NON_COMPAT_FIPS', m: 'Can\'t use this cipher with FIPS mode enabled', a: 'Can\'t use this cipher with FIPS mode enabled'},
|
|
89
|
+
66 => {r: false, c: 'PEER_REQUIRES_FIPS', m: 'Peer rejects cipher due to FIPS mode enabled on peer',
|
|
90
|
+
a: 'Peer rejects cipher due to FIPS mode enabled on peer'}
|
|
91
|
+
}.freeze
|
|
92
|
+
# rubocop:enable Layout/FirstHashElementLineBreak
|
|
9
93
|
# cspell: disable
|
|
10
94
|
OPERATIONS = %w[
|
|
11
95
|
NOP
|
|
@@ -187,7 +271,8 @@ module Aspera
|
|
|
187
271
|
ExtraCreatePolicy
|
|
188
272
|
]
|
|
189
273
|
# Management port start message
|
|
190
|
-
|
|
274
|
+
PROT_VERSION = '2'
|
|
275
|
+
MGT_HEADER = "FASPMGR #{PROT_VERSION}"
|
|
191
276
|
# empty line is separator to end event information
|
|
192
277
|
MGT_FRAME_SEPARATOR = ''
|
|
193
278
|
# fields description for JSON generation
|
|
@@ -204,33 +289,44 @@ module Aspera
|
|
|
204
289
|
# cspell: enable
|
|
205
290
|
|
|
206
291
|
class << self
|
|
207
|
-
#
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
else e.capital_to_snake
|
|
215
|
-
end
|
|
216
|
-
h[new_name] =
|
|
217
|
-
if INTEGER_FIELDS.include?(e) then event[e].to_i
|
|
218
|
-
elsif BOOLEAN_FIELDS.include?(e) then event[e].eql?(BOOLEAN_TRUE)
|
|
219
|
-
else
|
|
220
|
-
event[e]
|
|
221
|
-
end
|
|
222
|
-
end
|
|
292
|
+
# translate native event name to snake case
|
|
293
|
+
def field_native_to_snake(name)
|
|
294
|
+
case name
|
|
295
|
+
when 'Elapsedusec' then 'elapsed_usec'
|
|
296
|
+
when 'Bytescont' then 'bytes_cont'
|
|
297
|
+
else name.capital_to_snake
|
|
298
|
+
end
|
|
223
299
|
end
|
|
224
300
|
|
|
225
|
-
#
|
|
226
|
-
# @param
|
|
301
|
+
# translate snake case event name to native
|
|
302
|
+
# @param name [String] Field name
|
|
303
|
+
def field_snake_to_native(name)
|
|
304
|
+
field = name.delete('_')
|
|
305
|
+
result = PARAMETERS.find{ |w| w.casecmp?(field)}
|
|
306
|
+
raise "No such field: #{name}" if result.nil?
|
|
307
|
+
result
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Translates mgt port event into (enhanced) typed event
|
|
311
|
+
def event_native_to_snake(event)
|
|
312
|
+
event.each_with_object({}) do |(key, value), h|
|
|
313
|
+
h[field_native_to_snake(key)] =
|
|
314
|
+
if INTEGER_FIELDS.include?(key) then value.to_i
|
|
315
|
+
elsif BOOLEAN_FIELDS.include?(key) then value.eql?(BOOLEAN_TRUE)
|
|
316
|
+
else
|
|
317
|
+
value
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Build command to send on management port
|
|
323
|
+
# @param data [Hash] e.g. {'type'=>'START','source'=>_path_,'destination'=>_path_}
|
|
324
|
+
# @return [String] frame to send on management port
|
|
227
325
|
def command_to_stream(data)
|
|
228
|
-
# TODO: translate enhanced to capitalized ?
|
|
229
326
|
data
|
|
230
|
-
.
|
|
231
|
-
.map{ |k| "#{k.capitalize}: #{data[k]}"}
|
|
327
|
+
.map{ |key, value| "#{field_snake_to_native(key)}: #{value}"}
|
|
232
328
|
.unshift(MGT_HEADER)
|
|
233
|
-
.push(
|
|
329
|
+
.push(MGT_FRAME_SEPARATOR, '')
|
|
234
330
|
.join("\n")
|
|
235
331
|
end
|
|
236
332
|
end
|
data/lib/aspera/assert.rb
CHANGED
|
@@ -5,6 +5,10 @@ module Aspera
|
|
|
5
5
|
class Error < StandardError
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
+
# Any problem with parameter values
|
|
9
|
+
class ParameterError < Error
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
# Error that shall not happen, else it's a bug
|
|
9
13
|
class InternalError < Error
|
|
10
14
|
end
|
|
@@ -12,8 +16,10 @@ module Aspera
|
|
|
12
16
|
# An expected condition was not met
|
|
13
17
|
class AssertError < Error
|
|
14
18
|
end
|
|
19
|
+
|
|
15
20
|
class << self
|
|
16
|
-
#
|
|
21
|
+
# Replaces `raise` in assertion
|
|
22
|
+
# Allows sending exception, or just error log, when type is `:error`
|
|
17
23
|
# @param type [Exception,Symbol] Send to log if symbol, else raise exception
|
|
18
24
|
# @param message [String] Message for error.
|
|
19
25
|
def report_error(type, message)
|
|
@@ -28,32 +34,54 @@ module Aspera
|
|
|
28
34
|
# @param assertion [Bool] Must be true
|
|
29
35
|
# @param info [String,nil] Fixed message in case assert fails, else use `block`
|
|
30
36
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
31
|
-
# @param block [Proc] Produces a string that
|
|
37
|
+
# @param block [Proc] Produces a string that describes the problem for complex messages
|
|
32
38
|
# The block is executed in the context of the Aspera module
|
|
33
39
|
def assert(assertion, info = nil, type: AssertError)
|
|
34
40
|
raise InternalError, 'bad assert: both info and block given' unless info.nil? || !block_given?
|
|
35
41
|
return if assertion
|
|
36
42
|
message = 'assertion failed'
|
|
37
43
|
info = yield if block_given?
|
|
38
|
-
message = "#{message}: #{info}" if info
|
|
39
|
-
message = "#{message}: #{caller.find{ |call| !call.start_with?(__FILE__)}}"
|
|
44
|
+
message = type.eql?(AssertError) ? "#{message}: #{info}" : info if info
|
|
45
|
+
# message = "#{message}: #{caller.find{ |call| !call.start_with?(__FILE__)}}"
|
|
40
46
|
report_error(type, message)
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
# Assert that value has the given type
|
|
44
|
-
# @param value [Object]
|
|
45
|
-
# @param classes [Class, Array]
|
|
50
|
+
# @param value [Object] The value to check
|
|
51
|
+
# @param classes [Class, Array] The expected type(s)
|
|
46
52
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
47
|
-
# @param block [Proc]
|
|
53
|
+
# @param block [Proc] Additional description in front of message
|
|
48
54
|
def assert_type(value, *classes, type: AssertError)
|
|
49
|
-
assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting #{classes.join(', ')}, but have #{value.inspect}"}
|
|
55
|
+
assert(classes.any?{ |k| value.is_a?(k)}, type: type){"#{"#{yield}: " if block_given?}expecting #{classes.join(', ')}, but have (#{value.class})#{value.inspect}"}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Assert that all value of array are of the same specified type
|
|
59
|
+
# @param array [Array] The array to check
|
|
60
|
+
# @param klass [Class] The expected type of elements
|
|
61
|
+
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
62
|
+
# @param block [Proc] Additional description in front of message
|
|
63
|
+
def assert_array_all(array, klass, type: AssertError)
|
|
64
|
+
assert_type(array, Array, type: type)
|
|
65
|
+
assert(array.all?(klass), type: type){"#{"#{yield}: " if block_given?}expecting all as #{klass}, but have #{array.map(&:class).uniq}"}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Assert value is Hash, keys have type, and Values have type
|
|
69
|
+
# @param hash [Hash] The hash to check
|
|
70
|
+
# @param key_class [Class] The expected type of keys (or nil)
|
|
71
|
+
# @param value_class [Class] The expected type of values (or nil)
|
|
72
|
+
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
73
|
+
# @param block [Proc] Additional description in front of message
|
|
74
|
+
def assert_hash_all(hash, key_class, value_class, type: AssertError)
|
|
75
|
+
assert_type(hash, Hash, type: type)
|
|
76
|
+
assert_array_all(hash.keys, key_class, type: AssertError){"#{"#{yield}: " if block_given?}keys"} unless key_class.nil?
|
|
77
|
+
assert_array_all(hash.values, value_class, type: AssertError){"#{"#{yield}: " if block_given?}values"} unless value_class.nil?
|
|
50
78
|
end
|
|
51
79
|
|
|
52
80
|
# Assert that value is one of the given values
|
|
53
|
-
# @param value [any]
|
|
54
|
-
# @param values [Array]
|
|
81
|
+
# @param value [any] Value to check
|
|
82
|
+
# @param values [Array] Accepted values
|
|
55
83
|
# @param type [Exception,Symbol] Exception to raise, or Symbol for Log.log
|
|
56
|
-
# @param block [Proc]
|
|
84
|
+
# @param block [Proc] Additional description in front of message
|
|
57
85
|
def assert_values(value, values, type: AssertError)
|
|
58
86
|
assert(values.include?(value), type: type) do
|
|
59
87
|
val_list = values.inspect
|
data/lib/aspera/cli/error.rb
CHANGED
|
@@ -6,11 +6,13 @@ module Aspera
|
|
|
6
6
|
class Error < StandardError; end
|
|
7
7
|
# Raised when an unexpected argument is provided.
|
|
8
8
|
class BadArgument < Error; end
|
|
9
|
+
class MissingArgument < Error; end
|
|
9
10
|
class NoSuchElement < Error; end
|
|
10
11
|
|
|
11
12
|
class BadIdentifier < Error
|
|
12
|
-
def initialize(res_type, res_id)
|
|
13
|
-
|
|
13
|
+
def initialize(res_type, res_id, field: 'identifier', count: 0)
|
|
14
|
+
msg = count.eql?(0) ? 'not found' : "found #{count}"
|
|
15
|
+
super("#{res_type} with #{field}=#{res_id}: #{msg}")
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|