aspera-cli 4.13.0 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/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
|