aspera-cli 4.13.0 → 4.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +81 -7
- data/CONTRIBUTING.md +22 -6
- data/README.md +2038 -1080
- data/bin/ascli +18 -9
- data/bin/asession +12 -14
- data/examples/dascli +1 -1
- data/examples/proxy.pac +1 -1
- data/examples/rubyc +24 -0
- data/lib/aspera/aoc.rb +219 -159
- data/lib/aspera/ascmd.rb +25 -14
- data/lib/aspera/cli/basic_auth_plugin.rb +12 -9
- data/lib/aspera/cli/error.rb +17 -0
- data/lib/aspera/cli/extended_value.rb +47 -12
- data/lib/aspera/cli/formatter.rb +260 -179
- data/lib/aspera/cli/hints.rb +80 -0
- data/lib/aspera/cli/main.rb +104 -156
- data/lib/aspera/cli/manager.rb +259 -209
- data/lib/aspera/cli/plugin.rb +123 -63
- data/lib/aspera/cli/plugins/alee.rb +2 -3
- data/lib/aspera/cli/plugins/aoc.rb +341 -261
- data/lib/aspera/cli/plugins/ats.rb +22 -21
- data/lib/aspera/cli/plugins/bss.rb +5 -5
- data/lib/aspera/cli/plugins/config.rb +578 -627
- data/lib/aspera/cli/plugins/console.rb +44 -6
- data/lib/aspera/cli/plugins/cos.rb +15 -17
- data/lib/aspera/cli/plugins/faspex.rb +114 -100
- data/lib/aspera/cli/plugins/faspex5.rb +411 -264
- data/lib/aspera/cli/plugins/node.rb +354 -259
- data/lib/aspera/cli/plugins/orchestrator.rb +61 -29
- data/lib/aspera/cli/plugins/preview.rb +82 -90
- data/lib/aspera/cli/plugins/server.rb +79 -32
- data/lib/aspera/cli/plugins/shares.rb +55 -42
- data/lib/aspera/cli/sync_actions.rb +68 -0
- data/lib/aspera/cli/transfer_agent.rb +66 -73
- data/lib/aspera/cli/transfer_progress.rb +74 -0
- data/lib/aspera/cli/version.rb +1 -1
- data/lib/aspera/colors.rb +12 -8
- data/lib/aspera/command_line_builder.rb +14 -11
- data/lib/aspera/cos_node.rb +3 -2
- data/lib/aspera/data/6 +0 -0
- data/lib/aspera/environment.rb +24 -9
- data/lib/aspera/fasp/agent_aspera.rb +126 -0
- data/lib/aspera/fasp/agent_base.rb +31 -77
- data/lib/aspera/fasp/agent_connect.rb +25 -21
- data/lib/aspera/fasp/agent_direct.rb +89 -103
- data/lib/aspera/fasp/agent_httpgw.rb +231 -149
- data/lib/aspera/fasp/agent_node.rb +41 -34
- data/lib/aspera/fasp/agent_trsdk.rb +75 -32
- data/lib/aspera/fasp/error_info.rb +4 -2
- data/lib/aspera/fasp/faux_file.rb +52 -0
- data/lib/aspera/fasp/installation.rb +53 -195
- data/lib/aspera/fasp/management.rb +244 -0
- data/lib/aspera/fasp/parameters.rb +71 -37
- data/lib/aspera/fasp/parameters.yaml +76 -8
- data/lib/aspera/fasp/products.rb +162 -0
- data/lib/aspera/fasp/resume_policy.rb +3 -3
- data/lib/aspera/fasp/transfer_spec.rb +7 -6
- data/lib/aspera/fasp/uri.rb +26 -24
- data/lib/aspera/faspex_gw.rb +2 -2
- data/lib/aspera/faspex_postproc.rb +2 -2
- data/lib/aspera/hash_ext.rb +14 -4
- data/lib/aspera/json_rpc.rb +49 -0
- data/lib/aspera/keychain/macos_security.rb +13 -13
- data/lib/aspera/line_logger.rb +23 -0
- data/lib/aspera/log.rb +58 -16
- data/lib/aspera/node.rb +157 -92
- data/lib/aspera/oauth.rb +37 -19
- data/lib/aspera/open_application.rb +4 -4
- data/lib/aspera/persistency_action_once.rb +1 -1
- data/lib/aspera/persistency_folder.rb +2 -2
- data/lib/aspera/preview/file_types.rb +4 -2
- data/lib/aspera/preview/generator.rb +22 -35
- data/lib/aspera/preview/options.rb +2 -0
- data/lib/aspera/preview/terminal.rb +73 -16
- data/lib/aspera/preview/utils.rb +21 -28
- data/lib/aspera/proxy_auto_config.js +2 -2
- data/lib/aspera/rest.rb +136 -68
- data/lib/aspera/rest_call_error.rb +1 -1
- data/lib/aspera/rest_error_analyzer.rb +15 -14
- data/lib/aspera/rest_errors_aspera.rb +37 -34
- data/lib/aspera/secret_hider.rb +18 -15
- data/lib/aspera/ssh.rb +5 -2
- data/lib/aspera/sync.rb +127 -119
- data/lib/aspera/temp_file_manager.rb +10 -3
- data/lib/aspera/web_auth.rb +10 -7
- data/lib/aspera/web_server_simple.rb +9 -4
- data.tar.gz.sig +0 -0
- metadata +34 -17
- metadata.gz.sig +0 -0
- data/docs/test_env.conf +0 -186
- data/lib/aspera/cli/listener/line_dump.rb +0 -19
- data/lib/aspera/cli/listener/logger.rb +0 -22
- data/lib/aspera/cli/listener/progress.rb +0 -50
- data/lib/aspera/cli/listener/progress_multi.rb +0 -84
- data/lib/aspera/cli/plugins/sync.rb +0 -44
- data/lib/aspera/data/7 +0 -0
- data/lib/aspera/fasp/listener.rb +0 -13
@@ -7,15 +7,53 @@ module Aspera
|
|
7
7
|
module Cli
|
8
8
|
module Plugins
|
9
9
|
class Console < Aspera::Cli::BasicAuthPlugin
|
10
|
+
STANDARD_PATH = '/aspera/console'
|
11
|
+
class << self
|
12
|
+
def detect(address_or_url)
|
13
|
+
address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
|
14
|
+
urls = [address_or_url]
|
15
|
+
urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
|
16
|
+
|
17
|
+
urls.each do |base_url|
|
18
|
+
next unless base_url.start_with?('https://')
|
19
|
+
api = Rest.new(base_url: base_url, redirect_max: 2)
|
20
|
+
test_endpoint = 'login'
|
21
|
+
test_page = api.call({operation: 'GET', subpath: test_endpoint, url_params: {local: true}})
|
22
|
+
next unless test_page[:http].body.include?('Aspera Console')
|
23
|
+
version = 'unknown'
|
24
|
+
if (m = test_page[:http].body.match(/\(v([1-9]\..*)\)/))
|
25
|
+
version = m[1]
|
26
|
+
end
|
27
|
+
url = test_page[:http].uri.to_s
|
28
|
+
return {
|
29
|
+
version: version,
|
30
|
+
url: url[0..url.index(test_endpoint) - 2]
|
31
|
+
}
|
32
|
+
rescue StandardError => e
|
33
|
+
Log.log.debug{"detect error: #{e}"}
|
34
|
+
end
|
35
|
+
return nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def wizard(object:, private_key_path: nil, pub_key_pem: nil)
|
39
|
+
options = object.options
|
40
|
+
return {
|
41
|
+
preset_value: {
|
42
|
+
url: options.get_option(:url, mandatory: true),
|
43
|
+
username: options.get_option(:username, mandatory: true),
|
44
|
+
password: options.get_option(:password, mandatory: true)
|
45
|
+
},
|
46
|
+
test_args: 'transfer list'
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
10
50
|
DEFAULT_FILTER_AGE_SECONDS = 3 * 3600
|
11
51
|
private_constant :DEFAULT_FILTER_AGE_SECONDS
|
12
52
|
def initialize(env)
|
13
53
|
super(env)
|
14
|
-
options.add_opt_date(:filter_from, 'only after date')
|
15
|
-
options.add_opt_date(:filter_to, 'only before date')
|
16
54
|
time_now = Time.now
|
17
|
-
options.
|
18
|
-
options.
|
55
|
+
options.declare(:filter_from, 'Only after date', values: :date, default: Manager.time_to_string(time_now - DEFAULT_FILTER_AGE_SECONDS))
|
56
|
+
options.declare(:filter_to, 'Only before date', values: :date, default: Manager.time_to_string(time_now))
|
19
57
|
options.parse_options!
|
20
58
|
end
|
21
59
|
|
@@ -54,8 +92,8 @@ module Aspera
|
|
54
92
|
return {
|
55
93
|
type: :object_list,
|
56
94
|
data: api_console.read('transfers', {
|
57
|
-
'from' => options.get_option(:filter_from,
|
58
|
-
'to' => options.get_option(:filter_to,
|
95
|
+
'from' => options.get_option(:filter_from, mandatory: true),
|
96
|
+
'to' => options.get_option(:filter_to, mandatory: true)
|
59
97
|
})[:data],
|
60
98
|
fields: %w[id contact name status]}
|
61
99
|
end
|
@@ -10,15 +10,13 @@ module Aspera
|
|
10
10
|
class Cos < Aspera::Cli::Plugin
|
11
11
|
def initialize(env)
|
12
12
|
super(env)
|
13
|
-
|
14
|
-
options.
|
15
|
-
options.
|
16
|
-
options.
|
17
|
-
options.
|
18
|
-
options.
|
19
|
-
options.
|
20
|
-
options.add_opt_simple(:identity, "Authentication url (#{CosNode::IBM_CLOUD_TOKEN_URL})")
|
21
|
-
options.set_option(:identity, CosNode::IBM_CLOUD_TOKEN_URL)
|
13
|
+
options.declare(:bucket, 'Bucket name')
|
14
|
+
options.declare(:endpoint, 'Storage endpoint url')
|
15
|
+
options.declare(:apikey, 'Storage API key')
|
16
|
+
options.declare(:crn, 'Resource instance id')
|
17
|
+
options.declare(:service_credentials, 'IBM Cloud service credentials', types: Hash)
|
18
|
+
options.declare(:region, 'Storage region')
|
19
|
+
options.declare(:identity, "Authentication url (#{CosNode::IBM_CLOUD_TOKEN_URL})", default: CosNode::IBM_CLOUD_TOKEN_URL)
|
22
20
|
options.parse_options!
|
23
21
|
end
|
24
22
|
|
@@ -28,23 +26,23 @@ module Aspera
|
|
28
26
|
command = options.get_next_command(ACTIONS)
|
29
27
|
case command
|
30
28
|
when :node
|
31
|
-
bucket_name = options.get_option(:bucket,
|
29
|
+
bucket_name = options.get_option(:bucket, mandatory: true)
|
32
30
|
# get service credentials, Hash, e.g. @json:@file:...
|
33
31
|
service_credentials = options.get_option(:service_credentials)
|
34
32
|
storage_endpoint = options.get_option(:endpoint)
|
35
|
-
raise
|
36
|
-
raise
|
33
|
+
raise Cli::BadArgument, 'one of: endpoint or service_credentials is required' if service_credentials.nil? && storage_endpoint.nil?
|
34
|
+
raise Cli::BadArgument, 'endpoint and service_credentials are mutually exclusive' unless service_credentials.nil? || storage_endpoint.nil?
|
37
35
|
if service_credentials.nil?
|
38
|
-
service_api_key = options.get_option(:apikey,
|
39
|
-
instance_id = options.get_option(:crn,
|
36
|
+
service_api_key = options.get_option(:apikey, mandatory: true)
|
37
|
+
instance_id = options.get_option(:crn, mandatory: true)
|
40
38
|
else
|
41
|
-
params = CosNode.
|
39
|
+
params = CosNode.parameters_from_svc_credentials(service_credentials, options.get_option(:region, mandatory: true))
|
42
40
|
storage_endpoint = params[:storage_endpoint]
|
43
41
|
service_api_key = params[:service_api_key]
|
44
42
|
instance_id = params[:instance_id]
|
45
43
|
end
|
46
|
-
api_node = CosNode.new(bucket_name, storage_endpoint, instance_id, service_api_key, options.get_option(:identity,
|
47
|
-
node_plugin = Node.new(@agents
|
44
|
+
api_node = CosNode.new(bucket_name, storage_endpoint, instance_id, service_api_key, options.get_option(:identity, mandatory: true))
|
45
|
+
node_plugin = Node.new(@agents, api: api_node)
|
48
46
|
command = options.get_next_command(Node::COMMANDS_COS)
|
49
47
|
return node_plugin.execute_action(command)
|
50
48
|
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# cspell:ignore passcode xrds workgroups dmembership wmembership
|
3
4
|
require 'aspera/cli/basic_auth_plugin'
|
4
5
|
require 'aspera/cli/plugins/node'
|
5
6
|
require 'aspera/cli/plugins/config'
|
6
7
|
require 'aspera/cli/extended_value'
|
7
8
|
require 'aspera/cli/transfer_agent'
|
8
|
-
require 'aspera/persistency_action_once'
|
9
|
-
require 'aspera/open_application'
|
10
9
|
require 'aspera/fasp/uri'
|
11
10
|
require 'aspera/fasp/transfer_spec'
|
11
|
+
require 'aspera/persistency_action_once'
|
12
|
+
require 'aspera/open_application'
|
12
13
|
require 'aspera/nagios'
|
13
14
|
require 'aspera/id_generator'
|
14
15
|
require 'xmlsimple'
|
@@ -32,45 +33,68 @@ module Aspera
|
|
32
33
|
ATOM_EXT_PARAMS = [MAX_ITEMS, MAX_PAGES].concat(ATOM_PARAMS).freeze
|
33
34
|
# sub path in url for public link delivery
|
34
35
|
PUB_LINK_EXTERNAL_MATCH = 'external_deliveries/'
|
36
|
+
STANDARD_PATH = '/aspera/faspex'
|
35
37
|
private_constant(*%i[KEY_NODE KEY_PATH PACKAGE_MATCH_FIELD ATOM_MAILBOXES ATOM_PARAMS ATOM_EXT_PARAMS PUB_LINK_EXTERNAL_MATCH])
|
36
38
|
|
37
39
|
class << self
|
38
|
-
def detect(
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
def detect(address_or_url)
|
41
|
+
address_or_url = "https://#{address_or_url}" unless address_or_url.match?(%r{^[a-z]{1,6}://})
|
42
|
+
urls = [address_or_url]
|
43
|
+
urls.push("#{address_or_url}#{STANDARD_PATH}") unless address_or_url.end_with?(STANDARD_PATH)
|
44
|
+
|
45
|
+
urls.each do |base_url|
|
46
|
+
next unless base_url.start_with?('https://')
|
47
|
+
api = Rest.new(base_url: base_url, redirect_max: 1)
|
48
|
+
result = api.call(
|
49
|
+
operation: 'POST',
|
50
|
+
subpath: '',
|
51
|
+
headers: {'Accept' => 'application/xrds+xml', 'Content-type' => 'text/plain'},
|
52
|
+
text_body_params: '')
|
53
|
+
# 4.x
|
54
|
+
next unless result[:http].body.start_with?('<?xml')
|
47
55
|
res_s = XmlSimple.xml_in(result[:http].body, {'ForceArray' => false})
|
56
|
+
Log.log.debug{"version: #{result[:http]['X-IBM-Aspera']}"}
|
48
57
|
version = res_s['XRD']['application']['version']
|
49
|
-
|
58
|
+
# take redirect if any
|
59
|
+
return {version: version, url: result[:http].uri.to_s}
|
60
|
+
rescue StandardError => e
|
61
|
+
Log.log.debug{"detect error: #{e}"}
|
50
62
|
end
|
51
63
|
return nil
|
52
64
|
end
|
53
65
|
|
66
|
+
def wizard(object:, private_key_path: nil, pub_key_pem: nil)
|
67
|
+
options = object.options
|
68
|
+
return {
|
69
|
+
preset_value: {
|
70
|
+
url: options.get_option(:url, mandatory: true),
|
71
|
+
username: options.get_option(:username, mandatory: true),
|
72
|
+
password: options.get_option(:password, mandatory: true)
|
73
|
+
},
|
74
|
+
test_args: 'me'
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
54
78
|
# extract elements from anonymous faspex link
|
55
79
|
def get_link_data(public_url)
|
56
80
|
public_uri = URI.parse(public_url)
|
57
|
-
raise
|
81
|
+
raise Cli::BadArgument, 'Public link does not match Faspex format' unless (m = public_uri.path.match(%r{^(.*)/(external.*)$}))
|
58
82
|
base = m[1]
|
59
83
|
subpath = m[2]
|
60
84
|
port_add = public_uri.port.eql?(public_uri.default_port) ? '' : ":#{public_uri.port}"
|
61
85
|
result = {
|
62
86
|
base_url: "#{public_uri.scheme}://#{public_uri.host}#{port_add}#{base}",
|
63
87
|
subpath: subpath,
|
64
|
-
query:
|
88
|
+
query: Rest.decode_query(public_uri.query)
|
65
89
|
}
|
66
|
-
Log.dump('link data', result)
|
90
|
+
Log.log.debug{Log.dump('link data', result)}
|
67
91
|
return result
|
68
92
|
end
|
69
93
|
|
70
|
-
# get
|
94
|
+
# get Fasp::Uri::SCHEME URI from entry in xml, and fix problems..
|
71
95
|
def get_fasp_uri_from_entry(entry, raise_no_link: true)
|
72
96
|
unless entry.key?('link')
|
73
|
-
raise
|
97
|
+
raise Cli::BadArgument, 'package has no link (deleted?)' if raise_no_link
|
74
98
|
return nil
|
75
99
|
end
|
76
100
|
result = entry['link'].find{|e| e['rel'].eql?('package')}['href']
|
@@ -80,21 +104,11 @@ module Aspera
|
|
80
104
|
return result
|
81
105
|
end
|
82
106
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# field_sym : :id or :name
|
92
|
-
def get_source_id(source_list, source_name)
|
93
|
-
source_ids = source_list.select { |i| i['name'].eql?(source_name) }
|
94
|
-
if source_ids.empty?
|
95
|
-
raise CliError, %Q(No such Faspex source "#{source_name}" in [#{source_list.map{|i| %Q("#{i['name']}")}.join(', ')}])
|
96
|
-
end
|
97
|
-
return source_ids.first['id']
|
107
|
+
# @return [Integer] identifier of source
|
108
|
+
def get_source_id_by_name(source_name, source_list)
|
109
|
+
match_source = source_list.find { |i| i['name'].eql?(source_name) }
|
110
|
+
return match_source['id'] unless match_source.nil?
|
111
|
+
raise Cli::Error, %Q(No such Faspex source: "#{source_name}" in [#{source_list.map{|i| %Q("#{i['name']}")}.join(', ')}])
|
98
112
|
end
|
99
113
|
end
|
100
114
|
|
@@ -102,13 +116,12 @@ module Aspera
|
|
102
116
|
@api_v3 = nil
|
103
117
|
@api_v4 = nil
|
104
118
|
super(env)
|
105
|
-
options.
|
106
|
-
options.
|
107
|
-
options.
|
108
|
-
options.
|
109
|
-
options.
|
110
|
-
options.
|
111
|
-
options.set_option(:box, :inbox)
|
119
|
+
options.declare(:link, 'Public link for specific operation')
|
120
|
+
options.declare(:delivery_info, 'Package delivery information', types: Hash)
|
121
|
+
options.declare(:remote_source, 'Remote source for package send (id or %name:)')
|
122
|
+
options.declare(:storage, 'Faspex local storage definition (for browsing source)')
|
123
|
+
options.declare(:recipient, 'Use if recipient is a dropbox (with *)')
|
124
|
+
options.declare(:box, 'Package box', values: ATOM_MAILBOXES, default: :inbox)
|
112
125
|
options.parse_options!
|
113
126
|
end
|
114
127
|
|
@@ -121,13 +134,13 @@ module Aspera
|
|
121
134
|
|
122
135
|
def api_v4
|
123
136
|
if @api_v4.nil?
|
124
|
-
faspex_api_base = options.get_option(:url,
|
137
|
+
faspex_api_base = options.get_option(:url, mandatory: true)
|
125
138
|
@api_v4 = Rest.new({
|
126
139
|
base_url: faspex_api_base + '/api',
|
127
140
|
auth: {
|
128
141
|
type: :oauth2,
|
129
142
|
base_url: faspex_api_base + '/auth/oauth2',
|
130
|
-
auth: {type: :basic, username: options.get_option(:username,
|
143
|
+
auth: {type: :basic, username: options.get_option(:username, mandatory: true), password: options.get_option(:password, mandatory: true)},
|
131
144
|
grant_method: :generic,
|
132
145
|
generic: {grant_type: 'password'},
|
133
146
|
scope: 'admin'
|
@@ -138,11 +151,11 @@ module Aspera
|
|
138
151
|
|
139
152
|
# query supports : {"startIndex":10,"count":1,"page":109,"max":2,"pmax":1}
|
140
153
|
def mailbox_filtered_entries(stop_at_id: nil)
|
141
|
-
recipient_names = [options.get_option(:recipient) || options.get_option(:username,
|
154
|
+
recipient_names = [options.get_option(:recipient) || options.get_option(:username, mandatory: true)]
|
142
155
|
# some workgroup messages have no star in recipient name
|
143
156
|
recipient_names.push(recipient_names.first[1..-1]) if recipient_names.first.start_with?('*')
|
144
157
|
# mailbox is in ATOM_MAILBOXES
|
145
|
-
mailbox = options.get_option(:box,
|
158
|
+
mailbox = options.get_option(:box, mandatory: true)
|
146
159
|
# parameters
|
147
160
|
mailbox_query = options.get_option(:query)
|
148
161
|
max_items = nil
|
@@ -161,8 +174,8 @@ module Aspera
|
|
161
174
|
# get a batch of package information
|
162
175
|
# order: first batch is latest packages, and then in a batch ids are increasing
|
163
176
|
atom_xml = api_v3.call({operation: 'GET', subpath: "#{mailbox}.atom", headers: {'Accept' => 'application/xml'}, url_params: mailbox_query})[:http].body
|
164
|
-
box_data = XmlSimple.xml_in(atom_xml, {'ForceArray' =>
|
165
|
-
Log.dump(:box_data, box_data)
|
177
|
+
box_data = XmlSimple.xml_in(atom_xml, {'ForceArray' => %w[entry field link to]})
|
178
|
+
Log.log.debug{Log.dump(:box_data, box_data)}
|
166
179
|
items = box_data.key?('entry') ? box_data['entry'] : []
|
167
180
|
Log.log.debug{"new items: #{items.count}"}
|
168
181
|
# it is the end if page is empty
|
@@ -174,11 +187,14 @@ module Aspera
|
|
174
187
|
package[PACKAGE_MATCH_FIELD] =
|
175
188
|
case mailbox
|
176
189
|
when :inbox, :archive
|
177
|
-
recipient = package['to'].find{|i|recipient_names.include?(i['name']
|
178
|
-
recipient.nil? ? nil : recipient['recipient_delivery_id']
|
190
|
+
recipient = package['to'].find{|i|recipient_names.include?(i['name'])}
|
191
|
+
recipient.nil? ? nil : recipient['recipient_delivery_id']
|
179
192
|
else # :sent
|
180
|
-
package['delivery_id']
|
193
|
+
package['delivery_id']
|
181
194
|
end
|
195
|
+
# add special key
|
196
|
+
package['items'] = package['link'].is_a?(Array) ? package['link'].length : 0
|
197
|
+
package['metadata'] = package['metadata']['field'].each_with_object({}){|i, m| m[i['name']] = i['content'] }
|
182
198
|
# if we look for a specific package
|
183
199
|
stop_condition = true if !stop_at_id.nil? && stop_at_id.eql?(package[PACKAGE_MATCH_FIELD])
|
184
200
|
# keep only those for the specified recipient
|
@@ -198,7 +214,7 @@ module Aspera
|
|
198
214
|
break if link.nil?
|
199
215
|
# replace parameters with the ones from next link
|
200
216
|
params = CGI.parse(URI.parse(link['href']).query)
|
201
|
-
mailbox_query = params.keys.each_with_object({}){|i, m
|
217
|
+
mailbox_query = params.keys.each_with_object({}){|i, m| m[i] = params[i].first }
|
202
218
|
Log.log.debug{"query: #{mailbox_query}"}
|
203
219
|
break if !max_pages.nil? && (mailbox_query['page'].to_i > max_pages)
|
204
220
|
end
|
@@ -211,7 +227,7 @@ module Aspera
|
|
211
227
|
# pub link user
|
212
228
|
link_data = self.class.get_link_data(public_link_url)
|
213
229
|
if !['external/submissions/new', 'external/dropbox_submissions/new'].include?(link_data[:subpath])
|
214
|
-
raise
|
230
|
+
raise Cli::BadArgument, "pub link is #{link_data[:subpath]}, expecting external/submissions/new"
|
215
231
|
end
|
216
232
|
create_path = link_data[:subpath].split('/')[0..-2].join('/')
|
217
233
|
package_create_params[:passcode] = link_data[:query]['passcode']
|
@@ -226,11 +242,11 @@ module Aspera
|
|
226
242
|
subpath: create_path,
|
227
243
|
json_params: package_create_params,
|
228
244
|
headers: {'Accept' => 'text/javascript'}})[:http].body
|
229
|
-
# get
|
245
|
+
# get arguments of function call
|
230
246
|
package_creation_data.delete!("\n") # one line
|
231
247
|
package_creation_data.gsub!(/^[^"]+\("\{/, '{') # delete header
|
232
248
|
package_creation_data.gsub!(/"\);[^"]+$/, '"') # delete trailer
|
233
|
-
package_creation_data.gsub!(/\}", *"/, '},"') # between two
|
249
|
+
package_creation_data.gsub!(/\}", *"/, '},"') # between two arguments
|
234
250
|
package_creation_data.gsub!('\\"', '"') # remove protecting quote
|
235
251
|
begin
|
236
252
|
package_creation_data = JSON.parse("[#{package_creation_data}]")
|
@@ -255,18 +271,20 @@ module Aspera
|
|
255
271
|
end
|
256
272
|
return nagios.result
|
257
273
|
when :package
|
258
|
-
command_pkg = options.get_next_command(%i[send recv list])
|
274
|
+
command_pkg = options.get_next_command(%i[send recv list show])
|
259
275
|
case command_pkg
|
276
|
+
when :show
|
277
|
+
delivery_id = instance_identifier
|
278
|
+
return {type: :single_object, data: mailbox_filtered_entries(stop_at_id: delivery_id).find{|p|p[PACKAGE_MATCH_FIELD].eql?(delivery_id)} }
|
260
279
|
when :list
|
261
280
|
return {
|
262
|
-
type:
|
263
|
-
data:
|
264
|
-
fields:
|
265
|
-
textify: lambda {|table_data|Faspex.textify_package_list(table_data)}
|
281
|
+
type: :object_list,
|
282
|
+
data: mailbox_filtered_entries,
|
283
|
+
fields: [PACKAGE_MATCH_FIELD, 'title', 'items']
|
266
284
|
}
|
267
285
|
when :send
|
268
|
-
delivery_info = options.get_option(:delivery_info,
|
269
|
-
raise
|
286
|
+
delivery_info = options.get_option(:delivery_info, mandatory: true)
|
287
|
+
raise Cli::BadArgument, 'delivery_info must be hash, refer to doc' unless delivery_info.is_a?(Hash)
|
270
288
|
# actual parameter to faspex API
|
271
289
|
package_create_params = {'delivery' => delivery_info}
|
272
290
|
public_link_url = options.get_option(:link)
|
@@ -275,30 +293,30 @@ module Aspera
|
|
275
293
|
delivery_info['sources'] ||= [{'paths' => []}]
|
276
294
|
first_source = delivery_info['sources'].first
|
277
295
|
first_source['paths'].push(*transfer.source_list)
|
278
|
-
|
279
|
-
|
296
|
+
source_id = instance_identifier(as_option: :remote_source) do |field, value|
|
297
|
+
raise Cli::BadArgument, 'only name as selector, or give id' unless field.eql?('name')
|
280
298
|
source_list = api_v3.call({operation: 'GET', subpath: 'source_shares', headers: {'Accept' => 'application/json'}})[:data]['items']
|
281
|
-
|
282
|
-
first_source['id'] = source_id
|
299
|
+
self.class.get_source_id_by_name(value, source_list)
|
283
300
|
end
|
301
|
+
first_source['id'] = source_id.to_i unless source_id.nil?
|
284
302
|
pkg_created = api_v3.call({
|
285
303
|
operation: 'POST',
|
286
304
|
subpath: 'send',
|
287
305
|
json_params: package_create_params,
|
288
306
|
headers: {'Accept' => 'application/json'}
|
289
307
|
})[:data]
|
290
|
-
if
|
291
|
-
# no transfer spec if remote source
|
308
|
+
if first_source.key?('id')
|
309
|
+
# no transfer spec if remote source: handled by faspex
|
292
310
|
return {data: [pkg_created['links']['status']], type: :value_list, name: 'link'}
|
293
311
|
end
|
294
|
-
raise
|
312
|
+
raise Cli::BadArgument, 'expecting one session exactly' if pkg_created['xfer_sessions'].length != 1
|
295
313
|
transfer_spec = pkg_created['xfer_sessions'].first
|
296
314
|
# use source from cmd line, this one only contains destination (already in dest root)
|
297
315
|
transfer_spec.delete('paths')
|
298
316
|
else # public link
|
299
317
|
transfer_spec = send_public_link_to_ts(public_link_url, package_create_params)
|
300
318
|
end
|
301
|
-
# Log.dump('transfer_spec',transfer_spec)
|
319
|
+
# Log.log.debug{Log.dump('transfer_spec',transfer_spec)}
|
302
320
|
return Main.result_transfer(transfer.start(transfer_spec))
|
303
321
|
when :recv
|
304
322
|
link_url = options.get_option(:link)
|
@@ -308,31 +326,31 @@ module Aspera
|
|
308
326
|
skip_ids_persistency = nil
|
309
327
|
case link_url
|
310
328
|
when nil # usual case: no link
|
311
|
-
if options.get_option(:once_only,
|
329
|
+
if options.get_option(:once_only, mandatory: true)
|
312
330
|
skip_ids_persistency = PersistencyActionOnce.new(
|
313
331
|
manager: @agents[:persistency],
|
314
332
|
data: skip_ids_data,
|
315
333
|
id: IdGenerator.from_list([
|
316
334
|
'faspex_recv',
|
317
|
-
options.get_option(:url,
|
318
|
-
options.get_option(:username,
|
319
|
-
options.get_option(:box,
|
335
|
+
options.get_option(:url, mandatory: true),
|
336
|
+
options.get_option(:username, mandatory: true),
|
337
|
+
options.get_option(:box, mandatory: true).to_s
|
320
338
|
]))
|
321
339
|
end
|
322
340
|
# get command line parameters
|
323
341
|
delivery_id = instance_identifier
|
324
342
|
raise 'empty id' if delivery_id.empty?
|
325
343
|
recipient = options.get_option(:recipient)
|
326
|
-
if
|
344
|
+
if ExtendedValue::ALL.eql?(delivery_id)
|
327
345
|
pkg_id_uri = mailbox_filtered_entries.map{|i|{id: i[PACKAGE_MATCH_FIELD], uri: self.class.get_fasp_uri_from_entry(i, raise_no_link: false)}}
|
328
346
|
elsif !recipient.nil? && recipient.start_with?('*')
|
329
347
|
found_package_link = mailbox_filtered_entries(stop_at_id: delivery_id).find{|p|p[PACKAGE_MATCH_FIELD].eql?(delivery_id)}['link'].first['href']
|
330
|
-
raise
|
348
|
+
raise "Not Found. Dropbox and Workgroup packages can use the link option with #{Fasp::Uri::SCHEME}" if found_package_link.nil?
|
331
349
|
pkg_id_uri = [{id: delivery_id, uri: found_package_link}]
|
332
350
|
else
|
333
351
|
# TODO: delivery id is the right one if package was receive by workgroup
|
334
352
|
endpoint =
|
335
|
-
case options.get_option(:box,
|
353
|
+
case options.get_option(:box, mandatory: true)
|
336
354
|
when :inbox, :archive then'received'
|
337
355
|
when :sent then 'sent'
|
338
356
|
end
|
@@ -340,12 +358,12 @@ module Aspera
|
|
340
358
|
package_entry = XmlSimple.xml_in(entry_xml, {'ForceArray' => true})
|
341
359
|
pkg_id_uri = [{id: delivery_id, uri: self.class.get_fasp_uri_from_entry(package_entry)}]
|
342
360
|
end
|
343
|
-
when
|
361
|
+
when /^#{Fasp::Uri::SCHEME}:/o
|
344
362
|
pkg_id_uri = [{id: 'package', uri: link_url}]
|
345
363
|
else
|
346
364
|
link_data = self.class.get_link_data(link_url)
|
347
365
|
if !link_data[:subpath].start_with?(PUB_LINK_EXTERNAL_MATCH)
|
348
|
-
raise
|
366
|
+
raise Cli::BadArgument, "Pub link is #{link_data[:subpath]}. Expecting #{PUB_LINK_EXTERNAL_MATCH}"
|
349
367
|
end
|
350
368
|
# NOTE: unauthenticated API (authorization is in url params)
|
351
369
|
api_public_link = Rest.new({base_url: link_data[:base_url]})
|
@@ -356,10 +374,10 @@ module Aspera
|
|
356
374
|
headers: {'Accept' => 'application/xml'})
|
357
375
|
if !package_creation_data[:http].body.start_with?('<?xml ')
|
358
376
|
OpenApplication.instance.uri(link_url)
|
359
|
-
raise
|
377
|
+
raise Cli::Error, 'Unexpected response: package not found ?'
|
360
378
|
end
|
361
379
|
package_entry = XmlSimple.xml_in(package_creation_data[:http].body, {'ForceArray' => false})
|
362
|
-
Log.dump(:package_entry, package_entry)
|
380
|
+
Log.log.debug{Log.dump(:package_entry, package_entry)}
|
363
381
|
transfer_uri = self.class.get_fasp_uri_from_entry(package_entry)
|
364
382
|
pkg_id_uri = [{id: package_entry['id'], uri: transfer_uri}]
|
365
383
|
end # public link
|
@@ -367,7 +385,7 @@ module Aspera
|
|
367
385
|
# TODO : remove ids from skip not present in inbox to avoid growing too big
|
368
386
|
# skip_ids_data.select!{|id|pkg_id_uri.select{|p|p[:id].eql?(id)}}
|
369
387
|
pkg_id_uri.reject!{|i|skip_ids_data.include?(i[:id])}
|
370
|
-
Log.dump(:pkg_id_uri, pkg_id_uri)
|
388
|
+
Log.log.debug{Log.dump(:pkg_id_uri, pkg_id_uri)}
|
371
389
|
return Main.result_status('no new package') if pkg_id_uri.empty?
|
372
390
|
result_transfer = []
|
373
391
|
pkg_id_uri.each do |id_uri|
|
@@ -376,7 +394,7 @@ module Aspera
|
|
376
394
|
statuses = [:success]
|
377
395
|
else
|
378
396
|
transfer_spec = Fasp::Uri.new(id_uri[:uri]).transfer_spec
|
379
|
-
# NOTE: only external users have token in
|
397
|
+
# NOTE: only external users have token in Fasp::Uri::SCHEME link !
|
380
398
|
if !transfer_spec.key?('token')
|
381
399
|
sanitized = id_uri[:uri].gsub('&', '&')
|
382
400
|
xml_payload =
|
@@ -398,41 +416,37 @@ module Aspera
|
|
398
416
|
return Main.result_transfer_multiple(result_transfer)
|
399
417
|
end
|
400
418
|
when :source
|
401
|
-
command_source = options.get_next_command(%i[list
|
419
|
+
command_source = options.get_next_command(%i[list info node])
|
402
420
|
source_list = api_v3.call({operation: 'GET', subpath: 'source_shares', headers: {'Accept' => 'application/json'}})[:data]['items']
|
403
421
|
case command_source
|
404
422
|
when :list
|
405
423
|
return {type: :object_list, data: source_list}
|
406
|
-
else # :
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
source_name = source_ids.first['name']
|
414
|
-
# source_id=source_ids.first['id']
|
415
|
-
source_hash = options.get_option(:storage, is_type: :mandatory)
|
424
|
+
else # :info :node
|
425
|
+
source_id = instance_identifier do |field, value|
|
426
|
+
raise Cli::BadArgument, 'only name as selector, or give id' unless field.eql?('name')
|
427
|
+
self.class.get_source_id_by_name(value, source_list)
|
428
|
+
end.to_i
|
429
|
+
source_name = source_list.find{|i|i['id'].eql?(source_id)}['name']
|
430
|
+
source_hash = options.get_option(:storage, mandatory: true)
|
416
431
|
# check value of option
|
417
|
-
raise
|
432
|
+
raise Cli::Error, 'storage option must be a Hash' unless source_hash.is_a?(Hash)
|
418
433
|
source_hash.each do |name, storage|
|
419
|
-
raise
|
434
|
+
raise Cli::Error, "storage '#{name}' must be a Hash" unless storage.is_a?(Hash)
|
420
435
|
[KEY_NODE, KEY_PATH].each do |key|
|
421
|
-
raise
|
436
|
+
raise Cli::Error, "storage '#{name}' must have a '#{key}'" unless storage.key?(key)
|
422
437
|
end
|
423
438
|
end
|
424
439
|
if !source_hash.key?(source_name)
|
425
|
-
raise
|
440
|
+
raise Cli::Error, "No such storage in config file: \"#{source_name}\" in [#{source_hash.keys.join(', ')}]"
|
426
441
|
end
|
427
442
|
source_info = source_hash[source_name]
|
428
|
-
Log.log.debug{
|
429
|
-
|
430
|
-
case command_node
|
443
|
+
Log.log.debug{Log.dump(:source_info, source_info)}
|
444
|
+
case command_source
|
431
445
|
when :info
|
432
446
|
return {data: source_info, type: :single_object}
|
433
447
|
when :node
|
434
448
|
node_config = ExtendedValue.instance.evaluate(source_info[KEY_NODE])
|
435
|
-
raise
|
449
|
+
raise Cli::Error, "bad type for: \"#{source_info[KEY_NODE]}\"" unless node_config.is_a?(Hash)
|
436
450
|
Log.log.debug{"node=#{node_config}"}
|
437
451
|
api_node = Rest.new({
|
438
452
|
base_url: node_config['url'],
|
@@ -441,7 +455,7 @@ module Aspera
|
|
441
455
|
username: node_config['username'],
|
442
456
|
password: node_config['password']}})
|
443
457
|
command = options.get_next_command(Node::COMMANDS_FASPEX)
|
444
|
-
return Node.new(@agents
|
458
|
+
return Node.new(@agents, api: api_node).execute_action(command, source_info[KEY_PATH])
|
445
459
|
end
|
446
460
|
end
|
447
461
|
when :me
|