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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BUGS.md +20 -0
  4. data/CHANGELOG.md +509 -0
  5. data/CONTRIBUTING.md +118 -0
  6. data/README.md +621 -378
  7. data/bin/ascli +4 -4
  8. data/bin/asession +11 -11
  9. data/docs/test_env.conf +28 -19
  10. data/examples/aoc.rb +4 -4
  11. data/examples/dascli +11 -9
  12. data/examples/faspex4.rb +8 -8
  13. data/examples/node.rb +11 -11
  14. data/examples/server.rb +9 -9
  15. data/lib/aspera/aoc.rb +273 -266
  16. data/lib/aspera/ascmd.rb +56 -54
  17. data/lib/aspera/ats_api.rb +4 -4
  18. data/lib/aspera/cli/basic_auth_plugin.rb +15 -12
  19. data/lib/aspera/cli/extended_value.rb +5 -5
  20. data/lib/aspera/cli/formater.rb +64 -64
  21. data/lib/aspera/cli/listener/line_dump.rb +1 -1
  22. data/lib/aspera/cli/listener/logger.rb +1 -1
  23. data/lib/aspera/cli/listener/progress.rb +5 -6
  24. data/lib/aspera/cli/listener/progress_multi.rb +14 -19
  25. data/lib/aspera/cli/main.rb +66 -67
  26. data/lib/aspera/cli/manager.rb +110 -110
  27. data/lib/aspera/cli/plugin.rb +54 -37
  28. data/lib/aspera/cli/plugins/alee.rb +4 -4
  29. data/lib/aspera/cli/plugins/aoc.rb +308 -669
  30. data/lib/aspera/cli/plugins/ats.rb +44 -46
  31. data/lib/aspera/cli/plugins/bss.rb +10 -10
  32. data/lib/aspera/cli/plugins/config.rb +447 -344
  33. data/lib/aspera/cli/plugins/console.rb +12 -12
  34. data/lib/aspera/cli/plugins/cos.rb +18 -20
  35. data/lib/aspera/cli/plugins/faspex.rb +110 -112
  36. data/lib/aspera/cli/plugins/faspex5.rb +67 -46
  37. data/lib/aspera/cli/plugins/node.rb +364 -288
  38. data/lib/aspera/cli/plugins/orchestrator.rb +46 -46
  39. data/lib/aspera/cli/plugins/preview.rb +122 -114
  40. data/lib/aspera/cli/plugins/server.rb +137 -83
  41. data/lib/aspera/cli/plugins/shares.rb +30 -29
  42. data/lib/aspera/cli/plugins/sync.rb +13 -33
  43. data/lib/aspera/cli/transfer_agent.rb +57 -57
  44. data/lib/aspera/cli/version.rb +1 -1
  45. data/lib/aspera/colors.rb +3 -3
  46. data/lib/aspera/command_line_builder.rb +27 -27
  47. data/lib/aspera/cos_node.rb +22 -20
  48. data/lib/aspera/data_repository.rb +1 -1
  49. data/lib/aspera/environment.rb +30 -28
  50. data/lib/aspera/fasp/agent_base.rb +15 -15
  51. data/lib/aspera/fasp/agent_connect.rb +23 -21
  52. data/lib/aspera/fasp/agent_direct.rb +65 -67
  53. data/lib/aspera/fasp/agent_httpgw.rb +72 -68
  54. data/lib/aspera/fasp/agent_node.rb +23 -21
  55. data/lib/aspera/fasp/agent_trsdk.rb +20 -20
  56. data/lib/aspera/fasp/error.rb +3 -2
  57. data/lib/aspera/fasp/error_info.rb +11 -8
  58. data/lib/aspera/fasp/installation.rb +78 -78
  59. data/lib/aspera/fasp/listener.rb +1 -1
  60. data/lib/aspera/fasp/parameters.rb +75 -72
  61. data/lib/aspera/fasp/parameters.yaml +2 -2
  62. data/lib/aspera/fasp/resume_policy.rb +8 -8
  63. data/lib/aspera/fasp/transfer_spec.rb +35 -2
  64. data/lib/aspera/fasp/uri.rb +7 -7
  65. data/lib/aspera/faspex_gw.rb +7 -5
  66. data/lib/aspera/hash_ext.rb +3 -3
  67. data/lib/aspera/id_generator.rb +5 -5
  68. data/lib/aspera/keychain/encrypted_hash.rb +23 -28
  69. data/lib/aspera/keychain/macos_security.rb +21 -20
  70. data/lib/aspera/log.rb +7 -7
  71. data/lib/aspera/nagios.rb +19 -18
  72. data/lib/aspera/node.rb +209 -35
  73. data/lib/aspera/oauth.rb +37 -36
  74. data/lib/aspera/open_application.rb +19 -11
  75. data/lib/aspera/persistency_action_once.rb +4 -4
  76. data/lib/aspera/persistency_folder.rb +13 -13
  77. data/lib/aspera/preview/file_types.rb +8 -8
  78. data/lib/aspera/preview/generator.rb +67 -67
  79. data/lib/aspera/preview/utils.rb +27 -27
  80. data/lib/aspera/proxy_auto_config.js +41 -41
  81. data/lib/aspera/proxy_auto_config.rb +16 -16
  82. data/lib/aspera/rest.rb +56 -60
  83. data/lib/aspera/rest_call_error.rb +2 -1
  84. data/lib/aspera/rest_error_analyzer.rb +18 -17
  85. data/lib/aspera/rest_errors_aspera.rb +16 -16
  86. data/lib/aspera/secret_hider.rb +15 -13
  87. data/lib/aspera/ssh.rb +11 -10
  88. data/lib/aspera/sync.rb +158 -44
  89. data/lib/aspera/temp_file_manager.rb +2 -2
  90. data/lib/aspera/uri_reader.rb +4 -4
  91. data/lib/aspera/web_auth.rb +14 -13
  92. data.tar.gz.sig +0 -0
  93. metadata +8 -5
  94. 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
