aspera-cli 4.27.2 → 4.27.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +133 -0
- data/CONTRIBUTING.md +5 -2
- data/bin/ascli +3 -1
- data/docs/README.md +1006 -766
- data/lib/aspera/agent/base.rb +7 -2
- data/lib/aspera/agent/connect.rb +6 -8
- data/lib/aspera/agent/desktop.rb +2 -6
- data/lib/aspera/agent/direct.rb +52 -22
- data/lib/aspera/agent/node.rb +9 -8
- data/lib/aspera/agent/transferd.rb +2 -2
- data/lib/aspera/api/alee.rb +1 -1
- data/lib/aspera/api/aoc.rb +14 -12
- data/lib/aspera/api/ats.rb +1 -1
- data/lib/aspera/api/cos_node.rb +2 -2
- data/lib/aspera/api/faspex.rb +11 -7
- data/lib/aspera/api/httpgw.rb +38 -35
- data/lib/aspera/api/node.rb +39 -34
- data/lib/aspera/ascmd.rb +3 -1
- data/lib/aspera/ascp/installation.rb +63 -28
- data/lib/aspera/ascp/management.rb +1 -0
- data/lib/aspera/assert.rb +4 -0
- data/lib/aspera/cli/ascp_actions.rb +20 -41
- data/lib/aspera/cli/async_transfer_store.rb +12 -11
- data/lib/aspera/cli/bootstrapper.rb +14 -16
- data/lib/aspera/cli/command_line.rb +252 -0
- data/lib/aspera/cli/command_registry.rb +215 -37
- data/lib/aspera/cli/command_spec.rb +104 -15
- data/lib/aspera/cli/completion/ascli.bash +12 -0
- data/lib/aspera/cli/completion/ascli.fish +16 -0
- data/lib/aspera/cli/completion/ascli.zsh +19 -0
- data/lib/aspera/cli/context.rb +3 -0
- data/lib/aspera/cli/deprecation.rb +37 -0
- data/lib/aspera/cli/extended_value.rb +6 -3
- data/lib/aspera/cli/formatter.rb +94 -80
- data/lib/aspera/cli/gem_checker.rb +1 -1
- data/lib/aspera/cli/hints.rb +7 -6
- data/lib/aspera/cli/http.rb +22 -34
- data/lib/aspera/cli/info.rb +3 -0
- data/lib/aspera/cli/mcp_tool.rb +47 -83
- data/lib/aspera/cli/option_declarator.rb +33 -42
- data/lib/aspera/cli/option_registry.rb +69 -0
- data/lib/aspera/cli/option_types.rb +105 -0
- data/lib/aspera/cli/option_value.rb +278 -0
- data/lib/aspera/cli/options.schema.yaml +124 -15
- data/lib/aspera/cli/parser.rb +333 -862
- data/lib/aspera/cli/plugins/alee.rb +7 -4
- data/lib/aspera/cli/plugins/aoc.rb +545 -518
- data/lib/aspera/cli/plugins/ats.rb +59 -80
- data/lib/aspera/cli/plugins/base.rb +221 -265
- data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
- data/lib/aspera/cli/plugins/config.rb +263 -184
- data/lib/aspera/cli/plugins/console.rb +103 -39
- data/lib/aspera/cli/plugins/cos.rb +6 -23
- data/lib/aspera/cli/plugins/factory.rb +3 -0
- data/lib/aspera/cli/plugins/faspex5.rb +204 -182
- data/lib/aspera/cli/plugins/faspio.rb +6 -11
- data/lib/aspera/cli/plugins/httpgw.rb +8 -11
- data/lib/aspera/cli/plugins/mcp.rb +20 -55
- data/lib/aspera/cli/plugins/node.rb +300 -327
- data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
- data/lib/aspera/cli/plugins/preview.rb +96 -105
- data/lib/aspera/cli/plugins/server.rb +78 -53
- data/lib/aspera/cli/plugins/shares.rb +80 -131
- data/lib/aspera/cli/preset_actions.rb +44 -27
- data/lib/aspera/cli/preset_manager.rb +44 -19
- data/lib/aspera/cli/prompt.rb +36 -0
- data/lib/aspera/cli/result.rb +42 -36
- data/lib/aspera/cli/runner.rb +32 -59
- data/lib/aspera/cli/special_values.rb +5 -0
- data/lib/aspera/cli/sync_actions.rb +51 -46
- data/lib/aspera/cli/terminal_formatter.rb +9 -3
- data/lib/aspera/cli/transfer_actions.rb +14 -9
- data/lib/aspera/cli/transfer_agent.rb +34 -38
- data/lib/aspera/cli/transfer_progress.rb +290 -55
- data/lib/aspera/cli/vault_manager.rb +0 -17
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/cli/wizard.rb +5 -3
- data/lib/aspera/coverage.rb +1 -1
- data/lib/aspera/environment.rb +35 -5
- data/lib/aspera/faspex_gw.rb +2 -1
- data/lib/aspera/faspex_postproc.rb +1 -0
- data/lib/aspera/graphql.rb +5 -5
- data/lib/aspera/json_rpc/client.rb +5 -5
- data/lib/aspera/keychain/encrypted_hash.rb +2 -2
- data/lib/aspera/keychain/factory.rb +2 -1
- data/lib/aspera/keychain/one_password_api.rb +1 -1
- data/lib/aspera/link_header.rb +2 -2
- data/lib/aspera/log.rb +47 -27
- data/lib/aspera/markdown.rb +2 -0
- data/lib/aspera/mime.rb +25 -0
- data/lib/aspera/node_emulator.rb +759 -0
- data/lib/aspera/oauth/base.rb +37 -26
- data/lib/aspera/oauth/factory.rb +7 -3
- data/lib/aspera/oauth/generic.rb +1 -1
- data/lib/aspera/oauth/json_credentials.rb +34 -0
- data/lib/aspera/oauth/jwt.rb +4 -5
- data/lib/aspera/oauth/web.rb +9 -8
- data/lib/aspera/oauth.rb +1 -0
- data/lib/aspera/persistency_folder.rb +1 -3
- data/lib/aspera/preview/file_types.rb +4 -4
- data/lib/aspera/preview/generator.rb +11 -1
- data/lib/aspera/preview/options.schema.yaml +119 -0
- data/lib/aspera/preview/terminal.rb +4 -3
- data/lib/aspera/preview/utils.rb +9 -6
- data/lib/aspera/products/connect.rb +1 -1
- data/lib/aspera/rainbow.rb +7 -0
- data/lib/aspera/rest/aspera_errors.rb +72 -0
- data/lib/aspera/rest/call_error.rb +27 -0
- data/lib/aspera/rest/client.rb +523 -0
- data/lib/aspera/rest/error_analyzer.rb +113 -0
- data/lib/aspera/rest/list.rb +149 -0
- data/lib/aspera/rest/parameters.rb +55 -0
- data/lib/aspera/rest/util.rb +176 -0
- data/lib/aspera/rest.rb +7 -621
- data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
- data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
- data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
- data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
- data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
- data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
- data/lib/aspera/schema/documentation.rb +13 -3
- data/lib/aspera/schema/reader.rb +12 -18
- data/lib/aspera/schema/registry.rb +23 -1
- data/lib/aspera/schema/validator.rb +92 -0
- data/lib/aspera/secret_hider.rb +36 -25
- data/lib/aspera/string_ext.rb +15 -0
- data/lib/aspera/temp_file_manager.rb +6 -5
- data/lib/aspera/transfer/parameters.rb +2 -0
- data/lib/aspera/transfer/spec.rb +1 -0
- data/lib/aspera/uri_reader.rb +11 -11
- data/lib/aspera/web_auth/index.html +147 -0
- data/lib/aspera/web_auth/server.rb +81 -0
- data.tar.gz.sig +0 -0
- metadata +43 -9
- metadata.gz.sig +0 -0
- data/lib/aspera/colors.rb +0 -79
- data/lib/aspera/node_simulator.rb +0 -344
- data/lib/aspera/preview/options.rb +0 -45
- data/lib/aspera/rest_call_error.rb +0 -25
- data/lib/aspera/rest_error_analyzer.rb +0 -111
- data/lib/aspera/rest_errors_aspera.rb +0 -58
- data/lib/aspera/rest_list.rb +0 -136
- data/lib/aspera/web_auth.rb +0 -211
|
@@ -11,8 +11,7 @@ require 'aspera/hash_ext'
|
|
|
11
11
|
require 'aspera/id_generator'
|
|
12
12
|
require 'aspera/api/node'
|
|
13
13
|
require 'aspera/oauth'
|
|
14
|
-
require 'aspera/
|
|
15
|
-
require 'aspera/rest_list'
|
|
14
|
+
require 'aspera/rest/list'
|
|
16
15
|
require 'aspera/assert'
|
|
17
16
|
require 'base64'
|
|
18
17
|
require 'zlib'
|
|
@@ -47,7 +46,7 @@ module Aspera
|
|
|
47
46
|
error = nil
|
|
48
47
|
urls.each do |base_url|
|
|
49
48
|
next unless base_url.match?(%r{^https?://})
|
|
50
|
-
api = Rest.new(base_url: base_url)
|
|
49
|
+
api = Rest::Client.new(base_url: base_url)
|
|
51
50
|
test_endpoint = 'ping'
|
|
52
51
|
http = api.read(test_endpoint, ret: :resp)
|
|
53
52
|
next unless http.body.eql?('')
|
|
@@ -76,9 +75,9 @@ module Aspera
|
|
|
76
75
|
option :validator, description: 'Identifier of validator (optional for central)'
|
|
77
76
|
option :asperabrowserurl, description: 'URL for simple aspera web ui', default: 'https://asperabrowser.mybluemix.net'
|
|
78
77
|
option :node_api, description: 'Gen4: standard_ports: Use standard FASP ports (true) or get from node API (false). cache: Set to false to force actual file system read',
|
|
79
|
-
allowed: Hash,
|
|
78
|
+
allowed: Hash, on_set: Api::Node.method(:api_options=)
|
|
80
79
|
option :root_id, description: 'Gen4: File id of top folder when using access key (override AK root id)'
|
|
81
|
-
option :dynamic_key, description: 'Private key PEM to use for dynamic key auth',
|
|
80
|
+
option :dynamic_key, description: 'Private key PEM to use for dynamic key auth', on_set: Api::Node.method(:use_dynamic_key=)
|
|
82
81
|
|
|
83
82
|
# @param wizard [Wizard] The wizard object
|
|
84
83
|
# @param app_url [String] Tested URL
|
|
@@ -106,25 +105,21 @@ module Aspera
|
|
|
106
105
|
# Fields removed in result of search
|
|
107
106
|
SEARCH_REMOVE_FIELDS = %w[basename permissions].freeze
|
|
108
107
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# Shared DSL metadata for Gen3 commands (description:, arguments:, transfer_paths:, aliases:).
|
|
113
|
-
# Consumed by commands_under(:files) in shares.rb and commands_under %i[source node]) in faspex.rb.
|
|
114
|
-
# :sync is excluded (intermediate node with sub-commands, handled separately).
|
|
115
|
-
# action: entries are node-specific and intentionally omitted.
|
|
108
|
+
# DSL metadata for Gen3 root commands (description:, arguments:, transfer_paths:, aliases:).
|
|
109
|
+
# :access_keys is skipped (intermediate node declared separately), :sync is declared separately.
|
|
110
|
+
# action: entries are in GEN3_NODE_ACTIONS, or implicit.
|
|
116
111
|
COMMANDS_GEN3_SPEC = {
|
|
117
|
-
search: {description: 'Search for files', arguments: [{name: :
|
|
118
|
-
space: {description: 'Show space information', arguments: [{name: :
|
|
119
|
-
mkdir: {description: 'Create a folder (Gen3)', arguments: [{name: :
|
|
120
|
-
mklink: {description: 'Create a symbolic link (Gen3)', arguments: [{name: :target
|
|
121
|
-
mkfile: {description: 'Create a file (Gen3)', arguments: [{name: :
|
|
122
|
-
rename: {description: 'Rename a file or folder (Gen3)', arguments: [{name: :
|
|
112
|
+
search: {description: 'Search for files', arguments: [{name: :path}]},
|
|
113
|
+
space: {description: 'Show space information', arguments: [{name: :paths, multiple: true}]},
|
|
114
|
+
mkdir: {description: 'Create a folder (Gen3)', arguments: [{name: :paths, multiple: true}]},
|
|
115
|
+
mklink: {description: 'Create a symbolic link (Gen3)', arguments: [{name: :target}, {name: :link_path}]},
|
|
116
|
+
mkfile: {description: 'Create a file (Gen3)', arguments: [{name: :path}, {name: :contents, mandatory: false, default: nil}]},
|
|
117
|
+
rename: {description: 'Rename a file or folder (Gen3)', arguments: [{name: :folder}, {name: :source}, {name: :destination}]},
|
|
123
118
|
delete: {description: 'Delete files or folders (Gen3)', arguments: [{name: :paths, multiple: true}]},
|
|
124
|
-
ls: {description: 'List files (Gen3)', arguments: [{name: :path
|
|
119
|
+
ls: {description: 'List files (Gen3)', arguments: [{name: :path}], aliases: [:browse]},
|
|
125
120
|
upload: {description: 'Upload files (Gen3)', transfer_paths: :send},
|
|
126
121
|
download: {description: 'Download files (Gen3)', transfer_paths: :receive},
|
|
127
|
-
cat: {description: 'Show file contents (Gen3)', arguments: [{name: :
|
|
122
|
+
cat: {description: 'Show file contents (Gen3)', arguments: [{name: :path}]},
|
|
128
123
|
transport: {description: 'Show transport parameters'},
|
|
129
124
|
spec: {description: 'Show transfer spec base'},
|
|
130
125
|
api_details: {description: 'Show API details'},
|
|
@@ -136,60 +131,52 @@ module Aspera
|
|
|
136
131
|
access_keys: {description: 'Manage access keys'}
|
|
137
132
|
}.freeze
|
|
138
133
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
SPECIAL_ACTIONS = %i[health events info slash license].freeze
|
|
142
|
-
|
|
143
|
-
# commands for : `execute_simple_common`: actions used commonly when a node is involved
|
|
144
|
-
COMMON_ACTIONS = (%i[access_keys] + BASE_ACTIONS + SPECIAL_ACTIONS).freeze
|
|
134
|
+
private_constant :CENTRAL_SOAP_API_TEST, :SEARCH_REMOVE_FIELDS
|
|
145
135
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# used in aoc
|
|
136
|
+
# Gen4 read commands also exposed on AoC packages (`aoc packages ls <id>`)
|
|
149
137
|
NODE4_READ_ACTIONS = %i[bearer_token_node node_info ls find].freeze
|
|
150
138
|
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
# Consumed by commands_under %i[access_keys do]) in node.rb and by aoc.rb.
|
|
156
|
-
# :sync and :permission are excluded: they are intermediate nodes handled separately.
|
|
157
|
-
SINGLE_PATH_ARG = [{name: :path, type: String}].freeze
|
|
139
|
+
# DSL metadata for Gen4 commands under `access_keys do` (description:, arguments:, transfer_paths:, aliases:).
|
|
140
|
+
# Other plugins expose this sub-tree with `mount:` (aoc, ats).
|
|
141
|
+
# :sync, :permission and :v3 are excluded: they are intermediate nodes declared separately.
|
|
142
|
+
SINGLE_PATH_ARG = [{name: :path}].freeze
|
|
158
143
|
COMMANDS_GEN4_SPEC = {
|
|
159
144
|
mkdir: {description: 'Create folder', arguments: SINGLE_PATH_ARG},
|
|
160
145
|
mklink: {description: 'Create symbolic link', arguments: SINGLE_PATH_ARG},
|
|
161
|
-
mkfile: {description: 'Create file', arguments: [{name: :path
|
|
162
|
-
rename: {description: 'Rename entry', arguments: [{name: :source_path
|
|
163
|
-
delete: {description: 'Delete entry', arguments: [{name: :
|
|
146
|
+
mkfile: {description: 'Create file', arguments: [{name: :path}, {name: :contents, mandatory: false, default: nil}]},
|
|
147
|
+
rename: {description: 'Rename entry', arguments: [{name: :source_path}, {name: :new_name}]},
|
|
148
|
+
delete: {description: 'Delete entry', arguments: [{name: :path, bulk: true}]},
|
|
164
149
|
upload: {description: 'Upload files', transfer_paths: :send},
|
|
165
150
|
download: {description: 'Download files', transfer_paths: :receive},
|
|
166
|
-
modify: {description: 'Modify file', arguments: [{name: :path
|
|
151
|
+
modify: {description: 'Modify file', arguments: [{name: :path}, {name: :file, type: Hash, schema: 'node:components.schemas.files-id-put-request'}]},
|
|
167
152
|
cat: {description: 'Show file contents', arguments: SINGLE_PATH_ARG},
|
|
168
153
|
show: {description: 'Show file info', arguments: SINGLE_PATH_ARG},
|
|
169
154
|
thumbnail: {description: 'Show file thumbnail', arguments: SINGLE_PATH_ARG},
|
|
170
155
|
bearer_token_node: {description: 'Show bearer token for file node', arguments: SINGLE_PATH_ARG},
|
|
171
156
|
node_info: {description: 'Show node info for file', arguments: SINGLE_PATH_ARG},
|
|
172
157
|
ls: {description: 'List files', arguments: SINGLE_PATH_ARG, aliases: [:browse]},
|
|
173
|
-
find: {description: 'Find files', arguments: SINGLE_PATH_ARG + FILTER_ARGS}
|
|
174
|
-
v3: {description: 'Legacy v3 commands on files'}
|
|
158
|
+
find: {description: 'Find files', arguments: SINGLE_PATH_ARG + FILTER_ARGS}
|
|
175
159
|
}.freeze
|
|
176
160
|
private_constant :SINGLE_PATH_ARG
|
|
177
161
|
|
|
178
|
-
# commands
|
|
162
|
+
# Root commands exposed by `cos node` and `shares files` (mount: only:)
|
|
179
163
|
COMMANDS_COS = %i[upload download info access_keys api_details transfer].freeze
|
|
180
|
-
COMMANDS_SHARES =
|
|
164
|
+
COMMANDS_SHARES = %i[api_details space mkdir mklink mkfile rename delete ls upload download cat sync transport spec].freeze
|
|
181
165
|
# `browse` display fields for gen4
|
|
182
166
|
GEN4_LS_FIELDS = %w[name type recursive_size size modified_time access_level].freeze
|
|
183
167
|
|
|
184
|
-
# @param api [Rest] an existing API object for the Node API
|
|
168
|
+
# @param api [Rest::Client] an existing API object for the Node API
|
|
185
169
|
def initialize(context:, api: nil)
|
|
186
|
-
super(context: context
|
|
187
|
-
|
|
188
|
-
@api_node =
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
170
|
+
super(context: context)
|
|
171
|
+
# Api::Node or Rest::Client (Shares), when provided by the host plugin
|
|
172
|
+
@api_node = api
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Node API: provided by the host plugin, or built from CLI options on first use.
|
|
176
|
+
# @return [Api::Node, Rest::Client]
|
|
177
|
+
def api_node
|
|
178
|
+
@api_node ||=
|
|
179
|
+
if OAuth::Factory.bearer_auth?(options.get_option(:password, mandatory: true))
|
|
193
180
|
# info is provided like node_info of aoc
|
|
194
181
|
Api::Node.new(
|
|
195
182
|
base_url: options.get_option(:url, mandatory: true),
|
|
@@ -213,9 +200,10 @@ module Aspera
|
|
|
213
200
|
def browse_gen3(path)
|
|
214
201
|
folders_to_process = path
|
|
215
202
|
folders_to_process = [folders_to_process]
|
|
203
|
+
# not a URL query: merged into the POST body of files/browse
|
|
216
204
|
query = options.get_option(:query) || {}
|
|
217
205
|
# special parameter: max number of entries in result
|
|
218
|
-
max_items = query.delete(
|
|
206
|
+
max_items = query.delete(Rest::List::MAX_ITEMS)
|
|
219
207
|
# special parameter: recursive browsing
|
|
220
208
|
recursive = query.delete('recursive')
|
|
221
209
|
# special parameter: only return one entry for the path, even if folder
|
|
@@ -233,7 +221,7 @@ module Aspera
|
|
|
233
221
|
total_count = nil
|
|
234
222
|
loop do
|
|
235
223
|
# example: send_result={'items'=>[{'file'=>"filename1","permissions"=>[{'name'=>'read'},{'name'=>'write'}]}]}
|
|
236
|
-
response =
|
|
224
|
+
response = api_node.create('files/browse', query)
|
|
237
225
|
# 'file','symbolic_link'
|
|
238
226
|
return Result::SingleObject.new(response['self']) if !Node.gen3_entry_folder?(response['self']) || only_path
|
|
239
227
|
items = response['items']
|
|
@@ -251,13 +239,13 @@ module Aspera
|
|
|
251
239
|
break if all_items.count >= total_count
|
|
252
240
|
offset += items.count
|
|
253
241
|
query['skip'] = offset
|
|
254
|
-
|
|
242
|
+
Rest::Parameters.instance.spinner_cb.call(all_items.count)
|
|
255
243
|
end
|
|
256
244
|
query.delete('skip')
|
|
257
245
|
end
|
|
258
246
|
return Result::ObjectList.new(all_items)
|
|
259
247
|
ensure
|
|
260
|
-
|
|
248
|
+
Rest::Parameters.instance.spinner_cb.call(action: :success)
|
|
261
249
|
end
|
|
262
250
|
|
|
263
251
|
# Create async transfer spec request from direction and folders
|
|
@@ -299,20 +287,9 @@ module Aspera
|
|
|
299
287
|
if (m = Parser.percent_selector(path))
|
|
300
288
|
Aspera.assert_values(m[:field], ['id'], type: BadArgument) { 'file id' }
|
|
301
289
|
val = m[:value]
|
|
302
|
-
return Api::NodeFileId.new(
|
|
290
|
+
return Api::NodeFileId.new(api_node, val.nil? || val.empty? ? top_file_id : val)
|
|
303
291
|
end
|
|
304
|
-
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
# Legacy helper: resolve a NodeFileId by reading path from the CLI argument stream.
|
|
308
|
-
# Used only by setup_access_key_do_permission and action_access_keys_do_permission
|
|
309
|
-
# (intermediate nodes whose path cannot yet be injected via execute_leaf).
|
|
310
|
-
def apifid_from_next_arg(top_file_id)
|
|
311
|
-
file_path = options.instance_identifier(description: 'path or %id:<id> or %id:') do |attribute, value|
|
|
312
|
-
Aspera.assert_values(attribute, ['id'], type: BadArgument) { 'file id' }
|
|
313
|
-
return Api::NodeFileId.new(@api_node, value.nil? || value.empty? ? top_file_id : value)
|
|
314
|
-
end
|
|
315
|
-
@api_node.resolve_api_fid(top_file_id, file_path)
|
|
292
|
+
api_node.resolve_api_fid(top_file_id, path)
|
|
316
293
|
end
|
|
317
294
|
|
|
318
295
|
# Search /async by name
|
|
@@ -322,8 +299,8 @@ module Aspera
|
|
|
322
299
|
# @raise [Cli::BadArgument] if no such sync, or not by name
|
|
323
300
|
def async_lookup(field, value)
|
|
324
301
|
Aspera.assert_values(field, ['name'], type: Cli::BadArgument) { 'search field' }
|
|
325
|
-
async_ids =
|
|
326
|
-
summaries =
|
|
302
|
+
async_ids = api_node.read('async/list')['sync_ids']
|
|
303
|
+
summaries = api_node.create('async/summary', {'syncs' => async_ids})['sync_summaries']
|
|
327
304
|
selected = summaries.find { |s| s['name'].eql?(value) }
|
|
328
305
|
raise Cli::BadIdentifier.new('sync', value, field: field) if selected.nil?
|
|
329
306
|
return selected['snid']
|
|
@@ -335,7 +312,7 @@ module Aspera
|
|
|
335
312
|
# @return [String] the resolved access key id
|
|
336
313
|
def lookup_access_key_self_id(field, value, **)
|
|
337
314
|
Aspera.assert(field.eql?('id') && value.eql?('self'), type: BadArgument) { 'only selector: %id:self' }
|
|
338
|
-
|
|
315
|
+
api_node.read('access_keys/self')['id']
|
|
339
316
|
end
|
|
340
317
|
|
|
341
318
|
# Search /asyncs by name
|
|
@@ -345,8 +322,8 @@ module Aspera
|
|
|
345
322
|
# @raise [Cli::BadArgument] if no such sync, or not by name
|
|
346
323
|
def ssync_lookup(field, value)
|
|
347
324
|
Aspera.assert_values(field, ['name'], type: Cli::BadArgument) { 'search field' }
|
|
348
|
-
|
|
349
|
-
sync_info =
|
|
325
|
+
api_node.read('asyncs')['ids'].each do |id|
|
|
326
|
+
sync_info = api_node.read("asyncs/#{id}")['configuration']
|
|
350
327
|
# name is unique, so we can return
|
|
351
328
|
return id if sync_info[field].eql?(value)
|
|
352
329
|
end
|
|
@@ -359,17 +336,16 @@ module Aspera
|
|
|
359
336
|
# :sync is declared separately (intermediate node with sub-commands).
|
|
360
337
|
GEN3_NODE_ACTIONS = {
|
|
361
338
|
ls: ->(path:, **) { browse_gen3(path) },
|
|
362
|
-
transport: ->(**) { Result::SingleObject.new(
|
|
363
|
-
spec: ->(**) { Result::SingleObject.new(
|
|
364
|
-
api_details: ->(**) { Result::SingleObject.new({base_url:
|
|
365
|
-
events: ->(**) { Result::ObjectList.new(
|
|
366
|
-
info: ->(**) { Result::SingleObject.new(
|
|
367
|
-
slash: ->(**) { Result::SingleObject.new(
|
|
368
|
-
license: ->(**) { Result::SingleObject.new(
|
|
339
|
+
transport: ->(**) { Result::SingleObject.new(api_node.transport_params) },
|
|
340
|
+
spec: ->(**) { Result::SingleObject.new(api_node.base_spec, fields: Formatter.all_but(*Transfer::Spec::SPECIFIC)) },
|
|
341
|
+
api_details: ->(**) { Result::SingleObject.new({base_url: api_node.base_url}.merge(api_node.params)) },
|
|
342
|
+
events: ->(**) { Result::ObjectList.new(api_node.read('events', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'events'))), fields: ->(f) { !f.start_with?('data') }) },
|
|
343
|
+
info: ->(**) { Result::SingleObject.new(api_node.read('info')) },
|
|
344
|
+
slash: ->(**) { Result::SingleObject.new(api_node.read('')) },
|
|
345
|
+
license: ->(**) { Result::SingleObject.new(api_node.read('license')) }
|
|
369
346
|
}.freeze
|
|
370
347
|
private_constant :GEN3_NODE_ACTIONS
|
|
371
348
|
COMMANDS_GEN3_SPEC.each do |cmd, spec|
|
|
372
|
-
next if cmd.eql?(:sync) # intermediate node with sub-commands
|
|
373
349
|
next if cmd.eql?(:access_keys) # intermediate node declared separately below
|
|
374
350
|
action = GEN3_NODE_ACTIONS[cmd]
|
|
375
351
|
command cmd, **spec, **(action ? {action: action} : {})
|
|
@@ -379,8 +355,7 @@ module Aspera
|
|
|
379
355
|
Sync::Operations::DIRECTIONS.each do |dir|
|
|
380
356
|
command dir, description: "#{dir.capitalize}-sync (Gen3)", transfer_paths: :send, arguments: SyncActions::PATH_AND_INFO_ARGS
|
|
381
357
|
end
|
|
382
|
-
|
|
383
|
-
SyncActions.register_sync_admin_commands(self, :admin)
|
|
358
|
+
sync_admin_commands :admin
|
|
384
359
|
end
|
|
385
360
|
# access_keys sub-tree
|
|
386
361
|
command :access_keys, description: 'Manage access keys'
|
|
@@ -389,9 +364,9 @@ module Aspera
|
|
|
389
364
|
arguments: [{name: :access_key_id, type: :identifier}],
|
|
390
365
|
setup: :setup_access_key_do
|
|
391
366
|
command :set_bearer_key, description: 'Set bearer key on access key',
|
|
392
|
-
arguments: [{name: :access_key_id}, {name: :bearer_key_pem
|
|
367
|
+
arguments: [{name: :access_key_id, type: :identifier}, {name: :bearer_key_pem}]
|
|
393
368
|
crud_commands entity: 'access_keys',
|
|
394
|
-
api:
|
|
369
|
+
api: :api_node,
|
|
395
370
|
|
|
396
371
|
body_component: Schema::Registry::NODE,
|
|
397
372
|
lookup: :lookup_access_key_self_id
|
|
@@ -401,35 +376,49 @@ module Aspera
|
|
|
401
376
|
COMMANDS_GEN4_SPEC.each do |cmd, spec|
|
|
402
377
|
command cmd, **spec
|
|
403
378
|
end
|
|
404
|
-
command :
|
|
379
|
+
command :v3, description: 'Legacy v3 commands on files', mount: {plugin: self, instance: :v3_node_plugin}
|
|
380
|
+
command :permission, description: 'Manage permissions',
|
|
381
|
+
arguments: [{name: :path, description: 'Path, or %id:<file id>, or %id: for root'}],
|
|
382
|
+
setup: :setup_access_key_do_permission
|
|
405
383
|
command :sync, description: 'Synchronize folders'
|
|
406
384
|
commands_under :sync do
|
|
407
385
|
Sync::Operations::DIRECTIONS.each do |dir|
|
|
408
386
|
command dir, description: "#{dir.capitalize}-sync", transfer_paths: :send, arguments: SyncActions::PATH_AND_INFO_ARGS
|
|
409
387
|
end
|
|
410
|
-
|
|
411
|
-
SyncActions.register_sync_admin_commands(self, :admin)
|
|
388
|
+
sync_admin_commands :admin
|
|
412
389
|
end
|
|
413
390
|
end
|
|
414
391
|
commands_under %i[access_keys do permission] do
|
|
415
392
|
command :list, description: 'List permissions on a file'
|
|
416
393
|
command :show, description: 'Show a permission',
|
|
417
|
-
arguments: [{name: :
|
|
418
|
-
action: ->(apifid:,
|
|
394
|
+
arguments: [{name: :permission_id, type: :identifier}],
|
|
395
|
+
action: ->(apifid:, permission_id:, **) { Result::SingleObject.new(apifid.node_api.read("permissions/#{permission_id}")) }
|
|
419
396
|
command :create, description: 'Create a permission',
|
|
420
|
-
arguments: [{name: :
|
|
421
|
-
command
|
|
422
|
-
|
|
397
|
+
arguments: [{name: :permission, type: Hash, schema: 'node:components.schemas.permissions-post-request'}]
|
|
398
|
+
command(
|
|
399
|
+
:modify, description: 'Modify a permission',
|
|
400
|
+
arguments: [{name: :permission_id, type: :identifier}, {name: :permission, type: Hash, schema: 'node:components.schemas.permissions-id-put-request'}],
|
|
401
|
+
action: lambda do |permission:, apifid:, permission_id:, **|
|
|
402
|
+
apifid.node_api.update("permissions/#{permission_id}", permission)
|
|
403
|
+
Result::Status.new('Updated')
|
|
404
|
+
end
|
|
405
|
+
)
|
|
423
406
|
command :delete, description: 'Delete permissions',
|
|
424
|
-
arguments: [{name: :
|
|
407
|
+
arguments: [{name: :permission_id, type: :identifier, bulk: true}]
|
|
425
408
|
end
|
|
426
409
|
# async (legacy /async)
|
|
427
|
-
commands_under :async, description: '
|
|
428
|
-
command :list, description: 'List async sync IDs', action: -> { Result::ValueList.new(
|
|
410
|
+
commands_under :async, description: 'Manage legacy synchronizations (/async)' do
|
|
411
|
+
command :list, description: 'List async sync IDs', action: ->(**) { Result::ValueList.new(api_node.read('async/list')['sync_ids']) }
|
|
429
412
|
command :show, description: 'Show async summary',
|
|
430
413
|
arguments: [{name: :async_id, type: :identifier, lookup: :async_lookup}]
|
|
431
|
-
command
|
|
432
|
-
|
|
414
|
+
command(
|
|
415
|
+
:delete, description: 'Delete async',
|
|
416
|
+
arguments: [{name: :async_id, type: :identifier, lookup: :async_lookup}],
|
|
417
|
+
action: lambda do |async_id:, **|
|
|
418
|
+
async_ids = async_id.eql?(SpecialValues::ALL) ? api_node.read('async/list')['sync_ids'] : [async_id]
|
|
419
|
+
Result::SingleObject.new(api_node.create('async/delete', {'syncs' => async_ids}))
|
|
420
|
+
end
|
|
421
|
+
)
|
|
433
422
|
command :bandwidth, description: 'Show async bandwidth',
|
|
434
423
|
arguments: [{name: :async_id, type: :identifier, lookup: :async_lookup}]
|
|
435
424
|
command :files, description: 'List async files',
|
|
@@ -438,93 +427,138 @@ module Aspera
|
|
|
438
427
|
arguments: [{name: :async_id, type: :identifier, lookup: :async_lookup}]
|
|
439
428
|
end
|
|
440
429
|
# ssync (/asyncs)
|
|
441
|
-
|
|
442
|
-
commands_under :ssync, description: 'synchronization (/asyncs)' do
|
|
430
|
+
commands_under :ssync, description: 'Manage synchronizations (/asyncs)' do
|
|
443
431
|
crud_commands entity: 'asyncs',
|
|
444
|
-
|
|
432
|
+
name: 'sync session',
|
|
433
|
+
id_name: :ssync_id,
|
|
434
|
+
api: :api_node,
|
|
445
435
|
operations: %i[create list show delete],
|
|
446
436
|
items_key: 'ids',
|
|
447
437
|
lookup: :ssync_lookup
|
|
448
|
-
command
|
|
449
|
-
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
450
|
-
action:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
438
|
+
command(
|
|
439
|
+
:start, description: 'Start a sync', arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
440
|
+
action: lambda do |ssync_id:, **|
|
|
441
|
+
api_node.call(operation: 'POST', subpath: "asyncs/#{ssync_id}/start", content_type: Mime::TEXT, body: '', ret: :resp).body
|
|
442
|
+
Result::Status.new('Done')
|
|
443
|
+
end
|
|
444
|
+
)
|
|
445
|
+
command(
|
|
446
|
+
:stop, description: 'Stop a sync', arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
447
|
+
action: lambda do |ssync_id:, **|
|
|
448
|
+
api_node.call(operation: 'POST', subpath: "asyncs/#{ssync_id}/stop", content_type: Mime::TEXT, body: '', ret: :resp).body
|
|
449
|
+
Result::Status.new('Done')
|
|
450
|
+
end
|
|
451
|
+
)
|
|
454
452
|
command :bandwidth, description: 'Show sync bandwidth',
|
|
455
453
|
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
456
|
-
action: ->(ssync_id:, **) { Result::SingleObject.new(
|
|
454
|
+
action: ->(ssync_id:, **) { Result::SingleObject.new(api_node.read("asyncs/#{ssync_id}/bandwidth", query_read_delete(default: {}))) }
|
|
457
455
|
command :counters, description: 'Show sync counters',
|
|
458
456
|
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
459
|
-
action: ->(ssync_id:, **) { Result::SingleObject.new(
|
|
457
|
+
action: ->(ssync_id:, **) { Result::SingleObject.new(api_node.read("asyncs/#{ssync_id}/counters", query_read_delete(default: {}))) }
|
|
460
458
|
command :files, description: 'List sync files',
|
|
461
459
|
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
462
|
-
action: ->(ssync_id:, **) { Result::SingleObject.new(
|
|
460
|
+
action: ->(ssync_id:, **) { Result::SingleObject.new(api_node.read("asyncs/#{ssync_id}/files", query_read_delete(default: {}))) }
|
|
463
461
|
command :state, description: 'Show sync state',
|
|
464
462
|
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
465
|
-
action: ->(ssync_id:, **) { Result::SingleObject.new(
|
|
463
|
+
action: ->(ssync_id:, **) { Result::SingleObject.new(api_node.read("asyncs/#{ssync_id}/state")) }
|
|
466
464
|
command :summary, description: 'Show sync summary',
|
|
467
465
|
arguments: [{name: :ssync_id, type: :identifier, lookup: :ssync_lookup}],
|
|
468
|
-
action: ->(ssync_id:, **) { Result::SingleObject.new(
|
|
466
|
+
action: ->(ssync_id:, **) { Result::SingleObject.new(api_node.read("asyncs/#{ssync_id}/summary")) }
|
|
469
467
|
end
|
|
470
468
|
# stream
|
|
471
469
|
command :stream, description: 'Manage stream operations'
|
|
472
470
|
commands_under :stream do
|
|
473
|
-
command :list, description: 'List streams', action: -> { Result::ObjectList.new(
|
|
471
|
+
command :list, description: 'List streams', action: ->(**) { Result::ObjectList.new(api_node.read('ops/transfers', query_read_delete), fields: %w[id status]) }
|
|
474
472
|
command :create, description: 'Create a stream',
|
|
475
|
-
arguments: [{name: :
|
|
476
|
-
action: ->(
|
|
473
|
+
arguments: [{name: :stream, type: Hash, schema: 'node:components.schemas.transferPostRequest'}],
|
|
474
|
+
action: ->(stream:, **) { Result::SingleObject.new(api_node.create('streams', stream)) }
|
|
477
475
|
command :show, description: 'Show a stream',
|
|
478
476
|
arguments: [{name: :transfer_id, type: :identifier}],
|
|
479
|
-
action: ->(transfer_id:, **) { Result::SingleObject.new(
|
|
477
|
+
action: ->(transfer_id:, **) { Result::SingleObject.new(api_node.read("ops/transfers/#{transfer_id}")) }
|
|
480
478
|
command :modify, description: 'Modify a stream',
|
|
481
|
-
arguments: [{name: :transfer_id, type: :identifier}, {name: :
|
|
482
|
-
action: ->(
|
|
479
|
+
arguments: [{name: :transfer_id, type: :identifier}, {name: :stream, type: Hash, schema: 'node:components.schemas.transferPutRequest'}],
|
|
480
|
+
action: ->(stream:, transfer_id:, **) { Result::SingleObject.new(api_node.update("streams/#{transfer_id}", stream)) }
|
|
483
481
|
command :cancel, description: 'Cancel a stream',
|
|
484
482
|
arguments: [{name: :transfer_id, type: :identifier}],
|
|
485
|
-
action: ->(transfer_id:, **) { Result::SingleObject.new(
|
|
483
|
+
action: ->(transfer_id:, **) { Result::SingleObject.new(api_node.cancel("streams/#{transfer_id}")) }
|
|
486
484
|
end
|
|
487
485
|
# transfer
|
|
488
486
|
command :transfer, description: 'Manage transfer operations'
|
|
489
487
|
commands_under :transfer do
|
|
490
488
|
command :list, description: 'List transfers'
|
|
491
|
-
command
|
|
492
|
-
|
|
489
|
+
command(
|
|
490
|
+
:cancel, description: 'Cancel a transfer',
|
|
491
|
+
arguments: [{name: :transfer_id, type: :identifier}],
|
|
492
|
+
action: lambda do |transfer_id:, **|
|
|
493
|
+
api_node.cancel("ops/transfers/#{transfer_id}")
|
|
494
|
+
Result::Status.new('Cancelled')
|
|
495
|
+
end
|
|
496
|
+
)
|
|
493
497
|
command :show, description: 'Show a transfer',
|
|
494
498
|
arguments: [{name: :transfer_id, type: :identifier}],
|
|
495
|
-
action: ->(transfer_id:, **) { Result::SingleObject.new(
|
|
496
|
-
command
|
|
497
|
-
|
|
499
|
+
action: ->(transfer_id:, **) { Result::SingleObject.new(api_node.read("ops/transfers/#{transfer_id}")) }
|
|
500
|
+
command(
|
|
501
|
+
:modify, description: 'Modify a transfer',
|
|
502
|
+
arguments: [{name: :transfer_id, type: :identifier}, {name: :transfer, type: Hash, schema: 'node:components.schemas.transferPutRequest'}],
|
|
503
|
+
action: lambda do |transfer:, transfer_id:, **|
|
|
504
|
+
api_node.update("ops/transfers/#{transfer_id}", transfer)
|
|
505
|
+
Result::Status.new('Modified')
|
|
506
|
+
end
|
|
507
|
+
)
|
|
498
508
|
command :bandwidth_average, description: 'Show average bandwidth per period'
|
|
499
509
|
command :sessions, description: 'List transfer sessions'
|
|
500
510
|
end
|
|
501
511
|
# service
|
|
502
512
|
command :service, description: 'Manage services'
|
|
503
513
|
commands_under :service do
|
|
504
|
-
command :list,
|
|
505
|
-
command
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
514
|
+
command :list, description: 'List services', action: ->(**) { Result::ObjectList.new(api_node.read('rund/services')['services']) }
|
|
515
|
+
command(
|
|
516
|
+
:create, description: 'Create a service',
|
|
517
|
+
arguments: [{name: :service, type: Hash}],
|
|
518
|
+
action: lambda do |service:, **|
|
|
519
|
+
resp = api_node.create('rund/services', service)
|
|
520
|
+
Result::Status.new("#{resp['id']} created")
|
|
521
|
+
end
|
|
522
|
+
)
|
|
523
|
+
command(
|
|
524
|
+
:delete, description: 'Delete a service',
|
|
525
|
+
arguments: [{name: :service_id, type: :identifier}],
|
|
526
|
+
action: lambda do |service_id:, **|
|
|
527
|
+
api_node.delete("rund/services/#{service_id}")
|
|
528
|
+
Result::Status.new("#{service_id} deleted")
|
|
529
|
+
end
|
|
530
|
+
)
|
|
509
531
|
end
|
|
510
532
|
# watch_folder
|
|
511
533
|
command :watch_folder, description: 'Manage watch folders', setup: :setup_watch_folder
|
|
512
534
|
commands_under :watch_folder do
|
|
513
535
|
command :create, description: 'Create a watch folder',
|
|
514
|
-
arguments: [{name: :
|
|
515
|
-
action: ->(
|
|
536
|
+
arguments: [{name: :watch_folder, type: Hash}],
|
|
537
|
+
action: ->(watch_folder:, **) { Result::Status.new("#{api_node.create('v3/watchfolders', watch_folder)['id']} created") }
|
|
516
538
|
command :list, description: 'List watch folders',
|
|
517
|
-
action: -> { Result::ValueList.new(
|
|
539
|
+
action: ->(**) { Result::ValueList.new(api_node.read('v3/watchfolders', query_read_delete)['ids']) }
|
|
518
540
|
command :show, description: 'Show a watch folder',
|
|
519
541
|
arguments: [{name: :watch_folder_id, type: :identifier}],
|
|
520
|
-
action: ->(watch_folder_id:, **) { Result::SingleObject.new(
|
|
521
|
-
command
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
542
|
+
action: ->(watch_folder_id:, **) { Result::SingleObject.new(api_node.read("v3/watchfolders/#{watch_folder_id}")) }
|
|
543
|
+
command(
|
|
544
|
+
:modify, description: 'Modify a watch folder',
|
|
545
|
+
arguments: [{name: :watch_folder_id, type: :identifier}, {name: :watch_folder, type: Hash}],
|
|
546
|
+
action: lambda do |watch_folder:, watch_folder_id:, **|
|
|
547
|
+
api_node.update("v3/watchfolders/#{watch_folder_id}", watch_folder)
|
|
548
|
+
Result::Status.new("#{watch_folder_id} updated")
|
|
549
|
+
end
|
|
550
|
+
)
|
|
551
|
+
command(
|
|
552
|
+
:delete, description: 'Delete a watch folder',
|
|
553
|
+
arguments: [{name: :watch_folder_id, type: :identifier}],
|
|
554
|
+
action: lambda do |watch_folder_id:, **|
|
|
555
|
+
api_node.delete("v3/watchfolders/#{watch_folder_id}")
|
|
556
|
+
Result::Status.new("#{watch_folder_id} deleted")
|
|
557
|
+
end
|
|
558
|
+
)
|
|
559
|
+
command :state, description: 'Show watch folder state',
|
|
526
560
|
arguments: [{name: :watch_folder_id, type: :identifier}],
|
|
527
|
-
action: ->(watch_folder_id:, **) { Result::SingleObject.new(
|
|
561
|
+
action: ->(watch_folder_id:, **) { Result::SingleObject.new(api_node.read("v3/watchfolders/#{watch_folder_id}/state")) }
|
|
528
562
|
end
|
|
529
563
|
# central
|
|
530
564
|
command :central, description: 'Query Central service'
|
|
@@ -534,21 +568,29 @@ module Aspera
|
|
|
534
568
|
end
|
|
535
569
|
commands_under %i[central session] do
|
|
536
570
|
command :list, description: 'List sessions',
|
|
537
|
-
arguments: [{name: :
|
|
571
|
+
arguments: [{name: :criteria, type: Hash, mandatory: false, default: nil}]
|
|
538
572
|
end
|
|
539
573
|
commands_under %i[central file] do
|
|
540
574
|
command :list, description: 'List file transfers',
|
|
541
|
-
arguments: [{name: :
|
|
575
|
+
arguments: [{name: :criteria, type: Hash, mandatory: false, default: nil}]
|
|
542
576
|
command :modify, description: 'Modify file transfer validation',
|
|
543
|
-
arguments: [{name: :
|
|
577
|
+
arguments: [{name: :file, type: Hash, mandatory: false, default: nil}]
|
|
544
578
|
end
|
|
545
579
|
# Standalone leaf commands
|
|
546
580
|
command :asperabrowser, description: 'Open Aspera browser'
|
|
547
|
-
command :basic_token, description: 'Generate basic auth token', action: -> { Result::Text.new(Rest.basic_authorization(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true))) }
|
|
548
|
-
command
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
581
|
+
command :basic_token, description: 'Generate basic auth token', action: ->(**) { Result::Text.new(Rest.basic_authorization(options.get_option(:username, mandatory: true), options.get_option(:password, mandatory: true))) }
|
|
582
|
+
command(
|
|
583
|
+
:bearer_token, description: 'Generate bearer token',
|
|
584
|
+
arguments: [{name: :private_key_pem}, {name: :token, type: Hash, schema: 'opts:components.schemas.NodeBearerTokenOptions'}],
|
|
585
|
+
action: lambda do |private_key_pem:, token:, **|
|
|
586
|
+
private_key = OpenSSL::PKey::RSA.new(private_key_pem)
|
|
587
|
+
access_key = options.get_option(:username, mandatory: true)
|
|
588
|
+
Result::Text.new(Api::Node.bearer_token(payload: token, access_key: access_key, private_key: private_key))
|
|
589
|
+
end
|
|
590
|
+
)
|
|
591
|
+
command :emulator, description: 'Start node emulator',
|
|
592
|
+
aliases: [:simulator],
|
|
593
|
+
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}, schema: Schema::Registry::NODE_EMULATOR_OPTIONS}]
|
|
552
594
|
command :telemetry, description: 'Report telemetry to external system',
|
|
553
595
|
arguments: [{name: :parameters, type: Hash, mandatory: false, default: {}, schema: 'opts:components.schemas.NodeTelemetryOptions'}]
|
|
554
596
|
|
|
@@ -557,15 +599,16 @@ module Aspera
|
|
|
557
599
|
def action_delete(paths:, **)
|
|
558
600
|
# TODO: add query for recursive
|
|
559
601
|
paths_to_delete = Array(paths)
|
|
560
|
-
resp =
|
|
602
|
+
resp = api_node.create('files/delete', {paths: paths_to_delete.map { |i| {'path' => i.start_with?('/') ? i : "/#{i}"} }})
|
|
561
603
|
cli_result_from_paths_response(resp, 'file deleted')
|
|
562
604
|
end
|
|
563
605
|
|
|
564
|
-
def action_search(
|
|
565
|
-
parameters = {'path' =>
|
|
606
|
+
def action_search(path:, **)
|
|
607
|
+
parameters = {'path' => path}
|
|
608
|
+
# not a URL query: merged into the POST body of files/search
|
|
566
609
|
other_options = options.get_option(:query)
|
|
567
610
|
parameters.merge!(other_options) unless other_options.nil?
|
|
568
|
-
resp =
|
|
611
|
+
resp = api_node.create('files/search', parameters)
|
|
569
612
|
return Result::Empty.new if resp['items'].empty?
|
|
570
613
|
fields = resp['items'].first.keys.reject { |i| SEARCH_REMOVE_FIELDS.include?(i) }
|
|
571
614
|
formatter.display_item_count(resp['item_count'], resp['total_count'])
|
|
@@ -573,32 +616,32 @@ module Aspera
|
|
|
573
616
|
Result::ObjectList.new(resp['items'], fields: fields)
|
|
574
617
|
end
|
|
575
618
|
|
|
576
|
-
def action_space(
|
|
577
|
-
|
|
578
|
-
resp =
|
|
619
|
+
def action_space(paths:, **)
|
|
620
|
+
paths = Array(paths)
|
|
621
|
+
resp = api_node.create('space', {'paths' => paths.map { |i| {path: i} }})
|
|
579
622
|
Result::ObjectList.new(resp['paths'])
|
|
580
623
|
end
|
|
581
624
|
|
|
582
|
-
def action_mkdir(
|
|
583
|
-
|
|
584
|
-
resp =
|
|
625
|
+
def action_mkdir(paths:, **)
|
|
626
|
+
paths = Array(paths)
|
|
627
|
+
resp = api_node.create('files/create', {'paths' => paths.map { |i| {type: :directory, path: i} }})
|
|
585
628
|
cli_result_from_paths_response(resp, 'folder created')
|
|
586
629
|
end
|
|
587
630
|
|
|
588
631
|
def action_mklink(target:, link_path:, **)
|
|
589
|
-
resp =
|
|
632
|
+
resp = api_node.create('files/create', {'paths' => [{type: :symbolic_link, path: link_path, target: {path: target}}]})
|
|
590
633
|
cli_result_from_paths_response(resp, 'link created')
|
|
591
634
|
end
|
|
592
635
|
|
|
593
|
-
def action_mkfile(
|
|
636
|
+
def action_mkfile(path:, contents:, **)
|
|
594
637
|
contents64 = contents.nil? ? '' : Base64.strict_encode64(contents)
|
|
595
|
-
resp =
|
|
638
|
+
resp = api_node.create('files/create', {'paths' => [{type: :file, path: path, contents: contents64}]})
|
|
596
639
|
cli_result_from_paths_response(resp, 'file created')
|
|
597
640
|
end
|
|
598
641
|
|
|
599
|
-
def action_rename(
|
|
642
|
+
def action_rename(folder:, source:, destination:, **)
|
|
600
643
|
# TODO: multiple ?
|
|
601
|
-
resp =
|
|
644
|
+
resp = api_node.create('files/rename', {'paths' => [{'path' => folder, 'source' => source, 'destination' => destination}]})
|
|
602
645
|
cli_result_from_paths_response(resp, 'entry moved')
|
|
603
646
|
end
|
|
604
647
|
|
|
@@ -606,11 +649,11 @@ module Aspera
|
|
|
606
649
|
def sync_gen3_block
|
|
607
650
|
lambda do |direction, local_path, remote_path|
|
|
608
651
|
request_transfer_spec = sync_spec_request(direction, local_path, remote_path)
|
|
609
|
-
|
|
610
|
-
transfer_spec =
|
|
652
|
+
api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
|
|
653
|
+
transfer_spec = api_node.create(
|
|
611
654
|
'files/sync_setup',
|
|
612
655
|
{transfer_requests: [{transfer_request: request_transfer_spec}]}
|
|
613
|
-
)['transfer_specs'].first
|
|
656
|
+
)['transfer_specs'].first&.fetch('transfer_spec')
|
|
614
657
|
transfer_spec.delete_if { |_k, v| v.nil? }
|
|
615
658
|
Log.dump(:ts, transfer_spec)
|
|
616
659
|
transfer_spec
|
|
@@ -618,45 +661,45 @@ module Aspera
|
|
|
618
661
|
end
|
|
619
662
|
|
|
620
663
|
Sync::Operations::DIRECTIONS.each do |dir|
|
|
621
|
-
|
|
664
|
+
define_action_method([:sync, dir]) { |path:, sync_info: {}, **| run_sync_transfer(dir, path: path, sync_info: sync_info, &sync_gen3_block) }
|
|
622
665
|
end
|
|
623
666
|
|
|
624
|
-
def action_upload
|
|
667
|
+
def action_upload(**)
|
|
625
668
|
# empty transfer spec for authorization request
|
|
626
669
|
request_transfer_spec = {}
|
|
627
670
|
request_transfer_spec[:paths] = [{destination: transfer.destination_folder(Transfer::Spec::DIRECTION_SEND)}]
|
|
628
671
|
# add fixed parameters if any (for COS)
|
|
629
|
-
|
|
672
|
+
api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
|
|
630
673
|
Api::Node.add_public_key(request_transfer_spec)
|
|
631
674
|
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
|
632
|
-
transfer_spec =
|
|
675
|
+
transfer_spec = api_node.create('files/upload_setup', setup_payload)['transfer_specs'].first&.fetch('transfer_spec')
|
|
633
676
|
Api::Node.add_private_key(transfer_spec)
|
|
634
677
|
transfer_spec.delete('paths')
|
|
635
678
|
Runner.result_transfer(transfer.start(transfer_spec))
|
|
636
679
|
end
|
|
637
680
|
|
|
638
|
-
def action_download
|
|
681
|
+
def action_download(**)
|
|
639
682
|
# empty transfer spec for authorization request
|
|
640
683
|
request_transfer_spec = {}
|
|
641
684
|
request_transfer_spec[:paths] = transfer.ts_source_paths
|
|
642
685
|
# add fixed parameters if any (for COS)
|
|
643
|
-
|
|
686
|
+
api_node.add_tspec_info(request_transfer_spec) if api_node.respond_to?(:add_tspec_info)
|
|
644
687
|
Api::Node.add_public_key(request_transfer_spec)
|
|
645
688
|
setup_payload = {transfer_requests: [{transfer_request: request_transfer_spec}]}
|
|
646
|
-
transfer_spec =
|
|
689
|
+
transfer_spec = api_node.create('files/download_setup', setup_payload)['transfer_specs'].first&.fetch('transfer_spec')
|
|
647
690
|
Api::Node.add_private_key(transfer_spec)
|
|
648
691
|
Runner.result_transfer(transfer.start(transfer_spec))
|
|
649
692
|
end
|
|
650
693
|
|
|
651
|
-
def action_cat(
|
|
652
|
-
http =
|
|
694
|
+
def action_cat(path:, **)
|
|
695
|
+
http = api_node.read("files/#{URI.encode_www_form_component(path)}/contents", ret: :resp)
|
|
653
696
|
Result::Text.new(http.body)
|
|
654
697
|
end
|
|
655
698
|
|
|
656
|
-
def action_health
|
|
699
|
+
def action_health(**)
|
|
657
700
|
nagios = Nagios.new
|
|
658
701
|
begin
|
|
659
|
-
info =
|
|
702
|
+
info = api_node.read('info')
|
|
660
703
|
nagios.add_ok('node api', 'accessible')
|
|
661
704
|
nagios.check_time_offset(info['current_time'], 'node api')
|
|
662
705
|
nagios.check_product_version('node api', 'entsrv', info['version'])
|
|
@@ -664,7 +707,7 @@ module Aspera
|
|
|
664
707
|
nagios.add_critical('node api', e.to_s)
|
|
665
708
|
end
|
|
666
709
|
begin
|
|
667
|
-
|
|
710
|
+
api_node.call(
|
|
668
711
|
operation: 'POST',
|
|
669
712
|
subpath: 'services/soap/Transfer-201210',
|
|
670
713
|
content_type: Mime::TEXT,
|
|
@@ -681,49 +724,32 @@ module Aspera
|
|
|
681
724
|
|
|
682
725
|
# watch_folder setup: inject required API header (avoids "Unable to convert 2016_09_14 configuration")
|
|
683
726
|
def setup_watch_folder(**)
|
|
684
|
-
|
|
685
|
-
@api_node.params[:headers]['X-aspera-WF-version'] = '2017_10_23'
|
|
727
|
+
api_node.headers['X-aspera-WF-version'] = '2017_10_23'
|
|
686
728
|
{}
|
|
687
729
|
end
|
|
688
730
|
|
|
689
|
-
def action_access_keys_do_permission_modify(data:, apifid:, perm_id:, **)
|
|
690
|
-
apifid.node_api.update("permissions/#{perm_id}", data)
|
|
691
|
-
Result::Status.new('Updated')
|
|
692
|
-
end
|
|
693
|
-
|
|
694
|
-
def action_watch_folder_modify(data:, watch_folder_id:, **)
|
|
695
|
-
@api_node.update("v3/watchfolders/#{watch_folder_id}", data)
|
|
696
|
-
Result::Status.new("#{watch_folder_id} updated")
|
|
697
|
-
end
|
|
698
|
-
|
|
699
|
-
def action_watch_folder_delete(watch_folder_id:, **)
|
|
700
|
-
@api_node.delete("v3/watchfolders/#{watch_folder_id}")
|
|
701
|
-
Result::Status.new("#{watch_folder_id} deleted")
|
|
702
|
-
end
|
|
703
|
-
|
|
704
731
|
# access_keys > do - setup: resolve access key and root file id
|
|
705
|
-
# access_key_id is resolved by execute_leaf (via instance_arg in CommandSpec)
|
|
706
732
|
# @return [Hash] context hash containing :do_root_file_id
|
|
707
733
|
def setup_access_key_do(access_key_id:, **)
|
|
708
734
|
@do_root_file_id = options.get_option(:root_id)
|
|
709
735
|
if @do_root_file_id.nil?
|
|
710
|
-
ak_info =
|
|
711
|
-
ak_secret = context.secret_finder.lookup(url:
|
|
736
|
+
ak_info = api_node.read("access_keys/#{access_key_id}")
|
|
737
|
+
ak_secret = context.secret_finder.lookup(url: api_node.base_url, username: ak_info['id'])
|
|
712
738
|
if !access_key_id.eql?('self')
|
|
713
|
-
Aspera.assert(ak_secret, type: Cli::MissingArgument) { "Please provide secret for #{ak_info['id']} using option: secret or by setting a preset for #{ak_info['id']}@#{
|
|
714
|
-
|
|
715
|
-
|
|
739
|
+
Aspera.assert(ak_secret, type: Cli::MissingArgument) { "Please provide secret for #{ak_info['id']} using option: secret or by setting a preset for #{ak_info['id']}@#{api_node.base_url}." }
|
|
740
|
+
api_node.auth_params[:username] = ak_info['id']
|
|
741
|
+
api_node.auth_params[:password] = ak_secret
|
|
716
742
|
end
|
|
717
743
|
@do_root_file_id = ak_info['root_file_id']
|
|
718
744
|
end
|
|
719
745
|
{do_root_file_id: @do_root_file_id}
|
|
720
746
|
end
|
|
721
747
|
|
|
722
|
-
# access_keys > do > permission - setup: resolve apifid from
|
|
723
|
-
#
|
|
748
|
+
# access_keys > do > permission - setup: resolve apifid from the path argument of the node
|
|
749
|
+
# do_root_file_id: comes from ctx (setup_access_key_do, or seed of a mount)
|
|
724
750
|
# @return [Hash] context hash containing :apifid
|
|
725
|
-
def setup_access_key_do_permission(**)
|
|
726
|
-
{apifid:
|
|
751
|
+
def setup_access_key_do_permission(do_root_file_id:, path:, **)
|
|
752
|
+
{apifid: apifid_from_path(do_root_file_id, path)}
|
|
727
753
|
end
|
|
728
754
|
|
|
729
755
|
# access_keys > do > ls
|
|
@@ -737,7 +763,7 @@ module Aspera
|
|
|
737
763
|
# access_keys > do > find
|
|
738
764
|
def action_access_keys_do_find(path:, filter: nil, do_root_file_id:, **)
|
|
739
765
|
apifid = apifid_from_path(do_root_file_id, path)
|
|
740
|
-
Result::ObjectList.new(
|
|
766
|
+
Result::ObjectList.new(api_node.find_files(apifid.file_id, Base.file_matcher(filter)), fields: ['path'])
|
|
741
767
|
end
|
|
742
768
|
|
|
743
769
|
# access_keys > do > cat
|
|
@@ -753,9 +779,9 @@ module Aspera
|
|
|
753
779
|
end
|
|
754
780
|
|
|
755
781
|
# access_keys > do > modify
|
|
756
|
-
def action_access_keys_do_modify(path:,
|
|
782
|
+
def action_access_keys_do_modify(path:, file:, do_root_file_id:, **)
|
|
757
783
|
apifid = apifid_from_path(do_root_file_id, path)
|
|
758
|
-
apifid.node_api.update("files/#{apifid.file_id}",
|
|
784
|
+
apifid.node_api.update("files/#{apifid.file_id}", file)
|
|
759
785
|
Result::Status.new('Done')
|
|
760
786
|
end
|
|
761
787
|
|
|
@@ -767,19 +793,19 @@ module Aspera
|
|
|
767
793
|
|
|
768
794
|
# access_keys > do > rename
|
|
769
795
|
def action_access_keys_do_rename(source_path:, new_name:, do_root_file_id:, **)
|
|
770
|
-
apifid =
|
|
796
|
+
apifid = api_node.resolve_api_fid(do_root_file_id, source_path)
|
|
771
797
|
apifid.node_api.update("files/#{apifid.file_id}", {name: new_name})
|
|
772
798
|
Result::Status.new("renamed to #{new_name}")
|
|
773
799
|
end
|
|
774
800
|
|
|
775
801
|
# access_keys > do > delete
|
|
776
|
-
def action_access_keys_do_delete(
|
|
777
|
-
bulk_result(
|
|
802
|
+
def action_access_keys_do_delete(path:, do_root_file_id:, **)
|
|
803
|
+
bulk_result(path, command: :delete, id_result: 'path') do |l_path|
|
|
778
804
|
apifid = if (m = Parser.percent_selector(l_path))
|
|
779
805
|
Aspera.assert_values(m[:field], ['id'], type: BadIdentifier)
|
|
780
|
-
Api::NodeFileId.new(
|
|
806
|
+
Api::NodeFileId.new(api_node, m[:value])
|
|
781
807
|
else
|
|
782
|
-
|
|
808
|
+
api_node.resolve_api_fid(do_root_file_id, l_path)
|
|
783
809
|
end
|
|
784
810
|
apifid.node_api.delete("files/#{apifid.file_id}")
|
|
785
811
|
{'path' => l_path}
|
|
@@ -790,40 +816,33 @@ module Aspera
|
|
|
790
816
|
def sync_gen4_block(do_root_file_id)
|
|
791
817
|
lambda do |direction, _local_path, remote_path|
|
|
792
818
|
ts_direction = direction.eql?(:pull) ? Transfer::Spec::DIRECTION_RECEIVE : Transfer::Spec::DIRECTION_SEND
|
|
793
|
-
apifid =
|
|
819
|
+
apifid = api_node.resolve_api_fid(do_root_file_id, remote_path)
|
|
794
820
|
apifid.node_api.transfer_spec_gen4(apifid.file_id, ts_direction)
|
|
795
821
|
end
|
|
796
822
|
end
|
|
797
823
|
|
|
798
824
|
Sync::Operations::DIRECTIONS.each do |dir|
|
|
799
|
-
|
|
825
|
+
define_action_method([:access_keys, :do, :sync, dir]) do |path:, sync_info: {}, do_root_file_id:, **|
|
|
800
826
|
run_sync_transfer(dir, path: path, sync_info: sync_info, &sync_gen4_block(do_root_file_id))
|
|
801
827
|
end
|
|
802
828
|
end
|
|
803
829
|
|
|
804
830
|
# access_keys > do > upload
|
|
805
831
|
def action_access_keys_do_upload(do_root_file_id:, **)
|
|
806
|
-
apifid =
|
|
832
|
+
apifid = api_node.resolve_api_fid(do_root_file_id, transfer.destination_folder(Transfer::Spec::DIRECTION_SEND), true)
|
|
807
833
|
Runner.result_transfer(transfer.start(apifid.node_api.transfer_spec_gen4(apifid.file_id, Transfer::Spec::DIRECTION_SEND)))
|
|
808
834
|
end
|
|
809
835
|
|
|
810
836
|
# access_keys > do > download
|
|
811
837
|
def action_access_keys_do_download(do_root_file_id:, **)
|
|
812
|
-
apifid, source_paths =
|
|
838
|
+
apifid, source_paths = api_node.resolve_api_fid_paths(do_root_file_id, transfer.ts_source_paths)
|
|
813
839
|
Runner.result_transfer(transfer.start(apifid.node_api.transfer_spec_gen4(apifid.file_id, Transfer::Spec::DIRECTION_RECEIVE, {'paths'=>source_paths})))
|
|
814
840
|
end
|
|
815
841
|
|
|
816
|
-
# access_keys > do >
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
end
|
|
821
|
-
|
|
822
|
-
# access_keys > do > v3
|
|
823
|
-
def action_access_keys_do_v3(do_root_file_id:, **)
|
|
824
|
-
# Build a fresh Node for the v3 root, then let the DSL dispatcher consume the next command.
|
|
825
|
-
v3_node = Node.new(context: context, api: @api_node.resolve_api_fid(do_root_file_id, '').node_api)
|
|
826
|
-
v3_node.execute_action
|
|
842
|
+
# access_keys > do > v3 - mount target: Node plugin on the node hosting the root file
|
|
843
|
+
# @return [Node]
|
|
844
|
+
def v3_node_plugin(do_root_file_id:, **)
|
|
845
|
+
Node.new(context: context, api: api_node.resolve_api_fid(do_root_file_id, '').node_api)
|
|
827
846
|
end
|
|
828
847
|
|
|
829
848
|
# access_keys > do > node_info / bearer_token_node — shared helper builds the result hash
|
|
@@ -861,7 +880,8 @@ module Aspera
|
|
|
861
880
|
# @param path [String] full path (folder/name)
|
|
862
881
|
def gen4_mk_resolve(top_file_id, path)
|
|
863
882
|
containing_folder_path, new_item = Api::Node.split_folder(path)
|
|
864
|
-
apifid =
|
|
883
|
+
apifid = api_node.resolve_api_fid(top_file_id, containing_folder_path, true)
|
|
884
|
+
# not a URL query: special keys `check` and `target`, rest merged into the POST body
|
|
865
885
|
query = options.get_option(:query)
|
|
866
886
|
check_exists = true
|
|
867
887
|
payload = {name: new_item}
|
|
@@ -869,7 +889,7 @@ module Aspera
|
|
|
869
889
|
check_exists = !query.delete('check').eql?(false)
|
|
870
890
|
target = query.delete('target')
|
|
871
891
|
if target
|
|
872
|
-
target_apifid =
|
|
892
|
+
target_apifid = api_node.resolve_api_fid(top_file_id, target, true)
|
|
873
893
|
payload[:target_id] = target_apifid.file_id
|
|
874
894
|
end
|
|
875
895
|
payload.merge!(query.symbolize_keys)
|
|
@@ -913,8 +933,8 @@ module Aspera
|
|
|
913
933
|
Result::ObjectList.new(apifid.node_api.read_with_pages('permissions', list_query))
|
|
914
934
|
end
|
|
915
935
|
|
|
916
|
-
def action_access_keys_do_permission_delete(
|
|
917
|
-
bulk_result(
|
|
936
|
+
def action_access_keys_do_permission_delete(permission_id:, apifid:, **)
|
|
937
|
+
bulk_result(permission_id, command: :delete) do |one_id|
|
|
918
938
|
apifid.node_api.delete("permissions/#{one_id}")
|
|
919
939
|
the_app = apifid.node_api.app_info
|
|
920
940
|
the_app&.api&.permissions_send_event(event_data: {}, app_info: the_app, types: ['permission.deleted'])
|
|
@@ -922,8 +942,8 @@ module Aspera
|
|
|
922
942
|
end
|
|
923
943
|
end
|
|
924
944
|
|
|
925
|
-
def action_access_keys_do_permission_create(
|
|
926
|
-
create_param =
|
|
945
|
+
def action_access_keys_do_permission_create(permission:, apifid:, **)
|
|
946
|
+
create_param = permission
|
|
927
947
|
Aspera.assert(!create_param.key?('file_id'), type: Cli::BadArgument) { 'no file_id' }
|
|
928
948
|
create_param['file_id'] = apifid.file_id
|
|
929
949
|
create_param['access_levels'] = Api::Node::ACCESS_LEVELS unless create_param.key?('access_levels')
|
|
@@ -936,36 +956,31 @@ module Aspera
|
|
|
936
956
|
|
|
937
957
|
# access_keys > set_bearer_key
|
|
938
958
|
def action_access_keys_set_bearer_key(access_key_id:, bearer_key_pem:, **)
|
|
939
|
-
access_key_id =
|
|
959
|
+
access_key_id = api_node.read('access_keys/self')['id'] if access_key_id.eql?('self')
|
|
940
960
|
key = OpenSSL::PKey.read(bearer_key_pem)
|
|
941
961
|
key = key.public_key if key.private?
|
|
942
|
-
|
|
962
|
+
api_node.update("access_keys/#{access_key_id}", {token_verification_key: key.to_pem})
|
|
943
963
|
Result::Status.new('public key updated')
|
|
944
964
|
end
|
|
945
965
|
|
|
946
966
|
# async sub-commands: individual handlers
|
|
947
967
|
def action_async_show(async_id:, **)
|
|
948
|
-
async_ids =
|
|
968
|
+
async_ids = api_node.read('async/list')['sync_ids']
|
|
949
969
|
if async_id.eql?(SpecialValues::ALL)
|
|
950
|
-
resp =
|
|
970
|
+
resp = api_node.create('async/summary', {'syncs' => async_ids})['sync_summaries']
|
|
951
971
|
return Result::Empty.new if resp.empty?
|
|
952
972
|
return Result::ObjectList.new(resp, fields: %w[snid name local_dir remote_dir])
|
|
953
973
|
end
|
|
954
974
|
Integer(async_id)
|
|
955
|
-
resp =
|
|
975
|
+
resp = api_node.create('async/summary', {'syncs' => [async_id]})['sync_summaries']
|
|
956
976
|
return Result::Empty.new if resp.empty?
|
|
957
977
|
Result::SingleObject.new(resp.first)
|
|
958
978
|
end
|
|
959
979
|
|
|
960
|
-
def action_async_delete(async_id:, **)
|
|
961
|
-
async_ids = async_id.eql?(SpecialValues::ALL) ? @api_node.read('async/list')['sync_ids'] : [async_id]
|
|
962
|
-
Result::SingleObject.new(@api_node.create('async/delete', {'syncs' => async_ids}))
|
|
963
|
-
end
|
|
964
|
-
|
|
965
980
|
def action_async_bandwidth(async_id:, **)
|
|
966
981
|
Integer(async_id)
|
|
967
982
|
post_data = {'syncs' => [async_id], 'seconds' => 100}
|
|
968
|
-
resp =
|
|
983
|
+
resp = api_node.create('async/bandwidth', post_data)
|
|
969
984
|
data = resp['bandwidth_data']
|
|
970
985
|
return Result::Empty.new if data.empty?
|
|
971
986
|
Result::ObjectList.new(data.first[async_id]['data'])
|
|
@@ -974,9 +989,10 @@ module Aspera
|
|
|
974
989
|
def action_async_files(async_id:, **)
|
|
975
990
|
Integer(async_id)
|
|
976
991
|
post_data = {'syncs' => [async_id]}
|
|
992
|
+
# not a URL query: merged into the POST body of async/files
|
|
977
993
|
filter = options.get_option(:query)
|
|
978
994
|
post_data.merge!(filter) unless filter.nil?
|
|
979
|
-
resp =
|
|
995
|
+
resp = api_node.create('async/files', post_data)
|
|
980
996
|
data = resp['sync_files']
|
|
981
997
|
data = data.first[async_id] unless data.empty?
|
|
982
998
|
iteration_data = []
|
|
@@ -997,23 +1013,13 @@ module Aspera
|
|
|
997
1013
|
|
|
998
1014
|
def action_async_counters(async_id:, **)
|
|
999
1015
|
Integer(async_id)
|
|
1000
|
-
resp =
|
|
1016
|
+
resp = api_node.create('async/counters', {'syncs' => [async_id]})['sync_counters'].first&.[](async_id)&.last
|
|
1001
1017
|
return Result::Empty.new if resp.nil?
|
|
1002
1018
|
Result::SingleObject.new(resp)
|
|
1003
1019
|
end
|
|
1004
1020
|
|
|
1005
|
-
def action_ssync_start(ssync_id:, **)
|
|
1006
|
-
@api_node.call(operation: 'POST', subpath: "asyncs/#{ssync_id}/start", content_type: Mime::TEXT, body: '', ret: :resp).body
|
|
1007
|
-
Result::Status.new('Done')
|
|
1008
|
-
end
|
|
1009
|
-
|
|
1010
|
-
def action_ssync_stop(ssync_id:, **)
|
|
1011
|
-
@api_node.call(operation: 'POST', subpath: "asyncs/#{ssync_id}/stop", content_type: Mime::TEXT, body: '', ret: :resp).body
|
|
1012
|
-
Result::Status.new('Done')
|
|
1013
|
-
end
|
|
1014
|
-
|
|
1015
1021
|
# transfer sub-commands
|
|
1016
|
-
def action_transfer_list
|
|
1022
|
+
def action_transfer_list(**)
|
|
1017
1023
|
transfer_filter = query_read_delete(default: {}, schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers'))
|
|
1018
1024
|
iteration_persistency = nil
|
|
1019
1025
|
if options.get_option(:once_only, mandatory: true)
|
|
@@ -1030,13 +1036,13 @@ module Aspera
|
|
|
1030
1036
|
else
|
|
1031
1037
|
Aspera.assert(!transfer_filter.key?('reset'), 'reset only with once_only', type: Cli::BadArgument)
|
|
1032
1038
|
end
|
|
1033
|
-
transfers_data =
|
|
1039
|
+
transfers_data = api_node.read_with_paging('ops/transfers', transfer_filter, iteration: iteration_persistency&.data)
|
|
1034
1040
|
iteration_persistency&.save
|
|
1035
1041
|
Result::ObjectList.new(transfers_data, fields: %w[id status start_spec.direction start_spec.remote_user start_spec.remote_host start_spec.destination_path])
|
|
1036
1042
|
end
|
|
1037
1043
|
|
|
1038
|
-
def action_transfer_sessions
|
|
1039
|
-
transfers_data =
|
|
1044
|
+
def action_transfer_sessions(**)
|
|
1045
|
+
transfers_data = api_node.read('ops/transfers', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers')))
|
|
1040
1046
|
sessions = transfers_data.flat_map { |t| t['sessions'] }
|
|
1041
1047
|
sessions.each do |session|
|
|
1042
1048
|
SESSION_TIME_FIELDS.each do |what|
|
|
@@ -1046,18 +1052,8 @@ module Aspera
|
|
|
1046
1052
|
Result::ObjectList.new(sessions, fields: %w[id status start_time end_time target_rate_kbps])
|
|
1047
1053
|
end
|
|
1048
1054
|
|
|
1049
|
-
def
|
|
1050
|
-
|
|
1051
|
-
Result::Status.new('Cancelled')
|
|
1052
|
-
end
|
|
1053
|
-
|
|
1054
|
-
def action_transfer_modify(update_value:, transfer_id:, **)
|
|
1055
|
-
@api_node.update("ops/transfers/#{transfer_id}", update_value)
|
|
1056
|
-
Result::Status.new('Modified')
|
|
1057
|
-
end
|
|
1058
|
-
|
|
1059
|
-
def action_transfer_bandwidth_average
|
|
1060
|
-
transfers_data = @api_node.read('ops/transfers', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers')))
|
|
1055
|
+
def action_transfer_bandwidth_average(**)
|
|
1056
|
+
transfers_data = api_node.read('ops/transfers', query_read_delete(schema: Schema::Registry.query_params(Schema::Registry::NODE, 'ops/transfers')))
|
|
1061
1057
|
bandwidth_period = {}
|
|
1062
1058
|
dir_info = %i[avg_kbps sessions].freeze
|
|
1063
1059
|
transfers_data.each do |t|
|
|
@@ -1086,17 +1082,6 @@ module Aspera
|
|
|
1086
1082
|
Result::ObjectList.new(result)
|
|
1087
1083
|
end
|
|
1088
1084
|
|
|
1089
|
-
# service sub-commands
|
|
1090
|
-
def action_service_create(creation_data:, **)
|
|
1091
|
-
resp = @api_node.create('rund/services', creation_data)
|
|
1092
|
-
Result::Status.new("#{resp['id']} created")
|
|
1093
|
-
end
|
|
1094
|
-
|
|
1095
|
-
def action_service_delete(service_id:, **)
|
|
1096
|
-
@api_node.delete("rund/services/#{service_id}")
|
|
1097
|
-
Result::Status.new("#{service_id} deleted")
|
|
1098
|
-
end
|
|
1099
|
-
|
|
1100
1085
|
# central: shared helper
|
|
1101
1086
|
def central_validation
|
|
1102
1087
|
validator_id = options.get_option(:validator)
|
|
@@ -1104,35 +1089,35 @@ module Aspera
|
|
|
1104
1089
|
end
|
|
1105
1090
|
|
|
1106
1091
|
# central > session > list
|
|
1107
|
-
def action_central_session_list(
|
|
1108
|
-
|
|
1092
|
+
def action_central_session_list(criteria:, **)
|
|
1093
|
+
criteria ||= {}
|
|
1109
1094
|
validation = central_validation
|
|
1110
|
-
|
|
1111
|
-
resp =
|
|
1095
|
+
criteria.deep_merge!({'validation' => validation}) unless validation.nil?
|
|
1096
|
+
resp = api_node.create('services/rest/transfers/v1/sessions', criteria)
|
|
1112
1097
|
Result::ObjectList.new(resp['session_info_result']['session_info'], fields: %w[session_uuid status transport direction bytes_transferred])
|
|
1113
1098
|
end
|
|
1114
1099
|
|
|
1115
1100
|
# central > file > list
|
|
1116
|
-
def action_central_file_list(
|
|
1117
|
-
|
|
1101
|
+
def action_central_file_list(criteria:, **)
|
|
1102
|
+
criteria ||= {}
|
|
1118
1103
|
validation = central_validation
|
|
1119
|
-
|
|
1120
|
-
resp =
|
|
1104
|
+
criteria.deep_merge!({'validation' => validation}) unless validation.nil?
|
|
1105
|
+
resp = api_node.create('services/rest/transfers/v1/files', criteria)
|
|
1121
1106
|
resp = JSON.parse(resp) if resp.is_a?(String)
|
|
1122
1107
|
Log.dump(:resp, resp)
|
|
1123
1108
|
Result::ObjectList.new(resp['file_transfer_info_result']['file_transfer_info'], fields: %w[session_uuid file_id status path])
|
|
1124
1109
|
end
|
|
1125
1110
|
|
|
1126
1111
|
# central > file > modify
|
|
1127
|
-
def action_central_file_modify(
|
|
1128
|
-
|
|
1112
|
+
def action_central_file_modify(file:, **)
|
|
1113
|
+
file ||= {}
|
|
1129
1114
|
validation = central_validation
|
|
1130
|
-
|
|
1131
|
-
|
|
1115
|
+
file.deep_merge!(validation) unless validation.nil?
|
|
1116
|
+
api_node.update('services/rest/transfers/v1/files', file)
|
|
1132
1117
|
Result::Status.new('updated')
|
|
1133
1118
|
end
|
|
1134
1119
|
|
|
1135
|
-
def action_asperabrowser
|
|
1120
|
+
def action_asperabrowser(**)
|
|
1136
1121
|
browse_params = {
|
|
1137
1122
|
'nodeUser' => options.get_option(:username, mandatory: true),
|
|
1138
1123
|
'nodePW' => options.get_option(:password, mandatory: true),
|
|
@@ -1144,20 +1129,23 @@ module Aspera
|
|
|
1144
1129
|
return Result::Status.new('done')
|
|
1145
1130
|
end
|
|
1146
1131
|
|
|
1147
|
-
def
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1132
|
+
def action_emulator(parameters: {}, **)
|
|
1133
|
+
require 'aspera/node_emulator'
|
|
1134
|
+
# missing parameters take the default of the schema
|
|
1135
|
+
defaults = Schema::Registry.instance.reader(Schema::Registry::NODE_EMULATOR_OPTIONS).current['properties'].each_with_object({}) do |(name, property), result|
|
|
1136
|
+
result[name.to_sym] = property['default'] if property.key?('default')
|
|
1137
|
+
end
|
|
1138
|
+
parameters = defaults.merge(parameters.symbolize_keys)
|
|
1139
|
+
uri = URI.parse(parameters.delete(:url))
|
|
1140
|
+
config = parameters.except(*WebServerSimple::PARAMS)
|
|
1141
|
+
docroot = config.delete(:docroot)
|
|
1142
|
+
retention_sec = config.delete(:retention_sec)
|
|
1143
|
+
Aspera.assert(config[:username].nil? == config[:password].nil?, type: Cli::BadArgument) { 'Parameters username and password must be set together' }
|
|
1144
|
+
Log.log.warn('No username and password: emulator accepts requests without authentication') if config[:username].nil?
|
|
1157
1145
|
server = WebServerSimple.new(uri, **parameters.slice(*WebServerSimple::PARAMS))
|
|
1158
|
-
server.mount(uri.path,
|
|
1146
|
+
server.mount(uri.path, NodeEmulatorServlet, config, NodeEmulator.new(docroot: docroot, retention_sec: retention_sec))
|
|
1159
1147
|
server.start
|
|
1160
|
-
return Result::Status.new('
|
|
1148
|
+
return Result::Status.new('Emulator terminated')
|
|
1161
1149
|
end
|
|
1162
1150
|
|
|
1163
1151
|
def action_telemetry(parameters: {}, **)
|
|
@@ -1169,8 +1157,8 @@ module Aspera
|
|
|
1169
1157
|
parameters[:interval] = 10 unless parameters.key?(:interval)
|
|
1170
1158
|
parameters[:hostname] = Socket.gethostname unless parameters.key?(:hostname)
|
|
1171
1159
|
interval = parameters[:interval].to_f
|
|
1172
|
-
Aspera.assert(interval
|
|
1173
|
-
otel_api = Rest.new(
|
|
1160
|
+
Aspera.assert(interval >= 0, type: Cli::BadArgument) { 'Interval must be a positive number in seconds, or 0 for single shot' }
|
|
1161
|
+
otel_api = Rest::Client.new(
|
|
1174
1162
|
base_url: "#{parameters[:url]}/v1",
|
|
1175
1163
|
headers: {
|
|
1176
1164
|
# 'Authorization' => "apiToken #{parameters[:key]}",
|
|
@@ -1225,7 +1213,7 @@ module Aspera
|
|
|
1225
1213
|
}
|
|
1226
1214
|
loop do
|
|
1227
1215
|
timestamp = Time.now
|
|
1228
|
-
transfers_data =
|
|
1216
|
+
transfers_data = api_node.read_with_paging('ops/transfers', {active_only: true})
|
|
1229
1217
|
datapoint[:asInt] = transfers_data.length
|
|
1230
1218
|
datapoint[:timeUnixNano] = timestamp.to_i * 1_000_000_000 + timestamp.nsec
|
|
1231
1219
|
Log.log.info("#{datapoint[:asInt]} active transfers")
|
|
@@ -1236,21 +1224,6 @@ module Aspera
|
|
|
1236
1224
|
end
|
|
1237
1225
|
end
|
|
1238
1226
|
|
|
1239
|
-
# Dispatch a Gen3 command that has already been read from the argument stream.
|
|
1240
|
-
# Used when a new Node instance is created by an external plugin (shares, faspex, cos, :v3)
|
|
1241
|
-
# and the command symbol was already consumed before the Node was instantiated.
|
|
1242
|
-
# Re-enters the DSL registry so Proc actions and intermediate nodes (sync, access_keys)
|
|
1243
|
-
# are resolved normally.
|
|
1244
|
-
# Pre-resolved CLI arguments (e.g. path:) can be forwarded via +ctx+ to avoid
|
|
1245
|
-
# re-consuming tokens that were already resolved by the calling plugin's DSL.
|
|
1246
|
-
# @param command [Symbol] command already consumed from the argument stream
|
|
1247
|
-
# @param ctx [Hash] pre-resolved context (keyword args forwarded to dispatch)
|
|
1248
|
-
# @return [Object] CLI result
|
|
1249
|
-
def dispatch_v3_command(command, **ctx)
|
|
1250
|
-
Aspera.assert_values(command, COMMANDS_GEN3 + %i[health events info slash license api_details access_keys transfer]) { 'v3 command' }
|
|
1251
|
-
dispatch_from_registry([command], ctx)
|
|
1252
|
-
end
|
|
1253
|
-
|
|
1254
1227
|
private
|
|
1255
1228
|
|
|
1256
1229
|
# Response has key `paths`.
|