aspera-cli 4.0.0.pre1
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 +7 -0
- data/README.md +3592 -0
- data/bin/ascli +7 -0
- data/bin/asession +89 -0
- data/docs/Makefile +59 -0
- data/docs/README.erb.md +3012 -0
- data/docs/README.md +13 -0
- data/docs/diagrams.txt +49 -0
- data/docs/secrets.make +38 -0
- data/docs/test_env.conf +117 -0
- data/docs/transfer_spec.html +99 -0
- data/examples/aoc.rb +17 -0
- data/examples/proxy.pac +60 -0
- data/examples/transfer.rb +115 -0
- data/lib/aspera/api_detector.rb +60 -0
- data/lib/aspera/ascmd.rb +151 -0
- data/lib/aspera/ats_api.rb +43 -0
- data/lib/aspera/cli/basic_auth_plugin.rb +38 -0
- data/lib/aspera/cli/extended_value.rb +88 -0
- data/lib/aspera/cli/formater.rb +238 -0
- data/lib/aspera/cli/listener/line_dump.rb +17 -0
- data/lib/aspera/cli/listener/logger.rb +20 -0
- data/lib/aspera/cli/listener/progress.rb +52 -0
- data/lib/aspera/cli/listener/progress_multi.rb +91 -0
- data/lib/aspera/cli/main.rb +304 -0
- data/lib/aspera/cli/manager.rb +440 -0
- data/lib/aspera/cli/plugin.rb +90 -0
- data/lib/aspera/cli/plugins/alee.rb +24 -0
- data/lib/aspera/cli/plugins/ats.rb +231 -0
- data/lib/aspera/cli/plugins/bss.rb +71 -0
- data/lib/aspera/cli/plugins/config.rb +806 -0
- data/lib/aspera/cli/plugins/console.rb +62 -0
- data/lib/aspera/cli/plugins/cos.rb +106 -0
- data/lib/aspera/cli/plugins/faspex.rb +377 -0
- data/lib/aspera/cli/plugins/faspex5.rb +93 -0
- data/lib/aspera/cli/plugins/node.rb +438 -0
- data/lib/aspera/cli/plugins/oncloud.rb +937 -0
- data/lib/aspera/cli/plugins/orchestrator.rb +169 -0
- data/lib/aspera/cli/plugins/preview.rb +464 -0
- data/lib/aspera/cli/plugins/server.rb +216 -0
- data/lib/aspera/cli/plugins/shares.rb +63 -0
- data/lib/aspera/cli/plugins/shares2.rb +114 -0
- data/lib/aspera/cli/plugins/sync.rb +65 -0
- data/lib/aspera/cli/plugins/xnode.rb +115 -0
- data/lib/aspera/cli/transfer_agent.rb +251 -0
- data/lib/aspera/cli/version.rb +5 -0
- data/lib/aspera/colors.rb +39 -0
- data/lib/aspera/command_line_builder.rb +137 -0
- data/lib/aspera/fasp/aoc.rb +24 -0
- data/lib/aspera/fasp/connect.rb +99 -0
- data/lib/aspera/fasp/error.rb +21 -0
- data/lib/aspera/fasp/error_info.rb +60 -0
- data/lib/aspera/fasp/http_gw.rb +81 -0
- data/lib/aspera/fasp/installation.rb +240 -0
- data/lib/aspera/fasp/listener.rb +11 -0
- data/lib/aspera/fasp/local.rb +377 -0
- data/lib/aspera/fasp/manager.rb +69 -0
- data/lib/aspera/fasp/node.rb +88 -0
- data/lib/aspera/fasp/parameters.rb +235 -0
- data/lib/aspera/fasp/resume_policy.rb +76 -0
- data/lib/aspera/fasp/uri.rb +51 -0
- data/lib/aspera/faspex_gw.rb +196 -0
- data/lib/aspera/hash_ext.rb +28 -0
- data/lib/aspera/log.rb +80 -0
- data/lib/aspera/nagios.rb +71 -0
- data/lib/aspera/node.rb +14 -0
- data/lib/aspera/oauth.rb +319 -0
- data/lib/aspera/on_cloud.rb +421 -0
- data/lib/aspera/open_application.rb +72 -0
- data/lib/aspera/persistency_action_once.rb +42 -0
- data/lib/aspera/persistency_folder.rb +91 -0
- data/lib/aspera/preview/file_types.rb +300 -0
- data/lib/aspera/preview/generator.rb +258 -0
- data/lib/aspera/preview/image_error.png +0 -0
- data/lib/aspera/preview/options.rb +35 -0
- data/lib/aspera/preview/utils.rb +131 -0
- data/lib/aspera/preview/video_error.png +0 -0
- data/lib/aspera/proxy_auto_config.erb.js +287 -0
- data/lib/aspera/proxy_auto_config.rb +34 -0
- data/lib/aspera/rest.rb +296 -0
- data/lib/aspera/rest_call_error.rb +13 -0
- data/lib/aspera/rest_error_analyzer.rb +98 -0
- data/lib/aspera/rest_errors_aspera.rb +58 -0
- data/lib/aspera/ssh.rb +53 -0
- data/lib/aspera/sync.rb +82 -0
- data/lib/aspera/temp_file_manager.rb +37 -0
- data/lib/aspera/uri_reader.rb +25 -0
- metadata +288 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'aspera/cli/basic_auth_plugin'
|
2
|
+
require 'aspera/persistency_action_once'
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Aspera
|
6
|
+
module Cli
|
7
|
+
module Plugins
|
8
|
+
# experiments
|
9
|
+
class Xnode < BasicAuthPlugin
|
10
|
+
def initialize(env)
|
11
|
+
super(env)
|
12
|
+
self.options.add_opt_simple(:filter_transfer,"Ruby expression for filter at transfer level (cleanup)")
|
13
|
+
self.options.add_opt_simple(:filter_file,"Ruby expression for filter at file level (cleanup)")
|
14
|
+
self.options.parse_options!
|
15
|
+
end
|
16
|
+
# "transfer_filter"=>"t['status'].eql?('completed') and t['start_spec']['remote_user'].eql?('faspex')", :file_filter=>"f['status'].eql?('completed') and 0 != f['size'] and t['start_spec']['direction'].eql?('send')"
|
17
|
+
|
18
|
+
ACTIONS=[ :postprocess, :cleanup, :forward ]
|
19
|
+
|
20
|
+
# retrieve tranfer list using API and persistency file
|
21
|
+
def self.get_transfers_iteration(api_node,params)
|
22
|
+
# array with one element max
|
23
|
+
iteration_data=[]
|
24
|
+
iteration_persistency=nil
|
25
|
+
if self.options.get_option(:once_only,:mandatory)
|
26
|
+
iteration_persistency=PersistencyActionOnce.new(
|
27
|
+
manager: @agents[:persistency],
|
28
|
+
data: iteration_data,
|
29
|
+
ids: ['xnode',self.options.get_option(:url,:mandatory),self.options.get_option(:username,:mandatory)])
|
30
|
+
end
|
31
|
+
iteration_data[0]=process_file_events(iteration_data[0])
|
32
|
+
# first time run ? or subsequent run ?
|
33
|
+
params[:iteration_token]=iteration_data[0] unless iteration_data[0].nil?
|
34
|
+
resp=api_node.read('ops/transfers',params)
|
35
|
+
transfers=resp[:data]
|
36
|
+
if transfers.is_a?(Array) then
|
37
|
+
# 3.7.2, released API
|
38
|
+
iteration_data[0]=URI.decode_www_form(URI.parse(resp[:http]['Link'].match(/<([^>]+)>/)[1]).query).to_h['iteration_token']
|
39
|
+
else
|
40
|
+
# 3.5.2, deprecated API
|
41
|
+
iteration_data[0]=transfers['iteration_token']
|
42
|
+
transfers=transfers['transfers']
|
43
|
+
end
|
44
|
+
iteration_persistency.save unless iteration_persistency.nil?
|
45
|
+
return transfers
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute_action
|
49
|
+
api_node=Rest.new({
|
50
|
+
:base_url => self.options.get_option(:url,:mandatory),
|
51
|
+
:auth => {
|
52
|
+
:type => :basic,
|
53
|
+
:username => self.options.get_option(:username,:mandatory),
|
54
|
+
:password => self.options.get_option(:password,:mandatory)
|
55
|
+
}})
|
56
|
+
command=self.options.get_next_command(ACTIONS)
|
57
|
+
case command
|
58
|
+
when :cleanup
|
59
|
+
transfers=self.class.get_transfers_iteration(api_node,{:active_only=>false})
|
60
|
+
filter_transfer=self.options.get_option(:filter_transfer,:mandatory)
|
61
|
+
filter_file=self.options.get_option(:filter_file,:mandatory)
|
62
|
+
Log.log.debug("filter_transfer: #{filter_transfer}")
|
63
|
+
Log.log.debug("filter_file: #{filter_file}")
|
64
|
+
# build list of files to delete: non zero files, downloads, for specified user
|
65
|
+
paths_to_delete=[]
|
66
|
+
transfers.each do |t|
|
67
|
+
if eval(filter_transfer)
|
68
|
+
t['files'].each do |f|
|
69
|
+
if eval(filter_file)
|
70
|
+
if !paths_to_delete.include?(f['path'])
|
71
|
+
paths_to_delete.push(f['path'])
|
72
|
+
Log.log.info("to delete: #{f['path']}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# delete files, if any
|
79
|
+
if paths_to_delete.length != 0
|
80
|
+
Log.log.info("deletion")
|
81
|
+
return self.delete_files(api_node,paths_to_delete,nil)
|
82
|
+
else
|
83
|
+
Log.log.info("nothing to delete")
|
84
|
+
end
|
85
|
+
return Main.result_nothing
|
86
|
+
when :forward
|
87
|
+
# detect transfer sessions since last call
|
88
|
+
transfers=self.class.get_transfers_iteration(api_node,{:active_only=>false})
|
89
|
+
# build list of all files received in all sessions
|
90
|
+
filelist=[]
|
91
|
+
transfers.select { |t| t['status'].eql?('completed') and t['start_spec']['direction'].eql?('receive') }.each do |t|
|
92
|
+
t['files'].each { |f| filelist.push(f['path']) }
|
93
|
+
end
|
94
|
+
if filelist.empty?
|
95
|
+
Log.log.debug("NO TRANSFER".red)
|
96
|
+
return Main.result_nothing
|
97
|
+
end
|
98
|
+
Log.log.debug("file list=#{filelist}")
|
99
|
+
# get download transfer spec on destination node
|
100
|
+
transfer_params={ :transfer_requests => [ { :transfer_request => { :paths => filelist.map {|i| {:source=>i} } } } ] }
|
101
|
+
send_result=api_node.call({:operation=>'POST',:subpath=>'files/download_setup',:json_params=>transfer_params})
|
102
|
+
# only one request, so only one answer
|
103
|
+
transfer_spec=send_result[:data]['transfer_specs'].first['transfer_spec']
|
104
|
+
# execute transfer
|
105
|
+
return Main.result_transfer(self.transfer.start(transfer_spec,{:src=>:node_gen3}))
|
106
|
+
when :postprocess
|
107
|
+
transfers=self.class.get_transfers_iteration(api_node,{:view=>'summary',:direction=>'receive',:active_only=>false})
|
108
|
+
return { :type=>:object_list,:data => transfers }
|
109
|
+
end # case command
|
110
|
+
raise "ERROR: shall not reach this line"
|
111
|
+
end # execute_action
|
112
|
+
end # Main
|
113
|
+
end # Plugin
|
114
|
+
end # Cli
|
115
|
+
end # Aspera
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'aspera/fasp/local'
|
2
|
+
require 'aspera/fasp/connect'
|
3
|
+
require 'aspera/fasp/node'
|
4
|
+
require 'aspera/fasp/aoc'
|
5
|
+
require 'aspera/fasp/http_gw'
|
6
|
+
require 'aspera/cli/listener/logger'
|
7
|
+
require 'aspera/cli/listener/progress_multi'
|
8
|
+
|
9
|
+
module Aspera
|
10
|
+
module Cli
|
11
|
+
# The Transfer agent is a common interface to start a transfer using
|
12
|
+
# one of the supported transfer agents
|
13
|
+
# provides CLI options to select one of the transfer agents (fasp client)
|
14
|
+
class TransferAgent
|
15
|
+
# special value for --sources : read file list from arguments
|
16
|
+
FILE_LIST_FROM_ARGS='@args'
|
17
|
+
# special value for --sources : read file list from transfer spec (--ts)
|
18
|
+
FILE_LIST_FROM_TRANSFER_SPEC='@ts'
|
19
|
+
private_constant :FILE_LIST_FROM_ARGS,:FILE_LIST_FROM_TRANSFER_SPEC
|
20
|
+
# @param cli_objects external objects: option manager, config file manager
|
21
|
+
def initialize(cli_objects)
|
22
|
+
@opt_mgr=cli_objects[:options]
|
23
|
+
@config=cli_objects[:config]
|
24
|
+
# transfer spec overrides provided on command line
|
25
|
+
@transfer_spec_cmdline={}
|
26
|
+
# the currently selected transfer agent
|
27
|
+
@agent=nil
|
28
|
+
@progress_listener=Listener::ProgressMulti.new
|
29
|
+
# source/destination pair, like "paths" of transfer spec
|
30
|
+
@transfer_paths=nil
|
31
|
+
@opt_mgr.set_obj_attr(:ts,self,:option_transfer_spec)
|
32
|
+
@opt_mgr.add_opt_simple(:ts,"override transfer spec values (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:ts,:optional)}")
|
33
|
+
@opt_mgr.add_opt_simple(:local_resume,"set resume policy (Hash, use @json: prefix), current=#{@opt_mgr.get_option(:local_resume,:optional)}")
|
34
|
+
@opt_mgr.add_opt_simple(:to_folder,"destination folder for downloaded files")
|
35
|
+
@opt_mgr.add_opt_simple(:sources,"list of source files (see doc)")
|
36
|
+
@opt_mgr.add_opt_simple(:transfer_info,"additional information for transfer client")
|
37
|
+
@opt_mgr.add_opt_list(:src_type,[:list,:pair],"type of file list")
|
38
|
+
@opt_mgr.add_opt_list(:transfer,[:direct,:httpgw,:connect,:node,:aoc],"type of transfer")
|
39
|
+
@opt_mgr.add_opt_list(:progress,[:none,:native,:multi],"type of progress bar")
|
40
|
+
@opt_mgr.set_option(:transfer,:direct)
|
41
|
+
@opt_mgr.set_option(:src_type,:list)
|
42
|
+
@opt_mgr.set_option(:progress,:native) # use native ascp progress bar as it is more reliable
|
43
|
+
@opt_mgr.parse_options!
|
44
|
+
end
|
45
|
+
|
46
|
+
def option_transfer_spec; @transfer_spec_cmdline; end
|
47
|
+
|
48
|
+
def option_transfer_spec=(value); @transfer_spec_cmdline.merge!(value); end
|
49
|
+
|
50
|
+
def option_transfer_spec_deep_merge(ts); @transfer_spec_cmdline.deep_merge!(ts); end
|
51
|
+
|
52
|
+
def set_agent_instance(instance)
|
53
|
+
@agent=instance
|
54
|
+
@agent.add_listener(Listener::Logger.new)
|
55
|
+
# use local progress bar if asked so, or if native and non local ascp (because only local ascp has native progress bar)
|
56
|
+
if @opt_mgr.get_option(:progress,:mandatory).eql?(:multi) or
|
57
|
+
(@opt_mgr.get_option(:progress,:mandatory).eql?(:native) and !@opt_mgr.get_option(:transfer,:mandatory).eql?(:direct))
|
58
|
+
@agent.add_listener(@progress_listener)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# analyze options and create new agent if not already created or set
|
63
|
+
def set_agent_by_options
|
64
|
+
return nil unless @agent.nil?
|
65
|
+
agent_type=@opt_mgr.get_option(:transfer,:mandatory)
|
66
|
+
case agent_type
|
67
|
+
when :direct
|
68
|
+
agent_options=@opt_mgr.get_option(:transfer_info,:optional)
|
69
|
+
agent_options=agent_options.symbolize_keys if agent_options.is_a?(Hash)
|
70
|
+
new_agent=Fasp::Local.new(agent_options)
|
71
|
+
new_agent.quiet=false if @opt_mgr.get_option(:progress,:mandatory).eql?(:native)
|
72
|
+
when :httpgw
|
73
|
+
httpgw_config=@opt_mgr.get_option(:transfer_info,:mandatory)
|
74
|
+
new_agent=Fasp::HttpGW.new(httpgw_config)
|
75
|
+
when :connect
|
76
|
+
new_agent=Fasp::Connect.new
|
77
|
+
when :node
|
78
|
+
# way for code to setup alternate node api in avance
|
79
|
+
# support: @preset:<name>
|
80
|
+
# support extended values
|
81
|
+
node_config=@opt_mgr.get_option(:transfer_info,:optional)
|
82
|
+
# if not specified: use default node
|
83
|
+
if node_config.nil?
|
84
|
+
param_set_name=@config.get_plugin_default_config_name(:node)
|
85
|
+
raise CliBadArgument,"No default node configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
|
86
|
+
node_config=@config.preset_by_name(param_set_name)
|
87
|
+
end
|
88
|
+
Log.log.debug("node=#{node_config}")
|
89
|
+
raise CliBadArgument,"the node configuration shall be Hash, not #{node_config.class} (#{node_config}), use either @json:<json> or @preset:<parameter set name>" if !node_config.is_a?(Hash)
|
90
|
+
# now check there are required parameters
|
91
|
+
sym_config=[:url,:username,:password].inject({}) do |h,param|
|
92
|
+
raise CliBadArgument,"missing parameter [#{param}] in node specification: #{node_config}" if !node_config.has_key?(param.to_s)
|
93
|
+
h[param]=node_config[param.to_s]
|
94
|
+
h
|
95
|
+
end
|
96
|
+
node_api=Rest.new({
|
97
|
+
:base_url => sym_config[:url],
|
98
|
+
:auth => {
|
99
|
+
:type =>:basic,
|
100
|
+
:username => sym_config[:username],
|
101
|
+
:password => sym_config[:password]
|
102
|
+
}})
|
103
|
+
new_agent=Fasp::Node.new(node_api)
|
104
|
+
when :aoc
|
105
|
+
aoc_config=@opt_mgr.get_option(:transfer_info,:optional)
|
106
|
+
if aoc_config.nil?
|
107
|
+
param_set_name=@config.get_plugin_default_config_name(:aspera)
|
108
|
+
raise CliBadArgument,"No default AoC configured, Please specify --#{:transfer_info.to_s.gsub('_','-')}" if param_set_name.nil?
|
109
|
+
aoc_config=@config.preset_by_name(param_set_name)
|
110
|
+
end
|
111
|
+
Log.log.debug("aoc=#{aoc_config}")
|
112
|
+
raise CliBadArgument,"the aoc configuration shall be Hash, not #{aoc_config.class} (#{aoc_config}), refer to manual" if !aoc_config.is_a?(Hash)
|
113
|
+
# convert keys from string (config) to symbol (agent)
|
114
|
+
aoc_config=aoc_config.symbolize_keys
|
115
|
+
# convert auth value from string (config) to symbol (agent)
|
116
|
+
aoc_config[:auth]=aoc_config[:auth].to_sym if aoc_config[:auth].is_a?(String)
|
117
|
+
# private key could be @file:... in config
|
118
|
+
aoc_config[:private_key]=ExtendedValue.instance.evaluate(aoc_config[:private_key])
|
119
|
+
new_agent=Fasp::Aoc.new(aoc_config)
|
120
|
+
else
|
121
|
+
raise "INTERNAL ERROR"
|
122
|
+
end
|
123
|
+
set_agent_instance(new_agent)
|
124
|
+
return nil
|
125
|
+
end
|
126
|
+
|
127
|
+
# return destination folder for transfers
|
128
|
+
# sets default if needed
|
129
|
+
# param: 'send' or 'receive'
|
130
|
+
def destination_folder(direction)
|
131
|
+
dest_folder=@opt_mgr.get_option(:to_folder,:optional)
|
132
|
+
return dest_folder unless dest_folder.nil?
|
133
|
+
dest_folder=@transfer_spec_cmdline['destination_root']
|
134
|
+
return dest_folder unless dest_folder.nil?
|
135
|
+
# default: / on remote, . on local
|
136
|
+
case direction.to_s
|
137
|
+
when 'send';dest_folder='/'
|
138
|
+
when 'receive';dest_folder='.'
|
139
|
+
else raise "wrong direction: #{direction}"
|
140
|
+
end
|
141
|
+
return dest_folder
|
142
|
+
end
|
143
|
+
|
144
|
+
# This is how the list of files to be transfered is specified
|
145
|
+
# get paths suitable for transfer spec from command line
|
146
|
+
# @return {:source=>(mandatory), :destination=>(optional)}
|
147
|
+
# computation is done only once, cache is kept in @transfer_paths
|
148
|
+
def ts_source_paths
|
149
|
+
# return cache if set
|
150
|
+
return @transfer_paths unless @transfer_paths.nil?
|
151
|
+
# start with lower priority : get paths from transfer spec on command line
|
152
|
+
@transfer_paths=@transfer_spec_cmdline['paths'] if @transfer_spec_cmdline.has_key?('paths')
|
153
|
+
# is there a source list option ?
|
154
|
+
file_list=@opt_mgr.get_option(:sources,:optional)
|
155
|
+
case file_list
|
156
|
+
when nil,FILE_LIST_FROM_ARGS
|
157
|
+
Log.log.debug("getting file list as parameters")
|
158
|
+
# get remaining arguments
|
159
|
+
file_list=@opt_mgr.get_next_argument("source file list",:multiple)
|
160
|
+
raise CliBadArgument,"specify at least one file on command line or use --sources=#{FILE_LIST_FROM_TRANSFER_SPEC} to use transfer spec" if !file_list.is_a?(Array) or file_list.empty?
|
161
|
+
when FILE_LIST_FROM_TRANSFER_SPEC
|
162
|
+
Log.log.debug("assume list provided in transfer spec")
|
163
|
+
raise CliBadArgument,"transfer spec on command line must have sources" if @transfer_paths.nil?
|
164
|
+
# here we assume check of sources is made in transfer agent
|
165
|
+
return @transfer_paths
|
166
|
+
when Array
|
167
|
+
Log.log.debug("getting file list as extended value")
|
168
|
+
raise CliBadArgument,"sources must be a Array of String" if !file_list.select{|f|!f.is_a?(String)}.empty?
|
169
|
+
else
|
170
|
+
raise CliBadArgument,"sources must be a Array, not #{file_list.class}"
|
171
|
+
end
|
172
|
+
# here, file_list is an Array or String
|
173
|
+
if !@transfer_paths.nil?
|
174
|
+
Log.log.warn("--sources overrides paths from --ts")
|
175
|
+
end
|
176
|
+
case @opt_mgr.get_option(:src_type,:mandatory)
|
177
|
+
when :list
|
178
|
+
# when providing a list, just specify source
|
179
|
+
@transfer_paths=file_list.map{|i|{'source'=>i}}
|
180
|
+
when :pair
|
181
|
+
raise CliBadArgument,"whe using pair, provide even number of paths: #{file_list.length}" unless file_list.length.even?
|
182
|
+
@transfer_paths=file_list.each_slice(2).to_a.map{|s,d|{'source'=>s,'destination'=>d}}
|
183
|
+
else raise "ERROR"
|
184
|
+
end
|
185
|
+
Log.log.debug("paths=#{@transfer_paths}")
|
186
|
+
return @transfer_paths
|
187
|
+
end
|
188
|
+
|
189
|
+
# start a transfer and wait for completion, plugins shall use this method
|
190
|
+
# @param transfer_spec
|
191
|
+
# @param options specific options for the transfer_agent
|
192
|
+
# options[:src] specifies how destination_root is set (how transfer spec was generated)
|
193
|
+
# other options are carried to specific agent
|
194
|
+
def start(transfer_spec,options)
|
195
|
+
# check parameters
|
196
|
+
raise "transfer_spec must be hash" unless transfer_spec.is_a?(Hash)
|
197
|
+
raise "options must be hash" unless options.is_a?(Hash)
|
198
|
+
# process :src option
|
199
|
+
case transfer_spec['direction']
|
200
|
+
when 'receive'
|
201
|
+
# init default if required in any case
|
202
|
+
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
203
|
+
when 'send'
|
204
|
+
case options[:src]
|
205
|
+
when :direct
|
206
|
+
# init default if required
|
207
|
+
@transfer_spec_cmdline['destination_root']||=destination_folder(transfer_spec['direction'])
|
208
|
+
when :node_gen3
|
209
|
+
# in that case, destination is set in return by application (API/upload_setup)
|
210
|
+
# but to_folder was used in initial API call
|
211
|
+
@transfer_spec_cmdline.delete('destination_root')
|
212
|
+
when :node_gen4
|
213
|
+
@transfer_spec_cmdline.delete('destination_root') if @transfer_spec_cmdline.has_key?('destination_root_id')
|
214
|
+
else
|
215
|
+
raise StandardError,"InternalError: unsupported value: #{options[:src]}"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# only used here
|
220
|
+
options.delete(:src)
|
221
|
+
|
222
|
+
# update command line paths, unless destination already has one
|
223
|
+
@transfer_spec_cmdline['paths']=transfer_spec['paths'] || ts_source_paths
|
224
|
+
|
225
|
+
transfer_spec.merge!(@transfer_spec_cmdline)
|
226
|
+
# create transfer agent
|
227
|
+
self.set_agent_by_options
|
228
|
+
Log.log.debug("transfer agent is a #{@agent.class}")
|
229
|
+
@agent.start_transfer(transfer_spec,options)
|
230
|
+
result=@agent.wait_for_transfers_completion
|
231
|
+
@progress_listener.reset
|
232
|
+
Fasp::Manager.validate_status_list(result)
|
233
|
+
return result
|
234
|
+
end
|
235
|
+
|
236
|
+
# @return :success if all sessions statuses returned by "start" are success
|
237
|
+
# else return the first error exception object
|
238
|
+
def self.session_status(statuses)
|
239
|
+
error_statuses=statuses.select{|i|!i.eql?(:success)}
|
240
|
+
return :success if error_statuses.empty?
|
241
|
+
return error_statuses.first
|
242
|
+
end
|
243
|
+
|
244
|
+
# shut down if agent requires it
|
245
|
+
def shutdown
|
246
|
+
@agent.shutdown if @agent.respond_to?(:shutdown)
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# simple vt100 colors
|
2
|
+
class String
|
3
|
+
private
|
4
|
+
def self.vtcmd(code);"\e[#{code}m";end
|
5
|
+
# see https://en.wikipedia.org/wiki/ANSI_escape_code
|
6
|
+
# symbol is the method name added to String
|
7
|
+
# it adds control chars to set color (and reset at the end).
|
8
|
+
VTSTYLES = {
|
9
|
+
:bold=>1,
|
10
|
+
:italic=>3,
|
11
|
+
:underline=>4,
|
12
|
+
:blink=>5,
|
13
|
+
:reverse_color=>7,
|
14
|
+
:black=>30,
|
15
|
+
:red=>31,
|
16
|
+
:green=>32,
|
17
|
+
:brown=>33,
|
18
|
+
:blue=>34,
|
19
|
+
:magenta=>35,
|
20
|
+
:cyan=>36,
|
21
|
+
:gray=>37,
|
22
|
+
:bg_black=>40,
|
23
|
+
:bg_red=>41,
|
24
|
+
:bg_green=>42,
|
25
|
+
:bg_brown=>43,
|
26
|
+
:bg_blue=>44,
|
27
|
+
:bg_magenta=>45,
|
28
|
+
:bg_cyan=>46,
|
29
|
+
:bg_gray=>47,
|
30
|
+
}
|
31
|
+
private_constant :VTSTYLES
|
32
|
+
# defines methods to String, one per entry in VTSTYLES
|
33
|
+
VTSTYLES.each do |name,code|
|
34
|
+
begin_seq=vtcmd(code)
|
35
|
+
end_seq=vtcmd((code >= 10) ? 0 : code+20+(code.eql?(1)?1:0))
|
36
|
+
define_method(name){"#{begin_seq}#{self}#{end_seq}"}
|
37
|
+
public name
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Aspera
|
2
|
+
# helper class to build command line from a parameter list (key-value hash)
|
3
|
+
# constructor takes hash: { 'param1':'value1', ...}
|
4
|
+
# process_param is called repeatedly with all known parameters
|
5
|
+
# add_env_args is called to get resulting param list and env var (also checks that all params were used)
|
6
|
+
class CommandLineBuilder
|
7
|
+
|
8
|
+
private
|
9
|
+
# default value for command line based on option name
|
10
|
+
def switch_name(param_name,options)
|
11
|
+
return options[:option_switch] if options.has_key?(:option_switch)
|
12
|
+
return '--'+param_name.to_s.gsub('_','-')
|
13
|
+
end
|
14
|
+
|
15
|
+
def env_name(param_name,options)
|
16
|
+
return options[:variable]
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
BOOLEAN_CLASSES=[TrueClass,FalseClass]
|
22
|
+
|
23
|
+
# @param param_hash
|
24
|
+
def initialize(param_hash,params_definition)
|
25
|
+
@param_hash=param_hash # keep reference so that it can be modified by caller before calling `process_params`
|
26
|
+
@params_definition=params_definition
|
27
|
+
@result_env={}
|
28
|
+
@result_args=[]
|
29
|
+
@used_param_names=[]
|
30
|
+
end
|
31
|
+
|
32
|
+
def warn_unrecognized_params
|
33
|
+
# warn about non translated arguments
|
34
|
+
@param_hash.each_pair{|key,val|Log.log.warn("unrecognized parameter: #{key} = \"#{val}\"") if !@used_param_names.include?(key)}
|
35
|
+
end
|
36
|
+
|
37
|
+
# adds keys :env :args with resulting values after processing
|
38
|
+
# warns if some parameters were not used
|
39
|
+
def add_env_args(env,args)
|
40
|
+
Log.log.debug("ENV=#{@result_env}, ARGS=#{@result_args}")
|
41
|
+
warn_unrecognized_params
|
42
|
+
env.merge!(@result_env)
|
43
|
+
args.push(*@result_args)
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
# transform yes/no to trye/false
|
48
|
+
def self.yes_to_true(value)
|
49
|
+
case value
|
50
|
+
when 'yes'; return true
|
51
|
+
when 'no'; return false
|
52
|
+
end
|
53
|
+
raise "unsupported value: #{value}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# add options directly to ascp command line
|
57
|
+
def add_command_line_options(options)
|
58
|
+
return if options.nil?
|
59
|
+
options.each{|o|@result_args.push(o.to_s)}
|
60
|
+
end
|
61
|
+
|
62
|
+
def process_params
|
63
|
+
@params_definition.keys.each do |k|
|
64
|
+
process_param(k)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Process a parameter from transfer specification and generate command line param or env var
|
69
|
+
# @param param_name : key in transfer spec
|
70
|
+
# @param action : type of processing: ignore getvalue envvar opt_without_arg opt_with_arg defer
|
71
|
+
# @param options : options for type
|
72
|
+
def process_param(param_name,action=nil)
|
73
|
+
options=@params_definition[param_name]
|
74
|
+
action=options[:type] if action.nil?
|
75
|
+
# should not happen
|
76
|
+
raise "Internal error: ask processing of param #{param_name}" if options.nil?
|
77
|
+
# by default : not mandatory
|
78
|
+
options[:mandatory]||=false
|
79
|
+
if options.has_key?(:accepted_types)
|
80
|
+
# single type is placed in array
|
81
|
+
options[:accepted_types]=[options[:accepted_types]] unless options[:accepted_types].is_a?(Array)
|
82
|
+
else
|
83
|
+
# by default : string, unless it's without arg
|
84
|
+
options[:accepted_types]=action.eql?(:opt_without_arg) ? BOOLEAN_CLASSES : [String]
|
85
|
+
end
|
86
|
+
# check mandatory parameter (nil is valid value)
|
87
|
+
raise Fasp::Error.new("mandatory parameter: #{param_name}") if options[:mandatory] and !@param_hash.has_key?(param_name)
|
88
|
+
parameter_value=@param_hash[param_name]
|
89
|
+
parameter_value=options[:default] if parameter_value.nil? and options.has_key?(:default)
|
90
|
+
# check provided type
|
91
|
+
raise Fasp::Error.new("#{param_name} is : #{parameter_value.class} (#{parameter_value}), shall be #{options[:accepted_types]}, ") unless parameter_value.nil? or options[:accepted_types].inject(false){|m,v|m or parameter_value.is_a?(v)}
|
92
|
+
@used_param_names.push(param_name) unless action.eql?(:defer)
|
93
|
+
|
94
|
+
# process only non-nil values
|
95
|
+
return nil if parameter_value.nil?
|
96
|
+
|
97
|
+
if options.has_key?(:translate_values)
|
98
|
+
# translate using conversion table
|
99
|
+
new_value=options[:translate_values][parameter_value]
|
100
|
+
raise "unsupported value: #{parameter_value}" if new_value.nil?
|
101
|
+
parameter_value=new_value
|
102
|
+
end
|
103
|
+
raise "unsupported value: #{parameter_value}" unless options[:accepted_values].nil? or options[:accepted_values].include?(parameter_value)
|
104
|
+
if options[:encode]
|
105
|
+
newvalue=options[:encode].call(parameter_value)
|
106
|
+
raise Fasp::Error.new("unsupported #{param_name}: #{parameter_value}") if newvalue.nil?
|
107
|
+
parameter_value=newvalue
|
108
|
+
end
|
109
|
+
|
110
|
+
case action
|
111
|
+
when :ignore,:defer # ignore this parameter or process later
|
112
|
+
return
|
113
|
+
when :get_value # just get value
|
114
|
+
return parameter_value
|
115
|
+
when :envvar # set in env var
|
116
|
+
# define ascp parameter in env var from transfer spec
|
117
|
+
@result_env[env_name(param_name,options)] = parameter_value
|
118
|
+
when :opt_without_arg # if present and true : just add option without value
|
119
|
+
add_param=false
|
120
|
+
case parameter_value
|
121
|
+
when false# nothing to put on command line, no creation by default
|
122
|
+
when true; add_param=true
|
123
|
+
else raise Fasp::Error.new("unsupported #{param_name}: #{parameter_value}")
|
124
|
+
end
|
125
|
+
add_param=!add_param if options[:add_on_false]
|
126
|
+
add_command_line_options([switch_name(param_name,options)]) if add_param
|
127
|
+
when :opt_with_arg # transform into command line option with value
|
128
|
+
#parameter_value=parameter_value.to_s if parameter_value.is_a?(Integer)
|
129
|
+
parameter_value=[parameter_value] unless parameter_value.is_a?(Array)
|
130
|
+
# if transfer_spec value is an array, applies option many times
|
131
|
+
parameter_value.each{|v|add_command_line_options([switch_name(param_name,options),v])}
|
132
|
+
else
|
133
|
+
raise "Error"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|