- SAMPLE_SOAP_CALL = '<?xml version="1.0" encoding="UTF-8"?>'\
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
- SEARCH_REMOVE_FIELDS=%w[basename permissions].freeze
32
- private_constant :SAMPLE_SOAP_CALL,:SEARCH_REMOVE_FIELDS
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
- # this is added to transfer spec, for instance to add tags (COS)
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.has_key?(:node_api)
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
- Rest.new({
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
- 'X-Aspera-AccessKey' => options.get_option(:username,is_type: :mandatory),
55
- 'Authorization' => options.get_option(:password,is_type: :mandatory)
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
- basic_auth_api
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 {|i| i['permissions'] = i['permissions'].map { |x| x['name'] }.join(','); i }
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
- resres = { data: [], type: :object_list, fields: [type,'result']}
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.has_key?('error')
103
- Log.log.error("#{p['error']['user_message']} : #{p['path']}")
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
- SIMPLE_ACTIONS = %i[health events space info license mkdir mklink mkfile rename delete search].freeze
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',:multiple)
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
- # TODO: could be a list of path
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
- #TODO: a command for that ?
189
- #resp=@api_node.create('space',{ "paths" => path_list.map {|i| { type: :directory, path: i} } } )
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,:download
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,:hybrid
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.deep_merge!(@add_request_param)
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
- ts_direction =
248
- case command
249
- when :upload then Fasp::TransferSpec::DIRECTION_SEND
250
- when :download then Fasp::TransferSpec::DIRECTION_RECEIVE
251
- else raise 'Error: need upload or download'
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
- Aspera::Node.set_ak_basic_token(transfer_spec,@api_node.params[:auth][:username],@api_node.params[:auth][:password])
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 Main.result_transfer(transfer.start(transfer_spec,{ src: :node_gen3}))
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
- # navigate the path from given file id
272
- # @param id initial file id
273
- # @param path file path
274
- # @return {.api,.file_id}
275
- def resolve_api_fid(id, path)
276
- # TODO: implement
277
- Log.log.debug("TODO #{path}")
278
- return {api: @api_node, file_id: id}
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
- NODE4_COMMANDS = %i[browse find mkdir rename delete upload download http_node_download file permission bearer_token_node node_info].freeze
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 :node_info
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
- apifid[:api] = aoc_api.get_node_api(apifid[:node_info], use_secret: false)
290
- return {type: :single_object,data: {
291
- url: apifid[:node_info]['url'],
292
- username: apifid[:node_info]['access_key'],
293
- password: apifid[:api].oauth_token,
294
- root_id: apifid[:file_id]
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: aoc_api.find_files(apifid,test_block),fields: ['path']}
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(AoC::PATH_SEPARATOR)
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(AoC::PATH_SEPARATOR))
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
- add_ts = {'tags' => {'aspera' => {'files' => {'parentCwd' => "#{apifid[:node_info]['id']}:#{apifid[:file_id]}"}}}}
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
- source_folder = source_folder.split(AoC::PATH_SEPARATOR)
345
- source_paths = [{'source' => source_folder.pop}]
346
- source_folder = source_folder.join(AoC::PATH_SEPARATOR)
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 = resolve_api_fid(top_file_id,source_folder)
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(AoC::PATH_SEPARATOR)
493
+ source_folder = source_folder.split(Aspera::Node::PATH_SEPARATOR)
358
494
  source_paths = [{'source' => source_folder.pop}]
359
- source_folder = source_folder.join(AoC::PATH_SEPARATOR)
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
- file_path = options.get_option(:path)
410
- apifid =
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 'ERR'
428
- end # execute_node_gen4_command
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[postprocess stream transfer cleanup forward access_key watch_folder service async sync central asperabrowser
509
- basic_token].concat(COMMON_ACTIONS).freeze
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
- sync_command = options.get_next_command([Plugin::ALL_OPS,%i[bandwidth counters files start state stop summary]].flatten-%i[modify])
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,@api_node,'asyncs',item_list_key: 'ids')
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