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