aspera-cli 4.27.2 → 4.27.4
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 +133 -0
- data/CONTRIBUTING.md +5 -2
- data/bin/ascli +3 -1
- data/docs/README.md +1006 -766
- data/lib/aspera/agent/base.rb +7 -2
- data/lib/aspera/agent/connect.rb +6 -8
- data/lib/aspera/agent/desktop.rb +2 -6
- data/lib/aspera/agent/direct.rb +52 -22
- data/lib/aspera/agent/node.rb +9 -8
- data/lib/aspera/agent/transferd.rb +2 -2
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -12
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +11 -7
- data/lib/aspera/api/httpgw.rb +38 -35
- data/lib/aspera/api/node.rb +39 -34
- data/lib/aspera/ascmd.rb +3 -1
- data/lib/aspera/ascp/installation.rb +63 -28
- data/lib/aspera/ascp/management.rb +1 -0
- data/lib/aspera/assert.rb +4 -0
- data/lib/aspera/cli/ascp_actions.rb +20 -41
- data/lib/aspera/cli/async_transfer_store.rb +12 -11
- data/lib/aspera/cli/bootstrapper.rb +14 -16
- data/lib/aspera/cli/command_line.rb +252 -0
- data/lib/aspera/cli/command_registry.rb +215 -37
- data/lib/aspera/cli/command_spec.rb +104 -15
- data/lib/aspera/cli/completion/ascli.bash +12 -0
- data/lib/aspera/cli/completion/ascli.fish +16 -0
- data/lib/aspera/cli/completion/ascli.zsh +19 -0
- data/lib/aspera/cli/context.rb +3 -0
- data/lib/aspera/cli/deprecation.rb +37 -0
- data/lib/aspera/cli/extended_value.rb +6 -3
- data/lib/aspera/cli/formatter.rb +94 -80
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/http.rb +22 -34
- data/lib/aspera/cli/info.rb +3 -0
- data/lib/aspera/cli/mcp_tool.rb +47 -83
- data/lib/aspera/cli/option_declarator.rb +33 -42
- data/lib/aspera/cli/option_registry.rb +69 -0
- data/lib/aspera/cli/option_types.rb +105 -0
- data/lib/aspera/cli/option_value.rb +278 -0
- data/lib/aspera/cli/options.schema.yaml +124 -15
- data/lib/aspera/cli/parser.rb +333 -862
- data/lib/aspera/cli/plugins/alee.rb +7 -4
- data/lib/aspera/cli/plugins/aoc.rb +545 -518
- data/lib/aspera/cli/plugins/ats.rb +59 -80
- data/lib/aspera/cli/plugins/base.rb +221 -265
- data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
- data/lib/aspera/cli/plugins/config.rb +263 -184
- data/lib/aspera/cli/plugins/console.rb +103 -39
- data/lib/aspera/cli/plugins/cos.rb +6 -23
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex5.rb +204 -182
- data/lib/aspera/cli/plugins/faspio.rb +6 -11
- data/lib/aspera/cli/plugins/httpgw.rb +8 -11
- data/lib/aspera/cli/plugins/mcp.rb +20 -55
- data/lib/aspera/cli/plugins/node.rb +300 -327
- data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
- data/lib/aspera/cli/plugins/preview.rb +96 -105
- data/lib/aspera/cli/plugins/server.rb +78 -53
- data/lib/aspera/cli/plugins/shares.rb +80 -131
- data/lib/aspera/cli/preset_actions.rb +44 -27
- data/lib/aspera/cli/preset_manager.rb +44 -19
- data/lib/aspera/cli/prompt.rb +36 -0
- data/lib/aspera/cli/result.rb +42 -36
- data/lib/aspera/cli/runner.rb +32 -59
- data/lib/aspera/cli/special_values.rb +5 -0
- data/lib/aspera/cli/sync_actions.rb +51 -46
- data/lib/aspera/cli/terminal_formatter.rb +9 -3
- data/lib/aspera/cli/transfer_actions.rb +14 -9
- data/lib/aspera/cli/transfer_agent.rb +34 -38
- data/lib/aspera/cli/transfer_progress.rb +290 -55
- data/lib/aspera/cli/vault_manager.rb +0 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -3
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/environment.rb +35 -5
- data/lib/aspera/faspex_gw.rb +2 -1
- data/lib/aspera/faspex_postproc.rb +1 -0
- data/lib/aspera/graphql.rb +5 -5
- data/lib/aspera/json_rpc/client.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +2 -1
- data/lib/aspera/keychain/one_password_api.rb +1 -1
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +47 -27
- data/lib/aspera/markdown.rb +2 -0
- data/lib/aspera/mime.rb +25 -0
- data/lib/aspera/node_emulator.rb +759 -0
- data/lib/aspera/oauth/base.rb +37 -26
- data/lib/aspera/oauth/factory.rb +7 -3
- data/lib/aspera/oauth/generic.rb +1 -1
- data/lib/aspera/oauth/json_credentials.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +4 -5
- data/lib/aspera/oauth/web.rb +9 -8
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_folder.rb +1 -3
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +11 -1
- data/lib/aspera/preview/options.schema.yaml +119 -0
- data/lib/aspera/preview/terminal.rb +4 -3
- data/lib/aspera/preview/utils.rb +9 -6
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/rainbow.rb +7 -0
- data/lib/aspera/rest/aspera_errors.rb +72 -0
- data/lib/aspera/rest/call_error.rb +27 -0
- data/lib/aspera/rest/client.rb +523 -0
- data/lib/aspera/rest/error_analyzer.rb +113 -0
- data/lib/aspera/rest/list.rb +149 -0
- data/lib/aspera/rest/parameters.rb +55 -0
- data/lib/aspera/rest/util.rb +176 -0
- data/lib/aspera/rest.rb +7 -621
- data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
- data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
- data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
- data/lib/aspera/schema/documentation.rb +13 -3
- data/lib/aspera/schema/reader.rb +12 -18
- data/lib/aspera/schema/registry.rb +23 -1
- data/lib/aspera/schema/validator.rb +92 -0
- data/lib/aspera/secret_hider.rb +36 -25
- data/lib/aspera/string_ext.rb +15 -0
- data/lib/aspera/temp_file_manager.rb +6 -5
- data/lib/aspera/transfer/parameters.rb +2 -0
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/uri_reader.rb +11 -11
- data/lib/aspera/web_auth/index.html +147 -0
- data/lib/aspera/web_auth/server.rb +81 -0
- data.tar.gz.sig +0 -0
- metadata +43 -9
- metadata.gz.sig +0 -0
- data/lib/aspera/colors.rb +0 -79
- data/lib/aspera/node_simulator.rb +0 -344
- data/lib/aspera/preview/options.rb +0 -45
- data/lib/aspera/rest_call_error.rb +0 -25
- data/lib/aspera/rest_error_analyzer.rb +0 -111
- data/lib/aspera/rest_errors_aspera.rb +0 -58
- data/lib/aspera/rest_list.rb +0 -136
- data/lib/aspera/web_auth.rb +0 -211
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'aspera/assert'
|
|
4
|
-
require 'aspera/ascp/installation'
|
|
5
|
-
require 'aspera/agent/direct'
|
|
6
|
-
require 'aspera/log'
|
|
7
|
-
require 'webrick'
|
|
8
|
-
require 'json'
|
|
9
|
-
|
|
10
|
-
module Aspera
|
|
11
|
-
class NodeSimulator
|
|
12
|
-
def initialize
|
|
13
|
-
@agent = Agent::Direct.new(management_cb: ->(event) { process_event(event) })
|
|
14
|
-
@sessions = {}
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def start(ts)
|
|
18
|
-
@agent.start_transfer(ts)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def all_sessions
|
|
22
|
-
@agent.sessions.map { |session| session[:job_id] }.uniq.each.map { |job_id| job_to_transfer(job_id) }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# status: ('waiting', 'partially_completed', 'unknown', 'waiting(read error)',] 'running', 'completed', 'failed'
|
|
26
|
-
def job_to_transfer(job_id)
|
|
27
|
-
jobs = @agent.sessions_by_job(job_id)
|
|
28
|
-
ts = nil
|
|
29
|
-
sessions = jobs.map do |j|
|
|
30
|
-
ts ||= j[:ts]
|
|
31
|
-
{
|
|
32
|
-
id: j[:id],
|
|
33
|
-
client_node_id: '',
|
|
34
|
-
server_node_id: '2bbdcc39-f789-4d47-8163-6767fc14f421',
|
|
35
|
-
client_ip_address: '192.168.0.100',
|
|
36
|
-
server_ip_address: '5.10.114.4',
|
|
37
|
-
status: 'running',
|
|
38
|
-
retry_timeout: 3600,
|
|
39
|
-
retry_count: 0,
|
|
40
|
-
start_time_usec: 1701094040000000,
|
|
41
|
-
end_time_usec: nil,
|
|
42
|
-
elapsed_usec: 405312,
|
|
43
|
-
bytes_transferred: 26,
|
|
44
|
-
bytes_written: 26,
|
|
45
|
-
bytes_lost: 0,
|
|
46
|
-
files_completed: 1,
|
|
47
|
-
directories_completed: 0,
|
|
48
|
-
target_rate_kbps: 500000,
|
|
49
|
-
min_rate_kbps: 0,
|
|
50
|
-
calc_rate_kbps: 9900,
|
|
51
|
-
network_delay_usec: 40000,
|
|
52
|
-
avg_rate_kbps: 0.51,
|
|
53
|
-
error_code: 0,
|
|
54
|
-
error_desc: '',
|
|
55
|
-
source_statistics: {
|
|
56
|
-
args_scan_attempted: 1,
|
|
57
|
-
args_scan_completed: 1,
|
|
58
|
-
paths_scan_attempted: 1,
|
|
59
|
-
paths_scan_failed: 0,
|
|
60
|
-
paths_scan_skipped: 0,
|
|
61
|
-
paths_scan_excluded: 0,
|
|
62
|
-
dirs_scan_completed: 0,
|
|
63
|
-
files_scan_completed: 1,
|
|
64
|
-
dirs_xfer_attempted: 0,
|
|
65
|
-
dirs_xfer_fail: 0,
|
|
66
|
-
files_xfer_attempted: 1,
|
|
67
|
-
files_xfer_fail: 0,
|
|
68
|
-
files_xfer_noxfer: 0
|
|
69
|
-
},
|
|
70
|
-
precalc: {
|
|
71
|
-
enabled: true,
|
|
72
|
-
status: 'ready',
|
|
73
|
-
bytes_expected: 0,
|
|
74
|
-
directories_expected: 0,
|
|
75
|
-
files_expected: 0,
|
|
76
|
-
files_excluded: 0,
|
|
77
|
-
files_special: 0,
|
|
78
|
-
files_failed: 1
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
end
|
|
82
|
-
ts ||= {}
|
|
83
|
-
result = {
|
|
84
|
-
id: job_id,
|
|
85
|
-
status: 'running',
|
|
86
|
-
start_spec: ts,
|
|
87
|
-
sessions: sessions,
|
|
88
|
-
bytes_transferred: 26,
|
|
89
|
-
bytes_written: 26,
|
|
90
|
-
bytes_lost: 0,
|
|
91
|
-
avg_rate_kbps: 0.51,
|
|
92
|
-
files_completed: 1,
|
|
93
|
-
files_skipped: 0,
|
|
94
|
-
directories_completed: 0,
|
|
95
|
-
start_time_usec: 1701094040000000,
|
|
96
|
-
end_time_usec: 1701094040405312,
|
|
97
|
-
elapsed_usec: 405312,
|
|
98
|
-
error_code: 0,
|
|
99
|
-
error_desc: '',
|
|
100
|
-
precalc: {
|
|
101
|
-
status: 'ready',
|
|
102
|
-
bytes_expected: 0,
|
|
103
|
-
files_expected: 0,
|
|
104
|
-
directories_expected: 0,
|
|
105
|
-
files_special: 0,
|
|
106
|
-
files_failed: 1
|
|
107
|
-
},
|
|
108
|
-
files: [{
|
|
109
|
-
id: 'd1b5c112-82b75425-860745fc-93851671-64541bdd',
|
|
110
|
-
path: '/workspaces/45071/packages/bYA_ilq73g.asp-package/contents/data_file.bin',
|
|
111
|
-
start_time_usec: 1701094040000000,
|
|
112
|
-
elapsed_usec: 105616,
|
|
113
|
-
end_time_usec: 1701094040001355,
|
|
114
|
-
status: 'completed',
|
|
115
|
-
error_code: 0,
|
|
116
|
-
error_desc: '',
|
|
117
|
-
size: 26,
|
|
118
|
-
type: 'file',
|
|
119
|
-
checksum_type: 'none',
|
|
120
|
-
checksum: nil,
|
|
121
|
-
start_byte: 0,
|
|
122
|
-
bytes_written: 26,
|
|
123
|
-
session_id: 'bafc72b8-366c-4501-8095-47208183d6b8'
|
|
124
|
-
}]
|
|
125
|
-
}
|
|
126
|
-
Log.dump(:job, result, level: :trace2)
|
|
127
|
-
return result
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Process event from management port
|
|
131
|
-
def process_event(event)
|
|
132
|
-
# Code duplicated on purpose, as implementation goes, blocks will be replaced
|
|
133
|
-
case event['Type']
|
|
134
|
-
when 'NOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
135
|
-
# rubocop:disable Lint/DuplicateBranch
|
|
136
|
-
when 'START' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
137
|
-
when 'QUERY' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
138
|
-
when 'QUERYRSP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
139
|
-
when 'STATS' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
140
|
-
when 'STOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
141
|
-
when 'ERROR' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
142
|
-
when 'CANCEL' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
143
|
-
when 'DONE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
144
|
-
when 'RATE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
145
|
-
when 'FILEERROR' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
146
|
-
when 'SESSION' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
147
|
-
when 'NOTIFICATION' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
148
|
-
when 'INIT' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
149
|
-
when 'VLINK' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
150
|
-
when 'PUT' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
151
|
-
when 'WRITE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
152
|
-
when 'CLOSE' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
153
|
-
when 'SKIP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
154
|
-
when 'ARGSTOP' then Aspera.Log.debug { "event not managed: #{event['Type']}" }
|
|
155
|
-
# rubocop:enable Lint/DuplicateBranch
|
|
156
|
-
else Aspera.error_unreachable_line
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# this class answers the Faspex /send API and creates a package on Aspera on Cloud
|
|
162
|
-
# a new instance is created for each request
|
|
163
|
-
class NodeSimulatorServlet < WEBrick::HTTPServlet::AbstractServlet
|
|
164
|
-
PATH_TRANSFERS = '/ops/transfers'
|
|
165
|
-
PATH_ONE_TRANSFER = %r{/ops/transfers/(.+)$}
|
|
166
|
-
PATH_BROWSE = '/files/browse'
|
|
167
|
-
# @param credentials [Hash]
|
|
168
|
-
# @param simulator [NodeSimulator]
|
|
169
|
-
def initialize(server, credentials, simulator)
|
|
170
|
-
super(server)
|
|
171
|
-
@credentials = credentials
|
|
172
|
-
@simulator = simulator
|
|
173
|
-
# Resolve once at startup; default to current working directory
|
|
174
|
-
@browse_root = File.realpath(credentials[:browse_root] || Dir.pwd)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
require 'json'
|
|
178
|
-
require 'time'
|
|
179
|
-
|
|
180
|
-
def folder_to_structure(folder_path)
|
|
181
|
-
# Resolve and confine to browse_root (prevents path traversal via client-supplied path)
|
|
182
|
-
resolved = File.realpath(folder_path)
|
|
183
|
-
Aspera.assert(resolved.start_with?("#{@browse_root}/") || resolved.eql?(@browse_root)) { 'Browse path traversal attempt detected' }
|
|
184
|
-
Aspera.assert(Dir.exist?(resolved)) { "Path does not exist or is not a directory: #{resolved}" }
|
|
185
|
-
folder_path = resolved
|
|
186
|
-
|
|
187
|
-
# Build self structure
|
|
188
|
-
folder_stat = File.stat(folder_path)
|
|
189
|
-
structure = {
|
|
190
|
-
'self' => {
|
|
191
|
-
'path' => folder_path,
|
|
192
|
-
'basename' => File.basename(folder_path),
|
|
193
|
-
'type' => 'directory',
|
|
194
|
-
'size' => folder_stat.size,
|
|
195
|
-
'mtime' => folder_stat.mtime.utc.iso8601,
|
|
196
|
-
'permissions' => [
|
|
197
|
-
{'name' => 'view'},
|
|
198
|
-
{'name' => 'edit'},
|
|
199
|
-
{'name' => 'delete'}
|
|
200
|
-
]
|
|
201
|
-
},
|
|
202
|
-
'items' => []
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
# Iterate over folder contents
|
|
206
|
-
Dir.foreach(folder_path) do |entry|
|
|
207
|
-
next if entry == '.' || entry == '..' # Skip current and parent directory
|
|
208
|
-
|
|
209
|
-
item_path = File.join(folder_path, entry)
|
|
210
|
-
item_type = File.ftype(item_path) rescue 'unknown' # Get the type of file
|
|
211
|
-
item_stat = File.lstat(item_path) # Use lstat to handle symbolic links correctly
|
|
212
|
-
|
|
213
|
-
item = {
|
|
214
|
-
'path' => item_path,
|
|
215
|
-
'basename' => entry,
|
|
216
|
-
'type' => item_type,
|
|
217
|
-
'size' => item_stat.size,
|
|
218
|
-
'mtime' => item_stat.mtime.utc.iso8601,
|
|
219
|
-
'permissions' => [
|
|
220
|
-
{'name' => 'view'},
|
|
221
|
-
{'name' => 'edit'},
|
|
222
|
-
{'name' => 'delete'}
|
|
223
|
-
]
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
# Add additional details for specific types
|
|
227
|
-
case item_type
|
|
228
|
-
when 'file'
|
|
229
|
-
item['partial_file'] = false
|
|
230
|
-
when 'link'
|
|
231
|
-
item['target'] = File.readlink(item_path) rescue nil # Add the target of the symlink
|
|
232
|
-
when 'unknown'
|
|
233
|
-
item['note'] = 'File type could not be determined'
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
structure['items'] << item
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
structure
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def do_POST(request, response)
|
|
243
|
-
case request.path
|
|
244
|
-
when PATH_TRANSFERS
|
|
245
|
-
job_id = @simulator.start(JSON.parse(request.body))
|
|
246
|
-
sleep(0.5)
|
|
247
|
-
set_json_response(request, response, @simulator.job_to_transfer(job_id))
|
|
248
|
-
when PATH_BROWSE
|
|
249
|
-
req = JSON.parse(request.body)
|
|
250
|
-
# req['count']
|
|
251
|
-
set_json_response(request, response, folder_to_structure(req['path'] || @browse_root))
|
|
252
|
-
else
|
|
253
|
-
set_json_response(request, response, [{error: 'Bad request'}], code: 400)
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def do_GET(request, response)
|
|
258
|
-
case request.path
|
|
259
|
-
when '/info'
|
|
260
|
-
info = Ascp::Installation.instance.ascp_info
|
|
261
|
-
set_json_response(request, response, {
|
|
262
|
-
application: 'node',
|
|
263
|
-
current_time: Time.now.utc.iso8601(0),
|
|
264
|
-
version: info['sdk_ascp_version'].gsub(/ .*$/, ''),
|
|
265
|
-
license_expiration_date: info['expiration_date'],
|
|
266
|
-
license_max_rate: info['maximum_bandwidth'],
|
|
267
|
-
os: %x(uname -srv).chomp,
|
|
268
|
-
aej_status: 'disconnected',
|
|
269
|
-
async_reporting: 'no',
|
|
270
|
-
transfer_activity_reporting: 'no',
|
|
271
|
-
transfer_user: 'xfer',
|
|
272
|
-
docroot: 'file:////data/aoc/eudemo-sedemo',
|
|
273
|
-
node_id: '2bbdcc39-f789-4d47-8163-6767fc14f421',
|
|
274
|
-
cluster_id: '6dae2844-d1a9-47a5-916d-9b3eac3ea466',
|
|
275
|
-
acls: ['impersonation'],
|
|
276
|
-
access_key_configuration_capabilities: {
|
|
277
|
-
transfer: %w[
|
|
278
|
-
cipher
|
|
279
|
-
policy
|
|
280
|
-
target_rate_cap_kbps
|
|
281
|
-
target_rate_kbps
|
|
282
|
-
preserve_timestamps
|
|
283
|
-
content_protection_secret
|
|
284
|
-
aggressiveness
|
|
285
|
-
token_encryption_key
|
|
286
|
-
byok_enabled
|
|
287
|
-
bandwidth_flow_network_rc_module
|
|
288
|
-
file_checksum_type
|
|
289
|
-
],
|
|
290
|
-
server: %w[
|
|
291
|
-
activity_event_logging
|
|
292
|
-
activity_file_event_logging
|
|
293
|
-
recursive_counts
|
|
294
|
-
aej_logging
|
|
295
|
-
wss_enabled
|
|
296
|
-
activity_transfer_ignore_skipped_files
|
|
297
|
-
activity_files_max
|
|
298
|
-
access_key_credentials_encryption_type
|
|
299
|
-
discovery
|
|
300
|
-
auto_delete
|
|
301
|
-
allow
|
|
302
|
-
deny
|
|
303
|
-
]
|
|
304
|
-
},
|
|
305
|
-
capabilities: [
|
|
306
|
-
{name: 'sync', value: true},
|
|
307
|
-
{name: 'watchfolder', value: true},
|
|
308
|
-
{name: 'symbolic_links', value: true},
|
|
309
|
-
{name: 'move_file', value: true},
|
|
310
|
-
{name: 'move_directory', value: true},
|
|
311
|
-
{name: 'filelock', value: false},
|
|
312
|
-
{name: 'ssh_fingerprint', value: false},
|
|
313
|
-
{name: 'aej_version', value: '1.0'},
|
|
314
|
-
{name: 'page', value: true},
|
|
315
|
-
{name: 'file_id_version', value: '2.0'},
|
|
316
|
-
{name: 'auto_delete', value: false}
|
|
317
|
-
],
|
|
318
|
-
settings: [
|
|
319
|
-
{name: 'content_protection_required', value: false},
|
|
320
|
-
{name: 'content_protection_strong_pass_required', value: false},
|
|
321
|
-
{name: 'filelock_restriction', value: 'none'},
|
|
322
|
-
{name: 'ssh_fingerprint', value: nil},
|
|
323
|
-
{name: 'wss_enabled', value: false},
|
|
324
|
-
{name: 'wss_port', value: 443}
|
|
325
|
-
]
|
|
326
|
-
})
|
|
327
|
-
when PATH_TRANSFERS
|
|
328
|
-
set_json_response(request, response, @simulator.all_sessions)
|
|
329
|
-
when PATH_ONE_TRANSFER
|
|
330
|
-
job_id = request.path.match(PATH_ONE_TRANSFER)[1]
|
|
331
|
-
set_json_response(request, response, @simulator.job_to_transfer(job_id))
|
|
332
|
-
else
|
|
333
|
-
set_json_response(request, response, [{error: 'Unknown request'}], code: 400)
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
def set_json_response(request, response, json, code: 200)
|
|
338
|
-
response.status = code
|
|
339
|
-
response['Content-Type'] = Mime::JSON
|
|
340
|
-
response.body = json.to_json
|
|
341
|
-
Log.log.trace1 { Log.obj_dump("response for #{request.request_method} #{request.path}", json) }
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# cspell:ignore reencode conv transframes pauseframes
|
|
4
|
-
|
|
5
|
-
module Aspera
|
|
6
|
-
module Preview
|
|
7
|
-
# generator options. Used as parameter to preview generator object.
|
|
8
|
-
# also settable by command line.
|
|
9
|
-
class Options
|
|
10
|
-
# types of generation for video files
|
|
11
|
-
VIDEO_CONVERSION_METHODS = %i[reencode blend clips].freeze
|
|
12
|
-
VIDEO_THUMBNAIL_METHODS = %i[fixed animated].freeze
|
|
13
|
-
# methods for office document conversion
|
|
14
|
-
OFFICE_CONVERSION_METHODS = %i[soffice unoconv].freeze
|
|
15
|
-
# options used in generator
|
|
16
|
-
# for scaling see: https://trac.ffmpeg.org/wiki/Scaling
|
|
17
|
-
# iw/ih : input width or height
|
|
18
|
-
# -x : keep aspect ratio, having value a multiple of x
|
|
19
|
-
DESCRIPTIONS = [
|
|
20
|
-
{name: :max_size, default: 1 << 24, description: 'maximum size (in bytes) of preview file'},
|
|
21
|
-
{name: :thumb_vid_scale, default: '-1:min(ih,100)', description: 'png: video: size (ffmpeg scale argument)'},
|
|
22
|
-
{name: :thumb_vid_fraction, default: 0.1, description: 'png: video: time percent position of snapshot'},
|
|
23
|
-
{name: :thumb_img_size, default: 800, description: 'png: non-video: height (and width)'},
|
|
24
|
-
{name: :thumb_text_font, default: 'Courier', description: 'png: plaintext: font for text rendering: `magick identify -list font`'},
|
|
25
|
-
{name: :office_conversion, default: :soffice, description: 'office: method for office document conversion', values: OFFICE_CONVERSION_METHODS},
|
|
26
|
-
{name: :video_conversion, default: :reencode, description: 'mp4: method for preview generation', values: VIDEO_CONVERSION_METHODS},
|
|
27
|
-
{name: :video_png_conv, default: :fixed, description: 'mp4: method for thumbnail generation', values: VIDEO_THUMBNAIL_METHODS},
|
|
28
|
-
{name: :video_scale, default: 'min(iw,360):-2', description: 'mp4: all: video scale (ffmpeg scale argument)'},
|
|
29
|
-
{name: :video_start_sec, default: 10, description: 'mp4: all: start offset (seconds) of video preview'},
|
|
30
|
-
{name: :video_codec, default: nil, description: 'mp4: reencode: video codec for ffmpeg (e.g. libx264, h264_videotoolbox, h264_nvenc), default: auto-detect'},
|
|
31
|
-
{name: :reencode_ffmpeg, default: {}, description: 'mp4: reencode: options to ffmpeg, keys: `in`, `out`'},
|
|
32
|
-
{name: :blend_keyframes, default: 30, description: 'mp4: blend: # key frames'},
|
|
33
|
-
{name: :blend_pauseframes, default: 3, description: 'mp4: blend: # pause frames'},
|
|
34
|
-
{name: :blend_transframes, default: 5, description: 'mp4: blend: # transition blend frames'},
|
|
35
|
-
{name: :blend_fps, default: 15, description: 'mp4: blend: frame per second'},
|
|
36
|
-
{name: :clips_count, default: 5, description: 'mp4: clips: number of clips'},
|
|
37
|
-
{name: :clips_length, default: 5, description: 'mp4: clips: length in seconds of each clips'}
|
|
38
|
-
].freeze
|
|
39
|
-
# add accessors
|
|
40
|
-
DESCRIPTIONS.each do |opt|
|
|
41
|
-
attr_accessor opt[:name]
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Aspera
|
|
4
|
-
# raised on error after REST call
|
|
5
|
-
class RestCallError < StandardError
|
|
6
|
-
def request
|
|
7
|
-
@context[:request]
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def response
|
|
11
|
-
@context[:response]
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def data
|
|
15
|
-
@context[:data]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# @param context [Hash,String] with keys :messages, :request, :response, :data
|
|
19
|
-
def initialize(context)
|
|
20
|
-
context = {messages: [context]} if context.is_a?(String)
|
|
21
|
-
@context = context
|
|
22
|
-
super(@context[:messages].join("\n"))
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'aspera/rest_call_error'
|
|
4
|
-
require 'aspera/log'
|
|
5
|
-
require 'singleton'
|
|
6
|
-
require 'net/http'
|
|
7
|
-
|
|
8
|
-
module Aspera
|
|
9
|
-
# analyze error codes returned by REST calls and raise ruby exception
|
|
10
|
-
#
|
|
11
|
-
# @!method self.instance
|
|
12
|
-
# Returns the singleton instance of RestErrorAnalyzer
|
|
13
|
-
# @return [RestErrorAnalyzer] the singleton instance
|
|
14
|
-
class RestErrorAnalyzer
|
|
15
|
-
include Singleton
|
|
16
|
-
|
|
17
|
-
attr_accessor :log_file
|
|
18
|
-
|
|
19
|
-
# the singleton object is registered with application specific handlers
|
|
20
|
-
def initialize
|
|
21
|
-
# list of handlers
|
|
22
|
-
@error_handlers = []
|
|
23
|
-
@log_file = nil
|
|
24
|
-
add_handler('Type Generic') do |type, context|
|
|
25
|
-
if !context[:response].code.start_with?('2')
|
|
26
|
-
# add generic information
|
|
27
|
-
RestErrorAnalyzer.add_error(context, type, "#{context[:request]['host']} #{context[:response].code} #{context[:response].message}")
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Use this method to analyze a EST result and raise an exception
|
|
33
|
-
# Analyzes REST call response and raises a RestCallError exception
|
|
34
|
-
# if HTTP result code is not 2XX
|
|
35
|
-
# @param req [Net::HTTPRequest] the HTTP request
|
|
36
|
-
# @param data [Object] the parsed response body
|
|
37
|
-
# @param http [Net::HTTPResponse] the raw HTTP response
|
|
38
|
-
def raise_on_error(req, data, http)
|
|
39
|
-
Log.log.debug { "raise_on_error #{req.method} #{req.path} #{http.code}" }
|
|
40
|
-
context = {
|
|
41
|
-
messages: [],
|
|
42
|
-
request: req,
|
|
43
|
-
response: http,
|
|
44
|
-
data: data
|
|
45
|
-
}
|
|
46
|
-
# multiple error messages can be found
|
|
47
|
-
# analyze errors from provided handlers
|
|
48
|
-
# note that there can be an error even if code is 2XX
|
|
49
|
-
@error_handlers.each do |handler|
|
|
50
|
-
handler[:block].call(handler[:name], context)
|
|
51
|
-
rescue StandardError => e
|
|
52
|
-
Log.log.error { "ERROR in handler:\n#{e.message}\n#{e.backtrace}" }
|
|
53
|
-
end
|
|
54
|
-
raise RestCallError, context unless context[:messages].empty?
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Add a new error handler (done at application initialization)
|
|
58
|
-
# @param name [String] name of error handler (for logs)
|
|
59
|
-
# @param block [Proc] processing of response: takes two parameters: `name`, `context`
|
|
60
|
-
# name is the one provided here
|
|
61
|
-
# context is built in method raise_on_error
|
|
62
|
-
def add_handler(name, &block)
|
|
63
|
-
@error_handlers.unshift({name: name, block: block})
|
|
64
|
-
nil
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Add a simple error handler
|
|
68
|
-
# Check that key exists and is string under specified path (hash)
|
|
69
|
-
# Adds other keys as secondary information
|
|
70
|
-
# @param name [String] name of error handler (for logs)
|
|
71
|
-
# @param always [Boolean] if true, always add error message, even if response code is 2XX
|
|
72
|
-
# @param path [Array] path to error message in response
|
|
73
|
-
def add_simple_handler(name:, always: false, path:)
|
|
74
|
-
path.freeze
|
|
75
|
-
add_handler(name) do |type, context|
|
|
76
|
-
if context[:data].is_a?(Hash) && (!context[:response].code.start_with?('2') || always)
|
|
77
|
-
# Log.log.debug{"simple_handler: #{type} #{path} #{path.last}"}
|
|
78
|
-
# dig and find hash containing error message
|
|
79
|
-
error_struct = path.length.eql?(1) ? context[:data] : context[:data].dig(*path[0..-2])
|
|
80
|
-
# Log.log.debug{"found: #{error_struct.class} #{error_struct}"}
|
|
81
|
-
if error_struct.is_a?(Hash) && error_struct[path.last].is_a?(String)
|
|
82
|
-
RestErrorAnalyzer.add_error(context, type, error_struct[path.last])
|
|
83
|
-
error_struct.each do |k, v|
|
|
84
|
-
next if k.eql?(path.last)
|
|
85
|
-
RestErrorAnalyzer.add_error(context, "#{type}(sub)", "#{k}: #{v}") if [String, Integer].include?(v.class)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
class << self
|
|
93
|
-
# Used by handler to add an error description to list of errors
|
|
94
|
-
# For logging and tracing : collect error descriptions (create file to activate)
|
|
95
|
-
# @param context [Hash] the result context, provided to handler
|
|
96
|
-
# @param type [String] type of exception, for logging purpose
|
|
97
|
-
# @param message [String] one error message to add to list
|
|
98
|
-
def add_error(context, type, message)
|
|
99
|
-
context[:messages].push(message)
|
|
100
|
-
Log.log.trace1 { "Found error: #{type}: #{message}" }
|
|
101
|
-
log_file = instance.log_file
|
|
102
|
-
# log error for further analysis (file must exist to activate)
|
|
103
|
-
return if log_file.nil? || !File.exist?(log_file)
|
|
104
|
-
File.open(log_file, 'a+') do |f|
|
|
105
|
-
f.write("\n=#{type}=====\n#{context[:request].method} #{context[:request].path}\n#{context[:response].code}\n" \
|
|
106
|
-
"#{JSON.generate(context[:data])}\n#{context[:messages].join("\n")}")
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'aspera/rest_error_analyzer'
|
|
4
|
-
require 'aspera/log'
|
|
5
|
-
|
|
6
|
-
module Aspera
|
|
7
|
-
# REST error handlers for various Aspera REST APIs
|
|
8
|
-
class RestErrorsAspera
|
|
9
|
-
class << self
|
|
10
|
-
# handlers should probably be defined by plugins for modularity
|
|
11
|
-
def register_handlers
|
|
12
|
-
Log.log.debug('registering Aspera REST error handlers')
|
|
13
|
-
# Faspex 4: both user_message and internal_message, and code 200
|
|
14
|
-
# example: missing meta data on package creation
|
|
15
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 1: error:user_message', path: %w[error user_message], always: true)
|
|
16
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 2: error:description', path: %w[error description])
|
|
17
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 3: error:internal_message', path: %w[error internal_message])
|
|
18
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 5', path: ['error_description'])
|
|
19
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['message'])
|
|
20
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'Type 6', path: ['failure'], always: true)
|
|
21
|
-
# AoC Automation
|
|
22
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'AoC Automation', path: ['error'])
|
|
23
|
-
RestErrorAnalyzer.instance.add_handler('Type 7: errors[]') do |type, context|
|
|
24
|
-
next unless context[:data].is_a?(Hash) && context[:data]['errors'].is_a?(Hash)
|
|
25
|
-
# special for Shares: false positive ? (update global transfer_settings)
|
|
26
|
-
next if context[:data].key?('min_connect_version')
|
|
27
|
-
context[:data]['errors'].each do |k, v|
|
|
28
|
-
RestErrorAnalyzer.add_error(context, type, "#{k}: #{v}")
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
# call to upload_setup and download_setup of node api
|
|
32
|
-
RestErrorAnalyzer.instance.add_handler('T8:node: *_setup') do |type, context|
|
|
33
|
-
next unless context[:data].is_a?(Hash)
|
|
34
|
-
d_t_s = context[:data]['transfer_specs']
|
|
35
|
-
next unless d_t_s.is_a?(Array)
|
|
36
|
-
d_t_s.each do |res|
|
|
37
|
-
r_err = res.dig(*%w[transfer_spec error]) || res['error']
|
|
38
|
-
next unless r_err.is_a?(Hash)
|
|
39
|
-
RestErrorAnalyzer.add_error(context, type, r_err.values.join(': '))
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'T9:IBM cloud IAM', path: ['errorMessage'])
|
|
43
|
-
RestErrorAnalyzer.instance.add_simple_handler(name: 'T10:faspex v4', path: ['user_message'])
|
|
44
|
-
RestErrorAnalyzer.instance.add_handler('Orchestrator') do |type, context|
|
|
45
|
-
next if context[:response].code.start_with?('2')
|
|
46
|
-
data = context[:data]
|
|
47
|
-
next unless data.is_a?(Hash)
|
|
48
|
-
work_order = data['work_order']
|
|
49
|
-
next unless work_order.is_a?(Hash)
|
|
50
|
-
RestErrorAnalyzer.add_error(context, type, work_order['statusDetails'])
|
|
51
|
-
data['missing_parameters']&.each do |param|
|
|
52
|
-
RestErrorAnalyzer.add_error(context, type, "missing parameter: #{param}")
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|