aspera-cli 4.10.0 → 4.12.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/BUGS.md +19 -0
- data/CHANGELOG.md +528 -0
- data/CONTRIBUTING.md +143 -0
- data/README.md +977 -589
- data/bin/ascli +4 -4
- data/bin/asession +12 -12
- data/docs/test_env.conf +29 -19
- data/examples/aoc.rb +6 -6
- data/examples/dascli +18 -16
- data/examples/faspex4.rb +15 -15
- data/examples/node.rb +12 -12
- data/examples/proxy.pac +2 -2
- data/examples/server.rb +12 -12
- data/lib/aspera/aoc.rb +344 -272
- data/lib/aspera/ascmd.rb +56 -54
- data/lib/aspera/ats_api.rb +4 -4
- data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
- data/lib/aspera/cli/extended_value.rb +9 -9
- data/lib/aspera/cli/{formater.rb → formatter.rb} +69 -69
- data/lib/aspera/cli/listener/line_dump.rb +1 -1
- data/lib/aspera/cli/listener/logger.rb +1 -1
- data/lib/aspera/cli/listener/progress.rb +5 -6
- data/lib/aspera/cli/listener/progress_multi.rb +16 -21
- data/lib/aspera/cli/main.rb +72 -73
- data/lib/aspera/cli/manager.rb +112 -112
- data/lib/aspera/cli/plugin.rb +68 -48
- data/lib/aspera/cli/plugins/alee.rb +4 -4
- data/lib/aspera/cli/plugins/aoc.rb +322 -720
- data/lib/aspera/cli/plugins/ats.rb +50 -52
- data/lib/aspera/cli/plugins/bss.rb +10 -10
- data/lib/aspera/cli/plugins/config.rb +514 -410
- data/lib/aspera/cli/plugins/console.rb +12 -12
- data/lib/aspera/cli/plugins/cos.rb +18 -20
- data/lib/aspera/cli/plugins/faspex.rb +134 -136
- data/lib/aspera/cli/plugins/faspex5.rb +235 -70
- data/lib/aspera/cli/plugins/node.rb +378 -309
- data/lib/aspera/cli/plugins/orchestrator.rb +52 -49
- data/lib/aspera/cli/plugins/preview.rb +129 -120
- data/lib/aspera/cli/plugins/server.rb +137 -83
- data/lib/aspera/cli/plugins/shares.rb +77 -52
- data/lib/aspera/cli/plugins/sync.rb +13 -33
- data/lib/aspera/cli/transfer_agent.rb +61 -61
- data/lib/aspera/cli/version.rb +2 -1
- data/lib/aspera/colors.rb +3 -3
- data/lib/aspera/command_line_builder.rb +78 -74
- data/lib/aspera/cos_node.rb +31 -29
- data/lib/aspera/data_repository.rb +1 -1
- data/lib/aspera/environment.rb +30 -28
- data/lib/aspera/fasp/agent_base.rb +17 -15
- data/lib/aspera/fasp/agent_connect.rb +34 -32
- data/lib/aspera/fasp/agent_direct.rb +70 -73
- data/lib/aspera/fasp/agent_httpgw.rb +79 -74
- data/lib/aspera/fasp/agent_node.rb +26 -26
- data/lib/aspera/fasp/agent_trsdk.rb +20 -20
- data/lib/aspera/fasp/error.rb +3 -2
- data/lib/aspera/fasp/error_info.rb +11 -8
- data/lib/aspera/fasp/installation.rb +80 -80
- data/lib/aspera/fasp/listener.rb +2 -2
- data/lib/aspera/fasp/parameters.rb +103 -92
- data/lib/aspera/fasp/parameters.yaml +313 -214
- data/lib/aspera/fasp/resume_policy.rb +10 -10
- data/lib/aspera/fasp/transfer_spec.rb +22 -2
- data/lib/aspera/fasp/uri.rb +7 -7
- data/lib/aspera/faspex_gw.rb +80 -159
- data/lib/aspera/faspex_postproc.rb +77 -0
- data/lib/aspera/hash_ext.rb +3 -3
- data/lib/aspera/id_generator.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +23 -28
- data/lib/aspera/keychain/macos_security.rb +21 -20
- data/lib/aspera/log.rb +13 -13
- data/lib/aspera/nagios.rb +24 -23
- data/lib/aspera/node.rb +217 -38
- data/lib/aspera/oauth.rb +78 -74
- data/lib/aspera/open_application.rb +19 -11
- data/lib/aspera/persistency_action_once.rb +4 -4
- data/lib/aspera/persistency_folder.rb +13 -13
- data/lib/aspera/preview/file_types.rb +8 -8
- data/lib/aspera/preview/generator.rb +67 -67
- data/lib/aspera/preview/utils.rb +27 -27
- data/lib/aspera/proxy_auto_config.js +63 -63
- data/lib/aspera/proxy_auto_config.rb +19 -19
- data/lib/aspera/rest.rb +65 -67
- data/lib/aspera/rest_call_error.rb +2 -1
- data/lib/aspera/rest_error_analyzer.rb +22 -21
- data/lib/aspera/rest_errors_aspera.rb +16 -16
- data/lib/aspera/secret_hider.rb +17 -14
- data/lib/aspera/ssh.rb +15 -14
- data/lib/aspera/sync.rb +177 -62
- data/lib/aspera/temp_file_manager.rb +2 -2
- data/lib/aspera/uri_reader.rb +4 -4
- data/lib/aspera/web_auth.rb +13 -64
- data/lib/aspera/web_server_simple.rb +76 -0
- data.tar.gz.sig +0 -0
- metadata +11 -6
- metadata.gz.sig +0 -0
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'aspera/cli/basic_auth_plugin'
|
4
|
+
require 'aspera/cli/plugins/sync'
|
4
5
|
require 'aspera/nagios'
|
5
6
|
require 'aspera/hash_ext'
|
6
7
|
require 'aspera/id_generator'
|
7
8
|
require 'aspera/node'
|
9
|
+
require 'aspera/aoc'
|
8
10
|
require 'aspera/fasp/transfer_spec'
|
9
11
|
require 'base64'
|
10
12
|
require 'zlib'
|
@@ -12,7 +14,7 @@ require 'zlib'
|
|
12
14
|
module Aspera
|
13
15
|
module Cli
|
14
16
|
module Plugins
|
15
|
-
class Node < BasicAuthPlugin
|
17
|
+
class Node < Aspera::Cli::BasicAuthPlugin
|
16
18
|
class << self
|
17
19
|
def detect(base_url)
|
18
20
|
api = Rest.new({ base_url: base_url})
|
@@ -22,55 +24,101 @@ module Aspera
|
|
22
24
|
end
|
23
25
|
return nil
|
24
26
|
end
|
27
|
+
|
28
|
+
def register_node_options(env)
|
29
|
+
env[:options].add_opt_simple(:validator, 'identifier of validator (optional for central)')
|
30
|
+
env[:options].add_opt_simple(:asperabrowserurl, 'URL for simple aspera web ui')
|
31
|
+
env[:options].add_opt_simple(:sync_name, 'sync name')
|
32
|
+
env[:options].add_opt_simple(:path, 'file or folder path for gen4 operation "file"')
|
33
|
+
env[:options].add_opt_list(:token_type, %i[aspera basic hybrid], 'Type of token used for transfers')
|
34
|
+
env[:options].add_opt_boolean(:default_ports, 'use standard FASP ports or get from node api (gen4)')
|
35
|
+
env[:options].set_option(:asperabrowserurl, 'https://asperabrowser.mybluemix.net')
|
36
|
+
env[:options].set_option(:token_type, :aspera)
|
37
|
+
env[:options].set_option(:default_ports, :yes)
|
38
|
+
env[:options].parse_options!
|
39
|
+
Aspera::Node.use_standard_ports = env[:options].get_option(:default_ports)
|
40
|
+
end
|
25
41
|
end
|
26
|
-
|
42
|
+
|
43
|
+
# spellchecker: disable
|
44
|
+
# SOAP API call to test central API
|
45
|
+
CENTRAL_SOAP_API_TEST = '<?xml version="1.0" encoding="UTF-8"?>'\
|
27
46
|
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="urn:Aspera:XML:FASPSessionNET:2009/11:Types">'\
|
28
47
|
'<soapenv:Header></soapenv:Header>'\
|
29
48
|
'<soapenv:Body><typ:GetSessionInfoRequest><SessionFilter><SessionStatus>running</SessionStatus></SessionFilter></typ:GetSessionInfoRequest></soapenv:Body>'\
|
30
49
|
'</soapenv:Envelope>'
|
31
|
-
|
32
|
-
|
50
|
+
# spellchecker: enable
|
51
|
+
|
52
|
+
# fields removed in result of search
|
53
|
+
SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
|
54
|
+
|
55
|
+
# actions in execute_command_gen3
|
56
|
+
COMMANDS_GEN3 = %i[search space mkdir mklink mkfile rename delete browse upload download]
|
57
|
+
|
58
|
+
BASE_ACTIONS = %i[api_details].concat(COMMANDS_GEN3).freeze
|
59
|
+
|
60
|
+
SPECIAL_ACTIONS = %i[health events info license].freeze
|
61
|
+
|
62
|
+
# actions available in v3 in gen4
|
63
|
+
V3_IN_V4_ACTIONS = %i[access_key].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
|
64
|
+
|
65
|
+
# actions used commonly when a node is involved
|
66
|
+
COMMON_ACTIONS = %i[access_key].concat(BASE_ACTIONS).concat(SPECIAL_ACTIONS).freeze
|
67
|
+
|
68
|
+
private_constant(*%i[CENTRAL_SOAP_API_TEST SEARCH_REMOVE_FIELDS BASE_ACTIONS SPECIAL_ACTIONS V3_IN_V4_ACTIONS COMMON_ACTIONS])
|
69
|
+
|
70
|
+
# used in aoc
|
71
|
+
NODE4_READ_ACTIONS = %i[bearer_token_node node_info browse find].freeze
|
72
|
+
|
73
|
+
# commands for execute_command_gen4
|
74
|
+
COMMANDS_GEN4 = %i[mkdir rename delete upload download sync http_node_download file v3].concat(NODE4_READ_ACTIONS).freeze
|
75
|
+
|
76
|
+
COMMANDS_COS = %i[upload download info access_key api_details transfer].freeze
|
77
|
+
COMMANDS_SHARES = (BASE_ACTIONS - %i[search]).freeze
|
78
|
+
COMMANDS_FASPEX = COMMON_ACTIONS
|
33
79
|
|
34
80
|
def initialize(env)
|
35
81
|
super(env)
|
36
|
-
|
37
|
-
@add_request_param = env[:add_request_param] || {}
|
38
|
-
options.add_opt_simple(:validator,'identifier of validator (optional for central)')
|
39
|
-
options.add_opt_simple(:asperabrowserurl,'URL for simple aspera web ui')
|
40
|
-
options.add_opt_simple(:sync_name,'sync name')
|
41
|
-
options.add_opt_list(:token_type,%i[aspera basic hybrid],'Type of token used for transfers')
|
42
|
-
options.set_option(:asperabrowserurl,'https://asperabrowser.mybluemix.net')
|
43
|
-
options.set_option(:token_type,:aspera)
|
44
|
-
options.parse_options!
|
82
|
+
self.class.register_node_options(env) unless env[:skip_node_options]
|
45
83
|
return if env[:man_only]
|
46
84
|
@api_node =
|
47
|
-
if env.
|
85
|
+
if env.key?(:node_api)
|
86
|
+
# this can be Aspera::Node or Aspera::Rest (shares)
|
48
87
|
env[:node_api]
|
49
|
-
elsif options.get_option(:password,is_type: :mandatory).start_with?('Bearer ')
|
88
|
+
elsif options.get_option(:password, is_type: :mandatory).start_with?('Bearer ')
|
50
89
|
# info is provided like node_info of aoc
|
51
|
-
|
52
|
-
base_url: options.get_option(:url,is_type: :mandatory),
|
90
|
+
Aspera::Node.new(params: {
|
91
|
+
base_url: options.get_option(:url, is_type: :mandatory),
|
53
92
|
headers: {
|
54
|
-
|
55
|
-
'Authorization'
|
93
|
+
Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY => options.get_option(:username, is_type: :mandatory),
|
94
|
+
'Authorization' => options.get_option(:password, is_type: :mandatory)
|
56
95
|
}
|
57
96
|
})
|
58
97
|
else
|
59
98
|
# this is normal case
|
60
|
-
|
99
|
+
Aspera::Node.new(params: {
|
100
|
+
base_url: options.get_option(:url, is_type: :mandatory),
|
101
|
+
auth: {
|
102
|
+
type: :basic,
|
103
|
+
username: options.get_option(:username, is_type: :mandatory),
|
104
|
+
password: options.get_option(:password, is_type: :mandatory)
|
105
|
+
}})
|
61
106
|
end
|
62
107
|
end
|
63
108
|
|
64
109
|
def c_textify_browse(table_data)
|
65
|
-
return table_data.map
|
110
|
+
return table_data.map do |i|
|
111
|
+
i['permissions'] = i['permissions'].map { |x| x['name'] }.join(',')
|
112
|
+
i
|
113
|
+
end
|
66
114
|
end
|
67
115
|
|
68
116
|
# key/value is defined in main in hash_table
|
69
|
-
def c_textify_bool_list_result(list,name_list)
|
117
|
+
def c_textify_bool_list_result(list, name_list)
|
70
118
|
list.each_index do |i|
|
71
119
|
next unless name_list.include?(list[i]['key'])
|
72
120
|
list[i]['value'].each do |item|
|
73
|
-
list.push({'key' => item['name'],'value' => item['value']})
|
121
|
+
list.push({'key' => item['name'], 'value' => item['value']})
|
74
122
|
end
|
75
123
|
list.delete_at(i)
|
76
124
|
# continue at same index because we delete current one
|
@@ -79,7 +127,7 @@ module Aspera
|
|
79
127
|
end
|
80
128
|
|
81
129
|
# reduce the path from a result on given named column
|
82
|
-
def c_result_remove_prefix_path(result,column,path_prefix)
|
130
|
+
def c_result_remove_prefix_path(result, column, path_prefix)
|
83
131
|
if !path_prefix.nil?
|
84
132
|
case result[:type]
|
85
133
|
when :object_list
|
@@ -95,337 +143,368 @@ module Aspera
|
|
95
143
|
end
|
96
144
|
|
97
145
|
# translates paths results into CLI result, and removes prefix
|
98
|
-
def c_result_translate_rem_prefix(
|
99
|
-
|
100
|
-
|
146
|
+
def c_result_translate_rem_prefix(response, type, success_msg, path_prefix)
|
147
|
+
errors = []
|
148
|
+
resres = { data: [], type: :object_list, fields: [type, 'result']}
|
149
|
+
JSON.parse(response[:http].body)['paths'].each do |p|
|
101
150
|
result = success_msg
|
102
|
-
if p.
|
103
|
-
Log.log.error
|
151
|
+
if p.key?('error')
|
152
|
+
Log.log.error{"#{p['error']['user_message']} : #{p['path']}"}
|
104
153
|
result = 'ERROR: ' + p['error']['user_message']
|
154
|
+
errors.push([p['path'], p['error']['user_message']])
|
105
155
|
end
|
106
|
-
resres[:data].push({type => p['path'],'result' => result})
|
156
|
+
resres[:data].push({type => p['path'], 'result' => result})
|
157
|
+
end
|
158
|
+
# one error make all fail
|
159
|
+
unless errors.empty?
|
160
|
+
raise errors.map{|i|"#{i.first}: #{i.last}"}.join(', ')
|
107
161
|
end
|
108
|
-
return c_result_remove_prefix_path(resres,type,path_prefix)
|
162
|
+
return c_result_remove_prefix_path(resres, type, path_prefix)
|
109
163
|
end
|
110
164
|
|
111
165
|
# get path arguments from command line, and add prefix
|
112
|
-
def get_next_arg_add_prefix(path_prefix,name,number=:single)
|
113
|
-
|
114
|
-
return
|
115
|
-
return File.join(path_prefix,
|
116
|
-
return
|
117
|
-
raise StandardError,'expect: nil, String or Array'
|
166
|
+
def get_next_arg_add_prefix(path_prefix, name, number=:single)
|
167
|
+
path_or_list = options.get_next_argument(name, expected: number)
|
168
|
+
return path_or_list if path_prefix.nil?
|
169
|
+
return File.join(path_prefix, path_or_list) if path_or_list.is_a?(String)
|
170
|
+
return path_or_list.map {|p| File.join(path_prefix, p)} if path_or_list.is_a?(Array)
|
171
|
+
raise StandardError, 'expect: nil, String or Array'
|
118
172
|
end
|
119
173
|
|
120
|
-
|
121
|
-
|
122
|
-
COMMON_ACTIONS = %i[browse upload download api_details].concat(SIMPLE_ACTIONS).freeze
|
123
|
-
|
124
|
-
# common API to node and Shares
|
125
|
-
# prefix_path is used to list remote sources in Faspex
|
126
|
-
def execute_simple_common(command,prefix_path)
|
174
|
+
# file and folder related commands
|
175
|
+
def execute_command_gen3(command, prefix_path)
|
127
176
|
case command
|
128
|
-
when :health
|
129
|
-
nagios = Nagios.new
|
130
|
-
begin
|
131
|
-
info = @api_node.read('info')[:data]
|
132
|
-
nagios.add_ok('node api','accessible')
|
133
|
-
nagios.check_time_offset(info['current_time'],'node api')
|
134
|
-
nagios.check_product_version('node api','entsrv', info['version'])
|
135
|
-
rescue StandardError => e
|
136
|
-
nagios.add_critical('node api',e.to_s)
|
137
|
-
end
|
138
|
-
begin
|
139
|
-
@api_node.call(
|
140
|
-
operation: 'POST',
|
141
|
-
subpath: 'services/soap/Transfer-201210',
|
142
|
-
headers: {'Content-Type' => 'text/xml;charset=UTF-8','SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
|
143
|
-
text_body_params: SAMPLE_SOAP_CALL)[:http].body
|
144
|
-
nagios.add_ok('central','accessible by node')
|
145
|
-
rescue StandardError => e
|
146
|
-
nagios.add_critical('central',e.to_s)
|
147
|
-
end
|
148
|
-
return nagios.result
|
149
|
-
when :events
|
150
|
-
events = @api_node.read('events',options.get_option(:value))[:data]
|
151
|
-
return { type: :object_list, data: events}
|
152
|
-
when :info
|
153
|
-
node_info = @api_node.read('info')[:data]
|
154
|
-
return { type: :single_object, data: node_info, textify: lambda { |table_data| c_textify_bool_list_result(table_data,%w[capabilities settings])}}
|
155
|
-
when :license # requires: asnodeadmin -mu <node user> --acl-add=internal --internal
|
156
|
-
node_license = @api_node.read('license')[:data]
|
157
|
-
if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
|
158
|
-
Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal')
|
159
|
-
end
|
160
|
-
return { type: :single_object, data: node_license}
|
161
177
|
when :delete
|
162
|
-
paths_to_delete = get_next_arg_add_prefix(prefix_path,'file list'
|
163
|
-
resp = @api_node.create('files/delete',{ paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : '/' + i} }})
|
164
|
-
return c_result_translate_rem_prefix(resp,'file','deleted',prefix_path)
|
178
|
+
paths_to_delete = get_next_arg_add_prefix(prefix_path, 'file list', :multiple)
|
179
|
+
resp = @api_node.create('files/delete', { paths: paths_to_delete.map{|i| {'path' => i.start_with?('/') ? i : '/' + i} }})
|
180
|
+
return c_result_translate_rem_prefix(resp, 'file', 'deleted', prefix_path)
|
165
181
|
when :search
|
166
|
-
search_root = get_next_arg_add_prefix(prefix_path,'search root')
|
182
|
+
search_root = get_next_arg_add_prefix(prefix_path, 'search root')
|
167
183
|
parameters = {'path' => search_root}
|
168
184
|
other_options = options.get_option(:value)
|
169
185
|
parameters.merge!(other_options) unless other_options.nil?
|
170
|
-
resp = @api_node.create('files/search',parameters)
|
186
|
+
resp = @api_node.create('files/search', parameters)
|
171
187
|
result = { type: :object_list, data: resp[:data]['items']}
|
172
188
|
return Main.result_empty if result[:data].empty?
|
173
189
|
result[:fields] = result[:data].first.keys.reject{|i|SEARCH_REMOVE_FIELDS.include?(i)}
|
174
|
-
|
175
|
-
|
176
|
-
return c_result_remove_prefix_path(result,'path',prefix_path)
|
190
|
+
formatter.display_status("Items: #{resp[:data]['item_count']}/#{resp[:data]['total_count']}")
|
191
|
+
formatter.display_status("params: #{resp[:data]['parameters'].keys.map{|k|"#{k}:#{resp[:data]['parameters'][k]}"}.join(',')}")
|
192
|
+
return c_result_remove_prefix_path(result, 'path', prefix_path)
|
177
193
|
when :space
|
178
|
-
|
179
|
-
path_list = get_next_arg_add_prefix(prefix_path,'folder path or ext.val. list')
|
194
|
+
path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
|
180
195
|
path_list = [path_list] unless path_list.is_a?(Array)
|
181
|
-
resp = @api_node.create('space',{ 'paths' => path_list.map {|i| { path: i} } })
|
196
|
+
resp = @api_node.create('space', { 'paths' => path_list.map {|i| { path: i} } })
|
182
197
|
result = { data: resp[:data]['paths'], type: :object_list}
|
183
|
-
#return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
|
184
|
-
return c_result_remove_prefix_path(result,'path',prefix_path)
|
198
|
+
# return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
|
199
|
+
return c_result_remove_prefix_path(result, 'path', prefix_path)
|
185
200
|
when :mkdir
|
186
|
-
path_list = get_next_arg_add_prefix(prefix_path,'folder path or ext.val. list')
|
201
|
+
path_list = get_next_arg_add_prefix(prefix_path, 'folder path or ext.val. list')
|
187
202
|
path_list = [path_list] unless path_list.is_a?(Array)
|
188
|
-
|
189
|
-
|
190
|
-
resp = @api_node.create('files/create',{ 'paths' => [{ type: :directory, path: path_list }] })
|
191
|
-
return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
|
203
|
+
resp = @api_node.create('files/create', { 'paths' => [{ type: :directory, path: path_list }] })
|
204
|
+
return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
|
192
205
|
when :mklink
|
193
|
-
target = get_next_arg_add_prefix(prefix_path,'target')
|
194
|
-
path_list = get_next_arg_add_prefix(prefix_path,'link path')
|
195
|
-
resp = @api_node.create('files/create',{ 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
|
196
|
-
return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
|
206
|
+
target = get_next_arg_add_prefix(prefix_path, 'target')
|
207
|
+
path_list = get_next_arg_add_prefix(prefix_path, 'link path')
|
208
|
+
resp = @api_node.create('files/create', { 'paths' => [{ type: :symbolic_link, path: path_list, target: { path: target} }] })
|
209
|
+
return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
|
197
210
|
when :mkfile
|
198
|
-
path_list = get_next_arg_add_prefix(prefix_path,'file path')
|
211
|
+
path_list = get_next_arg_add_prefix(prefix_path, 'file path')
|
199
212
|
contents64 = Base64.strict_encode64(options.get_next_argument('contents'))
|
200
|
-
resp = @api_node.create('files/create',{ 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
|
201
|
-
return c_result_translate_rem_prefix(resp,'folder','created',prefix_path)
|
213
|
+
resp = @api_node.create('files/create', { 'paths' => [{ type: :file, path: path_list, contents: contents64 }] })
|
214
|
+
return c_result_translate_rem_prefix(resp, 'folder', 'created', prefix_path)
|
202
215
|
when :rename
|
203
|
-
path_base = get_next_arg_add_prefix(prefix_path,'path_base')
|
204
|
-
path_src = get_next_arg_add_prefix(prefix_path,'path_src')
|
205
|
-
path_dst = get_next_arg_add_prefix(prefix_path,'path_dst')
|
206
|
-
resp = @api_node.create('files/rename',{ 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
|
207
|
-
return c_result_translate_rem_prefix(resp,'entry','moved',prefix_path)
|
216
|
+
path_base = get_next_arg_add_prefix(prefix_path, 'path_base')
|
217
|
+
path_src = get_next_arg_add_prefix(prefix_path, 'path_src')
|
218
|
+
path_dst = get_next_arg_add_prefix(prefix_path, 'path_dst')
|
219
|
+
resp = @api_node.create('files/rename', { 'paths' => [{ 'path' => path_base, 'source' => path_src, 'destination' => path_dst }] })
|
220
|
+
return c_result_translate_rem_prefix(resp, 'entry', 'moved', prefix_path)
|
208
221
|
when :browse
|
209
|
-
|
210
|
-
query = { path: thepath}
|
222
|
+
query = { path: get_next_arg_add_prefix(prefix_path, 'path')}
|
211
223
|
additional_query = options.get_option(:query)
|
212
224
|
query.merge!(additional_query) unless additional_query.nil?
|
213
225
|
send_result = @api_node.create('files/browse', query)[:data]
|
214
|
-
#example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
|
226
|
+
# example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
|
215
227
|
# if there is no items
|
216
228
|
case send_result['self']['type']
|
217
|
-
when 'directory','container' # directory: node, container: shares
|
229
|
+
when 'directory', 'container' # directory: node, container: shares
|
218
230
|
result = { data: send_result['items'], type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
219
|
-
|
231
|
+
formatter.display_status("Items: #{send_result['item_count']}/#{send_result['total_count']}")
|
220
232
|
else # 'file','symbolic_link'
|
221
233
|
result = { data: send_result['self'], type: :single_object}
|
222
|
-
#result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
223
|
-
#raise "unknown type: #{send_result['self']['type']}"
|
234
|
+
# result={ data: [send_result['self']] , type: :object_list, textify: lambda { |table_data| c_textify_browse(table_data) } }
|
235
|
+
# raise "unknown type: #{send_result['self']['type']}"
|
224
236
|
end
|
225
|
-
return c_result_remove_prefix_path(result,'path',prefix_path)
|
226
|
-
when :upload
|
237
|
+
return c_result_remove_prefix_path(result, 'path', prefix_path)
|
238
|
+
when :upload, :download
|
227
239
|
token_type = options.get_option(:token_type)
|
228
240
|
# nil if Shares 1.x
|
229
241
|
token_type = :aspera if token_type.nil?
|
230
242
|
case token_type
|
231
|
-
when :aspera
|
243
|
+
when :aspera, :hybrid
|
232
244
|
# empty transfer spec for authorization request
|
233
|
-
request_transfer_spec={}
|
245
|
+
request_transfer_spec = {}
|
234
246
|
# set requested paths depending on direction
|
235
247
|
request_transfer_spec[:paths] = command.eql?(:download) ? transfer.ts_source_paths : [{ destination: transfer.destination_folder('send') }]
|
236
248
|
# add fixed parameters if any (for COS)
|
237
|
-
request_transfer_spec.
|
249
|
+
@api_node.add_tspec_info(request_transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
238
250
|
# prepare payload for single request
|
239
|
-
setup_payload={transfer_requests: [{transfer_request: request_transfer_spec}]}
|
251
|
+
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
240
252
|
# only one request, so only one answer
|
241
|
-
transfer_spec = @api_node.create("files/#{command}_setup",setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
253
|
+
transfer_spec = @api_node.create("files/#{command}_setup", setup_payload)[:data]['transfer_specs'].first['transfer_spec']
|
242
254
|
# delete this part, as the returned value contains only destination, and not sources
|
243
255
|
transfer_spec.delete('paths') if command.eql?(:upload)
|
244
256
|
when :basic
|
245
257
|
raise 'shall have auth' unless @api_node.params[:auth].is_a?(Hash)
|
246
258
|
raise 'shall be basic auth' unless @api_node.params[:auth][:type].eql?(:basic)
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
end
|
253
|
-
transfer_spec = {
|
254
|
-
'remote_host' => URI.parse(@api_node.params[:base_url]).host,
|
255
|
-
'remote_user' => Aspera::Fasp::TransferSpec::ACCESS_KEY_TRANSFER_USER,
|
256
|
-
'ssh_port' => Aspera::Fasp::TransferSpec::SSH_PORT,
|
257
|
-
'direction' => ts_direction,
|
258
|
-
'destination_root' => transfer.destination_folder(ts_direction)
|
259
|
-
}.deep_merge(@add_request_param)
|
259
|
+
transfer_spec = {}.merge(Aspera::Fasp::TransferSpec::AK_TSPEC_BASE)
|
260
|
+
transfer_spec['remote_host'] = URI.parse(@api_node.params[:base_url]).host
|
261
|
+
Fasp::TransferSpec.action_to_direction(transfer_spec, command)
|
262
|
+
transfer_spec['destination_root'] = transfer.destination_folder(transfer_spec['direction'])
|
263
|
+
@api_node.add_tspec_info(transfer_spec) if @api_node.respond_to?(:add_tspec_info)
|
260
264
|
else raise "ERROR: token_type #{tt}"
|
261
265
|
end
|
262
266
|
if %i[basic hybrid].include?(token_type)
|
263
|
-
|
267
|
+
@api_node.ts_basic_token(transfer_spec)
|
268
|
+
end
|
269
|
+
return Main.result_transfer(transfer.start(transfer_spec))
|
270
|
+
end
|
271
|
+
raise 'INTERNAL ERROR'
|
272
|
+
end
|
273
|
+
|
274
|
+
# common API to node and Shares
|
275
|
+
# prefix_path is used to list remote sources in Faspex
|
276
|
+
def execute_simple_common(command, prefix_path)
|
277
|
+
case command
|
278
|
+
when *COMMANDS_GEN3
|
279
|
+
execute_command_gen3(command, prefix_path)
|
280
|
+
when :access_key
|
281
|
+
ak_command = options.get_next_command([:do].concat(Plugin::ALL_OPS))
|
282
|
+
case ak_command
|
283
|
+
when *Plugin::ALL_OPS then return entity_command(ak_command, @api_node, 'access_keys', id_default: 'self')
|
284
|
+
when :do
|
285
|
+
access_key = options.get_next_argument('access key id')
|
286
|
+
ak_info = @api_node.read("access_keys/#{access_key}")[:data]
|
287
|
+
# change API credentials if different access key
|
288
|
+
if !access_key.eql?('self')
|
289
|
+
@api_node.params[:auth][:username] = ak_info['id']
|
290
|
+
@api_node.params[:auth][:password] = config.lookup_secret(url: @api_node.params[:base_url], username: ak_info['id'], mandatory: true)
|
291
|
+
end
|
292
|
+
command_repo = options.get_next_command(COMMANDS_GEN4)
|
293
|
+
return execute_command_gen4(command_repo, ak_info['root_file_id'])
|
294
|
+
end
|
295
|
+
when :health
|
296
|
+
nagios = Nagios.new
|
297
|
+
begin
|
298
|
+
info = @api_node.read('info')[:data]
|
299
|
+
nagios.add_ok('node api', 'accessible')
|
300
|
+
nagios.check_time_offset(info['current_time'], 'node api')
|
301
|
+
nagios.check_product_version('node api', 'entsrv', info['version'])
|
302
|
+
rescue StandardError => e
|
303
|
+
nagios.add_critical('node api', e.to_s)
|
304
|
+
end
|
305
|
+
begin
|
306
|
+
@api_node.call(
|
307
|
+
operation: 'POST',
|
308
|
+
subpath: 'services/soap/Transfer-201210',
|
309
|
+
headers: {'Content-Type' => 'text/xml;charset=UTF-8', 'SOAPAction' => 'FASPSessionNET-200911#GetSessionInfo'},
|
310
|
+
text_body_params: CENTRAL_SOAP_API_TEST)[:http].body
|
311
|
+
nagios.add_ok('central', 'accessible by node')
|
312
|
+
rescue StandardError => e
|
313
|
+
nagios.add_critical('central', e.to_s)
|
314
|
+
end
|
315
|
+
return nagios.result
|
316
|
+
when :events
|
317
|
+
events = @api_node.read('events', options.get_option(:value))[:data]
|
318
|
+
return { type: :object_list, data: events}
|
319
|
+
when :info
|
320
|
+
nd_info = @api_node.read('info')[:data]
|
321
|
+
return { type: :single_object, data: nd_info, textify: lambda { |table_data| c_textify_bool_list_result(table_data, %w[capabilities settings])}}
|
322
|
+
when :license
|
323
|
+
# requires: asnodeadmin -mu <node user> --acl-add=internal --internal
|
324
|
+
node_license = @api_node.read('license')[:data]
|
325
|
+
if node_license['failure'].is_a?(String) && node_license['failure'].include?('ACL')
|
326
|
+
Log.log.error('server must have: asnodeadmin -mu <node user> --acl-add=internal --internal')
|
264
327
|
end
|
265
|
-
return
|
328
|
+
return { type: :single_object, data: node_license}
|
266
329
|
when :api_details
|
267
330
|
return { type: :single_object, data: @api_node.params }
|
268
331
|
end
|
269
332
|
end
|
270
333
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
334
|
+
def execute_node_gen4_file_command(command_node_file, top_file_id)
|
335
|
+
file_path = options.get_option(:path)
|
336
|
+
apifid =
|
337
|
+
if file_path.nil?
|
338
|
+
{api: @api_node, file_id: instance_identifier}
|
339
|
+
else
|
340
|
+
@api_node.resolve_api_fid(top_file_id, file_path) # TODO: allow follow link ?
|
341
|
+
end
|
342
|
+
case command_node_file
|
343
|
+
when :show
|
344
|
+
items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
345
|
+
return {type: :single_object, data: items}
|
346
|
+
when :modify
|
347
|
+
update_param = options.get_next_argument('update data', type: Hash)
|
348
|
+
apifid[:api].update("files/#{apifid[:file_id]}", update_param)[:data]
|
349
|
+
return Main.result_status('Done')
|
350
|
+
when :permission
|
351
|
+
command_perm = options.get_next_command(%i[list create delete])
|
352
|
+
case command_perm
|
353
|
+
when :list
|
354
|
+
# generic options : TODO: as arg ? option_url_query
|
355
|
+
list_options ||= {'include' => [Rest::ARRAY_PARAMS, 'access_level', 'permission_count']}
|
356
|
+
# add which one to get
|
357
|
+
list_options['file_id'] = apifid[:file_id]
|
358
|
+
list_options['inherited'] ||= false
|
359
|
+
items = apifid[:api].read('permissions', list_options)[:data]
|
360
|
+
return {type: :object_list, data: items}
|
361
|
+
when :delete
|
362
|
+
perm_id = instance_identifier
|
363
|
+
return do_bulk_operation(perm_id, 'deleted') do |one_id|
|
364
|
+
# TODO: notify event ?
|
365
|
+
apifid[:api].delete("permissions/#{perm_id}")
|
366
|
+
{'id' => one_id}
|
367
|
+
end
|
368
|
+
when :create
|
369
|
+
create_param = options.get_next_argument('creation data', type: Hash)
|
370
|
+
raise 'no file_id' if create_param.key?('file_id')
|
371
|
+
create_param['file_id'] = apifid[:file_id]
|
372
|
+
create_param['access_levels'] = Aspera::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
|
373
|
+
# add application specific tags (AoC)
|
374
|
+
the_app = apifid[:api].app_info
|
375
|
+
the_app[:api].permissions_create_params(create_param: create_param, app_info: the_app) unless the_app.nil?
|
376
|
+
# create permission
|
377
|
+
created_data = apifid[:api].create('permissions', create_param)[:data]
|
378
|
+
# bnotify application of creation
|
379
|
+
the_app[:api].permissions_create_event(created_data: created_data, app_info: the_app) unless the_app.nil?
|
380
|
+
return { type: :single_object, data: created_data}
|
381
|
+
else raise "internal error:shall not reach here (#{command_perm})"
|
382
|
+
end
|
383
|
+
else raise "internal error:shall not reach here (#{command_node_file})"
|
384
|
+
end
|
279
385
|
end
|
280
386
|
|
281
|
-
|
282
|
-
|
283
|
-
def execute_node_gen4_command(command_repo, top_file_id)
|
284
|
-
#@api_node
|
387
|
+
def execute_command_gen4(command_repo, top_file_id)
|
285
388
|
case command_repo
|
286
|
-
when :
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
389
|
+
when :v3
|
390
|
+
# NOTE: other common actions are unauthorized with user scope
|
391
|
+
command_legacy = options.get_next_command(V3_IN_V4_ACTIONS)
|
392
|
+
# TODO: shall we support all methods here ? what if there is a link ?
|
393
|
+
apifid = @api_node.resolve_api_fid(top_file_id, '')
|
394
|
+
return Node.new(@agents.merge(skip_basic_auth_options: true, skip_node_options: true, node_api: apifid[:api])).execute_action(command_legacy)
|
395
|
+
when :node_info, :bearer_token_node
|
396
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
397
|
+
result = {
|
398
|
+
url: apifid[:api].params[:base_url],
|
399
|
+
root_id: apifid[:file_id]
|
400
|
+
}
|
401
|
+
raise 'No auth for node' if apifid[:api].params[:auth].nil?
|
402
|
+
case apifid[:api].params[:auth][:type]
|
403
|
+
when :basic
|
404
|
+
result[:username] = apifid[:api].params[:auth][:username]
|
405
|
+
result[:password] = apifid[:api].params[:auth][:password]
|
406
|
+
when :oauth2
|
407
|
+
result[:username] = apifid[:api].params[:headers][Aspera::Node::HEADER_X_ASPERA_ACCESS_KEY]
|
408
|
+
result[:password] = apifid[:api].oauth_token
|
409
|
+
else raise 'unknown'
|
410
|
+
end
|
411
|
+
return {type: :single_object, data: result} if command_repo.eql?(:node_info)
|
412
|
+
raise 'not bearer token' unless result[:password].start_with?('Bearer ')
|
413
|
+
return Main.result_status(result[:password])
|
296
414
|
when :browse
|
297
|
-
|
298
|
-
apifid = resolve_api_fid(top_file_id,thepath)
|
415
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
299
416
|
file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
300
417
|
if file_info['type'].eql?('folder')
|
301
|
-
result = apifid[:api].read("files/#{apifid[:file_id]}/files",options.get_option(:value))
|
418
|
+
result = apifid[:api].read("files/#{apifid[:file_id]}/files", options.get_option(:value))
|
302
419
|
items = result[:data]
|
303
|
-
|
420
|
+
formatter.display_status("Items: #{result[:data].length}/#{result[:http]['X-Total-Count']}")
|
304
421
|
else
|
305
422
|
items = [file_info]
|
306
423
|
end
|
307
|
-
return {type: :object_list,data: items,fields: %w[name type recursive_size size modified_time access_level]}
|
424
|
+
return {type: :object_list, data: items, fields: %w[name type recursive_size size modified_time access_level]}
|
308
425
|
when :find
|
309
|
-
|
310
|
-
apifid = resolve_api_fid(top_file_id,thepath)
|
426
|
+
apifid = @api_node.resolve_api_fid(top_file_id, options.get_next_argument('path'))
|
311
427
|
test_block = Aspera::Node.file_matcher(options.get_option(:value))
|
312
|
-
return {type: :object_list,data:
|
428
|
+
return {type: :object_list, data: @api_node.find_files(apifid[:file_id], test_block), fields: ['path']}
|
313
429
|
when :mkdir
|
314
|
-
|
315
|
-
containing_folder_path = thepath.split(AoC::PATH_SEPARATOR)
|
430
|
+
containing_folder_path = options.get_next_argument('path').split(Aspera::Node::PATH_SEPARATOR)
|
316
431
|
new_folder = containing_folder_path.pop
|
317
|
-
apifid = resolve_api_fid(top_file_id,containing_folder_path.join(
|
318
|
-
result = apifid[:api].create("files/#{apifid[:file_id]}/files",{name: new_folder,type: :folder})[:data]
|
432
|
+
apifid = @api_node.resolve_api_fid(top_file_id, containing_folder_path.join(Aspera::Node::PATH_SEPARATOR))
|
433
|
+
result = apifid[:api].create("files/#{apifid[:file_id]}/files", {name: new_folder, type: :folder})[:data]
|
319
434
|
return Main.result_status("created: #{result['name']} (id=#{result['id']})")
|
320
435
|
when :rename
|
321
|
-
|
436
|
+
file_path = options.get_next_argument('source path')
|
322
437
|
newname = options.get_next_argument('new name')
|
323
|
-
apifid = resolve_api_fid(top_file_id,
|
324
|
-
result = apifid[:api].update("files/#{apifid[:file_id]}",{name: newname})[:data]
|
325
|
-
return Main.result_status("renamed #{
|
438
|
+
apifid = @api_node.resolve_api_fid(top_file_id, file_path)
|
439
|
+
result = apifid[:api].update("files/#{apifid[:file_id]}", {name: newname})[:data]
|
440
|
+
return Main.result_status("renamed #{file_path} to #{newname}")
|
326
441
|
when :delete
|
327
|
-
|
328
|
-
return do_bulk_operation(thepath,'deleted','path') do |l_path|
|
442
|
+
return do_bulk_operation(options.get_next_argument('path'), 'deleted', id_result: 'path') do |l_path|
|
329
443
|
raise "expecting String (path), got #{l_path.class.name} (#{l_path})" unless l_path.is_a?(String)
|
330
|
-
apifid = resolve_api_fid(top_file_id,l_path)
|
444
|
+
apifid = @api_node.resolve_api_fid(top_file_id, l_path)
|
331
445
|
result = apifid[:api].delete("files/#{apifid[:file_id]}")[:data]
|
332
446
|
{'path' => l_path}
|
333
447
|
end
|
448
|
+
when :sync
|
449
|
+
# remote is specified by option to_folder
|
450
|
+
apifid = @api_node.resolve_api_fid(top_file_id, transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
|
451
|
+
transfer_spec = apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)
|
452
|
+
Log.dump(:ts, transfer_spec)
|
453
|
+
sync_plugin = Plugins::Sync.new(@agents, transfer_spec: transfer_spec)
|
454
|
+
return sync_plugin.execute_action
|
334
455
|
when :upload
|
335
|
-
apifid = resolve_api_fid(top_file_id,transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
|
336
|
-
|
337
|
-
return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_SEND,apifid,add_ts))
|
456
|
+
apifid = @api_node.resolve_api_fid(top_file_id, transfer.destination_folder(Fasp::TransferSpec::DIRECTION_SEND))
|
457
|
+
return Main.result_transfer(transfer.start(apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_SEND)))
|
338
458
|
when :download
|
339
459
|
source_paths = transfer.ts_source_paths
|
340
460
|
# special case for AoC : all files must be in same folder
|
341
461
|
source_folder = source_paths.shift['source']
|
342
462
|
# if a single file: split into folder and path
|
463
|
+
apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
|
343
464
|
if source_paths.empty?
|
344
|
-
|
345
|
-
|
346
|
-
|
465
|
+
file_info = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
466
|
+
case file_info['type']
|
467
|
+
when 'file'
|
468
|
+
# if the single source is a file, we need to split into folder path and filename
|
469
|
+
src_dir_elements = source_folder.split(Aspera::Node::PATH_SEPARATOR)
|
470
|
+
# filename is the last one
|
471
|
+
source_paths = [{'source' => src_dir_elements.pop}]
|
472
|
+
# source folder is what remains
|
473
|
+
source_folder = src_dir_elements.join(Aspera::Node::PATH_SEPARATOR)
|
474
|
+
# TODO: instead of creating a new object, use the same, and change file id with parent folder id ? possible ?
|
475
|
+
apifid = @api_node.resolve_api_fid(top_file_id, source_folder)
|
476
|
+
when 'link', 'folder'
|
477
|
+
# single source is 'folder' or 'link'
|
478
|
+
# TODO: add this ? , 'destination'=>file_info['name']
|
479
|
+
source_paths = [{'source' => '.'}]
|
480
|
+
else
|
481
|
+
raise "Unknown source type: #{file_info['type']}"
|
482
|
+
end
|
347
483
|
end
|
348
|
-
apifid
|
349
|
-
# override paths with just filename
|
350
|
-
add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{apifid[:node_info]['id']}:#{apifid[:file_id]}"}}}}
|
351
|
-
add_ts['paths'] = source_paths
|
352
|
-
return Main.result_transfer(transfer_start(AoC::FILES_APP,Fasp::TransferSpec::DIRECTION_RECEIVE,apifid,add_ts))
|
484
|
+
return Main.result_transfer(transfer.start(apifid[:api].transfer_spec_gen4(apifid[:file_id], Fasp::TransferSpec::DIRECTION_RECEIVE, {'paths'=>source_paths})))
|
353
485
|
when :http_node_download
|
354
486
|
source_paths = transfer.ts_source_paths
|
355
487
|
source_folder = source_paths.shift['source']
|
356
488
|
if source_paths.empty?
|
357
|
-
source_folder = source_folder.split(
|
489
|
+
source_folder = source_folder.split(Aspera::Node::PATH_SEPARATOR)
|
358
490
|
source_paths = [{'source' => source_folder.pop}]
|
359
|
-
source_folder = source_folder.join(
|
491
|
+
source_folder = source_folder.join(Aspera::Node::PATH_SEPARATOR)
|
360
492
|
end
|
361
|
-
raise CliBadArgument,'one file at a time only in HTTP mode' if source_paths.length > 1
|
493
|
+
raise CliBadArgument, 'one file at a time only in HTTP mode' if source_paths.length > 1
|
362
494
|
file_name = source_paths.first['source']
|
363
|
-
apifid = resolve_api_fid(top_file_id,File.join(source_folder,file_name))
|
495
|
+
apifid = @api_node.resolve_api_fid(top_file_id, File.join(source_folder, file_name))
|
364
496
|
apifid[:api].call(
|
365
497
|
operation: 'GET',
|
366
498
|
subpath: "files/#{apifid[:file_id]}/content",
|
367
|
-
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE),file_name))
|
499
|
+
save_to_file: File.join(transfer.destination_folder(Fasp::TransferSpec::DIRECTION_RECEIVE), file_name))
|
368
500
|
return Main.result_status("downloaded: #{file_name}")
|
369
|
-
when :permission
|
370
|
-
command_perm = options.get_next_command(%i[list create])
|
371
|
-
case command_perm
|
372
|
-
when :list
|
373
|
-
# generic options : TODO: as arg ? option_url_query
|
374
|
-
list_options ||= {'include' => ['[]','access_level','permission_count']}
|
375
|
-
# special value: ALL will show all permissions
|
376
|
-
if !VAL_ALL.eql?(apifid[:file_id])
|
377
|
-
# add which one to get
|
378
|
-
list_options['file_id'] = apifid[:file_id]
|
379
|
-
list_options['inherited'] ||= false
|
380
|
-
end
|
381
|
-
items = apifid[:api].read('permissions',list_options)[:data]
|
382
|
-
return {type: :object_list,data: items}
|
383
|
-
when :create
|
384
|
-
#create_param=self.options.get_next_argument('creation data (Hash)')
|
385
|
-
set_workspace_info
|
386
|
-
access_id = "#{ID_AK_ADMIN}_WS_#{@workspace_id}"
|
387
|
-
apifid[:node_info]
|
388
|
-
params = {
|
389
|
-
'file_id' => apifid[:file_id], # mandatory
|
390
|
-
'access_type' => 'user', # mandatory: user or group
|
391
|
-
'access_id' => access_id, # id of user or group
|
392
|
-
'access_levels' => Aspera::Node::ACCESS_LEVELS,
|
393
|
-
'tags' => {'aspera' => {'files' => {'workspace' => {
|
394
|
-
'id' => @workspace_id,
|
395
|
-
'workspace_name' => @workspace_name,
|
396
|
-
'user_name' => aoc_api.user_info['name'],
|
397
|
-
'shared_by_user_id' => aoc_api.user_info['id'],
|
398
|
-
'shared_by_name' => aoc_api.user_info['name'],
|
399
|
-
'shared_by_email' => aoc_api.user_info['email'],
|
400
|
-
'shared_with_name' => access_id,
|
401
|
-
'access_key' => apifid[:node_info]['access_key'],
|
402
|
-
'node' => apifid[:node_info]['name']}}}}}
|
403
|
-
item = apifid[:api].create('permissions',params)[:data]
|
404
|
-
return {type: :single_object,data: item}
|
405
|
-
else raise "internal error:shall not reach here (#{command_perm})"
|
406
|
-
end
|
407
501
|
when :file
|
408
|
-
command_node_file = options.get_next_command(%i[show modify])
|
409
|
-
|
410
|
-
|
411
|
-
if !file_path.nil?
|
412
|
-
resolve_api_fid(top_file_id,file_path) # TODO: allow follow link ?
|
413
|
-
else
|
414
|
-
{node_info: top_file_id[:node_info],file_id: instance_identifier}
|
415
|
-
end
|
416
|
-
case command_node_file
|
417
|
-
when :show
|
418
|
-
items = apifid[:api].read("files/#{apifid[:file_id]}")[:data]
|
419
|
-
return {type: :single_object,data: items}
|
420
|
-
when :modify
|
421
|
-
update_param = options.get_next_argument('update data (Hash)')
|
422
|
-
res = apifid[:api].update("files/#{apifid[:file_id]}",update_param)[:data]
|
423
|
-
return {type: :single_object,data: res}
|
424
|
-
else raise "internal error:shall not reach here (#{command_node_file})"
|
425
|
-
end
|
502
|
+
command_node_file = options.get_next_command(%i[show modify permission])
|
503
|
+
return execute_node_gen4_file_command(command_node_file, top_file_id)
|
504
|
+
else raise "INTERNAL ERROR: no case for #{command_repo}"
|
426
505
|
end # command_repo
|
427
|
-
raise '
|
428
|
-
end #
|
506
|
+
# raise 'INTERNAL ERROR: missing return'
|
507
|
+
end # execute_command_gen4
|
429
508
|
|
430
509
|
# This is older API
|
431
510
|
def execute_async
|
@@ -434,7 +513,7 @@ module Aspera
|
|
434
513
|
asyncname = options.get_option(:sync_name)
|
435
514
|
if asyncname.nil?
|
436
515
|
asyncid = instance_identifier
|
437
|
-
if asyncid.eql?(
|
516
|
+
if asyncid.eql?(VAL_ALL) && %i[show delete].include?(command)
|
438
517
|
asyncids = @api_node.read('async/list')[:data]['sync_ids']
|
439
518
|
else
|
440
519
|
Integer(asyncid) # must be integer
|
@@ -442,7 +521,7 @@ module Aspera
|
|
442
521
|
end
|
443
522
|
else
|
444
523
|
asyncids = @api_node.read('async/list')[:data]['sync_ids']
|
445
|
-
summaries = @api_node.create('async/summary',{'syncs' => asyncids})[:data]['sync_summaries']
|
524
|
+
summaries = @api_node.create('async/summary', {'syncs' => asyncids})[:data]['sync_summaries']
|
446
525
|
selected = summaries.find{|s|s['name'].eql?(asyncname)}
|
447
526
|
raise "no such sync: #{asyncname}" if selected.nil?
|
448
527
|
asyncid = selected['snid']
|
@@ -455,16 +534,16 @@ module Aspera
|
|
455
534
|
resp = @api_node.read('async/list')[:data]['sync_ids']
|
456
535
|
return { type: :value_list, data: resp, name: 'id' }
|
457
536
|
when :show
|
458
|
-
resp = @api_node.create('async/summary',pdata)[:data]['sync_summaries']
|
537
|
+
resp = @api_node.create('async/summary', pdata)[:data]['sync_summaries']
|
459
538
|
return Main.result_empty if resp.empty?
|
460
|
-
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?(
|
539
|
+
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?(VAL_ALL)
|
461
540
|
return { type: :single_object, data: resp.first }
|
462
541
|
when :delete
|
463
|
-
resp = @api_node.create('async/delete',pdata)[:data]
|
542
|
+
resp = @api_node.create('async/delete', pdata)[:data]
|
464
543
|
return { type: :single_object, data: resp, name: 'id' }
|
465
544
|
when :bandwidth
|
466
545
|
pdata['seconds'] = 100 # TODO: as parameter with --value
|
467
|
-
resp = @api_node.create('async/bandwidth',pdata)[:data]
|
546
|
+
resp = @api_node.create('async/bandwidth', pdata)[:data]
|
468
547
|
data = resp['bandwidth_data']
|
469
548
|
return Main.result_empty if data.empty?
|
470
549
|
data = data.first[asyncid]['data']
|
@@ -476,19 +555,19 @@ module Aspera
|
|
476
555
|
# status int
|
477
556
|
filter = options.get_option(:value)
|
478
557
|
pdata.merge!(filter) unless filter.nil?
|
479
|
-
resp = @api_node.create('async/files',pdata)[:data]
|
558
|
+
resp = @api_node.create('async/files', pdata)[:data]
|
480
559
|
data = resp['sync_files']
|
481
560
|
data = data.first[asyncid] unless data.empty?
|
482
561
|
iteration_data = []
|
483
562
|
skip_ids_persistency = nil
|
484
|
-
if options.get_option(:once_only,is_type: :mandatory)
|
563
|
+
if options.get_option(:once_only, is_type: :mandatory)
|
485
564
|
skip_ids_persistency = PersistencyActionOnce.new(
|
486
565
|
manager: @agents[:persistency],
|
487
566
|
data: iteration_data,
|
488
567
|
id: IdGenerator.from_list([
|
489
568
|
'sync_files',
|
490
|
-
options.get_option(:url,is_type: :mandatory),
|
491
|
-
options.get_option(:username,is_type: :mandatory),
|
569
|
+
options.get_option(:url, is_type: :mandatory),
|
570
|
+
options.get_option(:username, is_type: :mandatory),
|
492
571
|
asyncid]))
|
493
572
|
unless iteration_data.first.nil?
|
494
573
|
data.select!{|l| l['fnid'].to_i > iteration_data.first}
|
@@ -499,53 +578,59 @@ module Aspera
|
|
499
578
|
skip_ids_persistency&.save
|
500
579
|
return { type: :object_list, data: data, name: 'id' }
|
501
580
|
when :counters
|
502
|
-
resp = @api_node.create('async/counters',pdata)[:data]['sync_counters'].first[asyncid].last
|
581
|
+
resp = @api_node.create('async/counters', pdata)[:data]['sync_counters'].first[asyncid].last
|
503
582
|
return Main.result_empty if resp.nil?
|
504
583
|
return { type: :single_object, data: resp }
|
505
584
|
end
|
506
585
|
end
|
507
586
|
|
508
|
-
ACTIONS = %i[
|
509
|
-
|
587
|
+
ACTIONS = %i[
|
588
|
+
async
|
589
|
+
sync
|
590
|
+
stream
|
591
|
+
transfer
|
592
|
+
service
|
593
|
+
watch_folder
|
594
|
+
central
|
595
|
+
asperabrowser
|
596
|
+
basic_token].concat(COMMON_ACTIONS).freeze
|
510
597
|
|
511
|
-
def execute_action(command=nil,prefix_path=nil)
|
598
|
+
def execute_action(command=nil, prefix_path=nil)
|
512
599
|
command ||= options.get_next_command(ACTIONS)
|
513
600
|
case command
|
514
|
-
when *COMMON_ACTIONS then return execute_simple_common(command,prefix_path)
|
601
|
+
when *COMMON_ACTIONS then return execute_simple_common(command, prefix_path)
|
515
602
|
when :async then return execute_async
|
516
603
|
when :sync
|
517
|
-
|
604
|
+
# newer api
|
605
|
+
sync_command = options.get_next_command(%i[bandwidth counters files start state stop summary].concat(Plugin::ALL_OPS) - %i[modify])
|
518
606
|
case sync_command
|
519
|
-
when *Plugin::ALL_OPS then return entity_command(sync_command
|
607
|
+
when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids')
|
520
608
|
else
|
521
609
|
parameters = options.get_option(:value)
|
522
|
-
asyncs_id=instance_identifier
|
610
|
+
asyncs_id = instance_identifier
|
523
611
|
if %i[start stop].include?(sync_command)
|
524
|
-
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}",parameters)
|
612
|
+
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
|
525
613
|
return Main.result_status('ok')
|
526
614
|
end
|
527
|
-
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}",parameters)[:data] }
|
615
|
+
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
|
528
616
|
end
|
529
617
|
when :stream
|
530
618
|
command = options.get_next_command(%i[list create show modify cancel])
|
531
619
|
case command
|
532
620
|
when :list
|
533
|
-
resp = @api_node.read('ops/transfers',options.get_option(:value))
|
621
|
+
resp = @api_node.read('ops/transfers', options.get_option(:value))
|
534
622
|
return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
|
535
623
|
when :create
|
536
|
-
resp = @api_node.create('streams',options.get_option(:value,is_type: :mandatory))
|
624
|
+
resp = @api_node.create('streams', options.get_option(:value, is_type: :mandatory))
|
537
625
|
return { type: :single_object, data: resp[:data] }
|
538
626
|
when :show
|
539
|
-
|
540
|
-
resp = @api_node.read('ops/transfers/' + trid)
|
627
|
+
resp = @api_node.read("ops/transfers/#{options.get_next_argument('transfer id')}")
|
541
628
|
return { type: :other_struct, data: resp[:data] }
|
542
629
|
when :modify
|
543
|
-
|
544
|
-
resp = @api_node.update('streams/' + trid,options.get_option(:value,is_type: :mandatory))
|
630
|
+
resp = @api_node.update("streams/#{options.get_next_argument('transfer id')}", options.get_option(:value, is_type: :mandatory))
|
545
631
|
return { type: :other_struct, data: resp[:data] }
|
546
632
|
when :cancel
|
547
|
-
|
548
|
-
resp = @api_node.cancel('streams/' + trid)
|
633
|
+
resp = @api_node.cancel("streams/#{options.get_next_argument('transfer id')}")
|
549
634
|
return { type: :other_struct, data: resp[:data] }
|
550
635
|
else
|
551
636
|
raise 'error'
|
@@ -560,9 +645,9 @@ module Aspera
|
|
560
645
|
case command
|
561
646
|
when :list
|
562
647
|
# could use ? subpath: 'transfers'
|
563
|
-
query=options.get_option(:value) || options.get_option(:query)
|
648
|
+
query = options.get_option(:value) || options.get_option(:query)
|
564
649
|
raise 'Query must be a Hash' unless query.nil? || query.is_a?(Hash)
|
565
|
-
resp = @api_node.read(res_class_path,query)
|
650
|
+
resp = @api_node.read(res_class_path, query)
|
566
651
|
return {
|
567
652
|
type: :object_list,
|
568
653
|
data: resp[:data],
|
@@ -577,22 +662,6 @@ module Aspera
|
|
577
662
|
else
|
578
663
|
raise 'error'
|
579
664
|
end
|
580
|
-
when :access_key
|
581
|
-
ak_command = options.get_next_command([Plugin::ALL_OPS,:do].flatten)
|
582
|
-
case ak_command
|
583
|
-
when *Plugin::ALL_OPS then return entity_command(ak_command,@api_node,'access_keys',id_default: 'self')
|
584
|
-
when :do
|
585
|
-
access_key = options.get_next_argument('access key id')
|
586
|
-
ak_info=@api_node.read("access_keys/#{access_key}")[:data]
|
587
|
-
# change API if needed
|
588
|
-
if !access_key.eql?('self')
|
589
|
-
secret=config.vault.get(username: access_key)[:secret] #, url: @api_node.params[:base_url] : TODO: better handle vault
|
590
|
-
@api_node.params[:auth][:username]=access_key
|
591
|
-
@api_node.params[:auth][:password]=secret
|
592
|
-
end
|
593
|
-
command_repo = options.get_next_command(NODE4_COMMANDS)
|
594
|
-
return execute_node_gen4_command(command_repo,ak_info['root_file_id'])
|
595
|
-
end
|
596
665
|
when :service
|
597
666
|
command = options.get_next_command(%i[list create delete])
|
598
667
|
if [:delete].include?(command)
|
@@ -605,7 +674,7 @@ module Aspera
|
|
605
674
|
when :create
|
606
675
|
# @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
|
607
676
|
params = options.get_next_argument('Run creation data (structure)')
|
608
|
-
resp = @api_node.create('rund/services',params)
|
677
|
+
resp = @api_node.create('rund/services', params)
|
609
678
|
return Main.result_status("#{resp[:data]['id']} created")
|
610
679
|
when :delete
|
611
680
|
@api_node.delete("rund/services/#{svcid}")
|
@@ -623,15 +692,15 @@ module Aspera
|
|
623
692
|
@api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
|
624
693
|
case command
|
625
694
|
when :create
|
626
|
-
resp = @api_node.create(res_class_path,options.get_option(:value,is_type: :mandatory))
|
695
|
+
resp = @api_node.create(res_class_path, options.get_option(:value, is_type: :mandatory))
|
627
696
|
return Main.result_status("#{resp[:data]['id']} created")
|
628
697
|
when :list
|
629
|
-
resp = @api_node.read(res_class_path,options.get_option(:value))
|
698
|
+
resp = @api_node.read(res_class_path, options.get_option(:value))
|
630
699
|
return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
|
631
700
|
when :show
|
632
701
|
return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
|
633
702
|
when :modify
|
634
|
-
@api_node.update(one_res_path,options.get_option(:value,is_type: :mandatory))
|
703
|
+
@api_node.update(one_res_path, options.get_option(:value, is_type: :mandatory))
|
635
704
|
return Main.result_status("#{one_res_id} updated")
|
636
705
|
when :delete
|
637
706
|
@api_node.delete(one_res_path)
|
@@ -651,7 +720,7 @@ module Aspera
|
|
651
720
|
case command
|
652
721
|
when :list
|
653
722
|
request_data.deep_merge!({'validation' => validation}) unless validation.nil?
|
654
|
-
resp = @api_node.create('services/rest/transfers/v1/sessions',request_data)
|
723
|
+
resp = @api_node.create('services/rest/transfers/v1/sessions', request_data)
|
655
724
|
return {
|
656
725
|
type: :object_list,
|
657
726
|
data: resp[:data]['session_info_result']['session_info'],
|
@@ -663,28 +732,28 @@ module Aspera
|
|
663
732
|
case command
|
664
733
|
when :list
|
665
734
|
request_data.deep_merge!({'validation' => validation}) unless validation.nil?
|
666
|
-
resp = @api_node.create('services/rest/transfers/v1/files',request_data)[:data]
|
735
|
+
resp = @api_node.create('services/rest/transfers/v1/files', request_data)[:data]
|
667
736
|
resp = JSON.parse(resp) if resp.is_a?(String)
|
668
|
-
Log.dump(:resp,resp)
|
737
|
+
Log.dump(:resp, resp)
|
669
738
|
return { type: :object_list, data: resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path]}
|
670
739
|
when :modify
|
671
740
|
request_data.deep_merge!(validation) unless validation.nil?
|
672
|
-
@api_node.update('services/rest/transfers/v1/files',request_data)
|
741
|
+
@api_node.update('services/rest/transfers/v1/files', request_data)
|
673
742
|
return Main.result_status('updated')
|
674
743
|
end
|
675
744
|
end
|
676
745
|
when :asperabrowser
|
677
746
|
browse_params = {
|
678
|
-
'nodeUser' => options.get_option(:username,is_type: :mandatory),
|
679
|
-
'nodePW' => options.get_option(:password,is_type: :mandatory),
|
680
|
-
'nodeURL' => options.get_option(:url,is_type: :mandatory)
|
747
|
+
'nodeUser' => options.get_option(:username, is_type: :mandatory),
|
748
|
+
'nodePW' => options.get_option(:password, is_type: :mandatory),
|
749
|
+
'nodeURL' => options.get_option(:url, is_type: :mandatory)
|
681
750
|
}
|
682
751
|
# encode parameters so that it looks good in url
|
683
752
|
encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
|
684
753
|
OpenApplication.instance.uri(options.get_option(:asperabrowserurl) + '?goto=' + encoded_params)
|
685
754
|
return Main.result_status('done')
|
686
755
|
when :basic_token
|
687
|
-
return Main.result_status(Rest.basic_creds(options.get_option(:username,is_type: :mandatory),options.get_option(:password,is_type: :mandatory)))
|
756
|
+
return Main.result_status(Rest.basic_creds(options.get_option(:username, is_type: :mandatory), options.get_option(:password, is_type: :mandatory)))
|
688
757
|
end # case command
|
689
758
|
raise 'ERROR: shall not reach this line'
|
690
759
|
end # execute_action
|