aspera-cli 4.10.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 +621 -378
- data/bin/ascli +4 -4
- data/bin/asession +11 -11
- data/docs/test_env.conf +28 -19
- data/examples/aoc.rb +4 -4
- data/examples/dascli +11 -9
- data/examples/faspex4.rb +8 -8
- data/examples/node.rb +11 -11
- data/examples/server.rb +9 -9
- 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/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 +110 -110
- data/lib/aspera/cli/plugin.rb +54 -37
- data/lib/aspera/cli/plugins/alee.rb +4 -4
- data/lib/aspera/cli/plugins/aoc.rb +308 -669
- 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 +447 -344
- 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 +110 -112
- data/lib/aspera/cli/plugins/faspex5.rb +67 -46
- data/lib/aspera/cli/plugins/node.rb +364 -288
- 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 +57 -57
- 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 +30 -28
- 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 +65 -67
- data/lib/aspera/fasp/agent_httpgw.rb +72 -68
- 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 +78 -78
- data/lib/aspera/fasp/listener.rb +1 -1
- data/lib/aspera/fasp/parameters.rb +75 -72
- data/lib/aspera/fasp/parameters.yaml +2 -2
- 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 +23 -28
- data/lib/aspera/keychain/macos_security.rb +21 -20
- 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 +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 +41 -41
- data/lib/aspera/proxy_auto_config.rb +16 -16
- data/lib/aspera/rest.rb +56 -60
- 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 +8 -5
- 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,337 +141,374 @@ 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)
|
|
267
|
+
end
|
|
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')
|
|
264
326
|
end
|
|
265
|
-
return
|
|
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
|
|
|
430
513
|
# This is older API
|
|
431
514
|
def execute_async
|
|
@@ -442,7 +525,7 @@ module Aspera
|
|
|
442
525
|
end
|
|
443
526
|
else
|
|
444
527
|
asyncids = @api_node.read('async/list')[:data]['sync_ids']
|
|
445
|
-
summaries = @api_node.create('async/summary',{'syncs' => asyncids})[:data]['sync_summaries']
|
|
528
|
+
summaries = @api_node.create('async/summary', {'syncs' => asyncids})[:data]['sync_summaries']
|
|
446
529
|
selected = summaries.find{|s|s['name'].eql?(asyncname)}
|
|
447
530
|
raise "no such sync: #{asyncname}" if selected.nil?
|
|
448
531
|
asyncid = selected['snid']
|
|
@@ -455,16 +538,16 @@ module Aspera
|
|
|
455
538
|
resp = @api_node.read('async/list')[:data]['sync_ids']
|
|
456
539
|
return { type: :value_list, data: resp, name: 'id' }
|
|
457
540
|
when :show
|
|
458
|
-
resp = @api_node.create('async/summary',pdata)[:data]['sync_summaries']
|
|
541
|
+
resp = @api_node.create('async/summary', pdata)[:data]['sync_summaries']
|
|
459
542
|
return Main.result_empty if resp.empty?
|
|
460
543
|
return { type: :object_list, data: resp, fields: %w[snid name local_dir remote_dir] } if asyncid.eql?('ALL')
|
|
461
544
|
return { type: :single_object, data: resp.first }
|
|
462
545
|
when :delete
|
|
463
|
-
resp = @api_node.create('async/delete',pdata)[:data]
|
|
546
|
+
resp = @api_node.create('async/delete', pdata)[:data]
|
|
464
547
|
return { type: :single_object, data: resp, name: 'id' }
|
|
465
548
|
when :bandwidth
|
|
466
549
|
pdata['seconds'] = 100 # TODO: as parameter with --value
|
|
467
|
-
resp = @api_node.create('async/bandwidth',pdata)[:data]
|
|
550
|
+
resp = @api_node.create('async/bandwidth', pdata)[:data]
|
|
468
551
|
data = resp['bandwidth_data']
|
|
469
552
|
return Main.result_empty if data.empty?
|
|
470
553
|
data = data.first[asyncid]['data']
|
|
@@ -476,19 +559,19 @@ module Aspera
|
|
|
476
559
|
# status int
|
|
477
560
|
filter = options.get_option(:value)
|
|
478
561
|
pdata.merge!(filter) unless filter.nil?
|
|
479
|
-
resp = @api_node.create('async/files',pdata)[:data]
|
|
562
|
+
resp = @api_node.create('async/files', pdata)[:data]
|
|
480
563
|
data = resp['sync_files']
|
|
481
564
|
data = data.first[asyncid] unless data.empty?
|
|
482
565
|
iteration_data = []
|
|
483
566
|
skip_ids_persistency = nil
|
|
484
|
-
if options.get_option(:once_only,is_type: :mandatory)
|
|
567
|
+
if options.get_option(:once_only, is_type: :mandatory)
|
|
485
568
|
skip_ids_persistency = PersistencyActionOnce.new(
|
|
486
569
|
manager: @agents[:persistency],
|
|
487
570
|
data: iteration_data,
|
|
488
571
|
id: IdGenerator.from_list([
|
|
489
572
|
'sync_files',
|
|
490
|
-
options.get_option(:url,is_type: :mandatory),
|
|
491
|
-
options.get_option(:username,is_type: :mandatory),
|
|
573
|
+
options.get_option(:url, is_type: :mandatory),
|
|
574
|
+
options.get_option(:username, is_type: :mandatory),
|
|
492
575
|
asyncid]))
|
|
493
576
|
unless iteration_data.first.nil?
|
|
494
577
|
data.select!{|l| l['fnid'].to_i > iteration_data.first}
|
|
@@ -499,41 +582,50 @@ module Aspera
|
|
|
499
582
|
skip_ids_persistency&.save
|
|
500
583
|
return { type: :object_list, data: data, name: 'id' }
|
|
501
584
|
when :counters
|
|
502
|
-
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
|
|
503
586
|
return Main.result_empty if resp.nil?
|
|
504
587
|
return { type: :single_object, data: resp }
|
|
505
588
|
end
|
|
506
589
|
end
|
|
507
590
|
|
|
508
|
-
ACTIONS = %i[
|
|
509
|
-
|
|
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
|
|
510
601
|
|
|
511
|
-
def execute_action(command=nil,prefix_path=nil)
|
|
602
|
+
def execute_action(command=nil, prefix_path=nil)
|
|
512
603
|
command ||= options.get_next_command(ACTIONS)
|
|
513
604
|
case command
|
|
514
|
-
when *COMMON_ACTIONS then return execute_simple_common(command,prefix_path)
|
|
605
|
+
when *COMMON_ACTIONS then return execute_simple_common(command, prefix_path)
|
|
515
606
|
when :async then return execute_async
|
|
516
607
|
when :sync
|
|
517
|
-
|
|
608
|
+
# newer api
|
|
609
|
+
sync_command = options.get_next_command(%i[bandwidth counters files start state stop summary].concat(Plugin::ALL_OPS) - %i[modify])
|
|
518
610
|
case sync_command
|
|
519
|
-
when *Plugin::ALL_OPS then return entity_command(sync_command
|
|
611
|
+
when *Plugin::ALL_OPS then return entity_command(sync_command, @api_node, 'asyncs', item_list_key: 'ids')
|
|
520
612
|
else
|
|
521
613
|
parameters = options.get_option(:value)
|
|
522
|
-
asyncs_id=instance_identifier
|
|
614
|
+
asyncs_id = instance_identifier
|
|
523
615
|
if %i[start stop].include?(sync_command)
|
|
524
|
-
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}",parameters)
|
|
616
|
+
@api_node.create("asyncs/#{asyncs_id}/#{sync_command}", parameters)
|
|
525
617
|
return Main.result_status('ok')
|
|
526
618
|
end
|
|
527
|
-
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}",parameters)[:data] }
|
|
619
|
+
return { type: :single_object, data: @api_node.read("asyncs/#{asyncs_id}/#{sync_command}", parameters)[:data] }
|
|
528
620
|
end
|
|
529
621
|
when :stream
|
|
530
622
|
command = options.get_next_command(%i[list create show modify cancel])
|
|
531
623
|
case command
|
|
532
624
|
when :list
|
|
533
|
-
resp = @api_node.read('ops/transfers',options.get_option(:value))
|
|
625
|
+
resp = @api_node.read('ops/transfers', options.get_option(:value))
|
|
534
626
|
return { type: :object_list, data: resp[:data], fields: %w[id status] } # TODO: useful?
|
|
535
627
|
when :create
|
|
536
|
-
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))
|
|
537
629
|
return { type: :single_object, data: resp[:data] }
|
|
538
630
|
when :show
|
|
539
631
|
trid = options.get_next_argument('transfer id')
|
|
@@ -541,7 +633,7 @@ module Aspera
|
|
|
541
633
|
return { type: :other_struct, data: resp[:data] }
|
|
542
634
|
when :modify
|
|
543
635
|
trid = options.get_next_argument('transfer id')
|
|
544
|
-
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))
|
|
545
637
|
return { type: :other_struct, data: resp[:data] }
|
|
546
638
|
when :cancel
|
|
547
639
|
trid = options.get_next_argument('transfer id')
|
|
@@ -560,9 +652,9 @@ module Aspera
|
|
|
560
652
|
case command
|
|
561
653
|
when :list
|
|
562
654
|
# could use ? subpath: 'transfers'
|
|
563
|
-
query=options.get_option(:value) || options.get_option(:query)
|
|
655
|
+
query = options.get_option(:value) || options.get_option(:query)
|
|
564
656
|
raise 'Query must be a Hash' unless query.nil? || query.is_a?(Hash)
|
|
565
|
-
resp = @api_node.read(res_class_path,query)
|
|
657
|
+
resp = @api_node.read(res_class_path, query)
|
|
566
658
|
return {
|
|
567
659
|
type: :object_list,
|
|
568
660
|
data: resp[:data],
|
|
@@ -577,22 +669,6 @@ module Aspera
|
|
|
577
669
|
else
|
|
578
670
|
raise 'error'
|
|
579
671
|
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
672
|
when :service
|
|
597
673
|
command = options.get_next_command(%i[list create delete])
|
|
598
674
|
if [:delete].include?(command)
|
|
@@ -605,7 +681,7 @@ module Aspera
|
|
|
605
681
|
when :create
|
|
606
682
|
# @json:'{"type":"WATCHFOLDERD","run_as":{"user":"user1"}}'
|
|
607
683
|
params = options.get_next_argument('Run creation data (structure)')
|
|
608
|
-
resp = @api_node.create('rund/services',params)
|
|
684
|
+
resp = @api_node.create('rund/services', params)
|
|
609
685
|
return Main.result_status("#{resp[:data]['id']} created")
|
|
610
686
|
when :delete
|
|
611
687
|
@api_node.delete("rund/services/#{svcid}")
|
|
@@ -623,15 +699,15 @@ module Aspera
|
|
|
623
699
|
@api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
|
|
624
700
|
case command
|
|
625
701
|
when :create
|
|
626
|
-
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))
|
|
627
703
|
return Main.result_status("#{resp[:data]['id']} created")
|
|
628
704
|
when :list
|
|
629
|
-
resp = @api_node.read(res_class_path,options.get_option(:value))
|
|
705
|
+
resp = @api_node.read(res_class_path, options.get_option(:value))
|
|
630
706
|
return { type: :value_list, data: resp[:data]['ids'], name: 'id' }
|
|
631
707
|
when :show
|
|
632
708
|
return { type: :single_object, data: @api_node.read(one_res_path)[:data]}
|
|
633
709
|
when :modify
|
|
634
|
-
@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))
|
|
635
711
|
return Main.result_status("#{one_res_id} updated")
|
|
636
712
|
when :delete
|
|
637
713
|
@api_node.delete(one_res_path)
|
|
@@ -651,7 +727,7 @@ module Aspera
|
|
|
651
727
|
case command
|
|
652
728
|
when :list
|
|
653
729
|
request_data.deep_merge!({'validation' => validation}) unless validation.nil?
|
|
654
|
-
resp = @api_node.create('services/rest/transfers/v1/sessions',request_data)
|
|
730
|
+
resp = @api_node.create('services/rest/transfers/v1/sessions', request_data)
|
|
655
731
|
return {
|
|
656
732
|
type: :object_list,
|
|
657
733
|
data: resp[:data]['session_info_result']['session_info'],
|
|
@@ -663,28 +739,28 @@ module Aspera
|
|
|
663
739
|
case command
|
|
664
740
|
when :list
|
|
665
741
|
request_data.deep_merge!({'validation' => validation}) unless validation.nil?
|
|
666
|
-
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]
|
|
667
743
|
resp = JSON.parse(resp) if resp.is_a?(String)
|
|
668
|
-
Log.dump(:resp,resp)
|
|
744
|
+
Log.dump(:resp, resp)
|
|
669
745
|
return { type: :object_list, data: resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path]}
|
|
670
746
|
when :modify
|
|
671
747
|
request_data.deep_merge!(validation) unless validation.nil?
|
|
672
|
-
@api_node.update('services/rest/transfers/v1/files',request_data)
|
|
748
|
+
@api_node.update('services/rest/transfers/v1/files', request_data)
|
|
673
749
|
return Main.result_status('updated')
|
|
674
750
|
end
|
|
675
751
|
end
|
|
676
752
|
when :asperabrowser
|
|
677
753
|
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)
|
|
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)
|
|
681
757
|
}
|
|
682
758
|
# encode parameters so that it looks good in url
|
|
683
759
|
encoded_params = Base64.strict_encode64(Zlib::Deflate.deflate(JSON.generate(browse_params))).gsub(/=+$/, '').tr('+/', '-_').reverse
|
|
684
760
|
OpenApplication.instance.uri(options.get_option(:asperabrowserurl) + '?goto=' + encoded_params)
|
|
685
761
|
return Main.result_status('done')
|
|
686
762
|
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)))
|
|
763
|
+
return Main.result_status(Rest.basic_creds(options.get_option(:username, is_type: :mandatory), options.get_option(:password, is_type: :mandatory)))
|
|
688
764
|
end # case command
|
|
689
765
|
raise 'ERROR: shall not reach this line'
|
|
690
766
|
end # execute_action
|