aspera-cli 4.13.0 → 4.15.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/CHANGELOG.md +81 -7
- data/CONTRIBUTING.md +22 -6
- data/README.md +2038 -1080
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/dascli +1 -1
- data/examples/proxy.pac +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +219 -159
- data/lib/aspera/ascmd.rb +25 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -179
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +104 -156
- data/lib/aspera/cli/manager.rb +259 -209
- data/lib/aspera/cli/plugin.rb +123 -63
- data/lib/aspera/cli/plugins/alee.rb +2 -3
- data/lib/aspera/cli/plugins/aoc.rb +341 -261
- data/lib/aspera/cli/plugins/ats.rb +22 -21
- data/lib/aspera/cli/plugins/bss.rb +5 -5
- data/lib/aspera/cli/plugins/config.rb +578 -627
- data/lib/aspera/cli/plugins/console.rb +44 -6
- data/lib/aspera/cli/plugins/cos.rb +15 -17
- data/lib/aspera/cli/plugins/faspex.rb +114 -100
- data/lib/aspera/cli/plugins/faspex5.rb +411 -264
- data/lib/aspera/cli/plugins/node.rb +354 -259
- data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
- data/lib/aspera/cli/plugins/preview.rb +82 -90
- data/lib/aspera/cli/plugins/server.rb +79 -32
- data/lib/aspera/cli/plugins/shares.rb +55 -42
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +66 -73
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +12 -8
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +24 -9
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +25 -21
- data/lib/aspera/fasp/agent_direct.rb +89 -103
- data/lib/aspera/fasp/agent_httpgw.rb +231 -149
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -32
- data/lib/aspera/fasp/error_info.rb +4 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +53 -195
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +71 -37
- data/lib/aspera/fasp/parameters.yaml +76 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +7 -6
- data/lib/aspera/fasp/uri.rb +26 -24
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +14 -4
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +58 -16
- data/lib/aspera/node.rb +157 -92
- data/lib/aspera/oauth.rb +37 -19
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +73 -16
- data/lib/aspera/preview/utils.rb +21 -28
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +136 -68
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +18 -15
- data/lib/aspera/ssh.rb +5 -2
- data/lib/aspera/sync.rb +127 -119
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +34 -17
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/data/7 +0 -0
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -1,97 +1,90 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:ignore snid fnid bidi ssync asyncs rund asnodeadmin mkfile mklink asperabrowser asperabrowserurl watchfolders watchfolderd entsrv
|
3
4
|
require 'aspera/cli/basic_auth_plugin'
|
4
|
-
require 'aspera/cli/
|
5
|
+
require 'aspera/cli/sync_actions'
|
6
|
+
require 'aspera/fasp/transfer_spec'
|
5
7
|
require 'aspera/nagios'
|
6
8
|
require 'aspera/hash_ext'
|
7
9
|
require 'aspera/id_generator'
|
8
10
|
require 'aspera/node'
|
9
11
|
require 'aspera/aoc'
|
10
12
|
require 'aspera/sync'
|
11
|
-
require 'aspera/
|
13
|
+
require 'aspera/oauth'
|
12
14
|
require 'base64'
|
13
15
|
require 'zlib'
|
14
16
|
|
15
17
|
module Aspera
|
16
18
|
module Cli
|
17
19
|
module Plugins
|
18
|
-
class SyncSpecGen3
|
19
|
-
def initialize(api_node)
|
20
|
-
@api_node = api_node
|
21
|
-
end
|
22
|
-
|
23
|
-
def transfer_spec(direction, local_path, remote_path)
|
24
|
-
# empty transfer spec for authorization request
|
25
|
-
direction_sym = direction.to_sym
|
26
|
-
request_transfer_spec = {
|
27
|
-
type: Aspera::Sync::DIRECTION_TO_REQUEST_TYPE[direction_sym],
|
28
|
-
paths: {
|
29
|
-
source: remote_path,
|
30
|
-
destination: local_path
|
31
|
-
}
|
32
|
-
}
|
33
|
-
# add fixed parameters if any (for COS)
|
34
|
-
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
35
|
-
# prepare payload for single request
|
36
|
-
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
37
|
-
# only one request, so only one answer
|
38
|
-
transfer_spec = @api_node.create('files/sync_setup', setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
39
|
-
# API returns null tag... but async does not like it
|
40
|
-
transfer_spec.delete_if{ |_k, v| v.nil? }
|
41
|
-
# delete this part, as the returned value contains only destination, and not sources
|
42
|
-
# transfer_spec.delete('paths') if command.eql?(:upload)
|
43
|
-
Log.dump(:ts, transfer_spec)
|
44
|
-
return transfer_spec
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
class SyncSpecGen4
|
49
|
-
def initialize(api_node, top_file_id)
|
50
|
-
@api_node = api_node
|
51
|
-
@top_file_id = top_file_id
|
52
|
-
end
|
53
|
-
|
54
|
-
def transfer_spec(direction, local_path, remote_path)
|
55
|
-
# remote is specified by option to_folder
|
56
|
-
apifid = @api_node.resolve_api_fid(@top_file_id, remote_path)
|
57
|
-
transfer_spec = apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)
|
58
|
-
Log.dump(:ts, transfer_spec)
|
59
|
-
return transfer_spec
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
20
|
class Node < Aspera::Cli::BasicAuthPlugin
|
21
|
+
include SyncActions
|
64
22
|
class << self
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
23
|
+
@@node_options_declared = false # rubocop:disable Style/ClassVars
|
24
|
+
def application_name
|
25
|
+
'HSTS Node API'
|
26
|
+
end
|
27
|
+
|
28
|
+
def detect(address_or_url)
|
29
|
+
urls = if address_or_url.match?(%r{^[a-z]{1,6}://})
|
30
|
+
[address_or_url]
|
31
|
+
else
|
32
|
+
[
|
33
|
+
"https://#{address_or_url}",
|
34
|
+
"https://#{address_or_url}:9092",
|
35
|
+
"http://#{address_or_url}:9091"
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
urls.each do |base_url|
|
40
|
+
next unless base_url.match?('https?://')
|
41
|
+
api = Rest.new(base_url: base_url)
|
42
|
+
test_endpoint = 'ping'
|
43
|
+
result = api.call(operation: 'GET', subpath: test_endpoint)
|
44
|
+
next unless result[:http].body.eql?('')
|
45
|
+
url_length = -2 - test_endpoint.length
|
46
|
+
return {
|
47
|
+
url: result[:http].uri.to_s[0..url_length]
|
48
|
+
}
|
49
|
+
rescue StandardError => e
|
50
|
+
Log.log.debug{"detect error: #{e}"}
|
70
51
|
end
|
71
52
|
return nil
|
72
53
|
end
|
73
54
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
55
|
+
def wizard(object:, private_key_path: nil, pub_key_pem: nil)
|
56
|
+
options = object.options
|
57
|
+
return {
|
58
|
+
preset_value: {
|
59
|
+
url: options.get_option(:url, mandatory: true),
|
60
|
+
username: options.get_option(:username, mandatory: true),
|
61
|
+
password: options.get_option(:password, mandatory: true)
|
62
|
+
},
|
63
|
+
test_args: 'info'
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def declare_options(options, force: false)
|
68
|
+
return if @@node_options_declared && !force
|
69
|
+
@@node_options_declared = true # rubocop:disable Style/ClassVars
|
70
|
+
options.declare(:validator, 'Identifier of validator (optional for central)')
|
71
|
+
options.declare(:asperabrowserurl, 'URL for simple aspera web ui', default: 'https://asperabrowser.mybluemix.net')
|
72
|
+
options.declare(:sync_name, 'Sync name')
|
73
|
+
options.declare(
|
74
|
+
:default_ports, 'Use standard FASP ports or get from node api (gen4)', values: :bool, default: :yes,
|
75
|
+
handler: {o: Aspera::Node, m: :use_standard_ports})
|
76
|
+
options.declare(:root_id, 'File id of top folder if using bearer tokens')
|
77
|
+
SyncActions.declare_options(options)
|
78
|
+
options.parse_options!
|
86
79
|
end
|
87
80
|
end
|
88
81
|
|
89
82
|
# spellchecker: disable
|
90
83
|
# SOAP API call to test central API
|
91
|
-
CENTRAL_SOAP_API_TEST = '<?xml version="1.0" encoding="UTF-8"?>'\
|
92
|
-
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types">'\
|
93
|
-
'<soapenv:Header></soapenv:Header>'\
|
94
|
-
'<soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body>'\
|
84
|
+
CENTRAL_SOAP_API_TEST = '<?xml version="1.0" encoding="UTF-8"?>' \
|
85
|
+
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types">' \
|
86
|
+
'<soapenv:Header></soapenv:Header>' \
|
87
|
+
'<soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body>' \
|
95
88
|
'</soapenv:Envelope>'
|
96
89
|
# spellchecker: enable
|
97
90
|
|
@@ -99,17 +92,17 @@ module Aspera
|
|
99
92
|
SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
|
100
93
|
|
101
94
|
# actions in execute_command_gen3
|
102
|
-
COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download sync]
|
95
|
+
COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download http_node_download sync]
|
103
96
|
|
104
97
|
BASE_ACTIONS = %i[api_details].concat(COMMANDS_GEN3).freeze
|
105
98
|
|
106
|
-
SPECIAL_ACTIONS = %i[health events info license].freeze
|
99
|
+
SPECIAL_ACTIONS = %i[health events info slash license].freeze
|
107
100
|
|
108
101
|
# actions available in v3 in gen4
|
109
|
-
V3_IN_V4_ACTIONS = %i[
|
102
|
+
V3_IN_V4_ACTIONS = %i[access_keys].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
|
110
103
|
|
111
104
|
# actions used commonly when a node is involved
|
112
|
-
COMMON_ACTIONS = %i[
|
105
|
+
COMMON_ACTIONS = %i[access_keys].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
|
113
106
|
|
114
107
|
private_constant(*%i[CENTRAL_SOAP_API_TEST SEARCH_REMOVE_FIELDS BASE_ACTIONS SPECIAL_ACTIONS V3_IN_V4_ACTIONS COMMON_ACTIONS])
|
115
108
|
|
@@ -117,61 +110,37 @@ module Aspera
|
|
117
110
|
NODE4_READ_ACTIONS = %i[bearer_token_node node_info browse find].freeze
|
118
111
|
|
119
112
|
# commands for execute_command_gen4
|
120
|
-
COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download
|
113
|
+
COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download show modify permission thumbnail v3].concat(NODE4_READ_ACTIONS).freeze
|
121
114
|
|
122
|
-
COMMANDS_COS = %i[upload download info
|
115
|
+
COMMANDS_COS = %i[upload download info access_keys api_details transfer].freeze
|
123
116
|
COMMANDS_SHARES = (BASE_ACTIONS - %i[search]).freeze
|
124
117
|
COMMANDS_FASPEX = COMMON_ACTIONS
|
125
118
|
|
126
|
-
def initialize(env)
|
119
|
+
def initialize(env, api: nil)
|
127
120
|
super(env)
|
128
|
-
|
129
|
-
return if env[:man_only]
|
121
|
+
Node.declare_options(options, force: env[:all_manuals])
|
130
122
|
@api_node =
|
131
|
-
if
|
123
|
+
if !api.nil? || env[:all_manuals]
|
132
124
|
# this can be Aspera::Node or Aspera::Rest (shares)
|
133
|
-
|
134
|
-
elsif options.get_option(:password,
|
125
|
+
api
|
126
|
+
elsif Oauth.bearer?(options.get_option(:password, mandatory: true))
|
135
127
|
# info is provided like node_info of aoc
|
136
128
|
Aspera::Node.new(params: {
|
137
|
-
base_url: options.get_option(:url,
|
138
|
-
headers:
|
139
|
-
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username, is_type: :mandatory),
|
140
|
-
'Authorization' => options.get_option(:password, is_type: :mandatory)
|
141
|
-
}
|
129
|
+
base_url: options.get_option(:url, mandatory: true),
|
130
|
+
headers: Aspera::Node.bearer_headers(options.get_option(:password, mandatory: true))
|
142
131
|
})
|
143
132
|
else
|
144
133
|
# this is normal case
|
145
134
|
Aspera::Node.new(params: {
|
146
|
-
base_url: options.get_option(:url,
|
135
|
+
base_url: options.get_option(:url, mandatory: true),
|
147
136
|
auth: {
|
148
137
|
type: :basic,
|
149
|
-
username: options.get_option(:username,
|
150
|
-
password: options.get_option(:password,
|
138
|
+
username: options.get_option(:username, mandatory: true),
|
139
|
+
password: options.get_option(:password, mandatory: true)
|
151
140
|
}})
|
152
141
|
end
|
153
142
|
end
|
154
143
|
|
155
|
-
def c_textify_browse(table_data)
|
156
|
-
return table_data.map do |i|
|
157
|
-
i['permissions'] = i['permissions'].map { |x| x['name'] }.join(',')
|
158
|
-
i
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# key/value is defined in main in hash_table
|
163
|
-
def c_textify_bool_list_result(list, name_list)
|
164
|
-
list.each_index do |i|
|
165
|
-
next unless name_list.include?(list[i]['key'])
|
166
|
-
list[i]['value'].each do |item|
|
167
|
-
list.push({'key' => item['name'], 'value' => item['value']})
|
168
|
-
end
|
169
|
-
list.delete_at(i)
|
170
|
-
# continue at same index because we delete current one
|
171
|
-
redo
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
144
|
# reduce the path from a result on given named column
|
176
145
|
def c_result_remove_prefix_path(result, column, path_prefix)
|
177
146
|
if !path_prefix.nil?
|
@@ -227,7 +196,7 @@ module Aspera
|
|
227
196
|
when :search
|
228
197
|
search_root = get_next_arg_add_prefix(prefix_path, 'search root')
|
229
198
|
parameters = {'path' => search_root}
|
230
|
-
other_options =
|
199
|
+
other_options = query_option
|
231
200
|
parameters.merge!(other_options) unless other_options.nil?
|
232
201
|
resp = @api_node.create('files/search', parameters)
|
233
202
|
result = { type: :object_list, data: resp[:data]['items']}
|
@@ -273,53 +242,66 @@ module Aspera
|
|
273
242
|
# if there is no items
|
274
243
|
case send_result['self']['type']
|
275
244
|
when 'directory', 'container' # directory: node, container: shares
|
276
|
-
result = { data: send_result['items'], type: :object_list
|
245
|
+
result = { data: send_result['items'], type: :object_list }
|
277
246
|
formatter.display_item_count(send_result['item_count'], send_result['total_count'])
|
278
247
|
else # 'file','symbolic_link'
|
279
248
|
result = { data: send_result['self'], type: :single_object}
|
280
|
-
# result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
281
|
-
# raise "unknown type: #{send_result['self']['type']}"
|
282
249
|
end
|
283
250
|
return c_result_remove_prefix_path(result, 'path', prefix_path)
|
284
251
|
when :sync
|
285
|
-
|
286
|
-
|
287
|
-
when :upload, :download
|
288
|
-
token_type = options.get_option(:token_type)
|
289
|
-
# nil if Shares 1.x
|
290
|
-
token_type = :aspera if token_type.nil?
|
291
|
-
case token_type
|
292
|
-
when :aspera, :hybrid
|
252
|
+
return execute_sync_action do |sync_direction, local_path, remote_path|
|
253
|
+
# Gen3 API
|
293
254
|
# empty transfer spec for authorization request
|
294
|
-
request_transfer_spec = {
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
255
|
+
request_transfer_spec = {
|
256
|
+
type: case sync_direction
|
257
|
+
when :push then :sync_upload
|
258
|
+
when :pull then :sync_download
|
259
|
+
when :bidi then :sync
|
260
|
+
end,
|
261
|
+
paths: [{
|
262
|
+
source: remote_path,
|
263
|
+
destination: local_path
|
264
|
+
}]
|
265
|
+
}
|
301
266
|
# add fixed parameters if any (for COS)
|
302
267
|
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
303
268
|
# prepare payload for single request
|
304
269
|
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
305
270
|
# only one request, so only one answer
|
306
|
-
transfer_spec = @api_node.create(
|
271
|
+
transfer_spec = @api_node.create('files/sync_setup', setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
272
|
+
# API returns null tag... but async does not like it
|
273
|
+
transfer_spec.delete_if{ |_k, v| v.nil? }
|
307
274
|
# delete this part, as the returned value contains only destination, and not sources
|
308
|
-
transfer_spec.delete('paths') if command.eql?(:upload)
|
309
|
-
|
310
|
-
|
311
|
-
raise 'shall be basic auth' unless @api_node.params[:auth][:type].eql?(:basic)
|
312
|
-
transfer_spec = {}.merge(Aspera::Fasp::TransferSpec::AK_TSPEC_BASE)
|
313
|
-
transfer_spec['remote_host'] = URI.parse(@api_node.params[:base_url]).host
|
314
|
-
Fasp::TransferSpec.action_to_direction(transfer_spec, command)
|
315
|
-
transfer_spec['destination_root'] = transfer.destination_folder(transfer_spec['direction'])
|
316
|
-
@api_node.add_tspec_info(transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
317
|
-
else raise "ERROR: token_type #{tt}"
|
275
|
+
# transfer_spec.delete('paths') if command.eql?(:upload)
|
276
|
+
Log.log.debug{Log.dump(:ts, transfer_spec)}
|
277
|
+
transfer_spec
|
318
278
|
end
|
319
|
-
|
320
|
-
|
279
|
+
when :upload, :download
|
280
|
+
# empty transfer spec for authorization request
|
281
|
+
request_transfer_spec = {}
|
282
|
+
# set requested paths depending on direction
|
283
|
+
request_transfer_spec[:paths] = if command.eql?(:download)
|
284
|
+
transfer.ts_source_paths
|
285
|
+
else
|
286
|
+
[{ destination: transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND) }]
|
321
287
|
end
|
288
|
+
# add fixed parameters if any (for COS)
|
289
|
+
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
290
|
+
# prepare payload for single request
|
291
|
+
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
292
|
+
# only one request, so only one answer
|
293
|
+
transfer_spec = @api_node.create("files/#{command}_setup", setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
294
|
+
# delete this part, as the returned value contains only destination, and not sources
|
295
|
+
transfer_spec.delete('paths') if command.eql?(:upload)
|
322
296
|
return Main.result_transfer(transfer.start(transfer_spec))
|
297
|
+
when :http_node_download
|
298
|
+
remote_path = get_next_arg_add_prefix(prefix_path, 'remote path')
|
299
|
+
file_name = File.basename(remote_path)
|
300
|
+
@api_node.call(
|
301
|
+
operation: 'GET',
|
302
|
+
subpath: "files/#{URI.encode_www_form_component(remote_path)}/contents",
|
303
|
+
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE), file_name))
|
304
|
+
return Main.result_status("downloaded: #{file_name}")
|
323
305
|
end
|
324
306
|
raise 'INTERNAL ERROR'
|
325
307
|
end
|
@@ -330,20 +312,37 @@ module Aspera
|
|
330
312
|
case command
|
331
313
|
when *COMMANDS_GEN3
|
332
314
|
execute_command_gen3(command, prefix_path)
|
333
|
-
when :
|
334
|
-
ak_command = options.get_next_command([
|
315
|
+
when :access_keys
|
316
|
+
ak_command = options.get_next_command(%i[do set_bearer_key].concat(Plugin::ALL_OPS))
|
335
317
|
case ak_command
|
336
|
-
when *Plugin::ALL_OPS
|
318
|
+
when *Plugin::ALL_OPS
|
319
|
+
return entity_command(ak_command, @api_node, 'access_keys') do |field, value|
|
320
|
+
raise 'only selector: %id:self' unless field.eql?('id') && value.eql?('self')
|
321
|
+
@api_node.read('access_keys/self')[:data]['id']
|
322
|
+
end
|
337
323
|
when :do
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
324
|
+
access_key_id = options.get_next_argument('access key id')
|
325
|
+
root_file_id = options.get_option(:root_id)
|
326
|
+
if root_file_id.nil?
|
327
|
+
ak_info = @api_node.read("access_keys/#{access_key_id}")[:data]
|
328
|
+
# change API credentials if different access key
|
329
|
+
if !access_key_id.eql?('self')
|
330
|
+
@api_node.params[:auth][:username] = ak_info['id']
|
331
|
+
@api_node.params[:auth][:password] = config.lookup_secret(url: @api_node.params[:base_url], username: ak_info['id'], mandatory: true)
|
332
|
+
end
|
333
|
+
root_file_id = ak_info['root_file_id']
|
344
334
|
end
|
345
335
|
command_repo = options.get_next_command(COMMANDS_GEN4)
|
346
|
-
return execute_command_gen4(command_repo,
|
336
|
+
return execute_command_gen4(command_repo, root_file_id)
|
337
|
+
when :set_bearer_key
|
338
|
+
access_key_id = options.get_next_argument('access key id')
|
339
|
+
access_key_id = @api_node.read('access_keys/self')[:data]['id'] if access_key_id.eql?('self')
|
340
|
+
bearer_key_pem = options.get_next_argument('public or private RSA key PEM value', type: String)
|
341
|
+
key = OpenSSL::PKey.read(bearer_key_pem)
|
342
|
+
key = key.public_key if key.private?
|
343
|
+
bearer_key_pem = key.to_pem
|
344
|
+
@api_node.update("access_keys/#{access_key_id}", {token_verification_key: bearer_key_pem})
|
345
|
+
return Main.result_status('public key updated')
|
347
346
|
end
|
348
347
|
when :health
|
349
348
|
nagios = Nagios.new
|
@@ -367,11 +366,14 @@ module Aspera
|
|
367
366
|
end
|
368
367
|
return nagios.result
|
369
368
|
when :events
|
370
|
-
events = @api_node.read('events',
|
371
|
-
return { type: :object_list, data: events}
|
369
|
+
events = @api_node.read('events', query_read_delete)[:data]
|
370
|
+
return { type: :object_list, data: events, fields: ->(f){!f.start_with?('data')} }
|
372
371
|
when :info
|
373
372
|
nd_info = @api_node.read('info')[:data]
|
374
|
-
return { type: :single_object, data: nd_info
|
373
|
+
return { type: :single_object, data: nd_info}
|
374
|
+
when :slash
|
375
|
+
nd_info = @api_node.read('')[:data]
|
376
|
+
return { type: :single_object, data: nd_info}
|
375
377
|
when :license
|
376
378
|
# requires: asnodeadmin -mu <node user> --acl-add=internal --internal
|
377
379
|
node_license = @api_node.read('license')[:data]
|
@@ -383,66 +385,17 @@ module Aspera
|
|
383
385
|
return { type: :single_object, data: @api_node.params }
|
384
386
|
end
|
385
387
|
end
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
end
|
395
|
-
case command_node_file
|
396
|
-
when :show
|
397
|
-
items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
398
|
-
return {type: :single_object, data: items}
|
399
|
-
when :modify
|
400
|
-
update_param = options.get_next_argument('update data', type: Hash)
|
401
|
-
apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
|
402
|
-
return Main.result_status('Done')
|
403
|
-
when :thumbnail
|
404
|
-
result = apifid[:api].call(
|
405
|
-
operation: 'GET',
|
406
|
-
subpath: "files/#{apifid[:file_id]}/preview",
|
407
|
-
headers: {'Accept' => 'image/png'}
|
408
|
-
)
|
409
|
-
require 'aspera/preview/terminal'
|
410
|
-
return Main.result_status(Preview::Terminal.build(result[:http].body, reserved_lines: 3))
|
411
|
-
when :permission
|
412
|
-
command_perm = options.get_next_command(%i[list create delete])
|
413
|
-
case command_perm
|
414
|
-
when :list
|
415
|
-
# generic options : TODO: as arg ? option_url_query
|
416
|
-
list_options ||= {'include' => [Rest::ARRAY_PARAMS, 'access_level', 'permission_count']}
|
417
|
-
# add which one to get
|
418
|
-
list_options['file_id'] = apifid[:file_id]
|
419
|
-
list_options['inherited'] ||= false
|
420
|
-
items = apifid[:api].read('permissions', list_options)[:data]
|
421
|
-
return {type: :object_list, data: items}
|
422
|
-
when :delete
|
423
|
-
perm_id = instance_identifier
|
424
|
-
return do_bulk_operation(perm_id, 'deleted') do |one_id|
|
425
|
-
# TODO: notify event ?
|
426
|
-
apifid[:api].delete("permissions/#{perm_id}")
|
427
|
-
{'id' => one_id}
|
428
|
-
end
|
429
|
-
when :create
|
430
|
-
create_param = options.get_next_argument('creation data', type: Hash)
|
431
|
-
raise 'no file_id' if create_param.key?('file_id')
|
432
|
-
create_param['file_id'] = apifid[:file_id]
|
433
|
-
create_param['access_levels'] = Aspera::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
|
434
|
-
# add application specific tags (AoC)
|
435
|
-
the_app = apifid[:api].app_info
|
436
|
-
the_app[:api].permissions_create_params(create_param: create_param, app_info: the_app) unless the_app.nil?
|
437
|
-
# create permission
|
438
|
-
created_data = apifid[:api].create('permissions', create_param)[:data]
|
439
|
-
# notify application of creation
|
440
|
-
the_app[:api].permissions_create_event(created_data: created_data, app_info: the_app) unless the_app.nil?
|
441
|
-
return { type: :single_object, data: created_data}
|
442
|
-
else raise "internal error:shall not reach here (#{command_perm})"
|
443
|
-
end
|
444
|
-
else raise "internal error:shall not reach here (#{command_node_file})"
|
388
|
+
|
389
|
+
# @return [Hash] api and main file id for given path or id
|
390
|
+
# Allows to specify a file by its path or by its id on the node
|
391
|
+
def apifid_from_next_arg(top_file_id)
|
392
|
+
file_path = instance_identifier(description: 'path or %id:<id>') do |attribute, value|
|
393
|
+
raise 'Only selection "id" is supported (file id)' unless attribute.eql?('id')
|
394
|
+
# directly return result for method
|
395
|
+
return {api: @api_node, file_id: value}
|
445
396
|
end
|
397
|
+
# there was no selector, so it is a path
|
398
|
+
return @api_node.resolve_api_fid(top_file_id, file_path)
|
446
399
|
end
|
447
400
|
|
448
401
|
def execute_command_gen4(command_repo, top_file_id)
|
@@ -452,7 +405,7 @@ module Aspera
|
|
452
405
|
command_legacy = options.get_next_command(V3_IN_V4_ACTIONS)
|
453
406
|
# TODO: shall we support all methods here ? what if there is a link ?
|
454
407
|
apifid = @api_node.resolve_api_fid(top_file_id, '')
|
455
|
-
return Node.new(@agents
|
408
|
+
return Node.new(@agents, api: apifid[:api]).execute_action(command_legacy)
|
456
409
|
when :node_info, :bearer_token_node
|
457
410
|
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
458
411
|
result = {
|
@@ -467,16 +420,17 @@ module Aspera
|
|
467
420
|
when :oauth2
|
468
421
|
result[:username] = apifid[:api].params[:headers][Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY]
|
469
422
|
result[:password] = apifid[:api].oauth_token
|
470
|
-
else raise 'unknown'
|
423
|
+
else raise 'internal error: unknown auth type'
|
471
424
|
end
|
472
425
|
return {type: :single_object, data: result} if command_repo.eql?(:node_info)
|
473
|
-
|
426
|
+
# check format of bearer token
|
427
|
+
Oauth.bearer_extract(result[:password])
|
474
428
|
return Main.result_status(result[:password])
|
475
429
|
when :browse
|
476
430
|
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
477
431
|
file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
478
432
|
if file_info['type'].eql?('folder')
|
479
|
-
result = apifid[:api].read("files/#{apifid[:file_id]}/files",
|
433
|
+
result = apifid[:api].read("files/#{apifid[:file_id]}/files", old_query_read_delete)
|
480
434
|
items = result[:data]
|
481
435
|
formatter.display_item_count(result[:data].length, result[:http]['X-Total-Count'])
|
482
436
|
else
|
@@ -485,7 +439,7 @@ module Aspera
|
|
485
439
|
return {type: :object_list, data: items, fields: %w[name type recursive_size size modified_time access_level]}
|
486
440
|
when :find
|
487
441
|
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
488
|
-
test_block = Aspera::Node.
|
442
|
+
test_block = Aspera::Node.file_matcher_from_argument(options)
|
489
443
|
return {type: :object_list, data: @api_node.find_files(apifid[:file_id], test_block), fields: ['path']}
|
490
444
|
when :mkdir
|
491
445
|
containing_folder_path = options.get_next_argument('path').split(Aspera::Node::PATH_SEPARATOR)
|
@@ -495,20 +449,31 @@ module Aspera
|
|
495
449
|
return Main.result_status("created: #{result['name']} (id=#{result['id']})")
|
496
450
|
when :rename
|
497
451
|
file_path = options.get_next_argument('source path')
|
498
|
-
newname = options.get_next_argument('new name')
|
499
452
|
apifid = @api_node.resolve_api_fid(top_file_id, file_path)
|
453
|
+
newname = options.get_next_argument('new name')
|
500
454
|
result = apifid[:api].update("files/#{apifid[:file_id]}", {name: newname})[:data]
|
501
|
-
return Main.result_status("renamed
|
455
|
+
return Main.result_status("renamed to #{newname}")
|
502
456
|
when :delete
|
503
|
-
return do_bulk_operation(
|
504
|
-
raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
|
457
|
+
return do_bulk_operation(command: command_repo, descr: 'path', values: String, id_result: 'path') do |l_path|
|
505
458
|
apifid = @api_node.resolve_api_fid(top_file_id, l_path)
|
506
459
|
result = apifid[:api].delete("files/#{apifid[:file_id]}")[:data]
|
507
460
|
{'path' => l_path}
|
508
461
|
end
|
509
462
|
when :sync
|
510
|
-
|
511
|
-
|
463
|
+
return execute_sync_action do |sync_direction, _local_path, remote_path|
|
464
|
+
# Gen4 API
|
465
|
+
# direction is push pull, bidi
|
466
|
+
ts_direction = case sync_direction
|
467
|
+
when :push, :bidi then Fasp::TransferSpec::DIRECTION_SEND
|
468
|
+
when :pull then Fasp::TransferSpec::DIRECTION_RECEIVE
|
469
|
+
else raise "internal error: bad direction: #{sync_direction} (#{sync_direction.class})"
|
470
|
+
end
|
471
|
+
# remote is specified by option to_folder
|
472
|
+
apifid = @api_node.resolve_api_fid(top_file_id, remote_path)
|
473
|
+
transfer_spec = apifid[:api].transfer_spec_gen4(apifid[:file_id], ts_direction)
|
474
|
+
Log.log.debug{Log.dump(:ts, transfer_spec)}
|
475
|
+
transfer_spec
|
476
|
+
end
|
512
477
|
when :upload
|
513
478
|
apifid = @api_node.resolve_api_fid(top_file_id, transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
|
514
479
|
return Main.result_transfer(transfer.start(apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)))
|
@@ -547,7 +512,7 @@ module Aspera
|
|
547
512
|
source_paths = [{'source' => source_folder.pop}]
|
548
513
|
source_folder = source_folder.join(Aspera::Node::PATH_SEPARATOR)
|
549
514
|
end
|
550
|
-
raise
|
515
|
+
raise Cli::BadArgument, 'one file at a time only in HTTP mode' if source_paths.length > 1
|
551
516
|
file_name = source_paths.first['source']
|
552
517
|
apifid = @api_node.resolve_api_fid(top_file_id, File.join(source_folder, file_name))
|
553
518
|
apifid[:api].call(
|
@@ -555,9 +520,62 @@ module Aspera
|
|
555
520
|
subpath: "files/#{apifid[:file_id]}/content",
|
556
521
|
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE), file_name))
|
557
522
|
return Main.result_status("downloaded: #{file_name}")
|
558
|
-
when :
|
559
|
-
|
560
|
-
|
523
|
+
when :show
|
524
|
+
apifid = apifid_from_next_arg(top_file_id)
|
525
|
+
items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
526
|
+
return {type: :single_object, data: items}
|
527
|
+
when :modify
|
528
|
+
apifid = apifid_from_next_arg(top_file_id)
|
529
|
+
update_param = options.get_next_argument('update data', type: Hash)
|
530
|
+
apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
|
531
|
+
return Main.result_status('Done')
|
532
|
+
when :thumbnail
|
533
|
+
apifid = apifid_from_next_arg(top_file_id)
|
534
|
+
result = apifid[:api].call(
|
535
|
+
operation: 'GET',
|
536
|
+
subpath: "files/#{apifid[:file_id]}/preview",
|
537
|
+
headers: {'Accept' => 'image/png'}
|
538
|
+
)
|
539
|
+
require 'aspera/preview/terminal'
|
540
|
+
terminal_options = options.get_option(:query, default: {}).symbolize_keys
|
541
|
+
allowed_options = Preview::Terminal.method(:build).parameters.select{|i|i[0].eql?(:key)}.map{|i|i[1]}
|
542
|
+
unknown_options = terminal_options.keys - allowed_options
|
543
|
+
raise "invalid options: #{unknown_options.join(', ')}, use #{allowed_options.join(', ')}" unless unknown_options.empty?
|
544
|
+
return Main.result_status(Preview::Terminal.build(result[:http].body, **terminal_options))
|
545
|
+
when :permission
|
546
|
+
apifid = apifid_from_next_arg(top_file_id)
|
547
|
+
command_perm = options.get_next_command(%i[list create delete])
|
548
|
+
case command_perm
|
549
|
+
when :list
|
550
|
+
# generic options : TODO: as arg ? query_read_delete
|
551
|
+
list_options ||= {'include' => Rest.array_params(%w[access_level permission_count])}
|
552
|
+
# add which one to get
|
553
|
+
list_options['file_id'] = apifid[:file_id]
|
554
|
+
list_options['inherited'] ||= false
|
555
|
+
items = apifid[:api].read('permissions', list_options)[:data]
|
556
|
+
return {type: :object_list, data: items}
|
557
|
+
when :delete
|
558
|
+
return do_bulk_operation(command: command_perm, descr: 'identifier', values: :identifier) do |one_id|
|
559
|
+
apifid[:api].delete("permissions/#{one_id}")
|
560
|
+
# notify application of deletion
|
561
|
+
the_app[:api].permissions_send_event(created_data: created_data, app_info: the_app, types: ['permission.deleted']) unless the_app.nil?
|
562
|
+
{'id' => one_id}
|
563
|
+
end
|
564
|
+
when :create
|
565
|
+
create_param = options.get_next_argument('creation data', type: Hash)
|
566
|
+
raise 'no file_id' if create_param.key?('file_id')
|
567
|
+
create_param['file_id'] = apifid[:file_id]
|
568
|
+
create_param['access_levels'] = Aspera::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
|
569
|
+
# add application specific tags (AoC)
|
570
|
+
the_app = apifid[:api].app_info
|
571
|
+
the_app[:api].permissions_set_create_params(create_param: create_param, app_info: the_app) unless the_app.nil?
|
572
|
+
# create permission
|
573
|
+
created_data = apifid[:api].create('permissions', create_param)[:data]
|
574
|
+
# notify application of creation
|
575
|
+
the_app[:api].permissions_send_event(created_data: created_data, app_info: the_app) unless the_app.nil?
|
576
|
+
return { type: :single_object, data: created_data}
|
577
|
+
else raise "internal error:shall not reach here (#{command_perm})"
|
578
|
+
end
|
561
579
|
else raise "INTERNAL ERROR: no case for #{command_repo}"
|
562
580
|
end # command_repo
|
563
581
|
# raise 'INTERNAL ERROR: missing return'
|
@@ -570,7 +588,7 @@ module Aspera
|
|
570
588
|
async_name = options.get_option(:sync_name)
|
571
589
|
if async_name.nil?
|
572
590
|
async_id = instance_identifier
|
573
|
-
if async_id.eql?(
|
591
|
+
if async_id.eql?(ExtendedValue::ALL) && %i[show delete].include?(command)
|
574
592
|
async_ids = @api_node.read('async/list')[:data]['sync_ids']
|
575
593
|
else
|
576
594
|
Integer(async_id) # must be integer
|
@@ -593,7 +611,7 @@ module Aspera
|
|
593
611
|
when :show
|
594
612
|
resp = @api_node.create('async/summary', post_data)[:data]['sync_summaries']
|
595
613
|
return Main.result_empty if resp.empty?
|
596
|
-
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(
|
614
|
+
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if async_id.eql?(ExtendedValue::ALL)
|
597
615
|
return { type: :single_object, data: resp.first }
|
598
616
|
when :delete
|
599
617
|
resp = @api_node.create('async/delete', post_data)[:data]
|
@@ -610,21 +628,21 @@ module Aspera
|
|
610
628
|
# filename str
|
611
629
|
# skip int
|
612
630
|
# status int
|
613
|
-
filter =
|
631
|
+
filter = query_option
|
614
632
|
post_data.merge!(filter) unless filter.nil?
|
615
633
|
resp = @api_node.create('async/files', post_data)[:data]
|
616
634
|
data = resp['sync_files']
|
617
635
|
data = data.first[async_id] unless data.empty?
|
618
636
|
iteration_data = []
|
619
637
|
skip_ids_persistency = nil
|
620
|
-
if options.get_option(:once_only,
|
638
|
+
if options.get_option(:once_only, mandatory: true)
|
621
639
|
skip_ids_persistency = PersistencyActionOnce.new(
|
622
640
|
manager: @agents[:persistency],
|
623
641
|
data: iteration_data,
|
624
642
|
id: IdGenerator.from_list([
|
625
643
|
'sync_files',
|
626
|
-
options.get_option(:url,
|
627
|
-
options.get_option(:username,
|
644
|
+
options.get_option(:url, mandatory: true),
|
645
|
+
options.get_option(:username, mandatory: true),
|
628
646
|
async_id]))
|
629
647
|
unless iteration_data.first.nil?
|
630
648
|
data.select!{|l| l['fnid'].to_i > iteration_data.first}
|
@@ -641,6 +659,20 @@ module Aspera
|
|
641
659
|
end
|
642
660
|
end
|
643
661
|
|
662
|
+
# @return [Integer] id of the sync
|
663
|
+
# @raise [Cli::BadArgument] if no such sync, or not by name
|
664
|
+
# @param [String] field name of the field to search
|
665
|
+
# @param [String] value value of the field to search
|
666
|
+
def ssync_lookup(field, value)
|
667
|
+
raise Cli::BadArgument, "Only search by name is supported (#{field})" unless field.eql?('name')
|
668
|
+
@api_node.read('asyncs')[:data]['ids'].each do |id|
|
669
|
+
sync_info = @api_node.read("asyncs/#{id}")[:data]['configuration']
|
670
|
+
# name is unique, so we can return
|
671
|
+
return id if sync_info[field].eql?(value)
|
672
|
+
end
|
673
|
+
raise Cli::BadArgument, "no such sync: #{field}=#{value}"
|
674
|
+
end
|
675
|
+
|
644
676
|
ACTIONS = %i[
|
645
677
|
async
|
646
678
|
ssync
|
@@ -650,7 +682,8 @@ module Aspera
|
|
650
682
|
watch_folder
|
651
683
|
central
|
652
684
|
asperabrowser
|
653
|
-
basic_token
|
685
|
+
basic_token
|
686
|
+
bearer_token].concat(COMMON_ACTIONS).freeze
|
654
687
|
|
655
688
|
def execute_action(command=nil, prefix_path=nil)
|
656
689
|
command ||= options.get_next_command(ACTIONS)
|
@@ -659,32 +692,33 @@ module Aspera
|
|
659
692
|
when :async then return execute_async # former API
|
660
693
|
when :ssync
|
661
694
|
# newer API
|
662
|
-
sync_command = options.get_next_command(%i[bandwidth counters files
|
695
|
+
sync_command = options.get_next_command(%i[start stop bandwidth counters files state summary].concat(Plugin::ALL_OPS) - %i[modify])
|
663
696
|
case sync_command
|
664
|
-
when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids')
|
697
|
+
when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids'){|field, value|ssync_lookup(field, value)}
|
665
698
|
else
|
666
|
-
|
667
|
-
|
699
|
+
asyncs_id = instance_identifier {|field, value|ssync_lookup(field, value)}
|
700
|
+
parameters = nil
|
668
701
|
if %i[start stop].include?(sync_command)
|
669
702
|
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
|
670
|
-
return Main.result_status('
|
703
|
+
return Main.result_status('Done')
|
671
704
|
end
|
705
|
+
parameters = query_option(default: {}) if %i[bandwidth counters files].include?(sync_command)
|
672
706
|
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
|
673
707
|
end
|
674
708
|
when :stream
|
675
709
|
command = options.get_next_command(%i[list create show modify cancel])
|
676
710
|
case command
|
677
711
|
when :list
|
678
|
-
resp = @api_node.read('ops/transfers',
|
712
|
+
resp = @api_node.read('ops/transfers', old_query_read_delete)
|
679
713
|
return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
|
680
714
|
when :create
|
681
|
-
resp = @api_node.create('streams',
|
715
|
+
resp = @api_node.create('streams', value_create_modify(command: command))
|
682
716
|
return { type: :single_object, data: resp[:data] }
|
683
717
|
when :show
|
684
718
|
resp = @api_node.read("ops/transfers/#{options.get_next_argument('transfer id')}")
|
685
719
|
return { type: :other_struct, data: resp[:data] }
|
686
720
|
when :modify
|
687
|
-
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}",
|
721
|
+
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}", value_create_modify(command: command))
|
688
722
|
return { type: :other_struct, data: resp[:data] }
|
689
723
|
when :cancel
|
690
724
|
resp = @api_node.cancel("streams/#{options.get_next_argument('transfer id')}")
|
@@ -693,29 +727,86 @@ module Aspera
|
|
693
727
|
raise 'error'
|
694
728
|
end
|
695
729
|
when :transfer
|
696
|
-
command = options.get_next_command(%i[list cancel show])
|
730
|
+
command = options.get_next_command(%i[list cancel show modify bandwidth_average sessions])
|
697
731
|
res_class_path = 'ops/transfers'
|
698
|
-
if %i[cancel show].include?(command)
|
732
|
+
if %i[cancel show modify].include?(command)
|
699
733
|
one_res_id = instance_identifier
|
700
734
|
one_res_path = "#{res_class_path}/#{one_res_id}"
|
701
735
|
end
|
702
736
|
case command
|
703
737
|
when :list
|
704
|
-
|
705
|
-
query = options.get_option(:value) || options.get_option(:query)
|
706
|
-
raise 'Query must be a Hash' unless query.nil? || query.is_a?(Hash)
|
707
|
-
resp = @api_node.read(res_class_path, query)
|
738
|
+
transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
|
708
739
|
return {
|
709
740
|
type: :object_list,
|
710
|
-
data:
|
741
|
+
data: transfers_data,
|
711
742
|
fields: %w[id status start_spec.direction start_spec.remote_user start_spec.remote_host start_spec.destination_path]
|
712
743
|
}
|
744
|
+
when :sessions
|
745
|
+
transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
|
746
|
+
sessions = transfers_data.map{|t|t['sessions']}.flatten
|
747
|
+
sessions.each do |session|
|
748
|
+
session['start_time'] = Time.at(session['start_time_usec'] / 1_000_000.0).utc.iso8601(0)
|
749
|
+
session['end_time'] = Time.at(session['end_time_usec'] / 1_000_000.0).utc.iso8601(0)
|
750
|
+
end
|
751
|
+
return {
|
752
|
+
type: :object_list,
|
753
|
+
data: sessions,
|
754
|
+
fields: %w[id status start_time end_time target_rate_kbps]
|
755
|
+
}
|
713
756
|
when :cancel
|
714
757
|
resp = @api_node.cancel(one_res_path)
|
715
758
|
return { type: :other_struct, data: resp[:data] }
|
716
759
|
when :show
|
717
760
|
resp = @api_node.read(one_res_path)
|
718
761
|
return { type: :other_struct, data: resp[:data] }
|
762
|
+
when :modify
|
763
|
+
resp = @api_node.update(one_res_path, options.get_next_argument('update value', type: Hash))
|
764
|
+
return { type: :other_struct, data: resp[:data] }
|
765
|
+
when :bandwidth_average
|
766
|
+
transfers_data = @api_node.read(res_class_path, query_read_delete)[:data]
|
767
|
+
# collect all key dates
|
768
|
+
bandwidth_period = {}
|
769
|
+
dir_info = %i[avg_kbps sessions].freeze
|
770
|
+
transfers_data.each do |transfer|
|
771
|
+
session = transfer
|
772
|
+
# transfer['sessions'].each do |session|
|
773
|
+
next if session['avg_rate_kbps'].zero?
|
774
|
+
bandwidth_period[session['start_time_usec']] = 0
|
775
|
+
bandwidth_period[session['end_time_usec']] = 0
|
776
|
+
# end
|
777
|
+
end
|
778
|
+
result = []
|
779
|
+
# all dates sorted numerically
|
780
|
+
all_dates = bandwidth_period.keys.sort
|
781
|
+
all_dates.each_with_index do |start_date, index|
|
782
|
+
end_date = all_dates[index + 1]
|
783
|
+
# do not process last one
|
784
|
+
break if end_date.nil?
|
785
|
+
# init data for this period
|
786
|
+
period_bandwidth = Fasp::TransferSpec::DIRECTION_ENUM_VALUES.map(&:to_sym).each_with_object({}) do |direction, h|
|
787
|
+
h[direction] = dir_info.each_with_object({}) do |k2, h2|
|
788
|
+
h2[k2] = 0
|
789
|
+
end
|
790
|
+
end
|
791
|
+
# find all transfers that were active at this time
|
792
|
+
transfers_data.each do |transfer|
|
793
|
+
session = transfer
|
794
|
+
# transfer['sessions'].each do |session|
|
795
|
+
# skip if not information for this period
|
796
|
+
next if session['avg_rate_kbps'].zero?
|
797
|
+
# skip if not in this period
|
798
|
+
next if session['start_time_usec'] >= end_date || session['end_time_usec'] <= start_date
|
799
|
+
info = period_bandwidth[transfer['start_spec']['direction'].to_sym]
|
800
|
+
info[:avg_kbps] += session['avg_rate_kbps']
|
801
|
+
info[:sessions] += 1
|
802
|
+
# end
|
803
|
+
end
|
804
|
+
next if Fasp::TransferSpec::DIRECTION_ENUM_VALUES.map(&:to_sym).all? do |dir|
|
805
|
+
period_bandwidth[dir][:sessions].zero?
|
806
|
+
end
|
807
|
+
result.push({start: Time.at(start_date / 1_000_000), end: Time.at(end_date / 1_000_000)}.merge(period_bandwidth))
|
808
|
+
end
|
809
|
+
return { type: :object_list, data: result }
|
719
810
|
else
|
720
811
|
raise 'error'
|
721
812
|
end
|
@@ -749,15 +840,15 @@ module Aspera
|
|
749
840
|
@api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
|
750
841
|
case command
|
751
842
|
when :create
|
752
|
-
resp = @api_node.create(res_class_path,
|
843
|
+
resp = @api_node.create(res_class_path, value_create_modify(command: command))
|
753
844
|
return Main.result_status("#{resp[:data]['id']} created")
|
754
845
|
when :list
|
755
|
-
resp = @api_node.read(res_class_path,
|
846
|
+
resp = @api_node.read(res_class_path, old_query_read_delete)
|
756
847
|
return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
|
757
848
|
when :show
|
758
849
|
return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
|
759
850
|
when :modify
|
760
|
-
@api_node.update(one_res_path,
|
851
|
+
@api_node.update(one_res_path, query_option(mandatory: true))
|
761
852
|
return Main.result_status("#{one_res_id} updated")
|
762
853
|
when :delete
|
763
854
|
@api_node.delete(one_res_path)
|
@@ -769,8 +860,7 @@ module Aspera
|
|
769
860
|
command = options.get_next_command(%i[session file])
|
770
861
|
validator_id = options.get_option(:validator)
|
771
862
|
validation = {'validator_id' => validator_id} unless validator_id.nil?
|
772
|
-
request_data =
|
773
|
-
request_data ||= {}
|
863
|
+
request_data = query_option(default: {})
|
774
864
|
case command
|
775
865
|
when :session
|
776
866
|
command = options.get_next_command([:list])
|
@@ -791,7 +881,7 @@ module Aspera
|
|
791
881
|
request_data.deep_merge!({'validation' => validation}) unless validation.nil?
|
792
882
|
resp = @api_node.create('services/rest/transfers/v1/files', request_data)[:data]
|
793
883
|
resp = JSON.parse(resp) if resp.is_a?(String)
|
794
|
-
Log.dump(:resp, resp)
|
884
|
+
Log.log.debug{Log.dump(:resp, resp)}
|
795
885
|
return { type: :object_list, data: resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path]}
|
796
886
|
when :modify
|
797
887
|
request_data.deep_merge!(validation) unless validation.nil?
|
@@ -801,16 +891,21 @@ module Aspera
|
|
801
891
|
end
|
802
892
|
when :asperabrowser
|
803
893
|
browse_params = {
|
804
|
-
'nodeUser' => options.get_option(:username,
|
805
|
-
'nodePW' => options.get_option(:password,
|
806
|
-
'nodeURL' => options.get_option(:url,
|
894
|
+
'nodeUser' => options.get_option(:username, mandatory: true),
|
895
|
+
'nodePW' => options.get_option(:password, mandatory: true),
|
896
|
+
'nodeURL' => options.get_option(:url, mandatory: true)
|
807
897
|
}
|
808
898
|
# encode parameters so that it looks good in url
|
809
899
|
encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
|
810
900
|
OpenApplication.instance.uri(options.get_option(:asperabrowserurl) + '?goto=' + encoded_params)
|
811
901
|
return Main.result_status('done')
|
812
902
|
when :basic_token
|
813
|
-
return Main.result_status(Rest.
|
903
|
+
return Main.result_status(Rest.basic_token(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
|
904
|
+
when :bearer_token
|
905
|
+
private_key = OpenSSL::PKey::RSA.new(options.get_next_argument('private RSA key PEM value', type: String))
|
906
|
+
token_info = options.get_next_argument('user and group identification', type: Hash)
|
907
|
+
access_key = options.get_option(:username, mandatory: true)
|
908
|
+
return Main.result_status(Aspera::Node.bearer_token(payload: token_info, access_key: access_key, private_key: private_key))
|
814
909
|
end # case command
|
815
910
|
raise 'ERROR: shall not reach this line'
|
816
911
|
end # execute_action
|