aspera-cli 4.13.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +28 -5
- data/CONTRIBUTING.md +17 -1
- data/README.md +782 -401
- data/examples/dascli +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +21 -32
- data/lib/aspera/ascmd.rb +1 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +6 -6
- data/lib/aspera/cli/formatter.rb +17 -25
- data/lib/aspera/cli/main.rb +21 -27
- data/lib/aspera/cli/manager.rb +128 -114
- data/lib/aspera/cli/plugin.rb +87 -38
- data/lib/aspera/cli/plugins/alee.rb +2 -2
- data/lib/aspera/cli/plugins/aoc.rb +216 -102
- data/lib/aspera/cli/plugins/ats.rb +16 -18
- data/lib/aspera/cli/plugins/bss.rb +3 -3
- data/lib/aspera/cli/plugins/config.rb +177 -367
- data/lib/aspera/cli/plugins/console.rb +4 -6
- data/lib/aspera/cli/plugins/cos.rb +12 -13
- data/lib/aspera/cli/plugins/faspex.rb +17 -18
- data/lib/aspera/cli/plugins/faspex5.rb +332 -216
- data/lib/aspera/cli/plugins/node.rb +171 -142
- data/lib/aspera/cli/plugins/orchestrator.rb +15 -18
- data/lib/aspera/cli/plugins/preview.rb +38 -60
- data/lib/aspera/cli/plugins/server.rb +22 -15
- data/lib/aspera/cli/plugins/shares.rb +24 -33
- data/lib/aspera/cli/plugins/sync.rb +3 -3
- data/lib/aspera/cli/transfer_agent.rb +29 -26
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +9 -7
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +7 -3
- data/lib/aspera/fasp/agent_connect.rb +5 -0
- data/lib/aspera/fasp/agent_direct.rb +5 -5
- data/lib/aspera/fasp/agent_httpgw.rb +138 -60
- data/lib/aspera/fasp/agent_trsdk.rb +2 -0
- data/lib/aspera/fasp/error_info.rb +2 -0
- data/lib/aspera/fasp/installation.rb +18 -19
- data/lib/aspera/fasp/parameters.rb +18 -17
- data/lib/aspera/fasp/parameters.yaml +2 -1
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +6 -5
- data/lib/aspera/fasp/uri.rb +23 -21
- data/lib/aspera/faspex_postproc.rb +1 -1
- data/lib/aspera/hash_ext.rb +12 -2
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/log.rb +1 -0
- data/lib/aspera/node.rb +62 -80
- data/lib/aspera/oauth.rb +1 -1
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/preview/terminal.rb +61 -15
- data/lib/aspera/preview/utils.rb +3 -3
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +37 -0
- data/lib/aspera/secret_hider.rb +6 -1
- data/lib/aspera/ssh.rb +1 -1
- data/lib/aspera/sync.rb +2 -0
- data.tar.gz.sig +0 -0
- metadata +3 -4
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/data/7 +0 -0
@@ -72,15 +72,10 @@ module Aspera
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def register_node_options(env)
|
75
|
-
env[:options].
|
76
|
-
env[:options].
|
77
|
-
env[:options].
|
78
|
-
env[:options].
|
79
|
-
env[:options].add_opt_list(:token_type, %i[aspera basic hybrid], 'type of token used for transfers')
|
80
|
-
env[:options].add_opt_boolean(:default_ports, 'use standard FASP ports or get from node api (gen4)')
|
81
|
-
env[:options].set_option(:asperabrowserurl, 'https://asperabrowser.mybluemix.net')
|
82
|
-
env[:options].set_option(:token_type, :aspera)
|
83
|
-
env[:options].set_option(:default_ports, :yes)
|
75
|
+
env[:options].declare(:validator, 'Identifier of validator (optional for central)')
|
76
|
+
env[:options].declare(:asperabrowserurl, 'URL for simple aspera web ui', default: 'https://asperabrowser.mybluemix.net')
|
77
|
+
env[:options].declare(:sync_name, 'Sync name')
|
78
|
+
env[:options].declare(:default_ports, 'Use standard FASP ports or get from node api (gen4)', values: :bool, default: :yes)
|
84
79
|
env[:options].parse_options!
|
85
80
|
Aspera::Node.use_standard_ports = env[:options].get_option(:default_ports)
|
86
81
|
end
|
@@ -117,7 +112,7 @@ module Aspera
|
|
117
112
|
NODE4_READ_ACTIONS = %i[bearer_token_node node_info browse find].freeze
|
118
113
|
|
119
114
|
# commands for execute_command_gen4
|
120
|
-
COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download
|
115
|
+
COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download show modify permission thumbnail v3].concat(NODE4_READ_ACTIONS).freeze
|
121
116
|
|
122
117
|
COMMANDS_COS = %i[upload download info access_key api_details transfer].freeze
|
123
118
|
COMMANDS_SHARES = (BASE_ACTIONS - %i[search]).freeze
|
@@ -131,23 +126,23 @@ module Aspera
|
|
131
126
|
if env.key?(:node_api)
|
132
127
|
# this can be Aspera::Node or Aspera::Rest (shares)
|
133
128
|
env[:node_api]
|
134
|
-
elsif options.get_option(:password,
|
129
|
+
elsif options.get_option(:password, mandatory: true).start_with?('Bearer ')
|
135
130
|
# info is provided like node_info of aoc
|
136
131
|
Aspera::Node.new(params: {
|
137
|
-
base_url: options.get_option(:url,
|
132
|
+
base_url: options.get_option(:url, mandatory: true),
|
138
133
|
headers: {
|
139
|
-
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username,
|
140
|
-
'Authorization' => options.get_option(:password,
|
134
|
+
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username, mandatory: true),
|
135
|
+
'Authorization' => options.get_option(:password, mandatory: true)
|
141
136
|
}
|
142
137
|
})
|
143
138
|
else
|
144
139
|
# this is normal case
|
145
140
|
Aspera::Node.new(params: {
|
146
|
-
base_url: options.get_option(:url,
|
141
|
+
base_url: options.get_option(:url, mandatory: true),
|
147
142
|
auth: {
|
148
143
|
type: :basic,
|
149
|
-
username: options.get_option(:username,
|
150
|
-
password: options.get_option(:password,
|
144
|
+
username: options.get_option(:username, mandatory: true),
|
145
|
+
password: options.get_option(:password, mandatory: true)
|
151
146
|
}})
|
152
147
|
end
|
153
148
|
end
|
@@ -227,7 +222,7 @@ module Aspera
|
|
227
222
|
when :search
|
228
223
|
search_root = get_next_arg_add_prefix(prefix_path, 'search root')
|
229
224
|
parameters = {'path' => search_root}
|
230
|
-
other_options =
|
225
|
+
other_options = value_or_query(allowed_types: Hash)
|
231
226
|
parameters.merge!(other_options) unless other_options.nil?
|
232
227
|
resp = @api_node.create('files/search', parameters)
|
233
228
|
result = { type: :object_list, data: resp[:data]['items']}
|
@@ -285,40 +280,22 @@ module Aspera
|
|
285
280
|
node_sync = SyncSpecGen3.new(@api_node)
|
286
281
|
return Plugins::Sync.new(@agents, sync_spec: node_sync).execute_action
|
287
282
|
when :upload, :download
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
# set requested paths depending on direction
|
296
|
-
request_transfer_spec[:paths] = if command.eql?(:download)
|
297
|
-
transfer.ts_source_paths
|
298
|
-
else
|
299
|
-
[{ destination: transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND) }]
|
300
|
-
end
|
301
|
-
# add fixed parameters if any (for COS)
|
302
|
-
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
303
|
-
# prepare payload for single request
|
304
|
-
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
305
|
-
# only one request, so only one answer
|
306
|
-
transfer_spec = @api_node.create("files/#{command}_setup", setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
307
|
-
# delete this part, as the returned value contains only destination, and not sources
|
308
|
-
transfer_spec.delete('paths') if command.eql?(:upload)
|
309
|
-
when :basic
|
310
|
-
raise 'shall have auth' unless @api_node.params[:auth].is_a?(Hash)
|
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}"
|
318
|
-
end
|
319
|
-
if %i[basic hybrid].include?(token_type)
|
320
|
-
@api_node.ts_basic_token(transfer_spec)
|
283
|
+
# empty transfer spec for authorization request
|
284
|
+
request_transfer_spec = {}
|
285
|
+
# set requested paths depending on direction
|
286
|
+
request_transfer_spec[:paths] = if command.eql?(:download)
|
287
|
+
transfer.ts_source_paths
|
288
|
+
else
|
289
|
+
[{ destination: transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND) }]
|
321
290
|
end
|
291
|
+
# add fixed parameters if any (for COS)
|
292
|
+
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
293
|
+
# prepare payload for single request
|
294
|
+
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
295
|
+
# only one request, so only one answer
|
296
|
+
transfer_spec = @api_node.create("files/#{command}_setup", setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
297
|
+
# delete this part, as the returned value contains only destination, and not sources
|
298
|
+
transfer_spec.delete('paths') if command.eql?(:upload)
|
322
299
|
return Main.result_transfer(transfer.start(transfer_spec))
|
323
300
|
end
|
324
301
|
raise 'INTERNAL ERROR'
|
@@ -367,7 +344,7 @@ module Aspera
|
|
367
344
|
end
|
368
345
|
return nagios.result
|
369
346
|
when :events
|
370
|
-
events = @api_node.read('events',
|
347
|
+
events = @api_node.read('events', query_read_delete)[:data]
|
371
348
|
return { type: :object_list, data: events}
|
372
349
|
when :info
|
373
350
|
nd_info = @api_node.read('info')[:data]
|
@@ -383,66 +360,17 @@ module Aspera
|
|
383
360
|
return { type: :single_object, data: @api_node.params }
|
384
361
|
end
|
385
362
|
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})"
|
363
|
+
|
364
|
+
# @return [Hash] api and main file id for given path or id
|
365
|
+
# Allows to specify a file by its path or by its id on the node
|
366
|
+
def apifid_from_next_arg(top_file_id)
|
367
|
+
file_path = instance_identifier(description: 'path or id') do |attribute, value|
|
368
|
+
raise 'Only selection "id" is supported (file id)' unless attribute.eql?('id')
|
369
|
+
# directly return result for method
|
370
|
+
return {api: @api_node, file_id: value}
|
445
371
|
end
|
372
|
+
# there was no selector, so it is a path
|
373
|
+
return @api_node.resolve_api_fid(top_file_id, file_path)
|
446
374
|
end
|
447
375
|
|
448
376
|
def execute_command_gen4(command_repo, top_file_id)
|
@@ -476,7 +404,7 @@ module Aspera
|
|
476
404
|
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
477
405
|
file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
478
406
|
if file_info['type'].eql?('folder')
|
479
|
-
result = apifid[:api].read("files/#{apifid[:file_id]}/files",
|
407
|
+
result = apifid[:api].read("files/#{apifid[:file_id]}/files", old_query_read_delete)
|
480
408
|
items = result[:data]
|
481
409
|
formatter.display_item_count(result[:data].length, result[:http]['X-Total-Count'])
|
482
410
|
else
|
@@ -485,7 +413,7 @@ module Aspera
|
|
485
413
|
return {type: :object_list, data: items, fields: %w[name type recursive_size size modified_time access_level]}
|
486
414
|
when :find
|
487
415
|
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
488
|
-
test_block = Aspera::Node.file_matcher(
|
416
|
+
test_block = Aspera::Node.file_matcher(value_or_query(allowed_types: String))
|
489
417
|
return {type: :object_list, data: @api_node.find_files(apifid[:file_id], test_block), fields: ['path']}
|
490
418
|
when :mkdir
|
491
419
|
containing_folder_path = options.get_next_argument('path').split(Aspera::Node::PATH_SEPARATOR)
|
@@ -495,10 +423,10 @@ module Aspera
|
|
495
423
|
return Main.result_status("created: #{result['name']} (id=#{result['id']})")
|
496
424
|
when :rename
|
497
425
|
file_path = options.get_next_argument('source path')
|
498
|
-
newname = options.get_next_argument('new name')
|
499
426
|
apifid = @api_node.resolve_api_fid(top_file_id, file_path)
|
427
|
+
newname = options.get_next_argument('new name')
|
500
428
|
result = apifid[:api].update("files/#{apifid[:file_id]}", {name: newname})[:data]
|
501
|
-
return Main.result_status("renamed
|
429
|
+
return Main.result_status("renamed to #{newname}")
|
502
430
|
when :delete
|
503
431
|
return do_bulk_operation(options.get_next_argument('path'), 'deleted', id_result: 'path') do |l_path|
|
504
432
|
raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
|
@@ -555,9 +483,60 @@ module Aspera
|
|
555
483
|
subpath: "files/#{apifid[:file_id]}/content",
|
556
484
|
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE), file_name))
|
557
485
|
return Main.result_status("downloaded: #{file_name}")
|
558
|
-
when :
|
559
|
-
|
560
|
-
|
486
|
+
when :show
|
487
|
+
apifid = apifid_from_next_arg(top_file_id)
|
488
|
+
items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
489
|
+
return {type: :single_object, data: items}
|
490
|
+
when :modify
|
491
|
+
apifid = apifid_from_next_arg(top_file_id)
|
492
|
+
update_param = options.get_next_argument('update data', type: Hash)
|
493
|
+
apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
|
494
|
+
return Main.result_status('Done')
|
495
|
+
when :thumbnail
|
496
|
+
apifid = apifid_from_next_arg(top_file_id)
|
497
|
+
result = apifid[:api].call(
|
498
|
+
operation: 'GET',
|
499
|
+
subpath: "files/#{apifid[:file_id]}/preview",
|
500
|
+
headers: {'Accept' => 'image/png'}
|
501
|
+
)
|
502
|
+
require 'aspera/preview/terminal'
|
503
|
+
return Main.result_status(Preview::Terminal.build(result[:http].body, reserved_lines: 3))
|
504
|
+
when :permission
|
505
|
+
apifid = apifid_from_next_arg(top_file_id)
|
506
|
+
command_perm = options.get_next_command(%i[list create delete])
|
507
|
+
case command_perm
|
508
|
+
when :list
|
509
|
+
# generic options : TODO: as arg ? query_read_delete
|
510
|
+
list_options ||= {'include' => Rest.array_params(%w[access_level permission_count])}
|
511
|
+
# add which one to get
|
512
|
+
list_options['file_id'] = apifid[:file_id]
|
513
|
+
list_options['inherited'] ||= false
|
514
|
+
items = apifid[:api].read('permissions', list_options)[:data]
|
515
|
+
return {type: :object_list, data: items}
|
516
|
+
when :delete
|
517
|
+
perm_id = instance_identifier
|
518
|
+
return do_bulk_operation(perm_id, 'deleted') do |one_id|
|
519
|
+
# TODO: notify event ?
|
520
|
+
apifid[:api].delete("permissions/#{perm_id}")
|
521
|
+
# notify application of deletion
|
522
|
+
the_app[:api].permissions_send_event(created_data: created_data, app_info: the_app, types: ['permission.deleted']) unless the_app.nil?
|
523
|
+
{'id' => one_id}
|
524
|
+
end
|
525
|
+
when :create
|
526
|
+
create_param = options.get_next_argument('creation data', type: Hash)
|
527
|
+
raise 'no file_id' if create_param.key?('file_id')
|
528
|
+
create_param['file_id'] = apifid[:file_id]
|
529
|
+
create_param['access_levels'] = Aspera::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
|
530
|
+
# add application specific tags (AoC)
|
531
|
+
the_app = apifid[:api].app_info
|
532
|
+
the_app[:api].permissions_set_create_params(create_param: create_param, app_info: the_app) unless the_app.nil?
|
533
|
+
# create permission
|
534
|
+
created_data = apifid[:api].create('permissions', create_param)[:data]
|
535
|
+
# notify application of creation
|
536
|
+
the_app[:api].permissions_send_event(created_data: created_data, app_info: the_app) unless the_app.nil?
|
537
|
+
return { type: :single_object, data: created_data}
|
538
|
+
else raise "internal error:shall not reach here (#{command_perm})"
|
539
|
+
end
|
561
540
|
else raise "INTERNAL ERROR: no case for #{command_repo}"
|
562
541
|
end # command_repo
|
563
542
|
# raise 'INTERNAL ERROR: missing return'
|
@@ -610,21 +589,21 @@ module Aspera
|
|
610
589
|
# filename str
|
611
590
|
# skip int
|
612
591
|
# status int
|
613
|
-
filter =
|
592
|
+
filter = value_or_query(allowed_types: Hash)
|
614
593
|
post_data.merge!(filter) unless filter.nil?
|
615
594
|
resp = @api_node.create('async/files', post_data)[:data]
|
616
595
|
data = resp['sync_files']
|
617
596
|
data = data.first[async_id] unless data.empty?
|
618
597
|
iteration_data = []
|
619
598
|
skip_ids_persistency = nil
|
620
|
-
if options.get_option(:once_only,
|
599
|
+
if options.get_option(:once_only, mandatory: true)
|
621
600
|
skip_ids_persistency = PersistencyActionOnce.new(
|
622
601
|
manager: @agents[:persistency],
|
623
602
|
data: iteration_data,
|
624
603
|
id: IdGenerator.from_list([
|
625
604
|
'sync_files',
|
626
|
-
options.get_option(:url,
|
627
|
-
options.get_option(:username,
|
605
|
+
options.get_option(:url, mandatory: true),
|
606
|
+
options.get_option(:username, mandatory: true),
|
628
607
|
async_id]))
|
629
608
|
unless iteration_data.first.nil?
|
630
609
|
data.select!{|l| l['fnid'].to_i > iteration_data.first}
|
@@ -659,15 +638,18 @@ module Aspera
|
|
659
638
|
when :async then return execute_async # former API
|
660
639
|
when :ssync
|
661
640
|
# newer API
|
662
|
-
sync_command = options.get_next_command(%i[bandwidth counters files
|
641
|
+
sync_command = options.get_next_command(%i[start stop bandwidth counters files state summary].concat(Plugin::ALL_OPS) - %i[modify])
|
663
642
|
case sync_command
|
664
643
|
when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids')
|
665
644
|
else
|
666
|
-
parameters = options.get_option(:value)
|
667
645
|
asyncs_id = instance_identifier
|
646
|
+
parameters = nil
|
668
647
|
if %i[start stop].include?(sync_command)
|
669
648
|
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
|
670
|
-
return Main.result_status('
|
649
|
+
return Main.result_status('Done')
|
650
|
+
end
|
651
|
+
if %i[bandwidth counters files].include?(sync_command)
|
652
|
+
parameters = value_or_query(allowed_types: Hash, mandatory: false) || {}
|
671
653
|
end
|
672
654
|
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
|
673
655
|
end
|
@@ -675,16 +657,16 @@ module Aspera
|
|
675
657
|
command = options.get_next_command(%i[list create show modify cancel])
|
676
658
|
case command
|
677
659
|
when :list
|
678
|
-
resp = @api_node.read('ops/transfers',
|
660
|
+
resp = @api_node.read('ops/transfers', old_query_read_delete)
|
679
661
|
return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
|
680
662
|
when :create
|
681
|
-
resp = @api_node.create('streams',
|
663
|
+
resp = @api_node.create('streams', value_create_modify(command: command, type: Hash))
|
682
664
|
return { type: :single_object, data: resp[:data] }
|
683
665
|
when :show
|
684
666
|
resp = @api_node.read("ops/transfers/#{options.get_next_argument('transfer id')}")
|
685
667
|
return { type: :other_struct, data: resp[:data] }
|
686
668
|
when :modify
|
687
|
-
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}",
|
669
|
+
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}", value_create_modify(command: command, type: Hash))
|
688
670
|
return { type: :other_struct, data: resp[:data] }
|
689
671
|
when :cancel
|
690
672
|
resp = @api_node.cancel("streams/#{options.get_next_argument('transfer id')}")
|
@@ -693,21 +675,21 @@ module Aspera
|
|
693
675
|
raise 'error'
|
694
676
|
end
|
695
677
|
when :transfer
|
696
|
-
command = options.get_next_command(%i[list cancel show])
|
678
|
+
command = options.get_next_command(%i[list cancel show modify bandwidth_average])
|
697
679
|
res_class_path = 'ops/transfers'
|
698
|
-
if %i[cancel show].include?(command)
|
680
|
+
if %i[cancel show modify].include?(command)
|
699
681
|
one_res_id = instance_identifier
|
700
682
|
one_res_path = "#{res_class_path}/#{one_res_id}"
|
701
683
|
end
|
702
684
|
case command
|
703
685
|
when :list
|
704
686
|
# could use ? subpath: 'transfers'
|
705
|
-
query =
|
687
|
+
query = query_read_delete
|
706
688
|
raise 'Query must be a Hash' unless query.nil? || query.is_a?(Hash)
|
707
|
-
|
689
|
+
transfers_data = @api_node.read(res_class_path, query)[:data]
|
708
690
|
return {
|
709
691
|
type: :object_list,
|
710
|
-
data:
|
692
|
+
data: transfers_data,
|
711
693
|
fields: %w[id status start_spec.direction start_spec.remote_user start_spec.remote_host start_spec.destination_path]
|
712
694
|
}
|
713
695
|
when :cancel
|
@@ -716,6 +698,54 @@ module Aspera
|
|
716
698
|
when :show
|
717
699
|
resp = @api_node.read(one_res_path)
|
718
700
|
return { type: :other_struct, data: resp[:data] }
|
701
|
+
when :modify
|
702
|
+
resp = @api_node.update(one_res_path, options.get_next_argument('update value', type: Hash))
|
703
|
+
return { type: :other_struct, data: resp[:data] }
|
704
|
+
when :bandwidth_average
|
705
|
+
transfers_data = @api_node.read(res_class_path, query)[:data]
|
706
|
+
# collect all key dates
|
707
|
+
bandwidth_period = {}
|
708
|
+
dir_info = %i[avg_kbps sessions].freeze
|
709
|
+
transfers_data.each do |transfer|
|
710
|
+
session = transfer
|
711
|
+
# transfer['sessions'].each do |session|
|
712
|
+
next if session['avg_rate_kbps'].zero?
|
713
|
+
bandwidth_period[session['start_time_usec']] = 0
|
714
|
+
bandwidth_period[session['end_time_usec']] = 0
|
715
|
+
# end
|
716
|
+
end
|
717
|
+
result = []
|
718
|
+
# all dates sorted numerically
|
719
|
+
all_dates = bandwidth_period.keys.sort
|
720
|
+
all_dates.each_with_index do |start_date, index|
|
721
|
+
end_date = all_dates[index + 1]
|
722
|
+
# do not process last one
|
723
|
+
break if end_date.nil?
|
724
|
+
# init data for this period
|
725
|
+
period_bandwidth = Fasp::TransferSpec::DIRECTION_ENUM_VALUES.map(&:to_sym).each_with_object({}) do |direction, h|
|
726
|
+
h[direction] = dir_info.each_with_object({}) do |k2, h2|
|
727
|
+
h2[k2] = 0
|
728
|
+
end
|
729
|
+
end
|
730
|
+
# find all transfers that were active at this time
|
731
|
+
transfers_data.each do |transfer|
|
732
|
+
session = transfer
|
733
|
+
# transfer['sessions'].each do |session|
|
734
|
+
# skip if not information for this period
|
735
|
+
next if session['avg_rate_kbps'].zero?
|
736
|
+
# skip if not in this period
|
737
|
+
next if session['start_time_usec'] >= end_date || session['end_time_usec'] <= start_date
|
738
|
+
info = period_bandwidth[transfer['start_spec']['direction'].to_sym]
|
739
|
+
info[:avg_kbps] += session['avg_rate_kbps']
|
740
|
+
info[:sessions] += 1
|
741
|
+
# end
|
742
|
+
end
|
743
|
+
next if Fasp::TransferSpec::DIRECTION_ENUM_VALUES.map(&:to_sym).all? do |dir|
|
744
|
+
period_bandwidth[dir][:sessions].zero?
|
745
|
+
end
|
746
|
+
result.push({start: Time.at(start_date / 1_000_000), end: Time.at(end_date / 1_000_000)}.merge(period_bandwidth))
|
747
|
+
end
|
748
|
+
return { type: :object_list, data: result }
|
719
749
|
else
|
720
750
|
raise 'error'
|
721
751
|
end
|
@@ -749,15 +779,15 @@ module Aspera
|
|
749
779
|
@api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
|
750
780
|
case command
|
751
781
|
when :create
|
752
|
-
resp = @api_node.create(res_class_path,
|
782
|
+
resp = @api_node.create(res_class_path, value_create_modify(command: command, type: Hash))
|
753
783
|
return Main.result_status("#{resp[:data]['id']} created")
|
754
784
|
when :list
|
755
|
-
resp = @api_node.read(res_class_path,
|
785
|
+
resp = @api_node.read(res_class_path, old_query_read_delete)
|
756
786
|
return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
|
757
787
|
when :show
|
758
788
|
return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
|
759
789
|
when :modify
|
760
|
-
@api_node.update(one_res_path,
|
790
|
+
@api_node.update(one_res_path, value_or_query(mandatory: true, allowed_types: Hash))
|
761
791
|
return Main.result_status("#{one_res_id} updated")
|
762
792
|
when :delete
|
763
793
|
@api_node.delete(one_res_path)
|
@@ -769,8 +799,7 @@ module Aspera
|
|
769
799
|
command = options.get_next_command(%i[session file])
|
770
800
|
validator_id = options.get_option(:validator)
|
771
801
|
validation = {'validator_id' => validator_id} unless validator_id.nil?
|
772
|
-
request_data =
|
773
|
-
request_data ||= {}
|
802
|
+
request_data = value_create_modify(default: {}, type: Hash)
|
774
803
|
case command
|
775
804
|
when :session
|
776
805
|
command = options.get_next_command([:list])
|
@@ -801,16 +830,16 @@ module Aspera
|
|
801
830
|
end
|
802
831
|
when :asperabrowser
|
803
832
|
browse_params = {
|
804
|
-
'nodeUser' => options.get_option(:username,
|
805
|
-
'nodePW' => options.get_option(:password,
|
806
|
-
'nodeURL' => options.get_option(:url,
|
833
|
+
'nodeUser' => options.get_option(:username, mandatory: true),
|
834
|
+
'nodePW' => options.get_option(:password, mandatory: true),
|
835
|
+
'nodeURL' => options.get_option(:url, mandatory: true)
|
807
836
|
}
|
808
837
|
# encode parameters so that it looks good in url
|
809
838
|
encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
|
810
839
|
OpenApplication.instance.uri(options.get_option(:asperabrowserurl) + '?goto=' + encoded_params)
|
811
840
|
return Main.result_status('done')
|
812
841
|
when :basic_token
|
813
|
-
return Main.result_status(Rest.basic_creds(options.get_option(:username,
|
842
|
+
return Main.result_status(Rest.basic_creds(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true)))
|
814
843
|
end # case command
|
815
844
|
raise 'ERROR: shall not reach this line'
|
816
845
|
end # execute_action
|
@@ -9,15 +9,11 @@ module Aspera
|
|
9
9
|
class Orchestrator < Aspera::Cli::BasicAuthPlugin
|
10
10
|
def initialize(env)
|
11
11
|
super(env)
|
12
|
-
options.
|
13
|
-
options.
|
14
|
-
options.
|
15
|
-
options.
|
16
|
-
options.
|
17
|
-
options.set_option(:params, {})
|
18
|
-
options.set_option(:synchronous, :no)
|
19
|
-
options.set_option(:ret_style, :arg)
|
20
|
-
options.set_option(:auth_style, :head_basic)
|
12
|
+
options.declare(:params, 'Start parameters', types: Hash, default: {})
|
13
|
+
options.declare(:result, "Specify result value as: 'work step:parameter'")
|
14
|
+
options.declare(:synchronous, 'Work step:parameter expected as result', values: :bool, default: :no)
|
15
|
+
options.declare(:ret_style, 'How return type is requested in api', values: %i[header arg ext], default: :arg)
|
16
|
+
options.declare(:auth_style, 'Authentication type', values: %i[arg_pass head_basic apikey], default: :head_basic)
|
21
17
|
options.parse_options!
|
22
18
|
end
|
23
19
|
|
@@ -49,7 +45,7 @@ module Aspera
|
|
49
45
|
call_args[:subpath] = "#{opt[:prefix]}/#{call_args[:subpath]}" unless opt[:prefix].nil?
|
50
46
|
# specify id if necessary
|
51
47
|
call_args[:subpath] = "#{call_args[:subpath]}/#{opt[:id]}" if opt.key?(:id)
|
52
|
-
call_type = options.get_option(:ret_style,
|
48
|
+
call_type = options.get_option(:ret_style, mandatory: true)
|
53
49
|
call_type = opt[:ret_style] if opt.key?(:ret_style)
|
54
50
|
format = 'json'
|
55
51
|
format = opt[:format] if opt.key?(:format)
|
@@ -73,19 +69,19 @@ module Aspera
|
|
73
69
|
end
|
74
70
|
|
75
71
|
def execute_action
|
76
|
-
rest_params = {base_url: options.get_option(:url,
|
77
|
-
case options.get_option(:auth_style,
|
72
|
+
rest_params = {base_url: options.get_option(:url, mandatory: true)}
|
73
|
+
case options.get_option(:auth_style, mandatory: true)
|
78
74
|
when :arg_pass
|
79
75
|
rest_params[:auth] = {
|
80
76
|
type: :url,
|
81
77
|
url_creds: {
|
82
|
-
'login' => options.get_option(:username,
|
83
|
-
'password' => options.get_option(:password,
|
78
|
+
'login' => options.get_option(:username, mandatory: true),
|
79
|
+
'password' => options.get_option(:password, mandatory: true) }}
|
84
80
|
when :head_basic
|
85
81
|
rest_params[:auth] = {
|
86
82
|
type: :basic,
|
87
|
-
username: options.get_option(:username,
|
88
|
-
password: options.get_option(:password,
|
83
|
+
username: options.get_option(:username, mandatory: true),
|
84
|
+
password: options.get_option(:password, mandatory: true) }
|
89
85
|
when :apikey
|
90
86
|
raise 'Not implemented'
|
91
87
|
end
|
@@ -150,11 +146,12 @@ module Aspera
|
|
150
146
|
call_params = {format: :json}
|
151
147
|
override_accept = nil
|
152
148
|
# set external parameters if any
|
153
|
-
|
149
|
+
# TODO: make not an option, but a parameter
|
150
|
+
self.options.get_option(:params, mandatory: true).each do |name, value|
|
154
151
|
call_params["external_parameters[#{name}]"] = value
|
155
152
|
end
|
156
153
|
# synchronous call ?
|
157
|
-
call_params['synchronous'] = true if self.options.get_option(:synchronous,
|
154
|
+
call_params['synchronous'] = true if self.options.get_option(:synchronous, mandatory: true)
|
158
155
|
# expected result for synchro call ?
|
159
156
|
expected = self.options.get_option(:result)
|
160
157
|
unless expected.nil?
|